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.
- package/dist/clean-cache.js +188 -70
- package/dist/delete-chats.js +241 -81
- package/dist/index.js +242 -82
- package/dist/login.js +241 -81
- package/dist/reset-cooldowns.js +188 -70
- package/package.json +1 -1
- package/src/core/version.ts +1 -1
- package/src/services/playwright.ts +288 -85
package/dist/reset-cooldowns.js
CHANGED
|
@@ -3523,8 +3523,29 @@ function getStealthScript(profile) {
|
|
|
3523
3523
|
const _toBlob = HTMLCanvasElement.prototype.toBlob;
|
|
3524
3524
|
const _getImageData = CanvasRenderingContext2D.prototype.getImageData;
|
|
3525
3525
|
|
|
3526
|
+
function isCaptchaCanvas(canvas) {
|
|
3527
|
+
try {
|
|
3528
|
+
if (!canvas) return false;
|
|
3529
|
+
const id = (canvas.id || "").toLowerCase();
|
|
3530
|
+
const cls = (canvas.className || "").toLowerCase();
|
|
3531
|
+
return (
|
|
3532
|
+
id.includes("baxia") ||
|
|
3533
|
+
id.includes("nc_") ||
|
|
3534
|
+
id.includes("captcha") ||
|
|
3535
|
+
id.includes("punish") ||
|
|
3536
|
+
cls.includes("baxia") ||
|
|
3537
|
+
cls.includes("nc_") ||
|
|
3538
|
+
cls.includes("captcha") ||
|
|
3539
|
+
Boolean(canvas.closest?.("#baxia-dialog, #baxia-punish, [class*='baxia'], [class*='nc_'], .auth-layout"))
|
|
3540
|
+
);
|
|
3541
|
+
} catch(e) {
|
|
3542
|
+
return false;
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
|
|
3526
3546
|
function addNoise(canvas) {
|
|
3527
3547
|
try {
|
|
3548
|
+
if (isCaptchaCanvas(canvas)) return;
|
|
3528
3549
|
const ctx = canvas.getContext('2d');
|
|
3529
3550
|
if (!ctx) return;
|
|
3530
3551
|
const style = ctx.fillStyle;
|
|
@@ -3548,6 +3569,9 @@ function getStealthScript(profile) {
|
|
|
3548
3569
|
|
|
3549
3570
|
CanvasRenderingContext2D.prototype.getImageData = function(x, y, w, h) {
|
|
3550
3571
|
const imageData = _getImageData.apply(this, arguments);
|
|
3572
|
+
if (isCaptchaCanvas(this.canvas)) {
|
|
3573
|
+
return imageData;
|
|
3574
|
+
}
|
|
3551
3575
|
const data = imageData.data;
|
|
3552
3576
|
const maxPixels = Math.min(data.length / 4, 2500);
|
|
3553
3577
|
for (let i = 0; i < maxPixels * 4; i += 4) {
|
|
@@ -4120,37 +4144,52 @@ async function loginToQwen(accountId, email, password) {
|
|
|
4120
4144
|
async function loginViaApi(page, email, password) {
|
|
4121
4145
|
try {
|
|
4122
4146
|
const hashedPassword = crypto3.createHash("sha256").update(password).digest("hex");
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
email,
|
|
4131
|
-
password: hashedPassword,
|
|
4132
|
-
login_type: "email"
|
|
4133
|
-
},
|
|
4134
|
-
headers: {
|
|
4135
|
-
"content-type": "application/json",
|
|
4136
|
-
accept: "application/json, text/plain, */*",
|
|
4137
|
-
referer: qwenUrl("/auth"),
|
|
4138
|
-
origin: qwenOrigin(),
|
|
4139
|
-
source: "web"
|
|
4140
|
-
},
|
|
4141
|
-
timeout: 1e4
|
|
4142
|
-
});
|
|
4143
|
-
data = await response.json().catch(() => null);
|
|
4144
|
-
signinSuccess = Boolean(data && (data.success === true || data.token));
|
|
4145
|
-
} catch {
|
|
4146
|
-
}
|
|
4147
|
+
if (!page.url().includes("/auth")) {
|
|
4148
|
+
await page.goto(qwenUrl("/auth"), {
|
|
4149
|
+
waitUntil: "domcontentloaded",
|
|
4150
|
+
timeout: config.timeouts.navigation
|
|
4151
|
+
}).catch(() => {
|
|
4152
|
+
});
|
|
4153
|
+
await sleep2(1e3);
|
|
4147
4154
|
}
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4155
|
+
const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
|
|
4156
|
+
const requestId = crypto3.randomUUID();
|
|
4157
|
+
const evalRes = await page.evaluate(
|
|
4158
|
+
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
|
|
4159
|
+
try {
|
|
4160
|
+
const response = await fetch(signinUrl2, {
|
|
4161
|
+
method: "POST",
|
|
4162
|
+
signal: AbortSignal.timeout(15e3),
|
|
4163
|
+
credentials: "include",
|
|
4164
|
+
headers: {
|
|
4165
|
+
accept: "application/json, text/plain, */*",
|
|
4166
|
+
"content-type": "application/json",
|
|
4167
|
+
source: "web",
|
|
4168
|
+
version: "0.3.11",
|
|
4169
|
+
"x-request-origin": "https://chat.qwen.ai",
|
|
4170
|
+
timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
4171
|
+
"x-request-id": requestId2
|
|
4172
|
+
},
|
|
4173
|
+
body: JSON.stringify({ email: email2, password: password2 })
|
|
4174
|
+
});
|
|
4175
|
+
const json = await response.json().catch(() => null);
|
|
4176
|
+
return { ok: response.ok, status: response.status, data: json };
|
|
4177
|
+
} catch (e) {
|
|
4178
|
+
return { ok: false, error: e.message };
|
|
4179
|
+
}
|
|
4180
|
+
},
|
|
4181
|
+
{ email, password: hashedPassword, signinUrl, requestId }
|
|
4182
|
+
).catch(() => null);
|
|
4183
|
+
let data = evalRes?.data;
|
|
4184
|
+
let signinSuccess = Boolean(
|
|
4185
|
+
data && (data.success === true || data.token || data.data?.access_token || data.data?.token)
|
|
4186
|
+
);
|
|
4187
|
+
if (!signinSuccess && !data?.data?.code) {
|
|
4188
|
+
const fallbackUrl = qwenUrl("/api/v2/auths/signin");
|
|
4189
|
+
const fallbackRes = await page.evaluate(
|
|
4190
|
+
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
|
|
4152
4191
|
try {
|
|
4153
|
-
const response = await fetch(
|
|
4192
|
+
const response = await fetch(fallbackUrl2, {
|
|
4154
4193
|
method: "POST",
|
|
4155
4194
|
signal: AbortSignal.timeout(1e4),
|
|
4156
4195
|
credentials: "include",
|
|
@@ -4158,10 +4197,11 @@ async function loginViaApi(page, email, password) {
|
|
|
4158
4197
|
accept: "application/json, text/plain, */*",
|
|
4159
4198
|
"content-type": "application/json",
|
|
4160
4199
|
source: "web",
|
|
4200
|
+
version: "0.3.11",
|
|
4161
4201
|
timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
4162
4202
|
"x-request-id": requestId2
|
|
4163
4203
|
},
|
|
4164
|
-
body: JSON.stringify({ email: email2, password: password2
|
|
4204
|
+
body: JSON.stringify({ email: email2, password: password2 })
|
|
4165
4205
|
});
|
|
4166
4206
|
const json = await response.json().catch(() => null);
|
|
4167
4207
|
return { ok: response.ok, status: response.status, data: json };
|
|
@@ -4169,11 +4209,13 @@ async function loginViaApi(page, email, password) {
|
|
|
4169
4209
|
return { ok: false, error: e.message };
|
|
4170
4210
|
}
|
|
4171
4211
|
},
|
|
4172
|
-
{ email, password: hashedPassword,
|
|
4212
|
+
{ email, password: hashedPassword, fallbackUrl, requestId }
|
|
4173
4213
|
).catch(() => null);
|
|
4174
|
-
if (
|
|
4175
|
-
data =
|
|
4176
|
-
signinSuccess = Boolean(
|
|
4214
|
+
if (fallbackRes?.data) {
|
|
4215
|
+
data = fallbackRes.data;
|
|
4216
|
+
signinSuccess = Boolean(
|
|
4217
|
+
data && (data.success === true || data.token || data.data?.access_token || data.data?.token)
|
|
4218
|
+
);
|
|
4177
4219
|
}
|
|
4178
4220
|
}
|
|
4179
4221
|
if (data?.success === false) {
|
|
@@ -4187,7 +4229,8 @@ async function loginViaApi(page, email, password) {
|
|
|
4187
4229
|
};
|
|
4188
4230
|
}
|
|
4189
4231
|
if (signinSuccess) {
|
|
4190
|
-
const token = data?.data?.token || data?.token;
|
|
4232
|
+
const token = data?.data?.access_token || data?.data?.token || data?.access_token || data?.token;
|
|
4233
|
+
const refreshToken = data?.data?.refresh_token || data?.refresh_token;
|
|
4191
4234
|
if (token) {
|
|
4192
4235
|
try {
|
|
4193
4236
|
await page.context().addCookies([
|
|
@@ -4211,14 +4254,19 @@ async function loginViaApi(page, email, password) {
|
|
|
4211
4254
|
}).catch(() => {
|
|
4212
4255
|
});
|
|
4213
4256
|
if (token) {
|
|
4214
|
-
await page.evaluate(
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4257
|
+
await page.evaluate(
|
|
4258
|
+
({ tok, rTok }) => {
|
|
4259
|
+
try {
|
|
4260
|
+
localStorage.removeItem("qwen_token_logged_out_marker");
|
|
4261
|
+
localStorage.setItem("token", tok);
|
|
4262
|
+
localStorage.setItem("access_token", tok);
|
|
4263
|
+
if (rTok) localStorage.setItem("refresh_token", rTok);
|
|
4264
|
+
document.cookie = `token=${encodeURIComponent(tok)}; path=/; domain=.qwen.ai; max-age=31536000`;
|
|
4265
|
+
} catch {
|
|
4266
|
+
}
|
|
4267
|
+
},
|
|
4268
|
+
{ tok: token, rTok: refreshToken }
|
|
4269
|
+
).catch(() => {
|
|
4222
4270
|
});
|
|
4223
4271
|
await page.reload({ waitUntil: "domcontentloaded" }).catch(() => {
|
|
4224
4272
|
});
|
|
@@ -4246,11 +4294,23 @@ async function loginViaApi(page, email, password) {
|
|
|
4246
4294
|
}
|
|
4247
4295
|
async function loginViaUi(page, email, password) {
|
|
4248
4296
|
try {
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4297
|
+
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3).catch(() => false)) {
|
|
4298
|
+
return { success: true };
|
|
4299
|
+
}
|
|
4300
|
+
try {
|
|
4301
|
+
await page.goto(qwenUrl("/auth"), {
|
|
4302
|
+
waitUntil: "domcontentloaded",
|
|
4303
|
+
timeout: Math.min(config.timeouts.navigation, 15e3)
|
|
4304
|
+
});
|
|
4305
|
+
await sleep2(1e3);
|
|
4306
|
+
} catch (gotoErr) {
|
|
4307
|
+
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3).catch(() => false)) {
|
|
4308
|
+
return { success: true };
|
|
4309
|
+
}
|
|
4310
|
+
}
|
|
4311
|
+
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3).catch(() => false)) {
|
|
4312
|
+
return { success: true };
|
|
4313
|
+
}
|
|
4254
4314
|
const emailSelector = [
|
|
4255
4315
|
'input[type="email"]',
|
|
4256
4316
|
'input[name="email"]',
|
|
@@ -4260,7 +4320,7 @@ async function loginViaUi(page, email, password) {
|
|
|
4260
4320
|
].join(", ");
|
|
4261
4321
|
try {
|
|
4262
4322
|
await page.waitForSelector(emailSelector, {
|
|
4263
|
-
timeout: Math.min(config.timeouts.page,
|
|
4323
|
+
timeout: Math.min(config.timeouts.page, 5e3)
|
|
4264
4324
|
});
|
|
4265
4325
|
} catch {
|
|
4266
4326
|
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
|
|
@@ -4273,30 +4333,55 @@ async function loginViaUi(page, email, password) {
|
|
|
4273
4333
|
return { success: false, reason: "Campo de e-mail n\xE3o encontrado (poss\xEDvel captcha)" };
|
|
4274
4334
|
}
|
|
4275
4335
|
await clearVisibleChallenge(page);
|
|
4276
|
-
const
|
|
4277
|
-
return page.locator('
|
|
4278
|
-
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
|
|
4279
|
-
}).first();
|
|
4336
|
+
const isPasswordInputPresent = async () => {
|
|
4337
|
+
return await page.locator('input[type="password"], input[name="password"]').count().catch(() => 0) > 0;
|
|
4280
4338
|
};
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
|
|
4339
|
+
const revealPasswordMode = async () => {
|
|
4340
|
+
if (page.url().includes("/forget")) {
|
|
4341
|
+
await page.goto(qwenUrl("/auth"), { waitUntil: "domcontentloaded", timeout: 1e4 }).catch(() => {
|
|
4285
4342
|
});
|
|
4286
|
-
await sleep2(
|
|
4343
|
+
await sleep2(1e3);
|
|
4287
4344
|
}
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
if (await
|
|
4295
|
-
await
|
|
4345
|
+
if (await isPasswordInputPresent()) return;
|
|
4346
|
+
const btn = page.locator("button").filter({
|
|
4347
|
+
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
|
|
4348
|
+
}).filter({
|
|
4349
|
+
hasNotText: /(?:esqueci|forgot|olvid|reset|recover)/i
|
|
4350
|
+
}).first();
|
|
4351
|
+
if (await btn.isVisible({ timeout: 1500 }).catch(() => false)) {
|
|
4352
|
+
await btn.click({ timeout: 2e3 }).catch(() => {
|
|
4296
4353
|
});
|
|
4297
4354
|
await sleep2(500);
|
|
4298
4355
|
}
|
|
4356
|
+
if (await isPasswordInputPresent()) return;
|
|
4357
|
+
await page.evaluate(() => {
|
|
4358
|
+
try {
|
|
4359
|
+
const buttons = Array.from(document.querySelectorAll("button, [role='button'], a"));
|
|
4360
|
+
for (const b of buttons) {
|
|
4361
|
+
const txt = (b.innerText || "").trim().toLowerCase();
|
|
4362
|
+
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")) {
|
|
4363
|
+
b.click();
|
|
4364
|
+
return true;
|
|
4365
|
+
}
|
|
4366
|
+
}
|
|
4367
|
+
} catch {
|
|
4368
|
+
}
|
|
4369
|
+
return false;
|
|
4370
|
+
}).catch(() => false);
|
|
4371
|
+
await sleep2(500);
|
|
4372
|
+
};
|
|
4373
|
+
await revealPasswordMode();
|
|
4374
|
+
try {
|
|
4375
|
+
await page.fill(emailSelector, email, { timeout: 8e3 });
|
|
4376
|
+
await sleep2(300);
|
|
4299
4377
|
} catch {
|
|
4378
|
+
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
|
|
4379
|
+
return { success: true };
|
|
4380
|
+
}
|
|
4381
|
+
throw new Error(`Campo de e-mail n\xE3o encontrado na tela de autentica\xE7\xE3o`);
|
|
4382
|
+
}
|
|
4383
|
+
if (!await isPasswordInputPresent()) {
|
|
4384
|
+
await revealPasswordMode();
|
|
4300
4385
|
}
|
|
4301
4386
|
const passwordSelector = 'input[type="password"], input[name="password"]';
|
|
4302
4387
|
try {
|
|
@@ -4315,17 +4400,50 @@ async function loginViaUi(page, email, password) {
|
|
|
4315
4400
|
reason: "Campo de senha n\xE3o encontrado na tela de autentica\xE7\xE3o"
|
|
4316
4401
|
};
|
|
4317
4402
|
}
|
|
4318
|
-
|
|
4319
|
-
|
|
4403
|
+
try {
|
|
4404
|
+
await page.fill(passwordSelector, password, { timeout: 8e3 });
|
|
4405
|
+
await sleep2(500);
|
|
4406
|
+
} catch {
|
|
4407
|
+
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
|
|
4408
|
+
return { success: true };
|
|
4409
|
+
}
|
|
4410
|
+
throw new Error(`Campo de senha n\xE3o encontrado na tela de autentica\xE7\xE3o`);
|
|
4411
|
+
}
|
|
4412
|
+
await page.evaluate(() => {
|
|
4413
|
+
try {
|
|
4414
|
+
const emailEl = document.querySelector('input[name="email"], input[type="email"], input[type="text"]');
|
|
4415
|
+
const passEl = document.querySelector('input[type="password"], input[name="password"]');
|
|
4416
|
+
if (emailEl) {
|
|
4417
|
+
emailEl.dispatchEvent(new Event("input", { bubbles: true }));
|
|
4418
|
+
emailEl.dispatchEvent(new Event("change", { bubbles: true }));
|
|
4419
|
+
emailEl.dispatchEvent(new Event("blur", { bubbles: true }));
|
|
4420
|
+
}
|
|
4421
|
+
if (passEl) {
|
|
4422
|
+
passEl.dispatchEvent(new Event("input", { bubbles: true }));
|
|
4423
|
+
passEl.dispatchEvent(new Event("change", { bubbles: true }));
|
|
4424
|
+
passEl.dispatchEvent(new Event("blur", { bubbles: true }));
|
|
4425
|
+
}
|
|
4426
|
+
} catch {
|
|
4427
|
+
}
|
|
4428
|
+
}).catch(() => {
|
|
4429
|
+
});
|
|
4320
4430
|
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")';
|
|
4321
4431
|
const submitButton = page.locator(submitSelector).first();
|
|
4322
4432
|
try {
|
|
4323
4433
|
await page.waitForSelector('button[type="submit"]:not([disabled])', {
|
|
4324
|
-
timeout:
|
|
4434
|
+
timeout: 4e3
|
|
4325
4435
|
});
|
|
4326
4436
|
await submitButton.click();
|
|
4327
4437
|
} catch {
|
|
4328
|
-
await
|
|
4438
|
+
const isDisabled = await submitButton.isDisabled().catch(() => true);
|
|
4439
|
+
if (!isDisabled) {
|
|
4440
|
+
await submitButton.click({ force: true }).catch(() => {
|
|
4441
|
+
});
|
|
4442
|
+
} else {
|
|
4443
|
+
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3)) {
|
|
4444
|
+
return { success: true };
|
|
4445
|
+
}
|
|
4446
|
+
}
|
|
4329
4447
|
}
|
|
4330
4448
|
const postSubmitDeadline = Date.now() + 15e3;
|
|
4331
4449
|
while (Date.now() < postSubmitDeadline) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qwenproxy-cli",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.7",
|
|
4
4
|
"description": "High-performance OpenAI & Anthropic compatible API gateway for Qwen with multi-account rotation, interactive TUI, and resilient tool calling.",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"bin": {
|