GeminiBot commited on
Commit
3727a06
·
1 Parent(s): 2b6b3cb

Implement strict sequencer: start requests every 300ms to mimic human flow and avoid IP bans

Browse files
Files changed (1) hide show
  1. src/duckai.ts +27 -59
src/duckai.ts CHANGED
@@ -3,53 +3,32 @@ import { createHash } from "node:crypto";
3
  import { Buffer } from "node:buffer";
4
  import UserAgent from "user-agents";
5
 
 
6
  let activeRequests = 0;
7
- const MAX_CONCURRENT_CHATS = 30; // Увеличиваем до 30 для плотного потока
8
- const requestQueue: (() => void)[] = [];
9
-
10
- // Переменные для контроля темпа (Rate Limit)
11
- let lastStartTime = 0;
12
- const MIN_START_INTERVAL = 150; // Начинать новый запрос каждые 150мс (~6.6 зап/сек)
13
 
14
  export class DuckAI {
15
 
16
- // Ожидание слота + соблюдение темпа
17
- private async acquireSlot(reqId: string): Promise<void> {
18
- // 1. Ждем свободный слот из 20
19
- if (activeRequests >= MAX_CONCURRENT_CHATS) {
20
- console.log(`[${reqId}] [Queue] All slots full (${activeRequests}). Waiting...`);
21
- await new Promise((resolve) => requestQueue.push(() => resolve()));
22
- }
23
- activeRequests++;
24
-
25
- // 2. Соблюдаем темп старта (чтобы не пугать DDG всплесками)
26
  const now = Date.now();
27
- const timeSinceLastStart = now - lastStartTime;
28
- const wait = Math.max(0, MIN_START_INTERVAL - timeSinceLastStart);
29
 
30
- lastStartTime = now + wait; // Бронируем время старта
31
-
32
- if (wait > 0) {
33
- // console.log(`[${reqId}] [Queue] Pacing: waiting ${wait}ms...`);
34
- await new Promise(r => setTimeout(r, wait));
35
- }
36
- }
37
-
38
- private releaseSlot() {
39
- activeRequests--;
40
- if (requestQueue.length > 0) {
41
- const next = requestQueue.shift();
42
- if (next) next();
43
  }
44
  }
45
 
46
  private async solveChallenge(vqdHash: string, reqId: string): Promise<string> {
47
- const start = Date.now();
48
- const memBefore = process.memoryUsage().heapUsed / 1024 / 1024;
49
-
50
  try {
51
  const jsScript = Buffer.from(vqdHash, 'base64').toString('utf-8');
52
-
53
  const dom = new JSDOM(
54
  `<iframe id="jsa" sandbox="allow-scripts allow-same-origin" srcdoc="<!DOCTYPE html>
55
  <html>
@@ -91,17 +70,17 @@ export class DuckAI {
91
  dom.window.close();
92
  return solved;
93
  } catch (e: any) {
94
- console.error(`[${reqId}] [Challenge] ERROR: ${e.message}`);
95
- throw e;
96
  }
97
  }
98
 
99
  async chat(request: any): Promise<string> {
100
  const reqId = Math.random().toString(36).substring(7).toUpperCase();
101
 
102
- // Плавный вход в систему
103
- await this.acquireSlot(reqId);
104
 
 
105
  const startTime = Date.now();
106
  const userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36';
107
  const headers = {
@@ -112,29 +91,18 @@ export class DuckAI {
112
  };
113
 
114
  try {
115
- console.log(`[${reqId}] [Chat] START (Active: ${activeRequests}, Queued: ${requestQueue.length})`);
116
 
117
- let hashHeader = null;
118
- let statusAttempt = 0;
119
-
120
- while (!hashHeader && statusAttempt < 2) {
121
- const statusRes = await fetch("https://duckduckgo.com/duckchat/v1/status?q=1", { headers });
122
- hashHeader = statusRes.headers.get("x-vqd-hash-1");
123
-
124
- if (!hashHeader) {
125
- if (statusRes.status === 429) {
126
- await new Promise(r => setTimeout(r, 1000));
127
- } else {
128
- throw new Error(`Status ${statusRes.status}: No VQD`);
129
- }
130
- }
131
- statusAttempt++;
132
- }
133
 
134
- if (!hashHeader) throw new Error("Failed VQD after retries");
135
 
 
136
  const solvedVqd = await this.solveChallenge(hashHeader, reqId);
137
 
 
138
  const response = await fetch("https://duckduckgo.com/duckchat/v1/chat", {
139
  method: "POST",
140
  headers: { ...headers, "Content-Type": "application/json", "x-vqd-hash-1": solvedVqd },
@@ -167,9 +135,9 @@ export class DuckAI {
167
  console.error(`[${reqId}] [Chat] FAILED: ${error.message}`);
168
  throw error;
169
  } finally {
170
- this.releaseSlot();
171
  }
172
  }
173
 
174
  getAvailableModels() { return ["gpt-4o-mini", "gpt-5-mini", "openai/gpt-oss-120b"]; }
175
- }
 
3
  import { Buffer } from "node:buffer";
4
  import UserAgent from "user-agents";
5
 
6
+ // Глобальное управление потоком
7
  let activeRequests = 0;
8
+ const MAX_CONCURRENT = 50; // Сколько всего запросов может "висеть" одновременно
9
+ let lastRequestStartTime = 0;
10
+ const MIN_GAP_MS = 300; // ЖЕСТКИЙ ИНТЕРВАЛ: 3.3 запроса в секунду (безопасный порог для одного IP)
 
 
 
11
 
12
  export class DuckAI {
13
 
14
+ // Функция "Диспетчер" - гарантирует, что запросы стартуют строго по очереди с паузой
15
+ private async waitInQueue(reqId: string): Promise<void> {
 
 
 
 
 
 
 
 
16
  const now = Date.now();
 
 
17
 
18
+ // Вычисляем, когда этому запросу разрешено стартовать
19
+ const targetStartTime = Math.max(now, lastRequestStartTime + MIN_GAP_MS);
20
+ lastRequestStartTime = targetStartTime;
21
+
22
+ const waitTime = targetStartTime - now;
23
+ if (waitTime > 0) {
24
+ // console.log(`[${reqId}] [Queue] Waiting ${waitTime}ms to maintain pace...`);
25
+ await new Promise(r => setTimeout(r, waitTime));
 
 
 
 
 
26
  }
27
  }
28
 
29
  private async solveChallenge(vqdHash: string, reqId: string): Promise<string> {
 
 
 
30
  try {
31
  const jsScript = Buffer.from(vqdHash, 'base64').toString('utf-8');
 
32
  const dom = new JSDOM(
33
  `<iframe id="jsa" sandbox="allow-scripts allow-same-origin" srcdoc="<!DOCTYPE html>
34
  <html>
 
70
  dom.window.close();
71
  return solved;
72
  } catch (e: any) {
73
+ throw new Error(`Challenge Failed: ${e.message}`);
 
74
  }
75
  }
76
 
77
  async chat(request: any): Promise<string> {
78
  const reqId = Math.random().toString(36).substring(7).toUpperCase();
79
 
80
+ // 1. Становимся в очередь на старт
81
+ await this.waitInQueue(reqId);
82
 
83
+ activeRequests++;
84
  const startTime = Date.now();
85
  const userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36';
86
  const headers = {
 
91
  };
92
 
93
  try {
94
+ console.log(`[${reqId}] [Chat] EXECUTING (Parallel: ${activeRequests})`);
95
 
96
+ // Получаем токен
97
+ const statusRes = await fetch("https://duckduckgo.com/duckchat/v1/status?q=1", { headers });
98
+ const hashHeader = statusRes.headers.get("x-vqd-hash-1");
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
+ if (!hashHeader) throw new Error(`Status ${statusRes.status}: No VQD`);
101
 
102
+ // Решаем капчу
103
  const solvedVqd = await this.solveChallenge(hashHeader, reqId);
104
 
105
+ // Сам запрос
106
  const response = await fetch("https://duckduckgo.com/duckchat/v1/chat", {
107
  method: "POST",
108
  headers: { ...headers, "Content-Type": "application/json", "x-vqd-hash-1": solvedVqd },
 
135
  console.error(`[${reqId}] [Chat] FAILED: ${error.message}`);
136
  throw error;
137
  } finally {
138
+ activeRequests--;
139
  }
140
  }
141
 
142
  getAvailableModels() { return ["gpt-4o-mini", "gpt-5-mini", "openai/gpt-oss-120b"]; }
143
+ }