const fs = require('fs'); const path = require('path'); const { extractTextFromImage } = require('./imageProcessor'); function mapAnswer(soalArray, jawaban, botIndex) { return jawaban; } function normalizeText(text) { return text.toLowerCase().replace(/[^\w\s]/g, '').trim(); } function isValueMatch(value, 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', '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) { return true; } const convertLeet = (text) => { return text.split('').map(char => leetMap[char] || char).join(''); }; const leetValue = convertLeet(value); const leetSoal = convertLeet(targetSoal); if (leetValue === normalizedSoal) return true; if (normalizedValue === leetSoal) return true; if (leetValue === leetSoal) return true; const mappedValue = numberMap[normalizedValue] || numberMap[value] || normalizedValue; const mappedSoal = numberMap[normalizedSoal] || numberMap[targetSoal] || normalizedSoal; if (mappedValue === normalizedSoal) return true; if (normalizedValue === mappedSoal) return true; if (mappedValue === mappedSoal) return true; const similarity = calculateSimilarity(normalizedValue, normalizedSoal); if (similarity >= 0.8) { return true; } try { const valueResult = evaluateSimpleMath(value); const soalResult = evaluateSimpleMath(targetSoal); if (valueResult !== null && soalResult !== null && valueResult === soalResult) { return true; } } catch (e) {} 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) { const delimiters = /[.,:;\\/\s]+/; let parts = text.split(delimiters).filter(part => part.trim() !== ''); if (parts.length === 1) { parts = text.split(/\s+/).filter(part => part.trim() !== ''); } return parts; } async function antibot(data) { try { const { main, bots } = data; const mainBuffer = Buffer.from(main, 'base64'); const mainText = await extractTextFromImage(mainBuffer); const soalArray = parseSoalText(mainText.response); if (soalArray.length === 0) { throw new Error('Tidak ada soal yang terdeteksi'); } const botResults = []; for (let i = 0; i < bots.length; i++) { const bot = bots[i]; try { const botBuffer = Buffer.from(bot.img, 'base64'); const botText = await extractTextFromImage(botBuffer); const mappedValue = mapAnswer(soalArray, botText.response, i); botResults.push({ id: bot.id, text: botText.response, value: mappedValue, normalized: normalizeText(botText.response) }); } catch (error) { botResults.push({ id: bot.id, text: '', value: '', normalized: '', error: error.message }); } } const result = []; const usedIds = new Set(); let matchedCount = 0; for (let i = 0; i < soalArray.length; i++) { const targetSoal = soalArray[i]; let foundId = null; for (const bot of botResults) { if (!usedIds.has(bot.id) && bot.value && bot.value.trim() !== '' && isValueMatch(bot.value, targetSoal)) { foundId = bot.id; usedIds.add(bot.id); matchedCount++; break; } } result.push({ id: foundId, soal: targetSoal, matchType: foundId ? 'exact' : 'none' }); } if (matchedCount < soalArray.length) { for (let i = 0; i < result.length; i++) { if (!result[i].id) { const targetSoal = soalArray[i]; const normalizedSoal = normalizeText(targetSoal); for (const bot of botResults) { if (!usedIds.has(bot.id) && bot.normalized && bot.normalized.trim() !== '') { if (bot.normalized === normalizedSoal) { result[i].id = bot.id; result[i].matchType = 'normalized'; usedIds.add(bot.id); matchedCount++; break; } } } } } } if (matchedCount >= 2) { for (let i = 0; i < result.length; i++) { if (!result[i].id) { for (const bot of botResults) { if (!usedIds.has(bot.id) && bot.value && bot.value.trim() !== '') { result[i].id = bot.id; result[i].matchType = 'fallback'; usedIds.add(bot.id); break; } } } } } else if (matchedCount === 1) { for (let i = 0; i < result.length; i++) { if (!result[i].id) { result[i].id = 'invalid'; result[i].matchType = 'invalid'; } } } else { for (let i = 0; i < result.length; i++) { result[i].id = 'invalid'; result[i].matchType = 'invalid'; } } for (let i = 0; i < result.length; i++) { if (!result[i].id) { result[i].id = 'invalid'; result[i].matchType = 'invalid'; } } 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 })), totalMatches: matchedCount } } }; } catch (error) { return { success: false, error: error.message, data: { soal: [], botResults: [], result: [] } }; } } module.exports = antibot;