qwenproxy-cli 1.4.2 → 1.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/clean-cache.js +242 -109
- package/dist/delete-chats.js +295 -120
- package/dist/index.js +296 -121
- package/dist/login.js +297 -121
- package/dist/reset-cooldowns.js +242 -109
- package/package.json +1 -1
- package/src/core/version.ts +1 -1
- package/src/login.ts +1 -1
- package/src/services/playwright.ts +352 -130
package/dist/login.js
CHANGED
|
@@ -3864,8 +3864,29 @@ function getStealthScript(profile) {
|
|
|
3864
3864
|
const _toBlob = HTMLCanvasElement.prototype.toBlob;
|
|
3865
3865
|
const _getImageData = CanvasRenderingContext2D.prototype.getImageData;
|
|
3866
3866
|
|
|
3867
|
+
function isCaptchaCanvas(canvas) {
|
|
3868
|
+
try {
|
|
3869
|
+
if (!canvas) return false;
|
|
3870
|
+
const id = (canvas.id || "").toLowerCase();
|
|
3871
|
+
const cls = (canvas.className || "").toLowerCase();
|
|
3872
|
+
return (
|
|
3873
|
+
id.includes("baxia") ||
|
|
3874
|
+
id.includes("nc_") ||
|
|
3875
|
+
id.includes("captcha") ||
|
|
3876
|
+
id.includes("punish") ||
|
|
3877
|
+
cls.includes("baxia") ||
|
|
3878
|
+
cls.includes("nc_") ||
|
|
3879
|
+
cls.includes("captcha") ||
|
|
3880
|
+
Boolean(canvas.closest?.("#baxia-dialog, #baxia-punish, [class*='baxia'], [class*='nc_'], .auth-layout"))
|
|
3881
|
+
);
|
|
3882
|
+
} catch(e) {
|
|
3883
|
+
return false;
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
|
|
3867
3887
|
function addNoise(canvas) {
|
|
3868
3888
|
try {
|
|
3889
|
+
if (isCaptchaCanvas(canvas)) return;
|
|
3869
3890
|
const ctx = canvas.getContext('2d');
|
|
3870
3891
|
if (!ctx) return;
|
|
3871
3892
|
const style = ctx.fillStyle;
|
|
@@ -3889,6 +3910,9 @@ function getStealthScript(profile) {
|
|
|
3889
3910
|
|
|
3890
3911
|
CanvasRenderingContext2D.prototype.getImageData = function(x, y, w, h) {
|
|
3891
3912
|
const imageData = _getImageData.apply(this, arguments);
|
|
3913
|
+
if (isCaptchaCanvas(this.canvas)) {
|
|
3914
|
+
return imageData;
|
|
3915
|
+
}
|
|
3892
3916
|
const data = imageData.data;
|
|
3893
3917
|
const maxPixels = Math.min(data.length / 4, 2500);
|
|
3894
3918
|
for (let i = 0; i < maxPixels * 4; i += 4) {
|
|
@@ -4812,8 +4836,9 @@ async function loginToQwen(accountId, email, password) {
|
|
|
4812
4836
|
}
|
|
4813
4837
|
if (attempt < maxAttempts) {
|
|
4814
4838
|
const backoffMs = attempt * 5e3;
|
|
4839
|
+
const failReason = uiResult.reason || apiResult.reason || "falha tempor\xE1ria";
|
|
4815
4840
|
console.warn(
|
|
4816
|
-
`\u26A0\uFE0F [Playwright] Login attempt ${attempt}/${maxAttempts} failed for ${maskEmail(email)} (${
|
|
4841
|
+
`\u26A0\uFE0F [Playwright] Login attempt ${attempt}/${maxAttempts} failed for ${maskEmail(email)} (${failReason}), retrying in ${backoffMs / 1e3}s`
|
|
4817
4842
|
);
|
|
4818
4843
|
await sleep2(backoffMs);
|
|
4819
4844
|
}
|
|
@@ -4843,37 +4868,52 @@ async function loginToQwen(accountId, email, password) {
|
|
|
4843
4868
|
async function loginViaApi(page, email, password) {
|
|
4844
4869
|
try {
|
|
4845
4870
|
const hashedPassword = crypto3.createHash("sha256").update(password).digest("hex");
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
email,
|
|
4854
|
-
password: hashedPassword,
|
|
4855
|
-
login_type: "email"
|
|
4856
|
-
},
|
|
4857
|
-
headers: {
|
|
4858
|
-
"content-type": "application/json",
|
|
4859
|
-
accept: "application/json, text/plain, */*",
|
|
4860
|
-
referer: qwenUrl("/auth"),
|
|
4861
|
-
origin: qwenOrigin(),
|
|
4862
|
-
source: "web"
|
|
4863
|
-
},
|
|
4864
|
-
timeout: 1e4
|
|
4865
|
-
});
|
|
4866
|
-
data = await response.json().catch(() => null);
|
|
4867
|
-
signinSuccess = Boolean(data && (data.success === true || data.token));
|
|
4868
|
-
} catch {
|
|
4869
|
-
}
|
|
4871
|
+
if (!page.url().includes("/auth")) {
|
|
4872
|
+
await page.goto(qwenUrl("/auth"), {
|
|
4873
|
+
waitUntil: "domcontentloaded",
|
|
4874
|
+
timeout: config.timeouts.navigation
|
|
4875
|
+
}).catch(() => {
|
|
4876
|
+
});
|
|
4877
|
+
await sleep2(1e3);
|
|
4870
4878
|
}
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4879
|
+
const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
|
|
4880
|
+
const requestId = crypto3.randomUUID();
|
|
4881
|
+
const evalRes = await page.evaluate(
|
|
4882
|
+
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
|
|
4883
|
+
try {
|
|
4884
|
+
const response = await fetch(signinUrl2, {
|
|
4885
|
+
method: "POST",
|
|
4886
|
+
signal: AbortSignal.timeout(15e3),
|
|
4887
|
+
credentials: "include",
|
|
4888
|
+
headers: {
|
|
4889
|
+
accept: "application/json, text/plain, */*",
|
|
4890
|
+
"content-type": "application/json",
|
|
4891
|
+
source: "web",
|
|
4892
|
+
version: "0.3.11",
|
|
4893
|
+
"x-request-origin": "https://chat.qwen.ai",
|
|
4894
|
+
timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
4895
|
+
"x-request-id": requestId2
|
|
4896
|
+
},
|
|
4897
|
+
body: JSON.stringify({ email: email2, password: password2 })
|
|
4898
|
+
});
|
|
4899
|
+
const json = await response.json().catch(() => null);
|
|
4900
|
+
return { ok: response.ok, status: response.status, data: json };
|
|
4901
|
+
} catch (e) {
|
|
4902
|
+
return { ok: false, error: e.message };
|
|
4903
|
+
}
|
|
4904
|
+
},
|
|
4905
|
+
{ email, password: hashedPassword, signinUrl, requestId }
|
|
4906
|
+
).catch(() => null);
|
|
4907
|
+
let data = evalRes?.data;
|
|
4908
|
+
let signinSuccess = Boolean(
|
|
4909
|
+
data && (data.success === true || data.token || data.data?.access_token || data.data?.token)
|
|
4910
|
+
);
|
|
4911
|
+
if (!signinSuccess && !data?.data?.code) {
|
|
4912
|
+
const fallbackUrl = qwenUrl("/api/v2/auths/signin");
|
|
4913
|
+
const fallbackRes = await page.evaluate(
|
|
4914
|
+
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
|
|
4875
4915
|
try {
|
|
4876
|
-
const response = await fetch(
|
|
4916
|
+
const response = await fetch(fallbackUrl2, {
|
|
4877
4917
|
method: "POST",
|
|
4878
4918
|
signal: AbortSignal.timeout(1e4),
|
|
4879
4919
|
credentials: "include",
|
|
@@ -4881,10 +4921,11 @@ async function loginViaApi(page, email, password) {
|
|
|
4881
4921
|
accept: "application/json, text/plain, */*",
|
|
4882
4922
|
"content-type": "application/json",
|
|
4883
4923
|
source: "web",
|
|
4924
|
+
version: "0.3.11",
|
|
4884
4925
|
timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
4885
4926
|
"x-request-id": requestId2
|
|
4886
4927
|
},
|
|
4887
|
-
body: JSON.stringify({ email: email2, password: password2
|
|
4928
|
+
body: JSON.stringify({ email: email2, password: password2 })
|
|
4888
4929
|
});
|
|
4889
4930
|
const json = await response.json().catch(() => null);
|
|
4890
4931
|
return { ok: response.ok, status: response.status, data: json };
|
|
@@ -4892,11 +4933,13 @@ async function loginViaApi(page, email, password) {
|
|
|
4892
4933
|
return { ok: false, error: e.message };
|
|
4893
4934
|
}
|
|
4894
4935
|
},
|
|
4895
|
-
{ email, password: hashedPassword,
|
|
4936
|
+
{ email, password: hashedPassword, fallbackUrl, requestId }
|
|
4896
4937
|
).catch(() => null);
|
|
4897
|
-
if (
|
|
4898
|
-
data =
|
|
4899
|
-
signinSuccess = Boolean(
|
|
4938
|
+
if (fallbackRes?.data) {
|
|
4939
|
+
data = fallbackRes.data;
|
|
4940
|
+
signinSuccess = Boolean(
|
|
4941
|
+
data && (data.success === true || data.token || data.data?.access_token || data.data?.token)
|
|
4942
|
+
);
|
|
4900
4943
|
}
|
|
4901
4944
|
}
|
|
4902
4945
|
if (data?.success === false) {
|
|
@@ -4910,7 +4953,8 @@ async function loginViaApi(page, email, password) {
|
|
|
4910
4953
|
};
|
|
4911
4954
|
}
|
|
4912
4955
|
if (signinSuccess) {
|
|
4913
|
-
const token = data?.data?.token || data?.token;
|
|
4956
|
+
const token = data?.data?.access_token || data?.data?.token || data?.access_token || data?.token;
|
|
4957
|
+
const refreshToken = data?.data?.refresh_token || data?.refresh_token;
|
|
4914
4958
|
if (token) {
|
|
4915
4959
|
try {
|
|
4916
4960
|
await page.context().addCookies([
|
|
@@ -4934,14 +4978,19 @@ async function loginViaApi(page, email, password) {
|
|
|
4934
4978
|
}).catch(() => {
|
|
4935
4979
|
});
|
|
4936
4980
|
if (token) {
|
|
4937
|
-
await page.evaluate(
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4981
|
+
await page.evaluate(
|
|
4982
|
+
({ tok, rTok }) => {
|
|
4983
|
+
try {
|
|
4984
|
+
localStorage.removeItem("qwen_token_logged_out_marker");
|
|
4985
|
+
localStorage.setItem("token", tok);
|
|
4986
|
+
localStorage.setItem("access_token", tok);
|
|
4987
|
+
if (rTok) localStorage.setItem("refresh_token", rTok);
|
|
4988
|
+
document.cookie = `token=${encodeURIComponent(tok)}; path=/; domain=.qwen.ai; max-age=31536000`;
|
|
4989
|
+
} catch {
|
|
4990
|
+
}
|
|
4991
|
+
},
|
|
4992
|
+
{ tok: token, rTok: refreshToken }
|
|
4993
|
+
).catch(() => {
|
|
4945
4994
|
});
|
|
4946
4995
|
await page.reload({ waitUntil: "domcontentloaded" }).catch(() => {
|
|
4947
4996
|
});
|
|
@@ -4969,11 +5018,23 @@ async function loginViaApi(page, email, password) {
|
|
|
4969
5018
|
}
|
|
4970
5019
|
async function loginViaUi(page, email, password) {
|
|
4971
5020
|
try {
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
5021
|
+
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3).catch(() => false)) {
|
|
5022
|
+
return { success: true };
|
|
5023
|
+
}
|
|
5024
|
+
try {
|
|
5025
|
+
await page.goto(qwenUrl("/auth"), {
|
|
5026
|
+
waitUntil: "domcontentloaded",
|
|
5027
|
+
timeout: Math.min(config.timeouts.navigation, 15e3)
|
|
5028
|
+
});
|
|
5029
|
+
await sleep2(1e3);
|
|
5030
|
+
} catch (gotoErr) {
|
|
5031
|
+
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3).catch(() => false)) {
|
|
5032
|
+
return { success: true };
|
|
5033
|
+
}
|
|
5034
|
+
}
|
|
5035
|
+
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3).catch(() => false)) {
|
|
5036
|
+
return { success: true };
|
|
5037
|
+
}
|
|
4977
5038
|
const emailSelector = [
|
|
4978
5039
|
'input[type="email"]',
|
|
4979
5040
|
'input[name="email"]',
|
|
@@ -4983,7 +5044,7 @@ async function loginViaUi(page, email, password) {
|
|
|
4983
5044
|
].join(", ");
|
|
4984
5045
|
try {
|
|
4985
5046
|
await page.waitForSelector(emailSelector, {
|
|
4986
|
-
timeout: Math.min(config.timeouts.page,
|
|
5047
|
+
timeout: Math.min(config.timeouts.page, 5e3)
|
|
4987
5048
|
});
|
|
4988
5049
|
} catch {
|
|
4989
5050
|
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
|
|
@@ -4996,30 +5057,55 @@ async function loginViaUi(page, email, password) {
|
|
|
4996
5057
|
return { success: false, reason: "Campo de e-mail n\xE3o encontrado (poss\xEDvel captcha)" };
|
|
4997
5058
|
}
|
|
4998
5059
|
await clearVisibleChallenge(page);
|
|
4999
|
-
const
|
|
5000
|
-
return page.locator('
|
|
5001
|
-
hasText: /(?:log\s*in\s*with\s*(?:a\s*)?password|fazer\s*login\s*com\s*senha|entrar\s*com\s*(?:uma\s*)?senha|iniciar\s*sesi[oó]n\s*con\s*contrase[nñ]a|密码登录)/i
|
|
5002
|
-
}).first();
|
|
5060
|
+
const isPasswordInputPresent = async () => {
|
|
5061
|
+
return await page.locator('input[type="password"], input[name="password"]').count().catch(() => 0) > 0;
|
|
5003
5062
|
};
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
|
|
5063
|
+
const revealPasswordMode = async () => {
|
|
5064
|
+
if (page.url().includes("/forget")) {
|
|
5065
|
+
await page.goto(qwenUrl("/auth"), { waitUntil: "domcontentloaded", timeout: 1e4 }).catch(() => {
|
|
5008
5066
|
});
|
|
5009
|
-
await sleep2(
|
|
5067
|
+
await sleep2(1e3);
|
|
5010
5068
|
}
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
if (await
|
|
5018
|
-
await
|
|
5069
|
+
if (await isPasswordInputPresent()) return;
|
|
5070
|
+
const btn = page.locator("button").filter({
|
|
5071
|
+
hasText: /(?:log\s*in\s*with\s*(?:a\s*)?password|fazer\s*login\s*com\s*senha|entrar\s*com\s*(?:uma\s*)?senha|iniciar\s*sesi[oó]n\s*con\s*contrase[nñ]a|密码登录)/i
|
|
5072
|
+
}).filter({
|
|
5073
|
+
hasNotText: /(?:esqueci|forgot|olvid|reset|recover)/i
|
|
5074
|
+
}).first();
|
|
5075
|
+
if (await btn.isVisible({ timeout: 1500 }).catch(() => false)) {
|
|
5076
|
+
await btn.click({ timeout: 2e3 }).catch(() => {
|
|
5019
5077
|
});
|
|
5020
5078
|
await sleep2(500);
|
|
5021
5079
|
}
|
|
5080
|
+
if (await isPasswordInputPresent()) return;
|
|
5081
|
+
await page.evaluate(() => {
|
|
5082
|
+
try {
|
|
5083
|
+
const buttons = Array.from(document.querySelectorAll("button, [role='button'], a"));
|
|
5084
|
+
for (const b of buttons) {
|
|
5085
|
+
const txt = (b.innerText || "").trim().toLowerCase();
|
|
5086
|
+
if (/(?:log\s*in\s*with\s*(?:a\s*)?password|fazer\s*login\s*com\s*senha|entrar\s*com\s*(?:uma\s*)?senha|iniciar\s*sesi[oó]n\s*con\s*contrase[nñ]a|密码登录)/i.test(txt) && !txt.includes("esqueci") && !txt.includes("forgot") && !txt.includes("olvid") && !txt.includes("reset")) {
|
|
5087
|
+
b.click();
|
|
5088
|
+
return true;
|
|
5089
|
+
}
|
|
5090
|
+
}
|
|
5091
|
+
} catch {
|
|
5092
|
+
}
|
|
5093
|
+
return false;
|
|
5094
|
+
}).catch(() => false);
|
|
5095
|
+
await sleep2(500);
|
|
5096
|
+
};
|
|
5097
|
+
await revealPasswordMode();
|
|
5098
|
+
try {
|
|
5099
|
+
await page.fill(emailSelector, email, { timeout: 8e3 });
|
|
5100
|
+
await sleep2(300);
|
|
5022
5101
|
} catch {
|
|
5102
|
+
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
|
|
5103
|
+
return { success: true };
|
|
5104
|
+
}
|
|
5105
|
+
throw new Error(`Campo de e-mail n\xE3o encontrado na tela de autentica\xE7\xE3o`);
|
|
5106
|
+
}
|
|
5107
|
+
if (!await isPasswordInputPresent()) {
|
|
5108
|
+
await revealPasswordMode();
|
|
5023
5109
|
}
|
|
5024
5110
|
const passwordSelector = 'input[type="password"], input[name="password"]';
|
|
5025
5111
|
try {
|
|
@@ -5038,61 +5124,108 @@ async function loginViaUi(page, email, password) {
|
|
|
5038
5124
|
reason: "Campo de senha n\xE3o encontrado na tela de autentica\xE7\xE3o"
|
|
5039
5125
|
};
|
|
5040
5126
|
}
|
|
5041
|
-
|
|
5042
|
-
|
|
5127
|
+
try {
|
|
5128
|
+
await page.fill(passwordSelector, password, { timeout: 8e3 });
|
|
5129
|
+
await sleep2(500);
|
|
5130
|
+
} catch {
|
|
5131
|
+
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
|
|
5132
|
+
return { success: true };
|
|
5133
|
+
}
|
|
5134
|
+
throw new Error(`Campo de senha n\xE3o encontrado na tela de autentica\xE7\xE3o`);
|
|
5135
|
+
}
|
|
5136
|
+
await page.evaluate(() => {
|
|
5137
|
+
try {
|
|
5138
|
+
const emailEl = document.querySelector('input[name="email"], input[type="email"], input[type="text"]');
|
|
5139
|
+
const passEl = document.querySelector('input[type="password"], input[name="password"]');
|
|
5140
|
+
if (emailEl) {
|
|
5141
|
+
emailEl.dispatchEvent(new Event("input", { bubbles: true }));
|
|
5142
|
+
emailEl.dispatchEvent(new Event("change", { bubbles: true }));
|
|
5143
|
+
emailEl.dispatchEvent(new Event("blur", { bubbles: true }));
|
|
5144
|
+
}
|
|
5145
|
+
if (passEl) {
|
|
5146
|
+
passEl.dispatchEvent(new Event("input", { bubbles: true }));
|
|
5147
|
+
passEl.dispatchEvent(new Event("change", { bubbles: true }));
|
|
5148
|
+
passEl.dispatchEvent(new Event("blur", { bubbles: true }));
|
|
5149
|
+
}
|
|
5150
|
+
} catch {
|
|
5151
|
+
}
|
|
5152
|
+
}).catch(() => {
|
|
5153
|
+
});
|
|
5043
5154
|
const submitSelector = 'button.qwenchat-auth-pc-submit-button, button[type="submit"], button:has-text("Log in"), button:has-text("Sign in"), button:has-text("Fazer login"), button:has-text("Entrar"), button:has-text("Iniciar sesi\xF3n"), button:has-text("\u767B\u5F55")';
|
|
5044
5155
|
const submitButton = page.locator(submitSelector).first();
|
|
5045
5156
|
try {
|
|
5046
5157
|
await page.waitForSelector('button[type="submit"]:not([disabled])', {
|
|
5047
|
-
timeout:
|
|
5158
|
+
timeout: 4e3
|
|
5048
5159
|
});
|
|
5049
5160
|
await submitButton.click();
|
|
5050
5161
|
} catch {
|
|
5051
|
-
await
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
const codeSelector = 'input[placeholder*="code" i], input[placeholder*="c\xF3digo" i], input[name*="code" i]';
|
|
5061
|
-
const codeInput = page.locator(codeSelector).first();
|
|
5062
|
-
if (await codeInput.isVisible().catch(() => false)) {
|
|
5063
|
-
return {
|
|
5064
|
-
success: false,
|
|
5065
|
-
reason: "Conta requer c\xF3digo de verifica\xE7\xE3o enviado por e-mail (2FA/OTP)"
|
|
5066
|
-
};
|
|
5162
|
+
const isDisabled = await submitButton.isDisabled().catch(() => true);
|
|
5163
|
+
if (!isDisabled) {
|
|
5164
|
+
await submitButton.click({ force: true }).catch(() => {
|
|
5165
|
+
});
|
|
5166
|
+
} else {
|
|
5167
|
+
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3)) {
|
|
5168
|
+
return { success: true };
|
|
5169
|
+
}
|
|
5170
|
+
}
|
|
5067
5171
|
}
|
|
5068
|
-
const
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5172
|
+
const postSubmitDeadline = Date.now() + 15e3;
|
|
5173
|
+
while (Date.now() < postSubmitDeadline) {
|
|
5174
|
+
if (page.isClosed()) break;
|
|
5175
|
+
if (config.captcha.enabled) {
|
|
5176
|
+
await solveBaxiaCaptcha(page, {
|
|
5177
|
+
waitForMs: 1500,
|
|
5178
|
+
maxAttempts: config.captcha.maxAttempts,
|
|
5179
|
+
retryDelayMs: config.captcha.retryDelayMs
|
|
5180
|
+
}).catch(() => {
|
|
5181
|
+
});
|
|
5182
|
+
}
|
|
5183
|
+
const errorSelector = [
|
|
5184
|
+
".qwen-chat-v2-toast-text",
|
|
5185
|
+
".qwen-chat-v2-toast-content",
|
|
5186
|
+
".ant-form-item-explain-error",
|
|
5187
|
+
".ant-message-error",
|
|
5188
|
+
".ant-message-notice",
|
|
5189
|
+
'[role="alert"]',
|
|
5190
|
+
".qwenchat-auth-error",
|
|
5191
|
+
".auth-error-message"
|
|
5192
|
+
].join(", ");
|
|
5193
|
+
const errorEl = page.locator(errorSelector).first();
|
|
5194
|
+
if (await errorEl.isVisible().catch(() => false)) {
|
|
5195
|
+
const errorText = (await errorEl.innerText().catch(() => "")).trim();
|
|
5196
|
+
if (errorText) {
|
|
5197
|
+
const classified = classifyQwenAuthError(void 0, errorText);
|
|
5198
|
+
return {
|
|
5199
|
+
success: false,
|
|
5200
|
+
permanentFailure: classified.isPermanent,
|
|
5201
|
+
reason: `Formul\xE1rio: ${classified.reason}`
|
|
5202
|
+
};
|
|
5203
|
+
}
|
|
5204
|
+
}
|
|
5205
|
+
const codeSelector = 'input[placeholder*="code" i], input[placeholder*="c\xF3digo" i], input[name*="code" i]';
|
|
5206
|
+
const codeInput = page.locator(codeSelector).first();
|
|
5207
|
+
if (await codeInput.isVisible().catch(() => false)) {
|
|
5083
5208
|
return {
|
|
5084
5209
|
success: false,
|
|
5085
|
-
|
|
5086
|
-
reason: `Formul\xE1rio: ${classified.reason}`
|
|
5210
|
+
reason: "Conta requer c\xF3digo de verifica\xE7\xE3o enviado por e-mail (2FA/OTP)"
|
|
5087
5211
|
};
|
|
5088
5212
|
}
|
|
5213
|
+
try {
|
|
5214
|
+
const liveCookies = await page.context().cookies();
|
|
5215
|
+
if (liveCookies.some((c) => c.name === "token")) {
|
|
5216
|
+
return { success: true };
|
|
5217
|
+
}
|
|
5218
|
+
} catch {
|
|
5219
|
+
}
|
|
5220
|
+
const currentUrl = page.url();
|
|
5221
|
+
if (!currentUrl.includes("/auth") && !currentUrl.includes("/login")) {
|
|
5222
|
+
if (await isPageLoggedIn(page, 3e3)) {
|
|
5223
|
+
return { success: true };
|
|
5224
|
+
}
|
|
5225
|
+
}
|
|
5226
|
+
await sleep2(1e3);
|
|
5089
5227
|
}
|
|
5090
|
-
|
|
5091
|
-
if (isLoggedIn) {
|
|
5092
|
-
await page.goto(qwenUrl("/"), {
|
|
5093
|
-
waitUntil: "domcontentloaded",
|
|
5094
|
-
timeout: config.timeouts.navigation
|
|
5095
|
-
});
|
|
5228
|
+
if (await isPageLoggedIn(page, 3e3)) {
|
|
5096
5229
|
return { success: true };
|
|
5097
5230
|
}
|
|
5098
5231
|
return {
|
|
@@ -5620,6 +5753,17 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
|
|
|
5620
5753
|
}
|
|
5621
5754
|
};
|
|
5622
5755
|
if (forceReauth) {
|
|
5756
|
+
const alreadyIn = await isPageLoggedIn(page, 2e3).catch(() => false);
|
|
5757
|
+
if (alreadyIn) {
|
|
5758
|
+
const liveCookies = await page.context().cookies();
|
|
5759
|
+
if (liveCookies.some((c) => c.name === "token")) {
|
|
5760
|
+
const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
|
|
5761
|
+
cache.headers.cookie = cookieStr;
|
|
5762
|
+
cache.lastRefresh = Date.now();
|
|
5763
|
+
markAccountHeadersReady(accountId);
|
|
5764
|
+
return;
|
|
5765
|
+
}
|
|
5766
|
+
}
|
|
5623
5767
|
await executeReauth();
|
|
5624
5768
|
} else {
|
|
5625
5769
|
try {
|
|
@@ -6054,19 +6198,50 @@ async function keepAlivePlaywrightAccount(accountId) {
|
|
|
6054
6198
|
const now = Date.now();
|
|
6055
6199
|
const currentUrl = page.url();
|
|
6056
6200
|
const cookie = await getCookies(accountId);
|
|
6057
|
-
if (cookie && isTokenExpiringSoon(cookie,
|
|
6058
|
-
console.log(
|
|
6059
|
-
`\u{1F493} [SessionKeeper] Account ${accountId} token expires within 45m; proactively renewing session...`
|
|
6060
|
-
);
|
|
6201
|
+
if (cookie && isTokenExpiringSoon(cookie, 2)) {
|
|
6061
6202
|
try {
|
|
6062
|
-
await
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6203
|
+
await page.evaluate(async () => {
|
|
6204
|
+
await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
6205
|
+
method: "GET",
|
|
6206
|
+
credentials: "include"
|
|
6207
|
+
}).catch(() => {
|
|
6208
|
+
});
|
|
6209
|
+
await fetch("/api/v1/auths/", {
|
|
6210
|
+
method: "GET",
|
|
6211
|
+
credentials: "include"
|
|
6212
|
+
}).catch(() => {
|
|
6213
|
+
});
|
|
6214
|
+
}).catch(() => {
|
|
6215
|
+
});
|
|
6216
|
+
const liveCookies = await page.context().cookies();
|
|
6217
|
+
const tokenCookie = liveCookies.find((c) => c.name === "token");
|
|
6218
|
+
if (tokenCookie) {
|
|
6219
|
+
const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
|
|
6220
|
+
const cache = getHeaderCache(accountId);
|
|
6221
|
+
cache.headers.cookie = cookieStr;
|
|
6222
|
+
cache.lastRefresh = Date.now();
|
|
6223
|
+
markAccountHeadersReady(accountId);
|
|
6224
|
+
lastKeepAliveNavigation.set(accountId, now);
|
|
6225
|
+
touchAccountActivity(accountId);
|
|
6226
|
+
return true;
|
|
6227
|
+
}
|
|
6228
|
+
} catch {
|
|
6229
|
+
}
|
|
6230
|
+
const loggedIn = await isPageLoggedIn(page, 2e3).catch(() => false);
|
|
6231
|
+
if (!loggedIn) {
|
|
6232
|
+
console.log(
|
|
6233
|
+
`\u{1F493} [SessionKeeper] Account ${accountId} session expired; renewing session...`
|
|
6069
6234
|
);
|
|
6235
|
+
try {
|
|
6236
|
+
await refreshHeadersInternal(accountId, config.timeouts.headers, true);
|
|
6237
|
+
lastKeepAliveNavigation.set(accountId, now);
|
|
6238
|
+
touchAccountActivity(accountId);
|
|
6239
|
+
return true;
|
|
6240
|
+
} catch (err) {
|
|
6241
|
+
console.warn(
|
|
6242
|
+
`[SessionKeeper] Proactive session renewal failed for ${accountId}: ${getErrorMessage(err)}`
|
|
6243
|
+
);
|
|
6244
|
+
}
|
|
6070
6245
|
}
|
|
6071
6246
|
}
|
|
6072
6247
|
const lastNavigation = lastKeepAliveNavigation.get(accountId) ?? 0;
|
|
@@ -6446,7 +6621,8 @@ async function addAccountFlow() {
|
|
|
6446
6621
|
const { config: config3 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
6447
6622
|
const ok = await validateAccountLogin2(
|
|
6448
6623
|
{ id: tempId, email: trimmedEmail, password: trimmedPassword },
|
|
6449
|
-
|
|
6624
|
+
false,
|
|
6625
|
+
// Visible browser so user can see and complete challenges if needed
|
|
6450
6626
|
config3.playwright.browser
|
|
6451
6627
|
);
|
|
6452
6628
|
if (ok) {
|