297 lines
8.8 KiB
JavaScript
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;
|