Kenqt commited on
Commit
66b882d
·
verified ·
1 Parent(s): 694c899

Upload 3 files

Browse files
endpoints/antibot.js ADDED
@@ -0,0 +1,380 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { extractTextFromImage, uploadImageToHosting } = require('./imageProcessor');
4
+
5
+ async function extractTextFromBuffer(imageBuffer) {
6
+ try {
7
+ console.log('🖼️ DEBUG extractTextFromBuffer: Membuat file temporary');
8
+ const tempPath = path.join(__dirname, `temp_${Date.now()}.jpg`);
9
+ fs.writeFileSync(tempPath, imageBuffer);
10
+ console.log('📁 DEBUG: File temporary dibuat:', tempPath);
11
+
12
+ const result = await extractTextFromImage(tempPath);
13
+ console.log('✅ DEBUG extractTextFromBuffer: Hasil ekstraksi:', result);
14
+
15
+ fs.unlinkSync(tempPath);
16
+ console.log('🧹 DEBUG: File temporary dihapus');
17
+
18
+ return result;
19
+ } catch (error) {
20
+ console.error('❌ DEBUG extractTextFromBuffer Error:', error.message);
21
+ return { status: false, response: 'Gagal memproses gambar' };
22
+ }
23
+ }
24
+
25
+ function mapAnswer(soalArray, jawaban, botIndex) {
26
+ console.log(`🤖 DEBUG mapAnswer: Bot ${botIndex}, Jawaban: "${jawaban}"`);
27
+ return jawaban;
28
+ }
29
+
30
+ function normalizeText(text) {
31
+ const normalized = text.toLowerCase().replace(/[^\w\s]/g, '').trim();
32
+ console.log(`🔤 DEBUG normalizeText: "${text}" -> "${normalized}"`);
33
+ return normalized;
34
+ }
35
+
36
+ function isValueMatch(value, targetSoal) {
37
+ console.log(`🔍 DEBUG isValueMatch: Value="${value}", Soal="${targetSoal}"`);
38
+
39
+ const numberMap = {
40
+ '0': 'zero', '1': 'one', '2': 'two', '3': 'three', '4': 'four',
41
+ '5': 'five', '6': 'six', '7': 'seven', '8': 'eight', '9': 'nine', '10': 'ten',
42
+ 'zero': '0', 'one': '1', 'two': '2', 'three': '3', 'four': '4',
43
+ 'five': '5', 'six': '6', 'seven': '7', 'eight': '8', 'nine': '9', 'ten': '10',
44
+ 'I': '1', 'II': '2', 'III': '3', 'IV': '4', 'V': '5',
45
+ 'VI': '6', 'VII': '7', 'VIII': '8', 'IX': '9', 'X': '10',
46
+ 'slx': '6', 's1x': '6', 'six': '6',
47
+ 'f0ur': '4', 'f0r': '4', 'fuor': '4',
48
+ 'f1ve': '5', 'fiv': '5', 'f1v': '5',
49
+ 'e1ght': '8', 'elght': '8', 'eight': '8',
50
+ 'n1ne': '9', 'n1n': '9', 'nne': '9',
51
+ 'se7en': '7', 'sven': '7', 'seven': '7',
52
+ 'thre': '3', 'tree': '3', 'thr33': '3',
53
+ 'tw0': '2', 'to': '2', 'tw': '2',
54
+ '0ne': '1', 'on': '1', 'oen': '1'
55
+ };
56
+
57
+ const leetMap = {
58
+ 'O': '0', 'o': '0',
59
+ 'I': '1', 'i': '1', 'l': '1',
60
+ 'Z': '2', 'z': '2',
61
+ 'E': '3', 'e': '3',
62
+ 'A': '4', 'a': '4',
63
+ 'S': '5', 's': '5',
64
+ 'G': '6', 'g': '6',
65
+ 'T': '7', 't': '7',
66
+ 'B': '8', 'b': '8',
67
+ 'Q': '9', 'q': '9',
68
+ 'U': '4', 'u': '4',
69
+ 'R': '2', 'r': '2',
70
+ 'N': '9', 'n': '9',
71
+ 'V': '7', 'v': '7'
72
+ };
73
+
74
+ const normalizedValue = normalizeText(value);
75
+ const normalizedSoal = normalizeText(targetSoal);
76
+
77
+ if (normalizedValue === normalizedSoal) {
78
+ console.log('✅ DEBUG: Match exact normalized');
79
+ return true;
80
+ }
81
+
82
+ const convertLeet = (text) => {
83
+ return text.split('').map(char => leetMap[char] || char).join('');
84
+ };
85
+
86
+ const leetValue = convertLeet(value);
87
+ const leetSoal = convertLeet(targetSoal);
88
+
89
+ console.log(`🔢 DEBUG Leet: Value="${leetValue}", Soal="${leetSoal}"`);
90
+
91
+ if (leetValue === normalizedSoal) {
92
+ console.log('✅ DEBUG: Match leet value -> normalized soal');
93
+ return true;
94
+ }
95
+ if (normalizedValue === leetSoal) {
96
+ console.log('✅ DEBUG: Match normalized value -> leet soal');
97
+ return true;
98
+ }
99
+ if (leetValue === leetSoal) {
100
+ console.log('✅ DEBUG: Match leet value -> leet soal');
101
+ return true;
102
+ }
103
+
104
+ const mappedValue = numberMap[normalizedValue] || numberMap[value] || normalizedValue;
105
+ const mappedSoal = numberMap[normalizedSoal] || numberMap[targetSoal] || normalizedSoal;
106
+
107
+ console.log(`🔄 DEBUG Number Map: Value="${mappedValue}", Soal="${mappedSoal}"`);
108
+
109
+ if (mappedValue === normalizedSoal) {
110
+ console.log('✅ DEBUG: Match mapped value -> normalized soal');
111
+ return true;
112
+ }
113
+ if (normalizedValue === mappedSoal) {
114
+ console.log('✅ DEBUG: Match normalized value -> mapped soal');
115
+ return true;
116
+ }
117
+ if (mappedValue === mappedSoal) {
118
+ console.log('✅ DEBUG: Match mapped value -> mapped soal');
119
+ return true;
120
+ }
121
+
122
+ const similarity = calculateSimilarity(normalizedValue, normalizedSoal);
123
+ console.log(`📊 DEBUG Similarity: ${similarity}`);
124
+ if (similarity >= 0.8) {
125
+ console.log('✅ DEBUG: Match similarity >= 0.8');
126
+ return true;
127
+ }
128
+
129
+ try {
130
+ const valueResult = evaluateSimpleMath(value);
131
+ const soalResult = evaluateSimpleMath(targetSoal);
132
+ console.log(`🧮 DEBUG Math: Value=${valueResult}, Soal=${soalResult}`);
133
+ if (valueResult !== null && soalResult !== null && valueResult === soalResult) {
134
+ console.log('✅ DEBUG: Match math evaluation');
135
+ return true;
136
+ }
137
+ } catch (e) {
138
+ console.log('❌ DEBUG Math evaluation failed');
139
+ }
140
+
141
+ console.log('❌ DEBUG: No match found');
142
+ return false;
143
+ }
144
+
145
+ function calculateSimilarity(str1, str2) {
146
+ if (str1 === str2) return 1;
147
+ if (str1.length === 0 || str2.length === 0) return 0;
148
+
149
+ const longer = str1.length > str2.length ? str1 : str2;
150
+ const shorter = str1.length > str2.length ? str2 : str1;
151
+
152
+ if (longer.includes(shorter)) return shorter.length / longer.length;
153
+
154
+ let matches = 0;
155
+ for (let i = 0; i < shorter.length; i++) {
156
+ if (shorter[i] === longer[i]) matches++;
157
+ }
158
+
159
+ return matches / longer.length;
160
+ }
161
+
162
+ function evaluateSimpleMath(expression) {
163
+ if (!expression) return null;
164
+ const cleanExpr = expression.toString().replace(/[^\d+\-*/.()]/g, '');
165
+ if (!cleanExpr) return null;
166
+ try {
167
+ if (cleanExpr.length > 10) return null;
168
+ const result = Function(`"use strict"; return (${cleanExpr})`)();
169
+ return typeof result === 'number' ? result : null;
170
+ } catch (e) {
171
+ return null;
172
+ }
173
+ }
174
+
175
+ function parseSoalText(text) {
176
+ console.log(`📝 DEBUG parseSoalText: Input text: "${text}"`);
177
+ const delimiters = /[.,:;\\/\s]+/;
178
+ let parts = text.split(delimiters).filter(part => part.trim() !== '');
179
+ if (parts.length === 1) {
180
+ parts = text.split(/\s+/).filter(part => part.trim() !== '');
181
+ }
182
+ console.log(`📝 DEBUG parseSoalText: Parsed parts:`, parts);
183
+ return parts;
184
+ }
185
+
186
+ async function antibot(data) {
187
+ console.log('🚀 DEBUG antibot: Memulai proses antibot');
188
+ console.log('📊 DEBUG: Data received - main:', data.main ? '✅' : '❌', 'bots:', data.bots?.length || 0);
189
+
190
+ try {
191
+ const { main, bots } = data;
192
+
193
+ console.log('🖼️ DEBUG: Processing main image...');
194
+ const mainBuffer = Buffer.from(main, 'base64');
195
+ const mainText = await extractTextFromBuffer(mainBuffer);
196
+
197
+ console.log('📄 DEBUG Main Text Result:', mainText);
198
+
199
+ if (!mainText.status) {
200
+ throw new Error('Gagal mengekstrak teks dari gambar utama: ' + mainText.response);
201
+ }
202
+
203
+ const soalArray = parseSoalText(mainText.response);
204
+ console.log(`📋 DEBUG: Soal array:`, soalArray);
205
+
206
+ if (soalArray.length === 0) {
207
+ throw new Error('Tidak ada soal yang terdeteksi');
208
+ }
209
+
210
+ const botResults = [];
211
+ console.log(`🤖 DEBUG: Processing ${bots.length} bots...`);
212
+
213
+ for (let i = 0; i < bots.length; i++) {
214
+ const bot = bots[i];
215
+ console.log(`🤖 DEBUG: Processing bot ${i+1}/${bots.length} - ID: ${bot.id}`);
216
+ try {
217
+ const botBuffer = Buffer.from(bot.img, 'base64');
218
+ const botText = await extractTextFromBuffer(botBuffer);
219
+ const mappedValue = mapAnswer(soalArray, botText.response, i);
220
+
221
+ botResults.push({
222
+ id: bot.id,
223
+ text: botText.response,
224
+ value: mappedValue,
225
+ normalized: normalizeText(botText.response)
226
+ });
227
+
228
+ console.log(`✅ DEBUG Bot ${bot.id}:`, {
229
+ text: botText.response,
230
+ value: mappedValue,
231
+ normalized: normalizeText(botText.response)
232
+ });
233
+
234
+ } catch (error) {
235
+ console.error(`❌ DEBUG Bot ${bot.id} Error:`, error.message);
236
+ botResults.push({
237
+ id: bot.id,
238
+ text: '',
239
+ value: '',
240
+ normalized: '',
241
+ error: error.message
242
+ });
243
+ }
244
+ }
245
+
246
+ console.log('🔍 DEBUG: Starting matching process...');
247
+ const result = [];
248
+ const usedIds = new Set();
249
+ let matchedCount = 0;
250
+
251
+ // Step 1: Exact matching dengan soal
252
+ for (let i = 0; i < soalArray.length; i++) {
253
+ const targetSoal = soalArray[i];
254
+ console.log(`🎯 DEBUG: Matching soal "${targetSoal}"`);
255
+ let foundId = null;
256
+
257
+ for (const bot of botResults) {
258
+ if (!usedIds.has(bot.id) && bot.value && bot.value.trim() !== '' &&
259
+ isValueMatch(bot.value, targetSoal)) {
260
+ foundId = bot.id;
261
+ usedIds.add(bot.id);
262
+ matchedCount++;
263
+ console.log(`✅ DEBUG: Soal "${targetSoal}" matched with bot ${bot.id}`);
264
+ break;
265
+ }
266
+ }
267
+
268
+ result.push({
269
+ id: foundId,
270
+ soal: targetSoal,
271
+ matchType: foundId ? 'exact' : 'none'
272
+ });
273
+ }
274
+
275
+ console.log(`📊 DEBUG: Initial matches: ${matchedCount}/${soalArray.length}`);
276
+
277
+ // Step 2: Jika masih ada soal yang belum dapat match, coba normalized matching
278
+ if (matchedCount < soalArray.length) {
279
+ console.log('🔄 DEBUG: Trying normalized matching...');
280
+ for (let i = 0; i < result.length; i++) {
281
+ if (!result[i].id) {
282
+ const targetSoal = soalArray[i];
283
+ const normalizedSoal = normalizeText(targetSoal);
284
+
285
+ for (const bot of botResults) {
286
+ if (!usedIds.has(bot.id) && bot.normalized && bot.normalized.trim() !== '') {
287
+ if (bot.normalized === normalizedSoal) {
288
+ result[i].id = bot.id;
289
+ result[i].matchType = 'normalized';
290
+ usedIds.add(bot.id);
291
+ matchedCount++;
292
+ console.log(`✅ DEBUG: Soal "${targetSoal}" normalized match with bot ${bot.id}`);
293
+ break;
294
+ }
295
+ }
296
+ }
297
+ }
298
+ }
299
+ }
300
+
301
+ console.log(`📊 DEBUG: After normalized: ${matchedCount}/${soalArray.length}`);
302
+
303
+ // Step 3: Jika masih ada bot yang belum digunakan, assign ke soal yang belum ada jawaban
304
+ const unusedBots = botResults.filter(bot => !usedIds.has(bot.id) && bot.value && bot.value.trim() !== '');
305
+ console.log(`🔄 DEBUG: Unused bots: ${unusedBots.length}`);
306
+
307
+ for (let i = 0; i < result.length; i++) {
308
+ if (!result[i].id && unusedBots.length > 0) {
309
+ const bot = unusedBots.shift();
310
+ result[i].id = bot.id;
311
+ result[i].matchType = 'fallback';
312
+ usedIds.add(bot.id);
313
+ console.log(`🔄 DEBUG: Soal ${i+1} fallback to bot ${bot.id}`);
314
+ }
315
+ }
316
+
317
+ // Step 4: Jika masih ada bot yang belum digunakan, buat result tambahan
318
+ const remainingBots = botResults.filter(bot => !usedIds.has(bot.id) && bot.value && bot.value.trim() !== '');
319
+ console.log(`➕ DEBUG: Remaining bots to add: ${remainingBots.length}`);
320
+
321
+ for (const bot of remainingBots) {
322
+ result.push({
323
+ id: bot.id,
324
+ soal: '',
325
+ matchType: 'extra'
326
+ });
327
+ usedIds.add(bot.id);
328
+ console.log(`➕ DEBUG: Added extra bot ${bot.id} to result`);
329
+ }
330
+
331
+ // Step 5: Handle kasus dimana tidak ada match sama sekali
332
+ if (result.length === 0) {
333
+ console.log('❌ DEBUG: No matches found, all invalid');
334
+ for (let i = 0; i < soalArray.length; i++) {
335
+ result.push({
336
+ id: 'invalid',
337
+ soal: soalArray[i],
338
+ matchType: 'invalid'
339
+ });
340
+ }
341
+ }
342
+
343
+ console.log('🎉 DEBUG: Process completed successfully');
344
+ console.log('📋 DEBUG Final Result:', result);
345
+
346
+ return {
347
+ success: true,
348
+ data: {
349
+ soal: soalArray,
350
+ soalLeet: soalArray,
351
+ botResults: botResults,
352
+ result: result.map(r => ({ id: r.id })),
353
+ debug: {
354
+ parsedSoal: soalArray,
355
+ matches: result.map(r => ({
356
+ id: r.id,
357
+ matchType: r.matchType,
358
+ soal: r.soal
359
+ })),
360
+ totalMatches: matchedCount,
361
+ totalResults: result.length
362
+ }
363
+ }
364
+ };
365
+
366
+ } catch (error) {
367
+ console.error('💥 DEBUG antibot Error:', error.message);
368
+ return {
369
+ success: false,
370
+ error: error.message,
371
+ data: {
372
+ soal: [],
373
+ botResults: [],
374
+ result: []
375
+ }
376
+ };
377
+ }
378
+ }
379
+
380
+ module.exports = antibot;
endpoints/imageProcessor ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const fs = require('fs');
2
+ const https = require('https');
3
+ const FormData = require('form-data');
4
+
5
+ const SESSION_IDS = [
6
+ "neko-" + Date.now(),
7
+ "session-" + Math.random().toString(36).substring(2, 15),
8
+ "bot-" + Math.random().toString(36).substring(2, 10)
9
+ ];
10
+
11
+ function getRandomSessionId() {
12
+ const sessionId = SESSION_IDS[Math.floor(Math.random() * SESSION_IDS.length)];
13
+ console.log(`🎲 DEBUG getRandomSessionId: Selected session ID: ${sessionId}`);
14
+ return sessionId;
15
+ }
16
+
17
+ async function uploadImageToHosting(imageBuffer) {
18
+ console.log('📤 DEBUG uploadImageToHosting: Starting image upload');
19
+ return new Promise((resolve, reject) => {
20
+ try {
21
+ const formData = new FormData();
22
+ const filename = `upload-${Date.now()}.jpg`;
23
+ formData.append("file", imageBuffer, {
24
+ filename: filename,
25
+ contentType: "image/jpeg"
26
+ });
27
+
28
+ console.log(`📁 DEBUG: Uploading file: ${filename}, Size: ${imageBuffer.length} bytes`);
29
+
30
+ const options = {
31
+ hostname: 'fourmovie-uploader.hf.space',
32
+ path: '/api/upload',
33
+ method: 'POST',
34
+ headers: formData.getHeaders()
35
+ };
36
+
37
+ const req = https.request(options, (res) => {
38
+ let data = '';
39
+
40
+ console.log(`📡 DEBUG: Upload response status: ${res.statusCode}`);
41
+
42
+ res.on('data', (chunk) => {
43
+ data += chunk;
44
+ });
45
+
46
+ res.on('end', () => {
47
+ try {
48
+ console.log('📄 DEBUG: Upload response received');
49
+ const response = JSON.parse(data);
50
+ console.log('🔗 DEBUG: Upload response data:', response);
51
+
52
+ if (response.file?.url) {
53
+ console.log(`✅ DEBUG: Image uploaded successfully, URL: ${response.file.url}`);
54
+ resolve(response.file.url);
55
+ } else {
56
+ console.log('❌ DEBUG: No URL in upload response');
57
+ resolve(null);
58
+ }
59
+ } catch (error) {
60
+ console.error('❌ DEBUG: Error parsing upload response:', error);
61
+ resolve(null);
62
+ }
63
+ });
64
+ });
65
+
66
+ req.on('error', (error) => {
67
+ console.error('❌ DEBUG: Upload request error:', error);
68
+ resolve(null);
69
+ });
70
+
71
+ formData.pipe(req);
72
+ console.log('🔄 DEBUG: Form data piped to request');
73
+
74
+ } catch (error) {
75
+ console.error('❌ DEBUG: Upload failed:', error);
76
+ resolve(null);
77
+ }
78
+ });
79
+ }
80
+
81
+ async function extractTextFromImage(imagePath) {
82
+ console.log(`🖼️ DEBUG extractTextFromImage: Processing image: ${imagePath}`);
83
+ return new Promise(async (resolve, reject) => {
84
+ try {
85
+ console.log('📖 DEBUG: Reading image file...');
86
+ const imageBuffer = fs.readFileSync(imagePath);
87
+ console.log(`✅ DEBUG: Image read successfully, size: ${imageBuffer.length} bytes`);
88
+
89
+ console.log('☁️ DEBUG: Uploading image to hosting...');
90
+ const imageUrl = await uploadImageToHosting(imageBuffer);
91
+ if (!imageUrl) {
92
+ console.log('❌ DEBUG: Image upload failed');
93
+ return resolve({ status: false, response: 'Gagal mengunggah gambar ke hosting' });
94
+ }
95
+
96
+ console.log(`🔗 DEBUG: Image uploaded, URL: ${imageUrl}`);
97
+ const sessionId = getRandomSessionId();
98
+
99
+ const textParam = encodeURIComponent("hi how are you");
100
+ const systemPrompt = encodeURIComponent("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");
101
+ const imageUrlParam = encodeURIComponent(imageUrl);
102
+
103
+ const apiUrl = `https://api.nekolabs.web.id/ai/gemini/2.5-flash/v2?text=${textParam}&systemPrompt=${systemPrompt}&imageUrl=${imageUrlParam}&sessionId=${sessionId}`;
104
+ console.log(`🌐 DEBUG: API URL constructed (short): https://api.nekolabs.web.id/ai/gemini/2.5-flash/v2?...`);
105
+
106
+ const options = {
107
+ hostname: 'api.nekolabs.web.id',
108
+ path: `/ai/gemini/2.0-flash/v2?text=${textParam}&systemPrompt=${systemPrompt}&imageUrl=${imageUrlParam}&sessionId=${sessionId}`,
109
+ method: 'GET',
110
+ headers: {
111
+ 'Accept': 'application/json',
112
+ 'Content-Type': 'application/json'
113
+ }
114
+ };
115
+
116
+ console.log('🚀 DEBUG: Sending request to nekolabs API...');
117
+ const req = https.request(options, (res) => {
118
+ let data = '';
119
+
120
+ console.log(`📡 DEBUG: API response status: ${res.statusCode}`);
121
+
122
+ res.on('data', (chunk) => {
123
+ data += chunk;
124
+ });
125
+
126
+ res.on('end', () => {
127
+ try {
128
+ console.log('📄 DEBUG: API response received');
129
+ const response = JSON.parse(data);
130
+ console.log('🔍 DEBUG: Full API response:', response);
131
+
132
+ if (response.success && response.result) {
133
+ console.log(`✅ DEBUG: Text extraction successful: "${response.result}"`);
134
+ resolve({ status: true, response: response.result });
135
+ } else {
136
+ console.log('❌ DEBUG: API returned error:', response);
137
+ resolve({ status: false, response: response.result || 'Gagal mengekstrak teks' });
138
+ }
139
+ } catch (error) {
140
+ console.error('❌ DEBUG: Error parsing API response:', error);
141
+ reject(error);
142
+ }
143
+ });
144
+ });
145
+
146
+ req.on('error', (error) => {
147
+ console.error('❌ DEBUG: API request error:', error);
148
+ reject(error);
149
+ });
150
+
151
+ req.end();
152
+ console.log('📤 DEBUG: Request sent to API');
153
+
154
+ } catch (error) {
155
+ console.error('❌ DEBUG: extractTextFromImage error:', error);
156
+ reject(error);
157
+ }
158
+ });
159
+ }
160
+
161
+ module.exports = {
162
+ extractTextFromImage,
163
+ getRandomSessionId,
164
+ uploadImageToHosting
165
+ };
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;