0012 / endpoints /antibot.js
Fourstore's picture
Update endpoints/antibot.js
ff4e42d verified
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;