GeminiBot
commited on
Commit
·
adb9567
1
Parent(s):
387c337
Ultra-deep browser simulation with Canvas mock
Browse files- package.json +3 -2
- src/duckai.ts +43 -39
package.json
CHANGED
|
@@ -4,6 +4,7 @@
|
|
| 4 |
"main": "src/server.ts",
|
| 5 |
"dependencies": {
|
| 6 |
"jsdom": "^25.0.1",
|
| 7 |
-
"user-agents": "^1.1.0"
|
|
|
|
| 8 |
}
|
| 9 |
-
}
|
|
|
|
| 4 |
"main": "src/server.ts",
|
| 5 |
"dependencies": {
|
| 6 |
"jsdom": "^25.0.1",
|
| 7 |
+
"user-agents": "^1.1.0",
|
| 8 |
+
"canvas": "^2.11.2"
|
| 9 |
}
|
| 10 |
+
}
|
src/duckai.ts
CHANGED
|
@@ -7,35 +7,57 @@ export class DuckAI {
|
|
| 7 |
try {
|
| 8 |
const jsScript = Buffer.from(vqdHash, 'base64').toString('utf-8');
|
| 9 |
|
| 10 |
-
|
| 11 |
-
const dom = new JSDOM(`<!DOCTYPE html><html><head></head><body><div id="app"></div></body></html>`, {
|
| 12 |
url: "https://duckduckgo.com/",
|
| 13 |
referrer: "https://duckduckgo.com/",
|
| 14 |
-
contentType: "text/html",
|
| 15 |
runScripts: "dangerously",
|
| 16 |
resources: "usable"
|
| 17 |
});
|
| 18 |
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
});
|
| 29 |
|
| 30 |
-
// Установка обязательных переменных DDG
|
| 31 |
dom.window.top.__DDG_BE_VERSION__ = 1;
|
| 32 |
dom.window.top.__DDG_FE_CHAT_HASH__ = 1;
|
| 33 |
|
| 34 |
-
// Выполняем скрипт
|
| 35 |
const result = await dom.window.eval(jsScript) as any;
|
| 36 |
|
| 37 |
if (!result || !result.client_hashes) {
|
| 38 |
-
throw new Error("Challenge
|
| 39 |
}
|
| 40 |
|
| 41 |
result.client_hashes[0] = ua;
|
|
@@ -56,39 +78,23 @@ export class DuckAI {
|
|
| 56 |
const headers: any = {
|
| 57 |
"User-Agent": ua,
|
| 58 |
"Accept": "text/event-stream",
|
| 59 |
-
"Accept-Language": "en-US,en;q=0.9",
|
| 60 |
-
"Referer": "https://duckduckgo.com/",
|
| 61 |
-
"Origin": "https://duckduckgo.com",
|
| 62 |
"x-vqd-accept": "1"
|
| 63 |
};
|
| 64 |
|
| 65 |
try {
|
| 66 |
-
// 1. Получаем статус
|
| 67 |
const statusRes = await fetch("https://duckduckgo.com/duckchat/v1/status?q=1", { headers });
|
| 68 |
-
if (!statusRes.ok) throw new Error(`DDG Status Fail: ${statusRes.status}`);
|
| 69 |
-
|
| 70 |
const hashHeader = statusRes.headers.get("x-vqd-hash-1");
|
| 71 |
-
if (!hashHeader) throw new Error("Missing x-vqd-hash-1
|
| 72 |
|
| 73 |
const solvedVqd = await this.solveChallenge(hashHeader, ua);
|
| 74 |
|
| 75 |
-
|
| 76 |
-
const chatRes = await fetch("https://duckduckgo.com/duckchat/v1/chat", {
|
| 77 |
method: "POST",
|
| 78 |
-
headers: {
|
| 79 |
-
...headers,
|
| 80 |
-
"x-vqd-hash-1": solvedVqd,
|
| 81 |
-
"Content-Type": "application/json"
|
| 82 |
-
},
|
| 83 |
body: JSON.stringify(request)
|
| 84 |
});
|
| 85 |
|
| 86 |
-
|
| 87 |
-
const errText = await chatRes.text();
|
| 88 |
-
throw new Error(`DDG Chat Fail: ${chatRes.status} - ${errText.substring(0, 100)}`);
|
| 89 |
-
}
|
| 90 |
-
|
| 91 |
-
const text = await chatRes.text();
|
| 92 |
let llmResponse = "";
|
| 93 |
const lines = text.split("\n");
|
| 94 |
for (const line of lines) {
|
|
@@ -99,11 +105,9 @@ export class DuckAI {
|
|
| 99 |
} catch (e) {}
|
| 100 |
}
|
| 101 |
}
|
| 102 |
-
return llmResponse.trim() || "⚠️
|
| 103 |
-
|
| 104 |
} catch (error: any) {
|
| 105 |
-
console.error("Scraping Detail:", error.message);
|
| 106 |
return `⚠️ Ошибка бэкенда: ${error.message}`;
|
| 107 |
}
|
| 108 |
}
|
| 109 |
-
}
|
|
|
|
| 7 |
try {
|
| 8 |
const jsScript = Buffer.from(vqdHash, 'base64').toString('utf-8');
|
| 9 |
|
| 10 |
+
const dom = new JSDOM(`<!DOCTYPE html><html><body></body></html>`, {
|
|
|
|
| 11 |
url: "https://duckduckgo.com/",
|
| 12 |
referrer: "https://duckduckgo.com/",
|
|
|
|
| 13 |
runScripts: "dangerously",
|
| 14 |
resources: "usable"
|
| 15 |
});
|
| 16 |
|
| 17 |
+
const window = dom.window as any;
|
| 18 |
+
|
| 19 |
+
// ГЛУБОКАЯ ЭМУЛЯЦИЯ
|
| 20 |
+
Object.defineProperties(window.navigator, {
|
| 21 |
+
userAgent: { value: ua },
|
| 22 |
+
platform: { value: 'Win32' },
|
| 23 |
+
webdriver: { value: false },
|
| 24 |
+
languages: { value: ['en-US', 'en'] }
|
| 25 |
+
});
|
| 26 |
+
|
| 27 |
+
window.screen = { width: 1920, height: 1080, availWidth: 1920, availHeight: 1080 };
|
| 28 |
+
window.chrome = { runtime: {} };
|
| 29 |
+
|
| 30 |
+
// Заглушка для Canvas (часто ищут её)
|
| 31 |
+
window.HTMLCanvasElement.prototype.getContext = () => ({
|
| 32 |
+
fillRect: () => {},
|
| 33 |
+
clearRect: () => {},
|
| 34 |
+
getImageData: () => ({ data: new Uint8ClampedArray(4) }),
|
| 35 |
+
putImageData: () => {},
|
| 36 |
+
createImageData: () => ({ data: new Uint8ClampedArray(4) }),
|
| 37 |
+
setTransform: () => {},
|
| 38 |
+
drawImage: () => {},
|
| 39 |
+
save: () => {},
|
| 40 |
+
restore: () => {},
|
| 41 |
+
beginPath: () => {},
|
| 42 |
+
moveTo: () => {},
|
| 43 |
+
lineTo: () => {},
|
| 44 |
+
closePath: () => {},
|
| 45 |
+
stroke: () => {},
|
| 46 |
+
translate: () => {},
|
| 47 |
+
scale: () => {},
|
| 48 |
+
rotate: () => {},
|
| 49 |
+
arc: () => {},
|
| 50 |
+
fill: () => {},
|
| 51 |
+
measureText: () => ({ width: 0 })
|
| 52 |
});
|
| 53 |
|
|
|
|
| 54 |
dom.window.top.__DDG_BE_VERSION__ = 1;
|
| 55 |
dom.window.top.__DDG_FE_CHAT_HASH__ = 1;
|
| 56 |
|
|
|
|
| 57 |
const result = await dom.window.eval(jsScript) as any;
|
| 58 |
|
| 59 |
if (!result || !result.client_hashes) {
|
| 60 |
+
throw new Error("Invalid Challenge Response");
|
| 61 |
}
|
| 62 |
|
| 63 |
result.client_hashes[0] = ua;
|
|
|
|
| 78 |
const headers: any = {
|
| 79 |
"User-Agent": ua,
|
| 80 |
"Accept": "text/event-stream",
|
|
|
|
|
|
|
|
|
|
| 81 |
"x-vqd-accept": "1"
|
| 82 |
};
|
| 83 |
|
| 84 |
try {
|
|
|
|
| 85 |
const statusRes = await fetch("https://duckduckgo.com/duckchat/v1/status?q=1", { headers });
|
|
|
|
|
|
|
| 86 |
const hashHeader = statusRes.headers.get("x-vqd-hash-1");
|
| 87 |
+
if (!hashHeader) throw new Error("Missing x-vqd-hash-1");
|
| 88 |
|
| 89 |
const solvedVqd = await this.solveChallenge(hashHeader, ua);
|
| 90 |
|
| 91 |
+
const response = await fetch("https://duckduckgo.com/duckchat/v1/chat", {
|
|
|
|
| 92 |
method: "POST",
|
| 93 |
+
headers: { ...headers, "x-vqd-hash-1": solvedVqd, "Content-Type": "application/json" },
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
body: JSON.stringify(request)
|
| 95 |
});
|
| 96 |
|
| 97 |
+
const text = await response.text();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
let llmResponse = "";
|
| 99 |
const lines = text.split("\n");
|
| 100 |
for (const line of lines) {
|
|
|
|
| 105 |
} catch (e) {}
|
| 106 |
}
|
| 107 |
}
|
| 108 |
+
return llmResponse.trim() || "⚠️ Ошибка потока.";
|
|
|
|
| 109 |
} catch (error: any) {
|
|
|
|
| 110 |
return `⚠️ Ошибка бэкенда: ${error.message}`;
|
| 111 |
}
|
| 112 |
}
|
| 113 |
+
}
|