const fs = require('fs'); const path = require('path'); const { extractTextFromImage, uploadImageToHosting } = require('./imageProcessor'); async function extractTextFromBuffer(imageBuffer) { try { console.log('๐Ÿ–ผ๏ธ DEBUG extractTextFromBuffer: Membuat file temporary'); const tempPath = path.join(__dirname, `temp_${Date.now()}.jpg`); fs.writeFileSync(tempPath, imageBuffer); console.log('๐Ÿ“ DEBUG: File temporary dibuat:', tempPath); const result = await extractTextFromImage(tempPath); console.log('โœ… DEBUG extractTextFromBuffer: Hasil ekstraksi:', result); fs.unlinkSync(tempPath); console.log('๐Ÿงน DEBUG: File temporary dihapus'); return result; } catch (error) { console.error('โŒ DEBUG extractTextFromBuffer Error:', error.message); return { status: false, response: 'Gagal memproses gambar' }; } } function mapAnswer(soalArray, jawaban, botIndex) { console.log(`๐Ÿค– DEBUG mapAnswer: Bot ${botIndex}, Jawaban: "${jawaban}"`); return jawaban; } function normalizeText(text) { const normalized = text.toLowerCase().replace(/[^\w\s]/g, '').trim(); console.log(`๐Ÿ”ค DEBUG normalizeText: "${text}" -> "${normalized}"`); return normalized; } function isValueMatch(value, targetSoal) { console.log(`๐Ÿ” DEBUG isValueMatch: Value="${value}", Soal="${targetSoal}"`); const numberMap = { '0': 'zero', '1': 'one', '2': 'two', '3': 'three', '4': 'four', '5': 'five', '6': 'six', '7': 'seven', '8': 'eight', '9': 'nine', '10': 'ten', 'zero': '0', 'one': '1', 'two': '2', 'three': '3', 'four': '4', 'five': '5', 'six': '6', 'seven': '7', 'eight': '8', 'nine': '9', 'ten': '10', 'I': '1', 'II': '2', 'III': '3', 'IV': '4', 'V': '5', 'VI': '6', 'VII': '7', 'VIII': '8', 'IX': '9', 'X': '10', 'i': '1', 'ii': '2', 'iii': '3', 'iv': '4', 'v': '5', 'vi': '6', 'vii': '7', 'viii': '8', 'ix': '9', 'x': '10', 'slx': '6', 's1x': '6', 'six': '6', 'f0ur': '4', 'f0r': '4', 'fuor': '4', 'f1ve': '5', 'fiv': '5', 'f1v': '5', 'e1ght': '8', 'elght': '8', 'eight': '8', 'n1ne': '9', 'n1n': '9', 'nne': '9', 'se7en': '7', 'sven': '7', 'seven': '7', 'thre': '3', 'tree': '3', 'thr33': '3', 'tw0': '2', 'to': '2', 'tw': '2', '0ne': '1', 'on': '1', 'oen': '1' }; const leetMap = { 'O': '0', 'o': '0', 'I': '1', 'i': '1', 'l': '1', 'Z': '2', 'z': '2', 'E': '3', 'e': '3', 'A': '4', 'a': '4', 'S': '5', 's': '5', 'G': '6', 'g': '6', 'T': '7', 't': '7', 'B': '8', 'b': '8', 'Q': '9', 'q': '9', 'U': '4', 'u': '4', 'R': '2', 'r': '2', 'N': '9', 'n': '9', 'V': '7', 'v': '7' }; const normalizedValue = normalizeText(value); const normalizedSoal = normalizeText(targetSoal); if (normalizedValue === normalizedSoal) { console.log('โœ… DEBUG: Match exact normalized'); return true; } const convertLeet = (text) => { return text.split('').map(char => leetMap[char] || char).join(''); }; const leetValue = convertLeet(value); const leetSoal = convertLeet(targetSoal); console.log(`๐Ÿ”ข DEBUG Leet: Value="${leetValue}", Soal="${leetSoal}"`); if (leetValue === normalizedSoal) { console.log('โœ… DEBUG: Match leet value -> normalized soal'); return true; } if (normalizedValue === leetSoal) { console.log('โœ… DEBUG: Match normalized value -> leet soal'); return true; } if (leetValue === leetSoal) { console.log('โœ… DEBUG: Match leet value -> leet soal'); return true; } const mappedValue = numberMap[normalizedValue] || numberMap[value] || normalizedValue; const mappedSoal = numberMap[normalizedSoal] || numberMap[targetSoal] || normalizedSoal; console.log(`๐Ÿ”„ DEBUG Number Map: Value="${mappedValue}", Soal="${mappedSoal}"`); if (mappedValue === normalizedSoal) { console.log('โœ… DEBUG: Match mapped value -> normalized soal'); return true; } if (normalizedValue === mappedSoal) { console.log('โœ… DEBUG: Match normalized value -> mapped soal'); return true; } if (mappedValue === mappedSoal) { console.log('โœ… DEBUG: Match mapped value -> mapped soal'); return true; } const similarity = calculateSimilarity(normalizedValue, normalizedSoal); console.log(`๐Ÿ“Š DEBUG Similarity: ${similarity}`); if (similarity >= 0.8) { console.log('โœ… DEBUG: Match similarity >= 0.8'); return true; } try { const valueResult = evaluateSimpleMath(value); const soalResult = evaluateSimpleMath(targetSoal); console.log(`๐Ÿงฎ DEBUG Math: Value=${valueResult}, Soal=${soalResult}`); if (valueResult !== null && soalResult !== null && valueResult === soalResult) { console.log('โœ… DEBUG: Match math evaluation'); return true; } } catch (e) { console.log('โŒ DEBUG Math evaluation failed'); } console.log('โŒ DEBUG: No match found'); return false; } function calculateSimilarity(str1, str2) { if (str1 === str2) return 1; if (str1.length === 0 || str2.length === 0) return 0; const longer = str1.length > str2.length ? str1 : str2; const shorter = str1.length > str2.length ? str2 : str1; if (longer.includes(shorter)) return shorter.length / longer.length; let matches = 0; for (let i = 0; i < shorter.length; i++) { if (shorter[i] === longer[i]) matches++; } return matches / longer.length; } function evaluateSimpleMath(expression) { if (!expression) return null; const cleanExpr = expression.toString().replace(/[^\d+\-*/.()]/g, ''); if (!cleanExpr) return null; try { if (cleanExpr.length > 10) return null; const result = Function(`"use strict"; return (${cleanExpr})`)(); return typeof result === 'number' ? result : null; } catch (e) { return null; } } function parseSoalText(text) { console.log(`๐Ÿ“ DEBUG parseSoalText: Input text: "${text}"`); const ignoreWords = [ 'hi', 'how', 'are', 'you', 'hello', 'hey', 'tentu', 'berikut', 'adalah', 'teks', 'dari', 'gambar', 'dipisahkan', 'sesuai', 'permintaan', 'anda', 'hanya', 'berikan', 'jangan', 'tambahkan', 'kata', 'apapun', 'seperti', 'atau', 'penjelasan', 'lain', 'saja' ]; const delimiters = /[.,:;\\/\s]+/; let parts = text.split(delimiters) .filter(part => part.trim() !== '') .filter(part => !ignoreWords.includes(part.toLowerCase())); if (parts.length === 0) { parts = text.split(/\s+/) .filter(part => part.trim() !== '') .filter(part => !ignoreWords.includes(part.toLowerCase())); } parts = parts.filter(part => part.length <= 3 || !isNaN(part)); parts = parts.slice(0, 3); console.log(`๐Ÿ“ DEBUG parseSoalText: Filtered parts (max 3):`, parts); return parts; } async function antibot(data) { console.log('๐Ÿš€ DEBUG antibot: Memulai proses antibot'); console.log('๐Ÿ“Š DEBUG: Data received - main:', data.main ? 'โœ…' : 'โŒ', 'bots:', data.bots?.length || 0); try { const { main, bots } = data; console.log('๐Ÿ–ผ๏ธ DEBUG: Processing main image...'); const mainBuffer = Buffer.from(main, 'base64'); const mainText = await extractTextFromBuffer(mainBuffer); console.log('๐Ÿ“„ DEBUG Main Text Result:', mainText); if (!mainText.status) { throw new Error('Gagal mengekstrak teks dari gambar utama: ' + mainText.response); } const soalArray = parseSoalText(mainText.response); console.log(`๐Ÿ“‹ DEBUG: Soal array:`, soalArray); if (soalArray.length === 0) { throw new Error('Tidak ada soal yang terdeteksi'); } const botResults = []; console.log(`๐Ÿค– DEBUG: Processing ${bots.length} bots...`); for (let i = 0; i < bots.length; i++) { const bot = bots[i]; console.log(`๐Ÿค– DEBUG: Processing bot ${i+1}/${bots.length} - ID: ${bot.id}`); try { const botBuffer = Buffer.from(bot.img, 'base64'); const botText = await extractTextFromBuffer(botBuffer); const mappedValue = mapAnswer(soalArray, botText.response, i); botResults.push({ id: bot.id, text: botText.response, value: mappedValue, normalized: normalizeText(botText.response) }); console.log(`โœ… DEBUG Bot ${bot.id}:`, { text: botText.response, value: mappedValue, normalized: normalizeText(botText.response) }); } catch (error) { console.error(`โŒ DEBUG Bot ${bot.id} Error:`, error.message); botResults.push({ id: bot.id, text: '', value: '', normalized: '', error: error.message }); } } console.log('๐Ÿ” DEBUG: Starting matching process...'); const result = []; const usedIds = new Set(); let successfulMatches = 0; // Step 1: Match setiap bot dengan soal yang tersedia const availableSoal = [...soalArray]; for (const bot of botResults) { let matchedSoal = null; let matchIndex = -1; if (bot.value && bot.value.trim() !== '') { for (let i = 0; i < availableSoal.length; i++) { if (isValueMatch(bot.value, availableSoal[i])) { matchedSoal = availableSoal[i]; matchIndex = i; successfulMatches++; console.log(`โœ… DEBUG: Bot ${bot.id} matched with soal "${matchedSoal}"`); break; } } } if (matchedSoal) { result.push({ id: bot.id, soal: matchedSoal, matchType: 'exact' }); availableSoal.splice(matchIndex, 1); usedIds.add(bot.id); } else { result.push({ id: null, soal: '', matchType: 'none' }); } } console.log(`๐Ÿ“Š DEBUG: Successful matches: ${successfulMatches}`); // Step 2: Jika minimal 2 match, isi bot yang belum match dengan soal tersisa if (successfulMatches >= 2) { console.log('โœ… DEBUG: Minimal 2 match terpenuhi, mengisi bot yang belum match'); for (let i = 0; i < result.length; i++) { if (!result[i].id && availableSoal.length > 0) { const bot = botResults[i]; result[i].id = bot.id; result[i].soal = availableSoal.shift(); result[i].matchType = 'fallback'; console.log(`๐Ÿ”„ DEBUG: Bot ${bot.id} diisi dengan soal tersisa`); } } } // Step 3: Handle remaining cases for (let i = 0; i < result.length; i++) { if (!result[i].id) { if (successfulMatches >= 2) { result[i].id = botResults[i].id; result[i].matchType = 'qualified'; } else { result[i].id = 'invalid'; result[i].matchType = 'invalid'; } } } console.log('๐ŸŽ‰ DEBUG: Process completed successfully'); console.log('๐Ÿ“‹ DEBUG Final Result:', result); return { success: true, data: { soal: soalArray, soalLeet: soalArray, botResults: botResults, result: result.map(r => ({ id: r.id })), debug: { parsedSoal: soalArray, matches: result.map(r => ({ id: r.id, matchType: r.matchType, soal: r.soal })), totalResults: result.length, successfulMatches: successfulMatches } } }; } catch (error) { console.error('๐Ÿ’ฅ DEBUG antibot Error:', error.message); return { success: false, error: error.message, data: { soal: [], botResults: [], result: [] } }; } } module.exports = antibot;