temu-labels-crawler/utils.js

297 lines
8.8 KiB
JavaScript

const crypto = require("crypto");
const { authenticator } = require("otplib");
const dotenv = require("dotenv").config({ path: __dirname + "/.env" });
/**
* get crypto config
*/
const getCryptoConfig = function () {
const ivStr = "1851aa2e5f98522bbe1b8ac1f8305d8a";
return {
key: "tD0!wR6^cH8!mO0)wJ7;bZ0&eW6;iT6>",
iv: Buffer.from(ivStr, "hex"),
algo: "aes-256-cbc",
};
};
exports.getCryptoConfig = getCryptoConfig;
/**
* encrypt string
*/
const encryptString = function (str, algo, key, iv) {
// create cipher
const cipher = crypto.createCipheriv(algo, Buffer.from(key), iv);
// encrypt data
let encryptedStr = cipher.update(str);
encryptedStr = Buffer.concat([encryptedStr, cipher.final()]);
return encryptedStr.toString("hex");
};
exports.encryptString = encryptString;
/**
* decrypt string
*/
const decryptString = function (encryptedStr, algo, key, iv) {
// create decipher
const decipher = crypto.createDecipheriv(algo, Buffer.from(key), iv);
// decrypt string
let decryptedStr = decipher.update(Buffer.from(encryptedStr, "hex"));
decryptedStr = Buffer.concat([decryptedStr, decipher.final()]);
return decryptedStr.toString();
};
exports.decryptString = decryptString;
/**
* get browser config
*/
const getBrowserConfig = function (profilePath, env) {
let browserConfig = {};
browserConfig["ignoreDefaultArgs"] = ["--disable-extensions"];
browserConfig["headless"] = true;
// prod specific
if (env === "prod") {
browserConfig["args"] = [
"--disable-gpu",
"--disable-setuid-sandbox",
"--no-sandbox",
"--no-zygote",
`--user-data-dir=${profilePath}`,
];
browserConfig["executablePath"] = "/usr/bin/chromium-browser";
}
// dev specific
if (env === "dev") {
browserConfig["headless"] = false;
browserConfig["args"] = [`--user-data-dir=${profilePath}`];
}
return browserConfig;
};
exports.getBrowserConfig = getBrowserConfig;
/**
* try temu login
*/
const tryTemuLogin = async function (page, username, password) {
try {
// if login required
if( ! page.url().includes('seller.temu.com/login.html') ){
console.log( 'No Login Required' );
return;
}
// click on Sign in link
const selector = "div._31ia9mDL._3n05KUVj._1fE9CAB_";
await page.waitForSelector(selector, { timeout: 5000 }).catch(() => {});
const elementExists = (await page.$(selector)) !== null;
if (elementExists) {
await page.click(selector);
}
// email input
const inputSelectors = "input._3s66f8p-";
await page.waitForSelector(inputSelectors, { timeout: 5000 }).catch((e) => {
console.log(e)
});
const elements = await page.$$(inputSelectors);
console.log( elements )
for (let element of elements) {
const type = await page.evaluate((el) => el.type, element);
if ( type === "password" ) {
await element.type(password);
} else {
await element.type(username);
}
}
const loginBtn = "span._1YdMCUKt"
await page.waitForSelector(loginBtn, { timeout: 5000 }).catch(() => {});
let loginBtnElement = (await page.$(loginBtn)) !== null;
if (loginBtnElement) {
await page.click(loginBtn);
}
} catch (e) {
console.log(e);
}
};
exports.tryTemuLogin = tryTemuLogin;
/**
* try login
*/
const tryLogin = async function (page, username, password, finalUrl, secret) {
try {
// see if email is required to login
const requireEmail = await page.evaluate(async function () {
return document.querySelector("#ap_email") !== null;
});
if (requireEmail) {
console.log("Login: Email is required");
await page.evaluate(async function () {
document.querySelector("#ap_email").value = "";
});
await page.type("#ap_email", username);
}
// see if continue button is required
const continueBtn = await page.evaluate(async function () {
return document.querySelector("#continue") !== null;
});
if (continueBtn) {
console.log("Login: Continue button is required");
await page.click("#continue");
// wait 5s to page to load
await new Promise((resolve) => setTimeout(resolve, 5000));
}
// see if password is required to login
const requirePassword = await page.evaluate(async function () {
return document.querySelector("#ap_password") !== null;
});
if (requirePassword) {
await page.evaluate(async function () {
document.querySelector("#ap_password").value = "";
});
await page.type("#ap_password", password);
}
// check if it has remember me button
const rememberMeBtn = await page.evaluate(async function () {
return document.querySelector('[name="rememberMe"]') !== null;
});
if (rememberMeBtn) {
await page.click('[name="rememberMe"]');
}
// check if we need to click on sign-in button
const requireSubmit = await page.evaluate(async function () {
return document.querySelector("#signInSubmit") !== null;
});
if (requireSubmit) {
console.log("Login: Submit button is required");
await Promise.all([
page.click("#signInSubmit"),
page.waitForNavigation({ waitUntil: "networkidle0" }),
]);
}
// check if device option is required
const requireDeviceOption = await page.evaluate(async function () {
return document.querySelector("#auth-select-device-form") !== null;
});
if (requireDeviceOption) {
// select device option
console.log("Login: Device option is required");
await page.click('.a-radio.auth-TOTP [type="radio"]');
}
// check if send code option is required
const requireSendCode = await page.evaluate(async function () {
return document.querySelector("#auth-send-code") !== null;
});
if (requireSendCode) {
await Promise.all([
page.click("#auth-send-code"),
page.waitForNavigation({ waitUntil: "networkidle0" }),
]);
}
// check if mfa is required
const requireMfa = await page.evaluate(async function () {
return document.querySelector("#auth-mfa-otpcode") !== null;
});
if (requireMfa) {
console.log("Login: MFA is required");
// wait 30s to manually enter mfa
const toptCode = generateotpzfromHash(process.env["totp"]);
await page.type("#auth-mfa-otpcode", toptCode);
await page.click("#auth-signin-button"),
await new Promise((resolve) => setTimeout(resolve, 45 * 1000));
}
// see if we have skip phone number button
const requireSkipPhone = await page.evaluate(async function () {
return (
document.querySelector("#ap-account-fixup-phone-skip-link") !== null
);
});
if (requireSkipPhone) {
await Promise.all([
page.click("#ap-account-fixup-phone-skip-link"),
page.waitForNavigation({ waitUntil: "networkidle0" }),
]);
}
// goto final url
if (
requireEmail ||
requirePassword ||
requireSubmit ||
requireSkipPhone ||
rememberMeBtn
) {
await page.goto(finalUrl, {
waitUntil: ["domcontentloaded"],
});
}
} catch (e) {
console.log(e);
}
};
exports.tryLogin = tryLogin;
/**
* generate code
*/
const generateotpzfromHash = function (secret) {
return authenticator.generate(secret);
};
/**
* get sellercentral home url
*/
const getSellerCentralHomeUrl = function (marketplace) {
const marketplaceObj = marketplaces["marketplaces"][marketplace];
return `https://sellercentral.${marketplaceObj["url"]}/home?mons_sel_dir_mcid=${marketplaceObj["monsSelDirMcid"]}&mons_sel_mkid=${marketplaceObj["marketplaceId"]}&mons_sel_dir_paid=${marketplaceObj["monsSelDirPaid"]}&ignore_selection_changed=true`;
};
exports.getSellerCentralHomeUrl = getSellerCentralHomeUrl;
/**
* get sellercentral inventory url
*/
const getSellerCentralInventoryUrl = function (marketplace) {
const marketplaceObj = marketplaces["marketplaces"][marketplace];
return `https://sellercentral.${marketplaceObj["url"]}/myinventory/inventory?fulfilledBy=all&page=1&pageSize=25&sort=date_created_desc&status=all`;
};
exports.getSellerCentralInventoryUrl = getSellerCentralInventoryUrl;
/*
* temu image url
*/
const getLabelImageUrl = function (imageUrl) {
return `https://aimg.kwcdn.com/upload_aimg/temu/${imageUrl}`;
};
exports.getLabelImageUrl = getLabelImageUrl;
/*
* temu order page
*/
const getTemuOrderPage = function( poNumber ){
return `https://seller.temu.com/order-detail.html?parent_order_sn=${poNumber}`
}
exports.getTemuOrderPage = getTemuOrderPage;
/*
* temu orders page
*/
const getTemuOrdersPage = function( pageNumber ){
return `https://seller.temu.com/orders.html?activeTab=${pageNumber}`;
}
exports.getTemuOrdersPage = getTemuOrdersPage;
/*
* check list
*/
const isEmpty = function (arr) {
return arr.length === 0;
}
exports.isEmpty = isEmpty;