qwen-api-proxy 1.0.11 → 1.0.13
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/README.md +7 -5
- package/bin/qwen-api-proxy.js +64 -53
- package/index.js +106 -20
- package/package.json +8 -2
- package/src/api/chat.js +71 -71
- package/src/api/chatHistory.js +19 -19
- package/src/api/fileUpload.js +23 -23
- package/src/api/imageGeneration.js +23 -23
- package/src/api/modelMapping.js +145 -153
- package/src/api/routes.js +148 -148
- package/src/api/tokenManager.js +93 -93
- package/src/browser/auth.js +13 -13
- package/src/browser/browser.js +9 -9
- package/src/browser/session.js +3 -3
- package/src/config.js +4 -4
- package/src/utils/accountSetup.js +14 -14
- package/src/utils/botSettings.js +14 -14
- package/src/utils/permissionChecker.js +157 -157
- package/src/utils/prompt.js +1 -1
- package/src/utils/proxy.js +11 -11
- package/src/utils/telegramBot.js +656 -654
- package/src/utils/telegramNotifier.js +7 -7
package/src/api/chat.js
CHANGED
|
@@ -33,7 +33,7 @@ let authKeys = null;
|
|
|
33
33
|
let browserTokenRateLimited = false;
|
|
34
34
|
let resolvedDefaultModel = null; // Будет установлен при загрузке моделей
|
|
35
35
|
|
|
36
|
-
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
36
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
37
37
|
|
|
38
38
|
// ─── Page helpers ────────────────────────────────────────────────────────────
|
|
39
39
|
|
|
@@ -114,7 +114,7 @@ export const pagePool = {
|
|
|
114
114
|
|
|
115
115
|
releasePage(page) {
|
|
116
116
|
try {
|
|
117
|
-
if (page.isClosed()) return;
|
|
117
|
+
if (page.isClosed()) {return;}
|
|
118
118
|
} catch { return; }
|
|
119
119
|
|
|
120
120
|
const baseContext = getBrowserContext();
|
|
@@ -126,14 +126,14 @@ export const pagePool = {
|
|
|
126
126
|
if (this.pages.length < this.maxSize) {
|
|
127
127
|
this.pages.push(page);
|
|
128
128
|
} else {
|
|
129
|
-
page.close().catch(e => logError('Ошибка при закрытии страницы', e));
|
|
129
|
+
page.close().catch((e) => logError('Ошибка при закрытии страницы', e));
|
|
130
130
|
}
|
|
131
131
|
},
|
|
132
132
|
|
|
133
133
|
async clear() {
|
|
134
134
|
const baseContext = getBrowserContext();
|
|
135
135
|
for (const page of this.pages) {
|
|
136
|
-
if (page === baseContext) continue;
|
|
136
|
+
if (page === baseContext) {continue;}
|
|
137
137
|
try { await page.close(); } catch (e) {
|
|
138
138
|
logError('Ошибка при закрытии страницы в пуле', e);
|
|
139
139
|
}
|
|
@@ -171,7 +171,7 @@ export async function pollTaskStatus(taskId, page, token, maxAttempts = TASK_POL
|
|
|
171
171
|
|
|
172
172
|
if (!result.success) {
|
|
173
173
|
logWarn(`Ошибка при проверке статуса (попытка ${attempt}/${maxAttempts}): ${result.error}`);
|
|
174
|
-
if (attempt < maxAttempts) await delay(interval);
|
|
174
|
+
if (attempt < maxAttempts) {await delay(interval);}
|
|
175
175
|
continue;
|
|
176
176
|
}
|
|
177
177
|
|
|
@@ -189,10 +189,10 @@ export async function pollTaskStatus(taskId, page, token, maxAttempts = TASK_POL
|
|
|
189
189
|
return { success: false, status: 'failed', error: taskData.error || taskData.message || 'Task failed', data: taskData };
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
if (attempt < maxAttempts) await delay(interval);
|
|
192
|
+
if (attempt < maxAttempts) {await delay(interval);}
|
|
193
193
|
} catch (error) {
|
|
194
194
|
logError(`Ошибка при опросе задачи (попытка ${attempt}/${maxAttempts})`, error);
|
|
195
|
-
if (attempt < maxAttempts) await delay(interval);
|
|
195
|
+
if (attempt < maxAttempts) {await delay(interval);}
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
|
|
@@ -203,7 +203,7 @@ export async function pollTaskStatus(taskId, page, token, maxAttempts = TASK_POL
|
|
|
203
203
|
// ─── Token extraction ────────────────────────────────────────────────────────
|
|
204
204
|
|
|
205
205
|
export async function extractAuthToken(context, forceRefresh = false) {
|
|
206
|
-
if (authToken && !forceRefresh) return authToken;
|
|
206
|
+
if (authToken && !forceRefresh) {return authToken;}
|
|
207
207
|
|
|
208
208
|
try {
|
|
209
209
|
const page = await getPage(context);
|
|
@@ -213,7 +213,7 @@ export async function extractAuthToken(context, forceRefresh = false) {
|
|
|
213
213
|
await delay(RETRY_DELAY);
|
|
214
214
|
|
|
215
215
|
const newToken = await page.evaluate(() => localStorage.getItem('token'));
|
|
216
|
-
if (shouldClosePage) await page.close();
|
|
216
|
+
if (shouldClosePage) {await page.close();}
|
|
217
217
|
|
|
218
218
|
if (newToken) {
|
|
219
219
|
authToken = newToken;
|
|
@@ -224,7 +224,7 @@ export async function extractAuthToken(context, forceRefresh = false) {
|
|
|
224
224
|
logError('Токен авторизации не найден в браузере');
|
|
225
225
|
return null;
|
|
226
226
|
} catch (error) {
|
|
227
|
-
if (shouldClosePage) await page.close().catch(() => { });
|
|
227
|
+
if (shouldClosePage) {await page.close().catch(() => { });}
|
|
228
228
|
throw error;
|
|
229
229
|
}
|
|
230
230
|
} catch (error) {
|
|
@@ -248,7 +248,7 @@ export async function fetchModelsFromAPI() {
|
|
|
248
248
|
if (!MODELS_API_URL.includes('?')) {
|
|
249
249
|
// Qwen API использует limit для пагинации
|
|
250
250
|
const params = new URLSearchParams({
|
|
251
|
-
limit: '1000'
|
|
251
|
+
limit: '1000' // Максимальное количество моделей
|
|
252
252
|
});
|
|
253
253
|
modelsUrl = `${MODELS_API_URL}?${params.toString()}`;
|
|
254
254
|
logDebug(`URL с параметрами пагинации: ${modelsUrl}`);
|
|
@@ -311,13 +311,13 @@ export async function fetchModelsFromAPI() {
|
|
|
311
311
|
logDebug(`Извлечены модели: data.models (${data.models.length} элементов)`);
|
|
312
312
|
} else {
|
|
313
313
|
logWarn(`⚠️ Не удалось распознать формат ответа API. Keys: ${Object.keys(data).join(', ')}`);
|
|
314
|
-
logWarn(
|
|
314
|
+
logWarn('Полный JSON ответ от API:');
|
|
315
315
|
logWarn(JSON.stringify(data, null, 2));
|
|
316
316
|
}
|
|
317
317
|
|
|
318
318
|
// Извлекаем ID моделей
|
|
319
|
-
const modelIds = models.map(m => {
|
|
320
|
-
if (typeof m === 'string') return m;
|
|
319
|
+
const modelIds = models.map((m) => {
|
|
320
|
+
if (typeof m === 'string') {return m;}
|
|
321
321
|
return m.id || m.model_id || m.name;
|
|
322
322
|
}).filter(Boolean);
|
|
323
323
|
|
|
@@ -396,7 +396,7 @@ export async function ensureModelsLoaded() {
|
|
|
396
396
|
modelsLoadedFromAPI = true;
|
|
397
397
|
logInfo(`✅ Загружено ${apiModels.length} моделей с Qwen API`);
|
|
398
398
|
logInfo('===== ДОСТУПНЫЕ МОДЕЛИ (API) =====');
|
|
399
|
-
apiModels.forEach(m => logInfo(`- ${m}`));
|
|
399
|
+
apiModels.forEach((m) => logInfo(`- ${m}`));
|
|
400
400
|
logInfo('===================================');
|
|
401
401
|
|
|
402
402
|
// Устанавливаем default model как первую из API, если не задана в .env
|
|
@@ -414,7 +414,7 @@ export async function ensureModelsLoaded() {
|
|
|
414
414
|
logDebug(`loadModelsFromFile вернул: ${fileModels ? fileModels.length + ' моделей' : 'null'}`);
|
|
415
415
|
return fileModels;
|
|
416
416
|
} catch (error) {
|
|
417
|
-
logError(
|
|
417
|
+
logError('❌ Ошибка загрузки моделей из API', error);
|
|
418
418
|
logWarn('⚠️ Используем резервный список из AvailableModels.txt');
|
|
419
419
|
const fileModels = loadModelsFromFile();
|
|
420
420
|
logDebug(`loadModelsFromFile (catch) вернул: ${fileModels ? fileModels.length + ' моделей' : 'null'}`);
|
|
@@ -447,8 +447,8 @@ function loadModelsFromFile() {
|
|
|
447
447
|
|
|
448
448
|
const models = fileContent
|
|
449
449
|
.split('\n')
|
|
450
|
-
.map(l => l.trim())
|
|
451
|
-
.filter(l => l && !l.startsWith('#'));
|
|
450
|
+
.map((l) => l.trim())
|
|
451
|
+
.filter((l) => l && !l.startsWith('#'));
|
|
452
452
|
|
|
453
453
|
logDebug(`Распознано моделей: ${models.length}`);
|
|
454
454
|
|
|
@@ -457,7 +457,7 @@ function loadModelsFromFile() {
|
|
|
457
457
|
modelsLoadedFromAPI = false;
|
|
458
458
|
|
|
459
459
|
logInfo('===== ДОСТУПНЫЕ МОДЕЛИ (ФАЙЛ) =====');
|
|
460
|
-
models.forEach(m => logInfo(`- ${m}`));
|
|
460
|
+
models.forEach((m) => logInfo(`- ${m}`));
|
|
461
461
|
logInfo('====================================');
|
|
462
462
|
|
|
463
463
|
// Устанавливаем default model как первую из файла, если не задана в .env
|
|
@@ -471,7 +471,7 @@ function loadModelsFromFile() {
|
|
|
471
471
|
|
|
472
472
|
return models;
|
|
473
473
|
} catch (error) {
|
|
474
|
-
logError(
|
|
474
|
+
logError('❌ Ошибка при чтении файла с моделями', error);
|
|
475
475
|
const fallback = resolvedDefaultModel ? [resolvedDefaultModel] : [getActiveModel()];
|
|
476
476
|
logDebug(`Fallback модели после ошибки: ${fallback.join(', ')}`);
|
|
477
477
|
return fallback;
|
|
@@ -493,7 +493,7 @@ export function getAvailableModelsFromFile() {
|
|
|
493
493
|
function getAuthKeysFromFile() {
|
|
494
494
|
try {
|
|
495
495
|
if (!fs.existsSync(AUTH_KEYS_FILE)) {
|
|
496
|
-
const template =
|
|
496
|
+
const template = '# Файл API-ключей для прокси\n# --------------------------------------------\n# В этом файле перечислены токены, которые\n# прокси будет считать «действительными».\n# Один ключ — одна строка без пробелов.\n#\n# 1) Хотите ОТКЛЮЧИТЬ авторизацию целиком?\n# Оставьте файл пустым — сервер перестанет\n# проверять заголовок Authorization.\n#\n# 2) Хотите разрешить доступ нескольким людям?\n# Впишите каждый ключ в отдельной строке:\n# d35ab3e1-a6f9-4d...\n# f2b1cd9c-1b2e-4a...\n#\n# Пустые строки и строки, начинающиеся с «#»,\n# игнорируются.';
|
|
497
497
|
try {
|
|
498
498
|
fs.writeFileSync(AUTH_KEYS_FILE, template, { encoding: 'utf8', flag: 'wx' });
|
|
499
499
|
logInfo(`Создан шаблон файла ключей: ${AUTH_KEYS_FILE}`);
|
|
@@ -504,8 +504,8 @@ function getAuthKeysFromFile() {
|
|
|
504
504
|
}
|
|
505
505
|
return fs.readFileSync(AUTH_KEYS_FILE, 'utf8')
|
|
506
506
|
.split('\n')
|
|
507
|
-
.map(l => l.trim())
|
|
508
|
-
.filter(l => l && !l.startsWith('#'));
|
|
507
|
+
.map((l) => l.trim())
|
|
508
|
+
.filter((l) => l && !l.startsWith('#'));
|
|
509
509
|
} catch (error) {
|
|
510
510
|
logError('Ошибка при чтении файла с ключами авторизации', error);
|
|
511
511
|
return [];
|
|
@@ -513,7 +513,7 @@ function getAuthKeysFromFile() {
|
|
|
513
513
|
}
|
|
514
514
|
|
|
515
515
|
export function isValidModel(modelName) {
|
|
516
|
-
if (!availableModels) availableModels = getAvailableModelsFromFile();
|
|
516
|
+
if (!availableModels) {availableModels = getAvailableModelsFromFile();}
|
|
517
517
|
return availableModels.includes(modelName);
|
|
518
518
|
}
|
|
519
519
|
|
|
@@ -523,9 +523,9 @@ export function getDefaultModel() {
|
|
|
523
523
|
}
|
|
524
524
|
|
|
525
525
|
export function getAllModels() {
|
|
526
|
-
if (!availableModels) availableModels = getAvailableModelsFromFile();
|
|
526
|
+
if (!availableModels) {availableModels = getAvailableModelsFromFile();}
|
|
527
527
|
return {
|
|
528
|
-
models: availableModels.map(model => ({
|
|
528
|
+
models: availableModels.map((model) => ({
|
|
529
529
|
id: model,
|
|
530
530
|
name: model,
|
|
531
531
|
description: `Модель ${model}`
|
|
@@ -534,7 +534,7 @@ export function getAllModels() {
|
|
|
534
534
|
}
|
|
535
535
|
|
|
536
536
|
export function getApiKeys() {
|
|
537
|
-
if (!authKeys) authKeys = getAuthKeysFromFile();
|
|
537
|
+
if (!authKeys) {authKeys = getAuthKeysFromFile();}
|
|
538
538
|
return authKeys;
|
|
539
539
|
}
|
|
540
540
|
|
|
@@ -544,14 +544,14 @@ function validateAndPrepareMessage(message) {
|
|
|
544
544
|
if (message === null || message === undefined) {
|
|
545
545
|
return { error: 'Сообщение не может быть пустым' };
|
|
546
546
|
}
|
|
547
|
-
if (typeof message === 'string') return { content: message };
|
|
547
|
+
if (typeof message === 'string') {return { content: message };}
|
|
548
548
|
if (Array.isArray(message)) {
|
|
549
|
-
const isValid = message.every(item =>
|
|
549
|
+
const isValid = message.every((item) =>
|
|
550
550
|
(item.type === 'text' && typeof item.text === 'string') ||
|
|
551
551
|
(item.type === 'image' && typeof item.image === 'string') ||
|
|
552
552
|
(item.type === 'file' && typeof item.file === 'string')
|
|
553
553
|
);
|
|
554
|
-
if (!isValid) return { error: 'Некорректная структура составного сообщения' };
|
|
554
|
+
if (!isValid) {return { error: 'Некорректная структура составного сообщения' };}
|
|
555
555
|
return { content: message };
|
|
556
556
|
}
|
|
557
557
|
return { error: 'Неподдерживаемый формат сообщения' };
|
|
@@ -620,7 +620,7 @@ async function resolveAuthToken(browserContext) {
|
|
|
620
620
|
if (!getAuthenticationStatus()) {
|
|
621
621
|
logInfo('Проверка авторизации...');
|
|
622
622
|
const authCheck = await checkAuthentication(browserContext);
|
|
623
|
-
if (!authCheck) return null;
|
|
623
|
+
if (!authCheck) {return null;}
|
|
624
624
|
}
|
|
625
625
|
|
|
626
626
|
if (!authToken) {
|
|
@@ -674,7 +674,7 @@ function buildPayloadV2(messageContent, model, chatId, parentId, files, systemMe
|
|
|
674
674
|
timestamp: Math.floor(Date.now() / 1000)
|
|
675
675
|
};
|
|
676
676
|
|
|
677
|
-
if (size) payload.size = size;
|
|
677
|
+
if (size) {payload.size = size;}
|
|
678
678
|
|
|
679
679
|
if (systemMessage) {
|
|
680
680
|
payload.system_message = systemMessage;
|
|
@@ -721,8 +721,8 @@ function parseNonSseCompletionBody(body) {
|
|
|
721
721
|
|
|
722
722
|
async function executeApiRequestWithNodeStreaming(apiUrl, payload, token, onChunk) {
|
|
723
723
|
try {
|
|
724
|
-
if (!token) return { success: false, error: 'Токен авторизации не найден' };
|
|
725
|
-
if (typeof fetch !== 'function') return { success: false, error: 'Fetch API is unavailable' };
|
|
724
|
+
if (!token) {return { success: false, error: 'Токен авторизации не найден' };}
|
|
725
|
+
if (typeof fetch !== 'function') {return { success: false, error: 'Fetch API is unavailable' };}
|
|
726
726
|
|
|
727
727
|
const response = await fetchWithQwenProxy(apiUrl, {
|
|
728
728
|
method: 'POST',
|
|
@@ -770,7 +770,7 @@ async function executeApiRequestWithNodeStreaming(apiUrl, payload, token, onChun
|
|
|
770
770
|
|
|
771
771
|
while (!finished) {
|
|
772
772
|
const { done, value } = await reader.read();
|
|
773
|
-
if (done) break;
|
|
773
|
+
if (done) {break;}
|
|
774
774
|
|
|
775
775
|
buffer += decoder.decode(value, { stream: true });
|
|
776
776
|
const lines = buffer.split('\n');
|
|
@@ -778,10 +778,10 @@ async function executeApiRequestWithNodeStreaming(apiUrl, payload, token, onChun
|
|
|
778
778
|
|
|
779
779
|
for (const rawLine of lines) {
|
|
780
780
|
const line = rawLine.trim();
|
|
781
|
-
if (!line || !line.startsWith('data:')) continue;
|
|
781
|
+
if (!line || !line.startsWith('data:')) {continue;}
|
|
782
782
|
|
|
783
783
|
const jsonStr = line.substring(5).trim();
|
|
784
|
-
if (!jsonStr) continue;
|
|
784
|
+
if (!jsonStr) {continue;}
|
|
785
785
|
if (jsonStr === '[DONE]') {
|
|
786
786
|
finished = true;
|
|
787
787
|
break;
|
|
@@ -801,8 +801,8 @@ async function executeApiRequestWithNodeStreaming(apiUrl, payload, token, onChun
|
|
|
801
801
|
break;
|
|
802
802
|
}
|
|
803
803
|
|
|
804
|
-
if (chunk['response.created']) responseId = chunk['response.created'].response_id;
|
|
805
|
-
if (chunk.response_id) responseId = chunk.response_id;
|
|
804
|
+
if (chunk['response.created']) {responseId = chunk['response.created'].response_id;}
|
|
805
|
+
if (chunk.response_id) {responseId = chunk.response_id;}
|
|
806
806
|
|
|
807
807
|
if (chunk.choices && chunk.choices[0]) {
|
|
808
808
|
const delta = chunk.choices[0].delta;
|
|
@@ -813,11 +813,11 @@ async function executeApiRequestWithNodeStreaming(apiUrl, payload, token, onChun
|
|
|
813
813
|
hasStreamedChunks = true;
|
|
814
814
|
}
|
|
815
815
|
}
|
|
816
|
-
if (delta && delta.status === 'finished') finished = true;
|
|
817
|
-
if (chunk.choices[0].finish_reason) finished = true;
|
|
816
|
+
if (delta && delta.status === 'finished') {finished = true;}
|
|
817
|
+
if (chunk.choices[0].finish_reason) {finished = true;}
|
|
818
818
|
}
|
|
819
819
|
|
|
820
|
-
if (chunk.usage) usage = chunk.usage;
|
|
820
|
+
if (chunk.usage) {usage = chunk.usage;}
|
|
821
821
|
} catch {
|
|
822
822
|
// Ignore broken chunks, keep reading stream.
|
|
823
823
|
}
|
|
@@ -872,7 +872,7 @@ async function executeApiRequest(page, apiUrl, payload, token, onChunk = null) {
|
|
|
872
872
|
return page.evaluate(async (data) => {
|
|
873
873
|
try {
|
|
874
874
|
const t = data.token;
|
|
875
|
-
if (!t) return { success: false, error: 'Токен авторизации не найден' };
|
|
875
|
+
if (!t) {return { success: false, error: 'Токен авторизации не найден' };}
|
|
876
876
|
|
|
877
877
|
const response = await fetch(data.apiUrl, {
|
|
878
878
|
method: 'POST',
|
|
@@ -936,15 +936,15 @@ async function executeApiRequest(page, apiUrl, payload, token, onChunk = null) {
|
|
|
936
936
|
|
|
937
937
|
while (!finished) {
|
|
938
938
|
const { done, value } = await reader.read();
|
|
939
|
-
if (done) break;
|
|
939
|
+
if (done) {break;}
|
|
940
940
|
buffer += decoder.decode(value, { stream: true });
|
|
941
941
|
const lines = buffer.split('\n');
|
|
942
942
|
buffer = lines.pop() || '';
|
|
943
943
|
|
|
944
944
|
for (const line of lines) {
|
|
945
|
-
if (!line.trim() || !line.startsWith('data: ')) continue;
|
|
945
|
+
if (!line.trim() || !line.startsWith('data: ')) {continue;}
|
|
946
946
|
const jsonStr = line.substring(6).trim();
|
|
947
|
-
if (!jsonStr) continue;
|
|
947
|
+
if (!jsonStr) {continue;}
|
|
948
948
|
try {
|
|
949
949
|
const chunk = JSON.parse(jsonStr);
|
|
950
950
|
|
|
@@ -959,13 +959,13 @@ async function executeApiRequest(page, apiUrl, payload, token, onChunk = null) {
|
|
|
959
959
|
break;
|
|
960
960
|
}
|
|
961
961
|
|
|
962
|
-
if (chunk['response.created']) responseId = chunk['response.created'].response_id;
|
|
962
|
+
if (chunk['response.created']) {responseId = chunk['response.created'].response_id;}
|
|
963
963
|
if (chunk.choices && chunk.choices[0]) {
|
|
964
964
|
const delta = chunk.choices[0].delta;
|
|
965
|
-
if (delta && delta.content) fullContent += delta.content;
|
|
966
|
-
if (delta && delta.status === 'finished') finished = true;
|
|
965
|
+
if (delta && delta.content) {fullContent += delta.content;}
|
|
966
|
+
if (delta && delta.status === 'finished') {finished = true;}
|
|
967
967
|
}
|
|
968
|
-
if (chunk.usage) usage = chunk.usage;
|
|
968
|
+
if (chunk.usage) {usage = chunk.usage;}
|
|
969
969
|
} catch { /* ignore parse errors for individual chunks */ }
|
|
970
970
|
}
|
|
971
971
|
}
|
|
@@ -1005,10 +1005,10 @@ async function handleApiError(response, tokenObj, message, model, chatId, parent
|
|
|
1005
1005
|
logError(`Ошибка при получении ответа: ${errorMessage}`);
|
|
1006
1006
|
|
|
1007
1007
|
// Логируем дополнительную информацию для отладки
|
|
1008
|
-
if (response.status) logDebug(`HTTP статус: ${response.status}`);
|
|
1009
|
-
if (response.errorBody) logDebug(`Тело ответа с ошибкой: ${response.errorBody.substring(0, 500)}${response.errorBody.length > 500 ? '...' : ''}`);
|
|
1010
|
-
if (response.error) logDebug(`Ошибка из response: ${response.error}`);
|
|
1011
|
-
if (response.statusText) logDebug(`StatusText: ${response.statusText}`);
|
|
1008
|
+
if (response.status) {logDebug(`HTTP статус: ${response.status}`);}
|
|
1009
|
+
if (response.errorBody) {logDebug(`Тело ответа с ошибкой: ${response.errorBody.substring(0, 500)}${response.errorBody.length > 500 ? '...' : ''}`);}
|
|
1010
|
+
if (response.error) {logDebug(`Ошибка из response: ${response.error}`);}
|
|
1011
|
+
if (response.statusText) {logDebug(`StatusText: ${response.statusText}`);}
|
|
1012
1012
|
logDebug(`Полный объект ответа: ${JSON.stringify(response, null, 2).substring(0, 1000)}`);
|
|
1013
1013
|
|
|
1014
1014
|
if (response.html && response.html.includes('Verification')) {
|
|
@@ -1085,7 +1085,7 @@ async function handleApiError(response, tokenObj, message, model, chatId, parent
|
|
|
1085
1085
|
// ─── Main public API ─────────────────────────────────────────────────────────
|
|
1086
1086
|
|
|
1087
1087
|
export async function sendMessage(message, model = null, chatId = null, parentId = null, files = null, tools = null, toolChoice = null, systemMessage = null, chatType = 't2t', size = null, waitForCompletion = true, retryCount = 0, onChunk = null) {
|
|
1088
|
-
if (!availableModels) availableModels = getAvailableModelsFromFile();
|
|
1088
|
+
if (!availableModels) {availableModels = getAvailableModelsFromFile();}
|
|
1089
1089
|
|
|
1090
1090
|
if (!chatId) {
|
|
1091
1091
|
const newChatResult = await createChatV2(model);
|
|
@@ -1119,10 +1119,10 @@ export async function sendMessage(message, model = null, chatId = null, parentId
|
|
|
1119
1119
|
}
|
|
1120
1120
|
|
|
1121
1121
|
const browserContext = getBrowserContext();
|
|
1122
|
-
if (!browserContext) return { error: 'Браузер не инициализирован', chatId };
|
|
1122
|
+
if (!browserContext) {return { error: 'Браузер не инициализирован', chatId };}
|
|
1123
1123
|
|
|
1124
1124
|
const tokenObj = await resolveAuthToken(browserContext);
|
|
1125
|
-
if (!tokenObj) return { error: 'Ошибка авторизации: не удалось получить токен', chatId };
|
|
1125
|
+
if (!tokenObj) {return { error: 'Ошибка авторизации: не удалось получить токен', chatId };}
|
|
1126
1126
|
|
|
1127
1127
|
let page = null;
|
|
1128
1128
|
try {
|
|
@@ -1136,7 +1136,7 @@ export async function sendMessage(message, model = null, chatId = null, parentId
|
|
|
1136
1136
|
if (!authToken) {
|
|
1137
1137
|
logWarn('Токен отсутствует перед отправкой запроса');
|
|
1138
1138
|
authToken = await page.evaluate(() => localStorage.getItem('token'));
|
|
1139
|
-
if (!authToken) return { error: 'Токен авторизации не найден. Требуется перезапуск в ручном режиме.', chatId };
|
|
1139
|
+
if (!authToken) {return { error: 'Токен авторизации не найден. Требуется перезапуск в ручном режиме.', chatId };}
|
|
1140
1140
|
saveAuthToken(authToken);
|
|
1141
1141
|
}
|
|
1142
1142
|
|
|
@@ -1245,15 +1245,15 @@ export async function sendMessage(message, model = null, chatId = null, parentId
|
|
|
1245
1245
|
|
|
1246
1246
|
function extractTaskId(data) {
|
|
1247
1247
|
const firstMsg = data.data?.messages?.[0];
|
|
1248
|
-
if (firstMsg?.extra?.wanx?.task_id) return firstMsg.extra.wanx.task_id;
|
|
1248
|
+
if (firstMsg?.extra?.wanx?.task_id) {return firstMsg.extra.wanx.task_id;}
|
|
1249
1249
|
return data.id || data.task_id || data.response_id || data.data?.message_id || null;
|
|
1250
1250
|
}
|
|
1251
1251
|
|
|
1252
1252
|
function extractVideoUrl(taskData) {
|
|
1253
|
-
if (taskData.content) return taskData.content;
|
|
1254
|
-
if (typeof taskData.result === 'string') return taskData.result;
|
|
1255
|
-
if (taskData.result?.url) return taskData.result.url;
|
|
1256
|
-
if (taskData.result?.video_url) return taskData.result.video_url;
|
|
1253
|
+
if (taskData.content) {return taskData.content;}
|
|
1254
|
+
if (typeof taskData.result === 'string') {return taskData.result;}
|
|
1255
|
+
if (taskData.result?.url) {return taskData.result.url;}
|
|
1256
|
+
if (taskData.result?.video_url) {return taskData.result.video_url;}
|
|
1257
1257
|
return null;
|
|
1258
1258
|
}
|
|
1259
1259
|
|
|
@@ -1269,7 +1269,7 @@ export function getAuthToken() {
|
|
|
1269
1269
|
|
|
1270
1270
|
export async function createChatV2(model = getDefaultModel(), title = 'Новый чат', retryCount = 0) {
|
|
1271
1271
|
const browserContext = getBrowserContext();
|
|
1272
|
-
if (!browserContext) return { error: 'Браузер не инициализирован' };
|
|
1272
|
+
if (!browserContext) {return { error: 'Браузер не инициализирован' };}
|
|
1273
1273
|
|
|
1274
1274
|
// Используем безопасный токен
|
|
1275
1275
|
const tokenObj = await getSafeToken(TOKEN_EXPIRY_WARNING_MS);
|
|
@@ -1291,7 +1291,7 @@ export async function createChatV2(model = getDefaultModel(), title = 'Новы
|
|
|
1291
1291
|
if (!authToken) {
|
|
1292
1292
|
logInfo('Получение токена авторизации для создания чата...');
|
|
1293
1293
|
authToken = await extractAuthToken(browserContext);
|
|
1294
|
-
if (!authToken) return { error: 'Не удалось получить токен авторизации' };
|
|
1294
|
+
if (!authToken) {return { error: 'Не удалось получить токен авторизации' };}
|
|
1295
1295
|
}
|
|
1296
1296
|
|
|
1297
1297
|
let page = null;
|
|
@@ -1308,7 +1308,7 @@ export async function createChatV2(model = getDefaultModel(), title = 'Новы
|
|
|
1308
1308
|
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${data.token}` },
|
|
1309
1309
|
body: JSON.stringify(data.payload)
|
|
1310
1310
|
});
|
|
1311
|
-
if (response.ok) return { success: true, data: await response.json() };
|
|
1311
|
+
if (response.ok) {return { success: true, data: await response.json() };}
|
|
1312
1312
|
return { success: false, status: response.status, errorBody: await response.text() };
|
|
1313
1313
|
} catch (error) {
|
|
1314
1314
|
return { success: false, error: error.toString() };
|
|
@@ -1349,7 +1349,7 @@ export async function createChatV2(model = getDefaultModel(), title = 'Новы
|
|
|
1349
1349
|
|
|
1350
1350
|
export async function testToken(token) {
|
|
1351
1351
|
const browserContext = getBrowserContext();
|
|
1352
|
-
if (!browserContext) return 'ERROR';
|
|
1352
|
+
if (!browserContext) {return 'ERROR';}
|
|
1353
1353
|
|
|
1354
1354
|
let page;
|
|
1355
1355
|
let shouldClosePage = false;
|
|
@@ -1377,16 +1377,16 @@ export async function testToken(token) {
|
|
|
1377
1377
|
}
|
|
1378
1378
|
}, requestBody);
|
|
1379
1379
|
|
|
1380
|
-
if (result.ok || result.status === 400) return 'OK';
|
|
1381
|
-
if (result.status === 401 || result.status === 403) return 'UNAUTHORIZED';
|
|
1382
|
-
if (result.status === 429) return 'RATELIMIT';
|
|
1380
|
+
if (result.ok || result.status === 400) {return 'OK';}
|
|
1381
|
+
if (result.status === 401 || result.status === 403) {return 'UNAUTHORIZED';}
|
|
1382
|
+
if (result.status === 429) {return 'RATELIMIT';}
|
|
1383
1383
|
return 'ERROR';
|
|
1384
1384
|
} catch (e) {
|
|
1385
1385
|
logError('testToken error', e);
|
|
1386
1386
|
return 'ERROR';
|
|
1387
1387
|
} finally {
|
|
1388
1388
|
if (page) {
|
|
1389
|
-
try { if (shouldClosePage) await page.close(); } catch { }
|
|
1389
|
+
try { if (shouldClosePage) {await page.close();} } catch { }
|
|
1390
1390
|
}
|
|
1391
1391
|
}
|
|
1392
1392
|
}
|
package/src/api/chatHistory.js
CHANGED
|
@@ -157,9 +157,9 @@ export function addUserMessage(chatId, content) {
|
|
|
157
157
|
let contentDesc;
|
|
158
158
|
if (Array.isArray(content)) {
|
|
159
159
|
// Составное сообщение (текст + изображения)
|
|
160
|
-
const textParts = content.filter(item => item.type === 'text');
|
|
161
|
-
const imageParts = content.filter(item => item.type === 'image');
|
|
162
|
-
const fileParts = content.filter(item => item.type === 'file');
|
|
160
|
+
const textParts = content.filter((item) => item.type === 'text');
|
|
161
|
+
const imageParts = content.filter((item) => item.type === 'image');
|
|
162
|
+
const fileParts = content.filter((item) => item.type === 'file');
|
|
163
163
|
|
|
164
164
|
contentDesc = `составное сообщение (${textParts.length} текст., ${imageParts.length} изобр., ${fileParts.length} файл.)`;
|
|
165
165
|
} else if (typeof content === 'object' && content !== null) {
|
|
@@ -170,10 +170,10 @@ export function addUserMessage(chatId, content) {
|
|
|
170
170
|
|
|
171
171
|
const message = {
|
|
172
172
|
id: messageId,
|
|
173
|
-
role:
|
|
173
|
+
role: 'user',
|
|
174
174
|
content: content,
|
|
175
175
|
timestamp: timestamp,
|
|
176
|
-
chat_type:
|
|
176
|
+
chat_type: 't2t'
|
|
177
177
|
};
|
|
178
178
|
|
|
179
179
|
logInfo(`Добавление сообщения пользователя в чат ${chatId}: ${contentDesc}`);
|
|
@@ -186,11 +186,11 @@ export function addAssistantMessage(chatId, content, info = {}) {
|
|
|
186
186
|
|
|
187
187
|
const message = {
|
|
188
188
|
id: messageId,
|
|
189
|
-
role:
|
|
189
|
+
role: 'assistant',
|
|
190
190
|
content: content,
|
|
191
191
|
timestamp: timestamp,
|
|
192
192
|
info: info,
|
|
193
|
-
chat_type:
|
|
193
|
+
chat_type: 't2t'
|
|
194
194
|
};
|
|
195
195
|
|
|
196
196
|
logInfo(`Добавление ответа ассистента в чат ${chatId}, длина: ${content.length}`);
|
|
@@ -225,8 +225,8 @@ export function getAllChats() {
|
|
|
225
225
|
|
|
226
226
|
let convertedCount = 0;
|
|
227
227
|
const chats = files
|
|
228
|
-
.filter(file => file.endsWith('.json'))
|
|
229
|
-
.map(file => {
|
|
228
|
+
.filter((file) => file.endsWith('.json'))
|
|
229
|
+
.map((file) => {
|
|
230
230
|
const chatId = file.replace('.json', '');
|
|
231
231
|
const chatData = loadHistory(chatId);
|
|
232
232
|
|
|
@@ -240,7 +240,7 @@ export function getAllChats() {
|
|
|
240
240
|
created: chatData.created || 0,
|
|
241
241
|
messageCount: chatData.messages ? chatData.messages.length : 0,
|
|
242
242
|
userMessageCount: chatData.messages ?
|
|
243
|
-
chatData.messages.filter(m => m.role === 'user').length : 0
|
|
243
|
+
chatData.messages.filter((m) => m.role === 'user').length : 0
|
|
244
244
|
};
|
|
245
245
|
});
|
|
246
246
|
|
|
@@ -285,24 +285,24 @@ export function deleteChatsAutomatically(criteria = {}) {
|
|
|
285
285
|
// Фильтрация по возрасту (в миллисекундах)
|
|
286
286
|
if (olderThan) {
|
|
287
287
|
const cutoffTime = Date.now() - olderThan;
|
|
288
|
-
const oldChatsCount = chatsToDelete.filter(chat => chat.created < cutoffTime).length;
|
|
288
|
+
const oldChatsCount = chatsToDelete.filter((chat) => chat.created < cutoffTime).length;
|
|
289
289
|
logInfo(`Чатов старше ${olderThan}мс (${new Date(cutoffTime).toLocaleString()}): ${oldChatsCount}`);
|
|
290
|
-
chatsToDelete = chatsToDelete.filter(chat => chat.created < cutoffTime);
|
|
290
|
+
chatsToDelete = chatsToDelete.filter((chat) => chat.created < cutoffTime);
|
|
291
291
|
}
|
|
292
292
|
|
|
293
293
|
if (userMessageCountLessThan !== undefined) {
|
|
294
|
-
const lowUserMsgChatsCount = chatsToDelete.filter(chat =>
|
|
294
|
+
const lowUserMsgChatsCount = chatsToDelete.filter((chat) =>
|
|
295
295
|
chat.userMessageCount < userMessageCountLessThan).length;
|
|
296
296
|
logInfo(`Чатов с менее чем ${userMessageCountLessThan} сообщений пользователя: ${lowUserMsgChatsCount}`);
|
|
297
|
-
chatsToDelete = chatsToDelete.filter(chat =>
|
|
297
|
+
chatsToDelete = chatsToDelete.filter((chat) =>
|
|
298
298
|
chat.userMessageCount < userMessageCountLessThan);
|
|
299
299
|
}
|
|
300
300
|
|
|
301
301
|
if (messageCountLessThan !== undefined) {
|
|
302
|
-
const lowMsgChatsCount = chatsToDelete.filter(chat =>
|
|
302
|
+
const lowMsgChatsCount = chatsToDelete.filter((chat) =>
|
|
303
303
|
chat.messageCount < messageCountLessThan).length;
|
|
304
304
|
logInfo(`Чатов с менее чем ${messageCountLessThan} сообщений всего: ${lowMsgChatsCount}`);
|
|
305
|
-
chatsToDelete = chatsToDelete.filter(chat =>
|
|
305
|
+
chatsToDelete = chatsToDelete.filter((chat) =>
|
|
306
306
|
chat.messageCount < messageCountLessThan);
|
|
307
307
|
}
|
|
308
308
|
|
|
@@ -311,8 +311,8 @@ export function deleteChatsAutomatically(criteria = {}) {
|
|
|
311
311
|
const sortedChats = [...chats].sort((a, b) => a.created - b.created);
|
|
312
312
|
const oldestChats = sortedChats.slice(0, chats.length - maxChats);
|
|
313
313
|
|
|
314
|
-
oldestChats.forEach(chat => {
|
|
315
|
-
if (!chatsToDelete.some(c => c.id === chat.id)) {
|
|
314
|
+
oldestChats.forEach((chat) => {
|
|
315
|
+
if (!chatsToDelete.some((c) => c.id === chat.id)) {
|
|
316
316
|
chatsToDelete.push(chat);
|
|
317
317
|
}
|
|
318
318
|
});
|
|
@@ -341,4 +341,4 @@ export function deleteChatsAutomatically(criteria = {}) {
|
|
|
341
341
|
error: error.message
|
|
342
342
|
};
|
|
343
343
|
}
|
|
344
|
-
}
|
|
344
|
+
}
|