qwenproxy-cli 1.4.3 → 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.
@@ -3779,8 +3779,29 @@ function getStealthScript(profile) {
3779
3779
  const _toBlob = HTMLCanvasElement.prototype.toBlob;
3780
3780
  const _getImageData = CanvasRenderingContext2D.prototype.getImageData;
3781
3781
 
3782
+ function isCaptchaCanvas(canvas) {
3783
+ try {
3784
+ if (!canvas) return false;
3785
+ const id = (canvas.id || "").toLowerCase();
3786
+ const cls = (canvas.className || "").toLowerCase();
3787
+ return (
3788
+ id.includes("baxia") ||
3789
+ id.includes("nc_") ||
3790
+ id.includes("captcha") ||
3791
+ id.includes("punish") ||
3792
+ cls.includes("baxia") ||
3793
+ cls.includes("nc_") ||
3794
+ cls.includes("captcha") ||
3795
+ Boolean(canvas.closest?.("#baxia-dialog, #baxia-punish, [class*='baxia'], [class*='nc_'], .auth-layout"))
3796
+ );
3797
+ } catch(e) {
3798
+ return false;
3799
+ }
3800
+ }
3801
+
3782
3802
  function addNoise(canvas) {
3783
3803
  try {
3804
+ if (isCaptchaCanvas(canvas)) return;
3784
3805
  const ctx = canvas.getContext('2d');
3785
3806
  if (!ctx) return;
3786
3807
  const style = ctx.fillStyle;
@@ -3804,6 +3825,9 @@ function getStealthScript(profile) {
3804
3825
 
3805
3826
  CanvasRenderingContext2D.prototype.getImageData = function(x, y, w, h) {
3806
3827
  const imageData = _getImageData.apply(this, arguments);
3828
+ if (isCaptchaCanvas(this.canvas)) {
3829
+ return imageData;
3830
+ }
3807
3831
  const data = imageData.data;
3808
3832
  const maxPixels = Math.min(data.length / 4, 2500);
3809
3833
  for (let i = 0; i < maxPixels * 4; i += 4) {
@@ -4759,37 +4783,52 @@ async function loginToQwen(accountId, email, password) {
4759
4783
  async function loginViaApi(page, email, password) {
4760
4784
  try {
4761
4785
  const hashedPassword = crypto3.createHash("sha256").update(password).digest("hex");
4762
- const signinUrl = qwenUrl("/api/v2/auths/signin");
4763
- let signinSuccess = false;
4764
- let data = null;
4765
- if (page.request && typeof page.request.post === "function") {
4766
- try {
4767
- const response = await page.request.post(signinUrl, {
4768
- data: {
4769
- email,
4770
- password: hashedPassword,
4771
- login_type: "email"
4772
- },
4773
- headers: {
4774
- "content-type": "application/json",
4775
- accept: "application/json, text/plain, */*",
4776
- referer: qwenUrl("/auth"),
4777
- origin: qwenOrigin(),
4778
- source: "web"
4779
- },
4780
- timeout: 1e4
4781
- });
4782
- data = await response.json().catch(() => null);
4783
- signinSuccess = Boolean(data && (data.success === true || data.token));
4784
- } catch {
4785
- }
4786
+ if (!page.url().includes("/auth")) {
4787
+ await page.goto(qwenUrl("/auth"), {
4788
+ waitUntil: "domcontentloaded",
4789
+ timeout: config.timeouts.navigation
4790
+ }).catch(() => {
4791
+ });
4792
+ await sleep2(1e3);
4786
4793
  }
4787
- if (!signinSuccess && !data) {
4788
- const requestId = crypto3.randomUUID();
4789
- const evalRes = await page.evaluate(
4790
- async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
4794
+ const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
4795
+ const requestId = crypto3.randomUUID();
4796
+ const evalRes = await page.evaluate(
4797
+ async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
4798
+ try {
4799
+ const response = await fetch(signinUrl2, {
4800
+ method: "POST",
4801
+ signal: AbortSignal.timeout(15e3),
4802
+ credentials: "include",
4803
+ headers: {
4804
+ accept: "application/json, text/plain, */*",
4805
+ "content-type": "application/json",
4806
+ source: "web",
4807
+ version: "0.3.11",
4808
+ "x-request-origin": "https://chat.qwen.ai",
4809
+ timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4810
+ "x-request-id": requestId2
4811
+ },
4812
+ body: JSON.stringify({ email: email2, password: password2 })
4813
+ });
4814
+ const json = await response.json().catch(() => null);
4815
+ return { ok: response.ok, status: response.status, data: json };
4816
+ } catch (e) {
4817
+ return { ok: false, error: e.message };
4818
+ }
4819
+ },
4820
+ { email, password: hashedPassword, signinUrl, requestId }
4821
+ ).catch(() => null);
4822
+ let data = evalRes?.data;
4823
+ let signinSuccess = Boolean(
4824
+ data && (data.success === true || data.token || data.data?.access_token || data.data?.token)
4825
+ );
4826
+ if (!signinSuccess && !data?.data?.code) {
4827
+ const fallbackUrl = qwenUrl("/api/v2/auths/signin");
4828
+ const fallbackRes = await page.evaluate(
4829
+ async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
4791
4830
  try {
4792
- const response = await fetch(signinUrl2, {
4831
+ const response = await fetch(fallbackUrl2, {
4793
4832
  method: "POST",
4794
4833
  signal: AbortSignal.timeout(1e4),
4795
4834
  credentials: "include",
@@ -4797,10 +4836,11 @@ async function loginViaApi(page, email, password) {
4797
4836
  accept: "application/json, text/plain, */*",
4798
4837
  "content-type": "application/json",
4799
4838
  source: "web",
4839
+ version: "0.3.11",
4800
4840
  timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4801
4841
  "x-request-id": requestId2
4802
4842
  },
4803
- body: JSON.stringify({ email: email2, password: password2, login_type: "email" })
4843
+ body: JSON.stringify({ email: email2, password: password2 })
4804
4844
  });
4805
4845
  const json = await response.json().catch(() => null);
4806
4846
  return { ok: response.ok, status: response.status, data: json };
@@ -4808,11 +4848,13 @@ async function loginViaApi(page, email, password) {
4808
4848
  return { ok: false, error: e.message };
4809
4849
  }
4810
4850
  },
4811
- { email, password: hashedPassword, signinUrl, requestId }
4851
+ { email, password: hashedPassword, fallbackUrl, requestId }
4812
4852
  ).catch(() => null);
4813
- if (evalRes?.data) {
4814
- data = evalRes.data;
4815
- signinSuccess = Boolean(data && (data.success === true || data.token));
4853
+ if (fallbackRes?.data) {
4854
+ data = fallbackRes.data;
4855
+ signinSuccess = Boolean(
4856
+ data && (data.success === true || data.token || data.data?.access_token || data.data?.token)
4857
+ );
4816
4858
  }
4817
4859
  }
4818
4860
  if (data?.success === false) {
@@ -4826,7 +4868,8 @@ async function loginViaApi(page, email, password) {
4826
4868
  };
4827
4869
  }
4828
4870
  if (signinSuccess) {
4829
- const token = data?.data?.token || data?.token;
4871
+ const token = data?.data?.access_token || data?.data?.token || data?.access_token || data?.token;
4872
+ const refreshToken = data?.data?.refresh_token || data?.refresh_token;
4830
4873
  if (token) {
4831
4874
  try {
4832
4875
  await page.context().addCookies([
@@ -4850,14 +4893,19 @@ async function loginViaApi(page, email, password) {
4850
4893
  }).catch(() => {
4851
4894
  });
4852
4895
  if (token) {
4853
- await page.evaluate((tok) => {
4854
- try {
4855
- localStorage.removeItem("qwen_token_logged_out_marker");
4856
- localStorage.setItem("token", tok);
4857
- document.cookie = `token=${encodeURIComponent(tok)}; path=/; domain=.qwen.ai; max-age=31536000`;
4858
- } catch {
4859
- }
4860
- }, token).catch(() => {
4896
+ await page.evaluate(
4897
+ ({ tok, rTok }) => {
4898
+ try {
4899
+ localStorage.removeItem("qwen_token_logged_out_marker");
4900
+ localStorage.setItem("token", tok);
4901
+ localStorage.setItem("access_token", tok);
4902
+ if (rTok) localStorage.setItem("refresh_token", rTok);
4903
+ document.cookie = `token=${encodeURIComponent(tok)}; path=/; domain=.qwen.ai; max-age=31536000`;
4904
+ } catch {
4905
+ }
4906
+ },
4907
+ { tok: token, rTok: refreshToken }
4908
+ ).catch(() => {
4861
4909
  });
4862
4910
  await page.reload({ waitUntil: "domcontentloaded" }).catch(() => {
4863
4911
  });
@@ -4885,11 +4933,23 @@ async function loginViaApi(page, email, password) {
4885
4933
  }
4886
4934
  async function loginViaUi(page, email, password) {
4887
4935
  try {
4888
- await page.goto(qwenUrl("/auth"), {
4889
- waitUntil: "domcontentloaded",
4890
- timeout: config.timeouts.navigation
4891
- });
4892
- await sleep2(1500);
4936
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3).catch(() => false)) {
4937
+ return { success: true };
4938
+ }
4939
+ try {
4940
+ await page.goto(qwenUrl("/auth"), {
4941
+ waitUntil: "domcontentloaded",
4942
+ timeout: Math.min(config.timeouts.navigation, 15e3)
4943
+ });
4944
+ await sleep2(1e3);
4945
+ } catch (gotoErr) {
4946
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3).catch(() => false)) {
4947
+ return { success: true };
4948
+ }
4949
+ }
4950
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3).catch(() => false)) {
4951
+ return { success: true };
4952
+ }
4893
4953
  const emailSelector = [
4894
4954
  'input[type="email"]',
4895
4955
  'input[name="email"]',
@@ -4899,7 +4959,7 @@ async function loginViaUi(page, email, password) {
4899
4959
  ].join(", ");
4900
4960
  try {
4901
4961
  await page.waitForSelector(emailSelector, {
4902
- timeout: Math.min(config.timeouts.page, 8e3)
4962
+ timeout: Math.min(config.timeouts.page, 5e3)
4903
4963
  });
4904
4964
  } catch {
4905
4965
  if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
@@ -4912,30 +4972,55 @@ async function loginViaUi(page, email, password) {
4912
4972
  return { success: false, reason: "Campo de e-mail n\xE3o encontrado (poss\xEDvel captcha)" };
4913
4973
  }
4914
4974
  await clearVisibleChallenge(page);
4915
- const findPasswordModeButton = () => {
4916
- return page.locator('button, [role="button"], a, div').filter({
4917
- 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
4918
- }).first();
4975
+ const isPasswordInputPresent = async () => {
4976
+ return await page.locator('input[type="password"], input[name="password"]').count().catch(() => 0) > 0;
4919
4977
  };
4920
- try {
4921
- const pwdModeBtn = findPasswordModeButton();
4922
- if (await pwdModeBtn.isVisible({ timeout: 2e3 }).catch(() => false)) {
4923
- await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
4978
+ const revealPasswordMode = async () => {
4979
+ if (page.url().includes("/forget")) {
4980
+ await page.goto(qwenUrl("/auth"), { waitUntil: "domcontentloaded", timeout: 1e4 }).catch(() => {
4924
4981
  });
4925
- await sleep2(500);
4982
+ await sleep2(1e3);
4926
4983
  }
4927
- } catch {
4928
- }
4929
- await page.fill(emailSelector, email);
4930
- await sleep2(300);
4931
- try {
4932
- const pwdModeBtn = findPasswordModeButton();
4933
- if (await pwdModeBtn.isVisible({ timeout: 1500 }).catch(() => false)) {
4934
- await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
4984
+ if (await isPasswordInputPresent()) return;
4985
+ const btn = page.locator("button").filter({
4986
+ 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
4987
+ }).filter({
4988
+ hasNotText: /(?:esqueci|forgot|olvid|reset|recover)/i
4989
+ }).first();
4990
+ if (await btn.isVisible({ timeout: 1500 }).catch(() => false)) {
4991
+ await btn.click({ timeout: 2e3 }).catch(() => {
4935
4992
  });
4936
4993
  await sleep2(500);
4937
4994
  }
4995
+ if (await isPasswordInputPresent()) return;
4996
+ await page.evaluate(() => {
4997
+ try {
4998
+ const buttons = Array.from(document.querySelectorAll("button, [role='button'], a"));
4999
+ for (const b of buttons) {
5000
+ const txt = (b.innerText || "").trim().toLowerCase();
5001
+ 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")) {
5002
+ b.click();
5003
+ return true;
5004
+ }
5005
+ }
5006
+ } catch {
5007
+ }
5008
+ return false;
5009
+ }).catch(() => false);
5010
+ await sleep2(500);
5011
+ };
5012
+ await revealPasswordMode();
5013
+ try {
5014
+ await page.fill(emailSelector, email, { timeout: 8e3 });
5015
+ await sleep2(300);
4938
5016
  } catch {
5017
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
5018
+ return { success: true };
5019
+ }
5020
+ throw new Error(`Campo de e-mail n\xE3o encontrado na tela de autentica\xE7\xE3o`);
5021
+ }
5022
+ if (!await isPasswordInputPresent()) {
5023
+ await revealPasswordMode();
4939
5024
  }
4940
5025
  const passwordSelector = 'input[type="password"], input[name="password"]';
4941
5026
  try {
@@ -4954,17 +5039,50 @@ async function loginViaUi(page, email, password) {
4954
5039
  reason: "Campo de senha n\xE3o encontrado na tela de autentica\xE7\xE3o"
4955
5040
  };
4956
5041
  }
4957
- await page.fill(passwordSelector, password);
4958
- await sleep2(500);
5042
+ try {
5043
+ await page.fill(passwordSelector, password, { timeout: 8e3 });
5044
+ await sleep2(500);
5045
+ } catch {
5046
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
5047
+ return { success: true };
5048
+ }
5049
+ throw new Error(`Campo de senha n\xE3o encontrado na tela de autentica\xE7\xE3o`);
5050
+ }
5051
+ await page.evaluate(() => {
5052
+ try {
5053
+ const emailEl = document.querySelector('input[name="email"], input[type="email"], input[type="text"]');
5054
+ const passEl = document.querySelector('input[type="password"], input[name="password"]');
5055
+ if (emailEl) {
5056
+ emailEl.dispatchEvent(new Event("input", { bubbles: true }));
5057
+ emailEl.dispatchEvent(new Event("change", { bubbles: true }));
5058
+ emailEl.dispatchEvent(new Event("blur", { bubbles: true }));
5059
+ }
5060
+ if (passEl) {
5061
+ passEl.dispatchEvent(new Event("input", { bubbles: true }));
5062
+ passEl.dispatchEvent(new Event("change", { bubbles: true }));
5063
+ passEl.dispatchEvent(new Event("blur", { bubbles: true }));
5064
+ }
5065
+ } catch {
5066
+ }
5067
+ }).catch(() => {
5068
+ });
4959
5069
  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")';
4960
5070
  const submitButton = page.locator(submitSelector).first();
4961
5071
  try {
4962
5072
  await page.waitForSelector('button[type="submit"]:not([disabled])', {
4963
- timeout: 5e3
5073
+ timeout: 4e3
4964
5074
  });
4965
5075
  await submitButton.click();
4966
5076
  } catch {
4967
- await page.keyboard.press("Enter");
5077
+ const isDisabled = await submitButton.isDisabled().catch(() => true);
5078
+ if (!isDisabled) {
5079
+ await submitButton.click({ force: true }).catch(() => {
5080
+ });
5081
+ } else {
5082
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3)) {
5083
+ return { success: true };
5084
+ }
5085
+ }
4968
5086
  }
4969
5087
  const postSubmitDeadline = Date.now() + 15e3;
4970
5088
  while (Date.now() < postSubmitDeadline) {
@@ -5550,6 +5668,17 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
5550
5668
  }
5551
5669
  };
5552
5670
  if (forceReauth) {
5671
+ const alreadyIn = await isPageLoggedIn(page, 2e3).catch(() => false);
5672
+ if (alreadyIn) {
5673
+ const liveCookies = await page.context().cookies();
5674
+ if (liveCookies.some((c) => c.name === "token")) {
5675
+ const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
5676
+ cache.headers.cookie = cookieStr;
5677
+ cache.lastRefresh = Date.now();
5678
+ markAccountHeadersReady(accountId);
5679
+ return;
5680
+ }
5681
+ }
5553
5682
  await executeReauth();
5554
5683
  } else {
5555
5684
  try {
@@ -5984,19 +6113,50 @@ async function keepAlivePlaywrightAccount(accountId) {
5984
6113
  const now = Date.now();
5985
6114
  const currentUrl = page.url();
5986
6115
  const cookie = await getCookies(accountId);
5987
- if (cookie && isTokenExpiringSoon(cookie, 45)) {
5988
- console.log(
5989
- `\u{1F493} [SessionKeeper] Account ${accountId} token expires within 45m; proactively renewing session...`
5990
- );
6116
+ if (cookie && isTokenExpiringSoon(cookie, 2)) {
5991
6117
  try {
5992
- await refreshHeadersInternal(accountId, config.timeouts.headers, true);
5993
- lastKeepAliveNavigation.set(accountId, now);
5994
- touchAccountActivity(accountId);
5995
- return true;
5996
- } catch (err) {
5997
- console.warn(
5998
- `[SessionKeeper] Proactive session renewal failed for ${accountId}: ${getErrorMessage(err)}`
6118
+ await page.evaluate(async () => {
6119
+ await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
6120
+ method: "GET",
6121
+ credentials: "include"
6122
+ }).catch(() => {
6123
+ });
6124
+ await fetch("/api/v1/auths/", {
6125
+ method: "GET",
6126
+ credentials: "include"
6127
+ }).catch(() => {
6128
+ });
6129
+ }).catch(() => {
6130
+ });
6131
+ const liveCookies = await page.context().cookies();
6132
+ const tokenCookie = liveCookies.find((c) => c.name === "token");
6133
+ if (tokenCookie) {
6134
+ const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
6135
+ const cache = getHeaderCache(accountId);
6136
+ cache.headers.cookie = cookieStr;
6137
+ cache.lastRefresh = Date.now();
6138
+ markAccountHeadersReady(accountId);
6139
+ lastKeepAliveNavigation.set(accountId, now);
6140
+ touchAccountActivity(accountId);
6141
+ return true;
6142
+ }
6143
+ } catch {
6144
+ }
6145
+ const loggedIn = await isPageLoggedIn(page, 2e3).catch(() => false);
6146
+ if (!loggedIn) {
6147
+ console.log(
6148
+ `\u{1F493} [SessionKeeper] Account ${accountId} session expired; renewing session...`
5999
6149
  );
6150
+ try {
6151
+ await refreshHeadersInternal(accountId, config.timeouts.headers, true);
6152
+ lastKeepAliveNavigation.set(accountId, now);
6153
+ touchAccountActivity(accountId);
6154
+ return true;
6155
+ } catch (err) {
6156
+ console.warn(
6157
+ `[SessionKeeper] Proactive session renewal failed for ${accountId}: ${getErrorMessage(err)}`
6158
+ );
6159
+ }
6000
6160
  }
6001
6161
  }
6002
6162
  const lastNavigation = lastKeepAliveNavigation.get(accountId) ?? 0;