first running version

This commit is contained in:
koudo 2022-01-06 18:43:04 +01:00
commit 6418918a0b
10 changed files with 2003 additions and 0 deletions

2
.eslintignore Normal file
View File

@ -0,0 +1,2 @@
dist
node_modules

85
.eslintrc Normal file
View File

@ -0,0 +1,85 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint", "import"],
"extends": [
"prettier",
"plugin:prettier/recommended",
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"env": {
"node": true
},
"rules": {
"@typescript-eslint/no-var-requires":0,
"import/no-default-export": "error",
"@typescript-eslint/no-unused-vars": [
"error",
{ "ignoreRestSiblings": true, "args": "none" }
],
"padding-line-between-statements": [
"warn",
{
"blankLine": "always",
"prev": "const",
"next": "*"
},
{
"blankLine": "always",
"prev": "let",
"next": "*"
},
{
"blankLine": "always",
"prev": "var",
"next": "*"
},
{
"blankLine": "always",
"prev": "*",
"next": "return"
},
{
"blankLine": "always",
"prev": "*",
"next": "block-like"
},
{
"blankLine": "always",
"prev": "block-like",
"next": "*"
},
{
"blankLine": "always",
"prev": "*",
"next": "export"
},
{
"blankLine": "always",
"prev": "export",
"next": "*"
}
],
"import/order": [
"error",
{
"pathGroups": [
{
"pattern": "@app/**",
"group": "internal"
},
{
"pattern": ".**",
"group": "internal"
}
],
"pathGroupsExcludedImportTypes": ["builtin"],
"groups": ["external", "builtin", "internal"],
"newlines-between": "always"
}
]
}
}

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
dist
node_modules

1679
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

28
package.json Normal file
View File

@ -0,0 +1,28 @@
{
"name": "phantom-exo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"watch": "tsc --watch",
"auto-deploy": "phantombuster"
},
"engines": {
"node": "14.x"
},
"devDependencies": {
"@tsconfig/recommended": "^1.0.1",
"@types/node": "^17.0.8",
"@types/puppeteer": "1.6.2",
"@typescript-eslint/eslint-plugin": "^5.8.0",
"@typescript-eslint/parser": "^5.8.0",
"eslint": "^8.5.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-prettier": "^4.0.0",
"prettier": "^2.5.1",
"typescript": "^4.5.4"
},
"author": "",
"license": "ISC"
}

6
phantombuster.cson Normal file
View File

@ -0,0 +1,6 @@
[
name: 'marmiton_search'
apiKey: 'JBMqaH81N760ZXnsVU2aflZu6s1RausEdXE08WFyUWo'
scripts:
'marmiton_search.js': 'dist/marmiton_phantom.js'
]

29
src/marmitonType.d.ts vendored Normal file
View File

@ -0,0 +1,29 @@
declare type MarmitonSearchResult = {
name: string;
url: string;
score: number;
commentCount: number;
};
declare type SearchRecipeType = "season" | "recipe";
declare type SearchDishType =
| "entree"
| "platprincipal"
| "dessert"
| "amusegueule"
| "accompagnement"
| "sauce"
| "boisson"
| "confiserie"
| "conseil";
declare type SearchOptions = {
type?: SearchRecipeType | SearchRecipeType[];
dt?: SearchDishType | SearchDishType[];
page?: number;
};
declare type MarmitonSearchInput = {
query: string;
} & SearchOptions;

96
src/marmiton_phantom.ts Normal file
View File

@ -0,0 +1,96 @@
/* eslint-disable */
// Phantombuster configuration {
"phantombuster command: nodejs"
"phantombuster package: 5"
// }
/* eslint-enable */
import Buster from "phantombuster";
import puppeteer from "puppeteer";
const MARMITON_URL = "https://www.marmiton.org";
const buster = new Buster();
const optionsToUrlParams = (
opts: Record<string, unknown | unknown[]>
): string => {
return Object.keys(opts)
.map((key) => {
const value = opts[key];
if (Array.isArray(value)) {
return value.map((val) => `${key}=${val}`).join("&");
} else {
return `${key}=${value}`;
}
})
.join("&");
};
export const search = async (
search: string,
opts?: SearchOptions
): Promise<MarmitonSearchResult[]> => {
const browser = await puppeteer.launch({
args: ["--no-sandbox"],
});
const page = await browser.newPage();
const argsString = optionsToUrlParams({
...opts,
aqt: search,
});
await page.goto(`${MARMITON_URL}/recettes/recherche.aspx?${argsString}`);
const list = await page.$$eval(
"div>a[href^='/recettes/recette']",
(links: Element[], urlStart: string) =>
links.map((element) => {
const titleElem = element.querySelector("h4");
const bottomString =
titleElem?.parentElement?.lastElementChild?.textContent ?? "";
const [scorePart, commentString] = bottomString.split("(");
return {
name: titleElem?.innerText ?? "",
url: `${urlStart}${element.getAttribute("href")}` ?? "",
score: Number.parseFloat(scorePart.split("/")[0]),
commentCount: Number.parseInt(commentString.split(" ")[0]),
};
}),
MARMITON_URL
);
const results = list;
await page.close();
await browser.close();
return results;
};
const main = async () => {
const { query, ...opts } = buster.argument as MarmitonSearchInput;
let results: unknown[] = [];
try {
results = await search(query ?? "crepe", opts);
} catch (err) {
console.log("could not get result from marmiton:", err);
}
try {
await buster.setResultObject(results);
} catch (err) {
console.log("Could not set the result object:", err);
}
process.exit();
};
main();

60
src/phantombuster.d.ts vendored Normal file
View File

@ -0,0 +1,60 @@
declare module "phantombuster" {
class Buster {
public agentId: number;
public apiKey: string;
public argument: object;
public containerId: number;
public retryCount: number;
public maxRetries: number;
public proxyAddress: string;
public download(
url: string,
saveAs?: string,
headers?: { [name: string]: string }
): Promise<string>;
public save(
url: string,
saveAs?: string,
headers?: { [name: string]: string }
): Promise<string>;
public saveFolder(
localFolder?: string,
storageFolder?: string
): Promise<string>;
public saveText(
text: string,
saveAs: string,
mime?: string
): Promise<string>;
public saveBase64(
text: string,
saveAs: string,
mime?: string
): Promise<string>;
public solveCaptcha(selector: string): Promise<string>;
public solveCaptchaBase64(base64: string): Promise<string>;
public solveNoCaptcha(
url: string,
key: string,
secret?: string
): Promise<string>;
public solveCaptchaImage(
url: string,
headers?: { [name: string]: string }
): Promise<string>;
public mail(subject: string, text: string, to?: string): Promise<void>;
public pushover(message: string, options?: object): Promise<void>;
public progressHint(progress: number, label?: string): void;
public overrideTimeLimit(seconds: number): Promise<void>;
public getTimeLeft(): Promise<number>;
public setAgentObject(agentId: number, object: object): Promise<void>;
public setAgentObject(object: object): Promise<void>;
public getAgentObject(agentId?: number): Promise<unknown>;
public setGlobalObject(object: object): Promise<void>;
public getGlobalObject(): Promise<unknown>;
public setResultObject(object: object): Promise<void>;
}
export = Buster;
}

16
tsconfig.json Normal file
View File

@ -0,0 +1,16 @@
{
"compilerOptions": {
"moduleResolution": "node",
"target": "es5",
"module": "CommonJS",
"outDir": "dist",
"lib": ["dom", "es2015"],
"esModuleInterop": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"alwaysStrict": true,
},
"include": ["./src"]
}