upload
Browse files- Dockerfile +33 -0
- README.md +1 -3
- api_test.py +26 -0
- cache/cache.json +10 -0
- endpoints/antibot.js +277 -0
- endpoints/cloudflare.js +79 -0
- endpoints/imageProcessor +181 -0
- endpoints/turnstile.js +84 -0
- index.js +179 -0
- package.json +22 -0
Dockerfile
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM node:20-bullseye
|
| 2 |
+
|
| 3 |
+
# Install ALL dependencies untuk OpenCV + Chrome
|
| 4 |
+
RUN apt update && apt install -y \
|
| 5 |
+
wget gnupg ca-certificates xvfb \
|
| 6 |
+
fonts-liberation libappindicator3-1 libasound2 libatk-bridge2.0-0 \
|
| 7 |
+
libatk1.0-0 libxss1 libnss3 libxcomposite1 libxdamage1 libxrandr2 libgbm1 \
|
| 8 |
+
python3 make g++ pkg-config cmake \
|
| 9 |
+
libcairo2-dev libjpeg-dev libpng-dev libgif-dev librsvg2-dev \
|
| 10 |
+
libopencv-dev \
|
| 11 |
+
&& wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \
|
| 12 |
+
&& apt install -y ./google-chrome-stable_current_amd64.deb \
|
| 13 |
+
&& rm google-chrome-stable_current_amd64.deb \
|
| 14 |
+
&& apt clean
|
| 15 |
+
|
| 16 |
+
WORKDIR /app
|
| 17 |
+
|
| 18 |
+
RUN mkdir -p /app/endpoints && \
|
| 19 |
+
mkdir -p /app/cache
|
| 20 |
+
|
| 21 |
+
COPY package*.json ./
|
| 22 |
+
|
| 23 |
+
# Install OpenCV dengan build from source
|
| 24 |
+
RUN npm install
|
| 25 |
+
|
| 26 |
+
COPY . .
|
| 27 |
+
|
| 28 |
+
EXPOSE 7860
|
| 29 |
+
|
| 30 |
+
CMD rm -f /tmp/.X99-lock && \
|
| 31 |
+
Xvfb :99 -screen 0 1024x768x24 & \
|
| 32 |
+
export DISPLAY=:99 && \
|
| 33 |
+
npm start
|
README.md
CHANGED
|
@@ -3,10 +3,8 @@ title: Antibot3
|
|
| 3 |
emoji: 👁
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: yellow
|
| 6 |
-
sdk:
|
| 7 |
sdk_version: 6.0.0
|
| 8 |
-
app_file: app.py
|
| 9 |
-
pinned: false
|
| 10 |
license: apache-2.0
|
| 11 |
short_description: antibot ai
|
| 12 |
---
|
|
|
|
| 3 |
emoji: 👁
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: yellow
|
| 6 |
+
sdk: docker
|
| 7 |
sdk_version: 6.0.0
|
|
|
|
|
|
|
| 8 |
license: apache-2.0
|
| 9 |
short_description: antibot ai
|
| 10 |
---
|
api_test.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
+
import httpx
|
| 3 |
+
|
| 4 |
+
async def main():
|
| 5 |
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
| 6 |
+
resp1 = await client.post(
|
| 7 |
+
"http://localhost:8080/cloudflare",
|
| 8 |
+
json={
|
| 9 |
+
"domain": "https://olamovies.watch/generate",
|
| 10 |
+
"mode": "iuam",
|
| 11 |
+
},
|
| 12 |
+
)
|
| 13 |
+
print(resp1.json())
|
| 14 |
+
|
| 15 |
+
resp2 = await client.post(
|
| 16 |
+
"http://localhost:8080/cloudflare",
|
| 17 |
+
json={
|
| 18 |
+
"domain": "https://lksfy.com/",
|
| 19 |
+
"siteKey": "0x4AAAAAAA49NnPZwQijgRoi",
|
| 20 |
+
"mode": "turnstile",
|
| 21 |
+
},
|
| 22 |
+
)
|
| 23 |
+
print(resp2.json())
|
| 24 |
+
|
| 25 |
+
if __name__ == "__main__":
|
| 26 |
+
asyncio.run(main())
|
cache/cache.json
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"{\"domain\":\"https://v2links.org\",\"mode\":\"iuam\"}": {
|
| 3 |
+
"timestamp": 1758616160392,
|
| 4 |
+
"value": {
|
| 5 |
+
"cf_clearance": "qqs5f4MpFMgA0v78Qmh_HYDWoKhbwqlQ57bTW5KeIr8-1758616161-1.2.1.1-D5PdpmheHl.26ssIRKQBsXzPtPPkSXntEZ_H9FUJVt7MMTS_BEE8iH.E48MDzKtFBLwZqRYxE_1GLo1gj3ChXrwatOGEJcmGRUwavy2qvGgUPizn7qufd.sW0ULfhaRO7Gz_H_eO1TU3iEqCzltEUDNk0SviKRFkF8ozbvJ91MW_qmO.qrjQorbu_jxcgJv5BHs6rTNOitWtVDAmYMDukASq0viHXIUAIvTM.LIfQ88",
|
| 6 |
+
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36",
|
| 7 |
+
"elapsed_time": 5.028
|
| 8 |
+
}
|
| 9 |
+
}
|
| 10 |
+
}
|
endpoints/antibot.js
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const fs = require('fs');
|
| 2 |
+
const path = require('path');
|
| 3 |
+
const { extractTextFromImage } = require('./imageProcessor');
|
| 4 |
+
|
| 5 |
+
function mapAnswer(soalArray, jawaban, botIndex) {
|
| 6 |
+
return jawaban;
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
function normalizeText(text) {
|
| 10 |
+
return text.toLowerCase().replace(/[^\w\s]/g, '').trim();
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
function isValueMatch(value, targetSoal) {
|
| 14 |
+
const numberMap = {
|
| 15 |
+
'0': 'zero', '1': 'one', '2': 'two', '3': 'three', '4': 'four',
|
| 16 |
+
'5': 'five', '6': 'six', '7': 'seven', '8': 'eight', '9': 'nine', '10': 'ten',
|
| 17 |
+
'zero': '0', 'one': '1', 'two': '2', 'three': '3', 'four': '4',
|
| 18 |
+
'five': '5', 'six': '6', 'seven': '7', 'eight': '8', 'nine': '9', 'ten': '10',
|
| 19 |
+
'I': '1', 'II': '2', 'III': '3', 'IV': '4', 'V': '5',
|
| 20 |
+
'VI': '6', 'VII': '7', 'VIII': '8', 'IX': '9', 'X': '10',
|
| 21 |
+
'slx': '6', 's1x': '6', 'six': '6',
|
| 22 |
+
'f0ur': '4', 'f0r': '4', 'fuor': '4',
|
| 23 |
+
'f1ve': '5', 'fiv': '5', 'f1v': '5',
|
| 24 |
+
'e1ght': '8', 'elght': '8', 'eight': '8',
|
| 25 |
+
'n1ne': '9', 'n1n': '9', 'nne': '9',
|
| 26 |
+
'se7en': '7', 'sven': '7', 'seven': '7',
|
| 27 |
+
'thre': '3', 'tree': '3', 'thr33': '3',
|
| 28 |
+
'tw0': '2', 'to': '2', 'tw': '2',
|
| 29 |
+
'0ne': '1', 'on': '1', 'oen': '1'
|
| 30 |
+
};
|
| 31 |
+
|
| 32 |
+
const leetMap = {
|
| 33 |
+
'O': '0', 'o': '0',
|
| 34 |
+
'I': '1', 'i': '1', 'l': '1',
|
| 35 |
+
'Z': '2', 'z': '2',
|
| 36 |
+
'E': '3', 'e': '3',
|
| 37 |
+
'A': '4', 'a': '4',
|
| 38 |
+
'S': '5', 's': '5',
|
| 39 |
+
'G': '6', 'g': '6',
|
| 40 |
+
'T': '7', 't': '7',
|
| 41 |
+
'B': '8', 'b': '8',
|
| 42 |
+
'Q': '9', 'q': '9',
|
| 43 |
+
'U': '4', 'u': '4',
|
| 44 |
+
'R': '2', 'r': '2',
|
| 45 |
+
'N': '9', 'n': '9',
|
| 46 |
+
'V': '7', 'v': '7'
|
| 47 |
+
};
|
| 48 |
+
|
| 49 |
+
const normalizedValue = normalizeText(value);
|
| 50 |
+
const normalizedSoal = normalizeText(targetSoal);
|
| 51 |
+
|
| 52 |
+
if (normalizedValue === normalizedSoal) {
|
| 53 |
+
return true;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
const convertLeet = (text) => {
|
| 57 |
+
return text.split('').map(char => leetMap[char] || char).join('');
|
| 58 |
+
};
|
| 59 |
+
|
| 60 |
+
const leetValue = convertLeet(value);
|
| 61 |
+
const leetSoal = convertLeet(targetSoal);
|
| 62 |
+
|
| 63 |
+
if (leetValue === normalizedSoal) return true;
|
| 64 |
+
if (normalizedValue === leetSoal) return true;
|
| 65 |
+
if (leetValue === leetSoal) return true;
|
| 66 |
+
|
| 67 |
+
const mappedValue = numberMap[normalizedValue] || numberMap[value] || normalizedValue;
|
| 68 |
+
const mappedSoal = numberMap[normalizedSoal] || numberMap[targetSoal] || normalizedSoal;
|
| 69 |
+
|
| 70 |
+
if (mappedValue === normalizedSoal) return true;
|
| 71 |
+
if (normalizedValue === mappedSoal) return true;
|
| 72 |
+
if (mappedValue === mappedSoal) return true;
|
| 73 |
+
|
| 74 |
+
const similarity = calculateSimilarity(normalizedValue, normalizedSoal);
|
| 75 |
+
if (similarity >= 0.8) {
|
| 76 |
+
return true;
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
try {
|
| 80 |
+
const valueResult = evaluateSimpleMath(value);
|
| 81 |
+
const soalResult = evaluateSimpleMath(targetSoal);
|
| 82 |
+
if (valueResult !== null && soalResult !== null && valueResult === soalResult) {
|
| 83 |
+
return true;
|
| 84 |
+
}
|
| 85 |
+
} catch (e) {}
|
| 86 |
+
|
| 87 |
+
return false;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
function calculateSimilarity(str1, str2) {
|
| 91 |
+
if (str1 === str2) return 1;
|
| 92 |
+
if (str1.length === 0 || str2.length === 0) return 0;
|
| 93 |
+
|
| 94 |
+
const longer = str1.length > str2.length ? str1 : str2;
|
| 95 |
+
const shorter = str1.length > str2.length ? str2 : str1;
|
| 96 |
+
|
| 97 |
+
if (longer.includes(shorter)) return shorter.length / longer.length;
|
| 98 |
+
|
| 99 |
+
let matches = 0;
|
| 100 |
+
for (let i = 0; i < shorter.length; i++) {
|
| 101 |
+
if (shorter[i] === longer[i]) matches++;
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
return matches / longer.length;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
function evaluateSimpleMath(expression) {
|
| 108 |
+
if (!expression) return null;
|
| 109 |
+
const cleanExpr = expression.toString().replace(/[^\d+\-*/.()]/g, '');
|
| 110 |
+
if (!cleanExpr) return null;
|
| 111 |
+
try {
|
| 112 |
+
if (cleanExpr.length > 10) return null;
|
| 113 |
+
const result = Function(`"use strict"; return (${cleanExpr})`)();
|
| 114 |
+
return typeof result === 'number' ? result : null;
|
| 115 |
+
} catch (e) {
|
| 116 |
+
return null;
|
| 117 |
+
}
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
function parseSoalText(text) {
|
| 121 |
+
const delimiters = /[.,:;\\/\s]+/;
|
| 122 |
+
let parts = text.split(delimiters).filter(part => part.trim() !== '');
|
| 123 |
+
if (parts.length === 1) {
|
| 124 |
+
parts = text.split(/\s+/).filter(part => part.trim() !== '');
|
| 125 |
+
}
|
| 126 |
+
return parts;
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
async function antibot(data) {
|
| 130 |
+
try {
|
| 131 |
+
const { main, bots } = data;
|
| 132 |
+
|
| 133 |
+
const mainBuffer = Buffer.from(main, 'base64');
|
| 134 |
+
const mainText = await extractTextFromImage(mainBuffer);
|
| 135 |
+
|
| 136 |
+
const soalArray = parseSoalText(mainText.response);
|
| 137 |
+
|
| 138 |
+
if (soalArray.length === 0) {
|
| 139 |
+
throw new Error('Tidak ada soal yang terdeteksi');
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
const botResults = [];
|
| 143 |
+
|
| 144 |
+
for (let i = 0; i < bots.length; i++) {
|
| 145 |
+
const bot = bots[i];
|
| 146 |
+
try {
|
| 147 |
+
const botBuffer = Buffer.from(bot.img, 'base64');
|
| 148 |
+
const botText = await extractTextFromImage(botBuffer);
|
| 149 |
+
const mappedValue = mapAnswer(soalArray, botText.response, i);
|
| 150 |
+
|
| 151 |
+
botResults.push({
|
| 152 |
+
id: bot.id,
|
| 153 |
+
text: botText.response,
|
| 154 |
+
value: mappedValue,
|
| 155 |
+
normalized: normalizeText(botText.response)
|
| 156 |
+
});
|
| 157 |
+
|
| 158 |
+
} catch (error) {
|
| 159 |
+
botResults.push({
|
| 160 |
+
id: bot.id,
|
| 161 |
+
text: '',
|
| 162 |
+
value: '',
|
| 163 |
+
normalized: '',
|
| 164 |
+
error: error.message
|
| 165 |
+
});
|
| 166 |
+
}
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
const result = [];
|
| 170 |
+
const usedIds = new Set();
|
| 171 |
+
let matchedCount = 0;
|
| 172 |
+
|
| 173 |
+
for (let i = 0; i < soalArray.length; i++) {
|
| 174 |
+
const targetSoal = soalArray[i];
|
| 175 |
+
let foundId = null;
|
| 176 |
+
|
| 177 |
+
for (const bot of botResults) {
|
| 178 |
+
if (!usedIds.has(bot.id) && bot.value && bot.value.trim() !== '' &&
|
| 179 |
+
isValueMatch(bot.value, targetSoal)) {
|
| 180 |
+
foundId = bot.id;
|
| 181 |
+
usedIds.add(bot.id);
|
| 182 |
+
matchedCount++;
|
| 183 |
+
break;
|
| 184 |
+
}
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
result.push({
|
| 188 |
+
id: foundId,
|
| 189 |
+
soal: targetSoal,
|
| 190 |
+
matchType: foundId ? 'exact' : 'none'
|
| 191 |
+
});
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
if (matchedCount < soalArray.length) {
|
| 195 |
+
for (let i = 0; i < result.length; i++) {
|
| 196 |
+
if (!result[i].id) {
|
| 197 |
+
const targetSoal = soalArray[i];
|
| 198 |
+
const normalizedSoal = normalizeText(targetSoal);
|
| 199 |
+
|
| 200 |
+
for (const bot of botResults) {
|
| 201 |
+
if (!usedIds.has(bot.id) && bot.normalized && bot.normalized.trim() !== '') {
|
| 202 |
+
if (bot.normalized === normalizedSoal) {
|
| 203 |
+
result[i].id = bot.id;
|
| 204 |
+
result[i].matchType = 'normalized';
|
| 205 |
+
usedIds.add(bot.id);
|
| 206 |
+
matchedCount++;
|
| 207 |
+
break;
|
| 208 |
+
}
|
| 209 |
+
}
|
| 210 |
+
}
|
| 211 |
+
}
|
| 212 |
+
}
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
if (matchedCount >= 2) {
|
| 216 |
+
for (let i = 0; i < result.length; i++) {
|
| 217 |
+
if (!result[i].id) {
|
| 218 |
+
for (const bot of botResults) {
|
| 219 |
+
if (!usedIds.has(bot.id) && bot.value && bot.value.trim() !== '') {
|
| 220 |
+
result[i].id = bot.id;
|
| 221 |
+
result[i].matchType = 'fallback';
|
| 222 |
+
usedIds.add(bot.id);
|
| 223 |
+
break;
|
| 224 |
+
}
|
| 225 |
+
}
|
| 226 |
+
}
|
| 227 |
+
}
|
| 228 |
+
} else if (matchedCount === 1) {
|
| 229 |
+
for (let i = 0; i < result.length; i++) {
|
| 230 |
+
if (!result[i].id) {
|
| 231 |
+
result[i].id = 'invalid';
|
| 232 |
+
result[i].matchType = 'invalid';
|
| 233 |
+
}
|
| 234 |
+
}
|
| 235 |
+
} else {
|
| 236 |
+
for (let i = 0; i < result.length; i++) {
|
| 237 |
+
result[i].id = 'invalid';
|
| 238 |
+
result[i].matchType = 'invalid';
|
| 239 |
+
}
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
for (let i = 0; i < result.length; i++) {
|
| 243 |
+
if (!result[i].id) {
|
| 244 |
+
result[i].id = 'invalid';
|
| 245 |
+
result[i].matchType = 'invalid';
|
| 246 |
+
}
|
| 247 |
+
}
|
| 248 |
+
|
| 249 |
+
return {
|
| 250 |
+
success: true,
|
| 251 |
+
data: {
|
| 252 |
+
soal: soalArray,
|
| 253 |
+
soalLeet: soalArray,
|
| 254 |
+
botResults: botResults,
|
| 255 |
+
result: result.map(r => ({ id: r.id })),
|
| 256 |
+
debug: {
|
| 257 |
+
parsedSoal: soalArray,
|
| 258 |
+
matches: result.map(r => ({ id: r.id, matchType: r.matchType, soal: r.soal })),
|
| 259 |
+
totalMatches: matchedCount
|
| 260 |
+
}
|
| 261 |
+
}
|
| 262 |
+
};
|
| 263 |
+
|
| 264 |
+
} catch (error) {
|
| 265 |
+
return {
|
| 266 |
+
success: false,
|
| 267 |
+
error: error.message,
|
| 268 |
+
data: {
|
| 269 |
+
soal: [],
|
| 270 |
+
botResults: [],
|
| 271 |
+
result: []
|
| 272 |
+
}
|
| 273 |
+
};
|
| 274 |
+
}
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
module.exports = antibot;
|
endpoints/cloudflare.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
async function cloudflare(data, page) {
|
| 2 |
+
return new Promise(async (resolve, reject) => {
|
| 3 |
+
if (!data.domain) return reject(new Error("Missing domain parameter"))
|
| 4 |
+
|
| 5 |
+
const startTime = Date.now()
|
| 6 |
+
let isResolved = false
|
| 7 |
+
let userAgent = null
|
| 8 |
+
|
| 9 |
+
const cl = setTimeout(() => {
|
| 10 |
+
if (!isResolved) {
|
| 11 |
+
isResolved = true
|
| 12 |
+
const elapsedTime = (Date.now() - startTime) / 1000
|
| 13 |
+
resolve({
|
| 14 |
+
cf_clearance: null,
|
| 15 |
+
user_agent: userAgent,
|
| 16 |
+
elapsed_time: elapsedTime,
|
| 17 |
+
})
|
| 18 |
+
}
|
| 19 |
+
}, 20000)
|
| 20 |
+
|
| 21 |
+
try {
|
| 22 |
+
if (data.proxy?.username && data.proxy?.password) {
|
| 23 |
+
await page.authenticate({
|
| 24 |
+
username: data.proxy.username,
|
| 25 |
+
password: data.proxy.password,
|
| 26 |
+
})
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
page.removeAllListeners("request")
|
| 30 |
+
page.removeAllListeners("response")
|
| 31 |
+
await page.setRequestInterception(true)
|
| 32 |
+
|
| 33 |
+
page.on("request", async (req) => {
|
| 34 |
+
try {
|
| 35 |
+
await req.continue()
|
| 36 |
+
} catch (_) {}
|
| 37 |
+
})
|
| 38 |
+
|
| 39 |
+
page.on("response", async (res) => {
|
| 40 |
+
try {
|
| 41 |
+
const url = res.url()
|
| 42 |
+
if (url.includes("/cdn-cgi/challenge-platform/")) {
|
| 43 |
+
const headers = res.headers()
|
| 44 |
+
if (headers["set-cookie"]) {
|
| 45 |
+
const match = headers["set-cookie"].match(/cf_clearance=([^;]+)/)
|
| 46 |
+
if (match) {
|
| 47 |
+
const cf_clearance = match[1]
|
| 48 |
+
const userAgent = (await res.request().headers())["user-agent"]
|
| 49 |
+
const elapsedTime = (Date.now() - startTime) / 1000
|
| 50 |
+
|
| 51 |
+
if (!isResolved) {
|
| 52 |
+
isResolved = true
|
| 53 |
+
clearTimeout(cl)
|
| 54 |
+
|
| 55 |
+
resolve({
|
| 56 |
+
cf_clearance,
|
| 57 |
+
user_agent: userAgent,
|
| 58 |
+
elapsed_time: elapsedTime,
|
| 59 |
+
})
|
| 60 |
+
}
|
| 61 |
+
}
|
| 62 |
+
}
|
| 63 |
+
}
|
| 64 |
+
} catch (_) {}
|
| 65 |
+
})
|
| 66 |
+
|
| 67 |
+
await page.goto(data.domain, { waitUntil: "domcontentloaded" })
|
| 68 |
+
userAgent = await page.evaluate(() => navigator.userAgent)
|
| 69 |
+
} catch (err) {
|
| 70 |
+
if (!isResolved) {
|
| 71 |
+
isResolved = true
|
| 72 |
+
clearTimeout(cl)
|
| 73 |
+
reject(err)
|
| 74 |
+
}
|
| 75 |
+
}
|
| 76 |
+
})
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
module.exports = cloudflare
|
endpoints/imageProcessor
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const fs = require('fs');
|
| 2 |
+
const https = require('https');
|
| 3 |
+
|
| 4 |
+
// API Keys configuration
|
| 5 |
+
const API_KEYS = [
|
| 6 |
+
"fw_3ZNhzPGSUhHBxTL5PuB5Cpkv",
|
| 7 |
+
"fw_3ZchAJSfZBupby3hnESzSkbk"
|
| 8 |
+
];
|
| 9 |
+
|
| 10 |
+
function getRandomApiKey() {
|
| 11 |
+
return API_KEYS[Math.floor(Math.random() * API_KEYS.length)];
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
async function extractTextFromImage(imagePath) {
|
| 15 |
+
return new Promise(async (resolve, reject) => {
|
| 16 |
+
try {
|
| 17 |
+
// Baca file gambar dan encode ke base64
|
| 18 |
+
const imageBuffer = fs.readFileSync(imagePath);
|
| 19 |
+
const base64Image = imageBuffer.toString('base64');
|
| 20 |
+
|
| 21 |
+
// Dapatkan API key secara random
|
| 22 |
+
const apiKey = getRandomApiKey();
|
| 23 |
+
|
| 24 |
+
// Request payload untuk Fireworks.ai
|
| 25 |
+
const requestData = JSON.stringify({
|
| 26 |
+
model: "accounts/fireworks/models/llama4-maverick-instruct-basic",
|
| 27 |
+
max_tokens: 1024,
|
| 28 |
+
temperature: 0.1, // Temperature rendah untuk hasil yang lebih konsisten
|
| 29 |
+
messages: [
|
| 30 |
+
{
|
| 31 |
+
role: "user",
|
| 32 |
+
content: [
|
| 33 |
+
{
|
| 34 |
+
type: "text",
|
| 35 |
+
text: "Berikan text yang ada di gambar ini saja, tidak ada informasi lain cukup yang ada di gambar saja, jangan ada text lain kalo bukan dari gambar nya. dan ini adalah pembatas iya _ , : ; dan tolong tiap text berbeda beda kalo ada pembatas itu kirim satu kalimat saja"
|
| 36 |
+
},
|
| 37 |
+
{
|
| 38 |
+
type: "image_url",
|
| 39 |
+
image_url: {
|
| 40 |
+
url: `data:image/png;base64,${base64Image}`
|
| 41 |
+
}
|
| 42 |
+
}
|
| 43 |
+
]
|
| 44 |
+
}
|
| 45 |
+
]
|
| 46 |
+
});
|
| 47 |
+
|
| 48 |
+
const options = {
|
| 49 |
+
hostname: 'api.fireworks.ai',
|
| 50 |
+
path: '/inference/v1/chat/completions',
|
| 51 |
+
method: 'POST',
|
| 52 |
+
headers: {
|
| 53 |
+
'Accept': 'application/json',
|
| 54 |
+
'Content-Type': 'application/json',
|
| 55 |
+
'Authorization': `Bearer ${apiKey}`,
|
| 56 |
+
'Content-Length': Buffer.byteLength(requestData)
|
| 57 |
+
}
|
| 58 |
+
};
|
| 59 |
+
|
| 60 |
+
const req = https.request(options, (res) => {
|
| 61 |
+
let data = '';
|
| 62 |
+
|
| 63 |
+
res.on('data', (chunk) => {
|
| 64 |
+
data += chunk;
|
| 65 |
+
});
|
| 66 |
+
|
| 67 |
+
res.on('end', () => {
|
| 68 |
+
try {
|
| 69 |
+
const response = JSON.parse(data);
|
| 70 |
+
|
| 71 |
+
if (response.choices && response.choices[0] && response.choices[0].message) {
|
| 72 |
+
const text = response.choices[0].message.content;
|
| 73 |
+
resolve({ status: true, response: text });
|
| 74 |
+
} else {
|
| 75 |
+
console.error('API Response structure unexpected:', response);
|
| 76 |
+
resolve({ status: false, response: 'Struktur respons tidak sesuai' });
|
| 77 |
+
}
|
| 78 |
+
} catch (error) {
|
| 79 |
+
console.error('Error parsing response:', error);
|
| 80 |
+
reject(error);
|
| 81 |
+
}
|
| 82 |
+
});
|
| 83 |
+
});
|
| 84 |
+
|
| 85 |
+
req.on('error', (error) => {
|
| 86 |
+
console.error('Request error:', error);
|
| 87 |
+
reject(error);
|
| 88 |
+
});
|
| 89 |
+
|
| 90 |
+
req.write(requestData);
|
| 91 |
+
req.end();
|
| 92 |
+
|
| 93 |
+
} catch (error) {
|
| 94 |
+
reject(error);
|
| 95 |
+
}
|
| 96 |
+
});
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
// Fungsi untuk upload gambar ke hosting (opsional)
|
| 100 |
+
async function uploadImageToHosting(buffer) {
|
| 101 |
+
try {
|
| 102 |
+
// Karena kita menggunakan base64 langsung, fungsi ini mungkin tidak diperlukan
|
| 103 |
+
// Tapi tetap disediakan untuk kompatibilitas
|
| 104 |
+
return null;
|
| 105 |
+
} catch (error) {
|
| 106 |
+
console.error("Gagal mengunggah gambar:", error);
|
| 107 |
+
return null;
|
| 108 |
+
}
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
// Fungsi untuk analisis gambar yang lebih detail
|
| 112 |
+
async function analyzeImage(imagePath) {
|
| 113 |
+
try {
|
| 114 |
+
const imageBuffer = fs.readFileSync(imagePath);
|
| 115 |
+
const base64Image = imageBuffer.toString('base64');
|
| 116 |
+
const apiKey = getRandomApiKey();
|
| 117 |
+
|
| 118 |
+
const payload = {
|
| 119 |
+
model: "accounts/fireworks/models/llama4-maverick-instruct-basic",
|
| 120 |
+
max_tokens: 1024,
|
| 121 |
+
temperature: 0.6,
|
| 122 |
+
messages: [
|
| 123 |
+
{
|
| 124 |
+
role: "user",
|
| 125 |
+
content: [
|
| 126 |
+
{ type: "text", text: "Berikan text yang ada di gambar ini saja, tidak ada informasi lain cukup yang ada di gambar saja, jangan ada text lain kalo bukan dari gambar nya. dan ini adalah pembatas iya _ , : ; dan tolong tiap text berbeda beda kalo ada pembatas itu kirim satu kalimat saja" },
|
| 127 |
+
{ type: "image_url", image_url: { url: `data:image/jpeg;base64,${base64Image}` } }
|
| 128 |
+
]
|
| 129 |
+
}
|
| 130 |
+
]
|
| 131 |
+
};
|
| 132 |
+
|
| 133 |
+
const response = await fetch("https://api.fireworks.ai/inference/v1/chat/completions", {
|
| 134 |
+
method: "POST",
|
| 135 |
+
headers: {
|
| 136 |
+
"Accept": "application/json",
|
| 137 |
+
"Content-Type": "application/json",
|
| 138 |
+
"Authorization": `Bearer ${apiKey}`
|
| 139 |
+
},
|
| 140 |
+
body: JSON.stringify(payload)
|
| 141 |
+
});
|
| 142 |
+
|
| 143 |
+
if (!response.ok) {
|
| 144 |
+
const errorText = await response.text();
|
| 145 |
+
console.error("API Error response:", errorText);
|
| 146 |
+
throw new Error(`HTTP error! status: ${response.status}`);
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
const data = await response.json();
|
| 150 |
+
return data.choices?.[0]?.message?.content || "Tidak bisa menganalisis gambar ini";
|
| 151 |
+
} catch (error) {
|
| 152 |
+
console.error("Error analyzing image:", error);
|
| 153 |
+
return "Error saat menganalisis gambar";
|
| 154 |
+
}
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
// Contoh penggunaan
|
| 158 |
+
async function main() {
|
| 159 |
+
try {
|
| 160 |
+
const result = await extractTextFromImage('main_instruction.png');
|
| 161 |
+
if (result.status) {
|
| 162 |
+
console.log('Teks yang diekstrak:', result.response);
|
| 163 |
+
} else {
|
| 164 |
+
console.log('Gagal:', result.response);
|
| 165 |
+
}
|
| 166 |
+
} catch (error) {
|
| 167 |
+
console.error('Error:', error);
|
| 168 |
+
}
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
// Jalankan jika file dijalankan langsung
|
| 172 |
+
if (require.main === module) {
|
| 173 |
+
main();
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
module.exports = {
|
| 177 |
+
extractTextFromImage,
|
| 178 |
+
uploadImageToHosting,
|
| 179 |
+
analyzeImage,
|
| 180 |
+
getRandomApiKey
|
| 181 |
+
};
|
endpoints/turnstile.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
async function turnstile({ domain, proxy, siteKey }, page) {
|
| 2 |
+
if (!domain) throw new Error("Missing domain parameter");
|
| 3 |
+
if (!siteKey) throw new Error("Missing siteKey parameter");
|
| 4 |
+
|
| 5 |
+
const timeout = global.timeOut || 60000;
|
| 6 |
+
let isResolved = false;
|
| 7 |
+
|
| 8 |
+
const cl = setTimeout(async () => {
|
| 9 |
+
if (!isResolved) {
|
| 10 |
+
throw new Error("Timeout Error");
|
| 11 |
+
}
|
| 12 |
+
}, timeout);
|
| 13 |
+
|
| 14 |
+
try {
|
| 15 |
+
if (proxy?.username && proxy?.password) {
|
| 16 |
+
await page.authenticate({
|
| 17 |
+
username: proxy.username,
|
| 18 |
+
password: proxy.password,
|
| 19 |
+
});
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
const htmlContent = `
|
| 23 |
+
<!DOCTYPE html>
|
| 24 |
+
<html lang="en">
|
| 25 |
+
<body>
|
| 26 |
+
<div class="turnstile"></div>
|
| 27 |
+
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback" defer></script>
|
| 28 |
+
<script>
|
| 29 |
+
window.onloadTurnstileCallback = function () {
|
| 30 |
+
turnstile.render('.turnstile', {
|
| 31 |
+
sitekey: '${siteKey}',
|
| 32 |
+
callback: function (token) {
|
| 33 |
+
var c = document.createElement('input');
|
| 34 |
+
c.type = 'hidden';
|
| 35 |
+
c.name = 'cf-response';
|
| 36 |
+
c.value = token;
|
| 37 |
+
document.body.appendChild(c);
|
| 38 |
+
},
|
| 39 |
+
});
|
| 40 |
+
};
|
| 41 |
+
</script>
|
| 42 |
+
</body>
|
| 43 |
+
</html>
|
| 44 |
+
`;
|
| 45 |
+
|
| 46 |
+
await page.setRequestInterception(true);
|
| 47 |
+
page.removeAllListeners("request");
|
| 48 |
+
page.on("request", async (request) => {
|
| 49 |
+
if ([domain, domain + "/"].includes(request.url()) && request.resourceType() === "document") {
|
| 50 |
+
await request.respond({
|
| 51 |
+
status: 200,
|
| 52 |
+
contentType: "text/html",
|
| 53 |
+
body: htmlContent,
|
| 54 |
+
});
|
| 55 |
+
} else {
|
| 56 |
+
await request.continue();
|
| 57 |
+
}
|
| 58 |
+
});
|
| 59 |
+
|
| 60 |
+
await page.goto(domain, { waitUntil: "domcontentloaded" });
|
| 61 |
+
|
| 62 |
+
await page.waitForSelector('[name="cf-response"]', { timeout });
|
| 63 |
+
|
| 64 |
+
const token = await page.evaluate(() => {
|
| 65 |
+
try {
|
| 66 |
+
return document.querySelector('[name="cf-response"]').value;
|
| 67 |
+
} catch {
|
| 68 |
+
return null;
|
| 69 |
+
}
|
| 70 |
+
});
|
| 71 |
+
|
| 72 |
+
isResolved = true;
|
| 73 |
+
clearTimeout(cl);
|
| 74 |
+
|
| 75 |
+
if (!token || token.length < 10) throw new Error("Failed to get token");
|
| 76 |
+
return token;
|
| 77 |
+
|
| 78 |
+
} catch (e) {
|
| 79 |
+
clearTimeout(cl);
|
| 80 |
+
throw e;
|
| 81 |
+
}
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
module.exports = turnstile;
|
index.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const express = require('express');
|
| 2 |
+
const { connect } = require("puppeteer-real-browser");
|
| 3 |
+
const fs = require('fs');
|
| 4 |
+
const path = require('path');
|
| 5 |
+
|
| 6 |
+
const app = express();
|
| 7 |
+
const port = process.env.PORT || 7860;
|
| 8 |
+
const authToken = process.env.authToken || null;
|
| 9 |
+
const domain = process.env.DOMAIN || `https://fourstore-antibot3.hf.space`;
|
| 10 |
+
|
| 11 |
+
global.browserLimit = Number(process.env.browserLimit) || 20;
|
| 12 |
+
global.timeOut = Number(process.env.timeOut) || 60000;
|
| 13 |
+
|
| 14 |
+
const CACHE_DIR = path.join(__dirname, "cache");
|
| 15 |
+
const CACHE_FILE = path.join(CACHE_DIR, "cache.json");
|
| 16 |
+
const CACHE_TTL = 5 * 60 * 1000;
|
| 17 |
+
|
| 18 |
+
function loadCache() {
|
| 19 |
+
if (!fs.existsSync(CACHE_FILE)) return {};
|
| 20 |
+
try {
|
| 21 |
+
return JSON.parse(fs.readFileSync(CACHE_FILE, 'utf-8'));
|
| 22 |
+
} catch {
|
| 23 |
+
return {};
|
| 24 |
+
}
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
function saveCache(cache) {
|
| 28 |
+
if (!fs.existsSync(CACHE_DIR)) {
|
| 29 |
+
fs.mkdirSync(CACHE_DIR, { recursive: true });
|
| 30 |
+
}
|
| 31 |
+
fs.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2), 'utf-8');
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
function readCache(key) {
|
| 35 |
+
const cache = loadCache();
|
| 36 |
+
const entry = cache[key];
|
| 37 |
+
if (entry && Date.now() - entry.timestamp < CACHE_TTL) {
|
| 38 |
+
return entry.value;
|
| 39 |
+
}
|
| 40 |
+
return null;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
function writeCache(key, value) {
|
| 44 |
+
const cache = loadCache();
|
| 45 |
+
cache[key] = { timestamp: Date.now(), value };
|
| 46 |
+
saveCache(cache);
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
app.use(express.json({ limit: "50mb" }));
|
| 50 |
+
app.use(express.urlencoded({ extended: true, limit: "50mb" }));
|
| 51 |
+
|
| 52 |
+
app.get("/", (req, res) => {
|
| 53 |
+
res.json({
|
| 54 |
+
message: "Server is running!",
|
| 55 |
+
domain: domain,
|
| 56 |
+
endpoints: {
|
| 57 |
+
cloudflare: `${domain}/cloudflare`,
|
| 58 |
+
antibot: `${domain}/antibot`
|
| 59 |
+
},
|
| 60 |
+
status: {
|
| 61 |
+
browserLimit: global.browserLimit,
|
| 62 |
+
timeOut: global.timeOut,
|
| 63 |
+
authRequired: authToken !== null
|
| 64 |
+
}
|
| 65 |
+
});
|
| 66 |
+
});
|
| 67 |
+
|
| 68 |
+
if (process.env.NODE_ENV !== 'development') {
|
| 69 |
+
let server = app.listen(port, () => {
|
| 70 |
+
console.log(`Server running on port ${port}`);
|
| 71 |
+
console.log(`Domain: ${domain}`);
|
| 72 |
+
console.log(`Auth required: ${authToken !== null}`);
|
| 73 |
+
});
|
| 74 |
+
try {
|
| 75 |
+
server.timeout = global.timeOut;
|
| 76 |
+
} catch {}
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
async function createBrowser(proxyServer = null) {
|
| 80 |
+
const connectOptions = {
|
| 81 |
+
headless: false,
|
| 82 |
+
turnstile: true,
|
| 83 |
+
connectOption: { defaultViewport: null },
|
| 84 |
+
disableXvfb: false,
|
| 85 |
+
};
|
| 86 |
+
if (proxyServer) connectOptions.args = [`--proxy-server=${proxyServer}`];
|
| 87 |
+
|
| 88 |
+
const { browser } = await connect(connectOptions);
|
| 89 |
+
const [page] = await browser.pages();
|
| 90 |
+
|
| 91 |
+
await page.goto('about:blank');
|
| 92 |
+
await page.setRequestInterception(true);
|
| 93 |
+
page.on('request', (req) => {
|
| 94 |
+
const type = req.resourceType();
|
| 95 |
+
if (["image", "stylesheet", "font", "media"].includes(type)) req.abort();
|
| 96 |
+
else req.continue();
|
| 97 |
+
});
|
| 98 |
+
|
| 99 |
+
return { browser, page };
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
const turnstile = require('./endpoints/turnstile');
|
| 103 |
+
const cloudflare = require('./endpoints/cloudflare');
|
| 104 |
+
const antibot = require('./endpoints/antibot');
|
| 105 |
+
|
| 106 |
+
app.post('/cloudflare', async (req, res) => {
|
| 107 |
+
const data = req.body;
|
| 108 |
+
if (!data || typeof data.mode !== 'string')
|
| 109 |
+
return res.status(400).json({ message: 'Bad Request: missing or invalid mode' });
|
| 110 |
+
|
| 111 |
+
if (global.browserLimit <= 0)
|
| 112 |
+
return res.status(429).json({ message: 'Too Many Requests' });
|
| 113 |
+
|
| 114 |
+
let cacheKey, cached;
|
| 115 |
+
if (data.mode === "iuam") {
|
| 116 |
+
cacheKey = JSON.stringify(data);
|
| 117 |
+
cached = readCache(cacheKey);
|
| 118 |
+
if (cached) return res.status(200).json({ ...cached, cached: true });
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
global.browserLimit--;
|
| 122 |
+
let result, browser;
|
| 123 |
+
|
| 124 |
+
try {
|
| 125 |
+
const proxyServer = data.proxy ? `${data.proxy.hostname}:${data.proxy.port}` : null;
|
| 126 |
+
const ctx = await createBrowser(proxyServer);
|
| 127 |
+
browser = ctx.browser;
|
| 128 |
+
const page = ctx.page;
|
| 129 |
+
|
| 130 |
+
await page.goto('about:blank');
|
| 131 |
+
|
| 132 |
+
switch (data.mode) {
|
| 133 |
+
case "turnstile":
|
| 134 |
+
result = await turnstile(data, page).then(t => ({ token: t }));
|
| 135 |
+
break;
|
| 136 |
+
|
| 137 |
+
case "iuam":
|
| 138 |
+
result = await cloudflare(data, page).then(r => ({ ...r }));
|
| 139 |
+
writeCache(cacheKey, result);
|
| 140 |
+
break;
|
| 141 |
+
|
| 142 |
+
case "antibot":
|
| 143 |
+
result = await antibot(data);
|
| 144 |
+
break;
|
| 145 |
+
|
| 146 |
+
default:
|
| 147 |
+
result = { code: 400, message: 'Invalid mode' };
|
| 148 |
+
}
|
| 149 |
+
} catch (err) {
|
| 150 |
+
result = { code: 500, message: err.message };
|
| 151 |
+
} finally {
|
| 152 |
+
if (browser) try { await browser.close(); } catch {}
|
| 153 |
+
global.browserLimit++;
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
res.status(result.code ?? 200).json(result);
|
| 157 |
+
});
|
| 158 |
+
|
| 159 |
+
app.post("/antibot", async (req, res) => {
|
| 160 |
+
const data = req.body;
|
| 161 |
+
|
| 162 |
+
if (!data || !data.main || !Array.isArray(data.bots))
|
| 163 |
+
return res.status(400).json({ message: "Invalid body" });
|
| 164 |
+
|
| 165 |
+
try {
|
| 166 |
+
const result = await antibot(data);
|
| 167 |
+
res.json(result);
|
| 168 |
+
} catch (err) {
|
| 169 |
+
res.status(500).json({ message: err.message });
|
| 170 |
+
}
|
| 171 |
+
});
|
| 172 |
+
|
| 173 |
+
app.use((req, res) => {
|
| 174 |
+
res.status(404).json({ message: 'Not Found' });
|
| 175 |
+
});
|
| 176 |
+
|
| 177 |
+
if (process.env.NODE_ENV === 'development') {
|
| 178 |
+
module.exports = app;
|
| 179 |
+
}
|
package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "cf-bypass",
|
| 3 |
+
"version": "1.0",
|
| 4 |
+
"description": "get the cf_clearance cookie from any website",
|
| 5 |
+
"scripts": {
|
| 6 |
+
"start": "node index.js",
|
| 7 |
+
"dev": "nodemon index.js"
|
| 8 |
+
},
|
| 9 |
+
"dependencies": {
|
| 10 |
+
"express": "^5.1.0",
|
| 11 |
+
"canvas": "^2.11.2",
|
| 12 |
+
"sharp": "^0.32.0",
|
| 13 |
+
"puppeteer-real-browser": "^1.4.0",
|
| 14 |
+
"axios": "^1.9.0",
|
| 15 |
+
"child_process": "*",
|
| 16 |
+
"tesseract.js": "^5.0.3",
|
| 17 |
+
"jimp": "^0.22.10"
|
| 18 |
+
},
|
| 19 |
+
"devDependencies": {
|
| 20 |
+
"nodemon": "^3.1.10"
|
| 21 |
+
}
|
| 22 |
+
}
|