Limit / endpoints /antibot.js
NAME
Pesan singkat tentang perubahan
4f05ffd
const fs = require('fs');
const path = require('path');
const { extractTextFromImage, uploadImageToHosting } = require('./imageProcessor');
// Map angka kata -> digit
const NUMBER_WORDS = {
'nol': '0', 'satu': '1', 'dua': '2', 'tiga': '3', 'empat': '4', 'lima': '5',
'enam': '6', 'tujuh': '7', 'delapan': '8', 'sembilan': '9', 'sepuluh': '10',
'sebelas': '11', 'belas': '', 'puluh': '', 'ratus': '', 'ribu': ''
};
// Simple leet replacements
const LEET_MAP = {
'4': 'a', '@': 'a', '8': 'b', '3': 'e', '6': 'g', '1': 'i', '!': 'i', '0': 'o',
'5': 's', '$': 's', '7': 't', '+': 't', '2': 'z'
};
function safeString(s) {
return (s || '').toString();
}
async function extractTextFromBuffer(imageBuffer) {
try {
if (!imageBuffer) throw new Error('No image buffer provided');
if (!Buffer.isBuffer(imageBuffer)) throw new Error('extractTextFromBuffer expects a Buffer');
console.log('πŸ–ΌοΈ DEBUG extractTextFromBuffer: Received buffer, size:', imageBuffer.length, 'bytes');
const result = await extractTextFromImage(imageBuffer);
console.log('βœ… DEBUG extractTextFromBuffer: Hasil ekstraksi:', result);
return result;
} catch (error) {
console.error('❌ DEBUG extractTextFromBuffer Error:', error.message);
return { status: false, response: 'Gagal memproses gambar' };
}
}
function removeAccents(str) {
return safeString(str).normalize('NFD').replace(/[\u0300-\u036f]/g, '');
}
function applyLeetMap(str) {
let out = '';
for (const ch of str) {
out += (LEET_MAP[ch] !== undefined) ? LEET_MAP[ch] : ch;
}
return out;
}
function wordsToNumbers(str) {
// Very simple conversion for single-word numbers in Indonesian (e.g., "tujuh" -> "7")
const tokens = str.split(/\s+/);
return tokens.map(t => {
const low = t.toLowerCase();
return NUMBER_WORDS[low] !== undefined ? NUMBER_WORDS[low] : t;
}).join(' ');
}
function normalizeText(text) {
const original = safeString(text);
let normalized = removeAccents(original);
normalized = normalized.toLowerCase();
normalized = applyLeetMap(normalized);
// keep letters, numbers and spaces only
normalized = normalized.replace(/[^a-z0-9\s]/g, ' ').replace(/\s+/g, ' ').trim();
// map simple number words
normalized = wordsToNumbers(normalized);
console.log(`πŸ”€ DEBUG normalizeText: "${original}" -> "${normalized}"`);
return normalized;
}
// Levenshtein distance for fuzzy matching
function levenshtein(a = '', b = '') {
const alen = a.length, blen = b.length;
if (alen === 0) return blen;
if (blen === 0) return alen;
const matrix = Array.from({ length: alen + 1 }, () => new Array(blen + 1));
for (let i = 0; i <= alen; i++) matrix[i][0] = i;
for (let j = 0; j <= blen; j++) matrix[0][j] = j;
for (let i = 1; i <= alen; i++) {
for (let j = 1; j <= blen; j++) {
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
matrix[i][j] = Math.min(
matrix[i - 1][j] + 1,
matrix[i][j - 1] + 1,
matrix[i - 1][j - 1] + cost
);
}
}
return matrix[alen][blen];
}
function similarity(a, b) {
a = safeString(a);
b = safeString(b);
if (a === b) return 1;
const dist = levenshtein(a, b);
const maxLen = Math.max(a.length, b.length);
return maxLen === 0 ? 1 : 1 - (dist / maxLen);
}
function tryEvaluateMathExpression(s) {
// allow simple expressions like "3+4", " 7 - 2 ", "2 * (3+1)"
try {
const cleaned = s.replace(/[^0-9+\-*/().\s]/g, '');
if (!/[0-9]/.test(cleaned)) return null;
// eslint-disable-next-line no-new-func
const val = Function(`"use strict"; return (${cleaned});`)();
if (typeof val === 'number' && isFinite(val)) return String(val);
return null;
} catch {
return null;
}
}
// compare OCR value with expected soal (question) with multiple heuristics
function isValueMatch(value, soal, options = {}) {
const { fuzzyThreshold = 0.75 } = options;
console.log('πŸ” DEBUG isValueMatch: Value="%s", Soal="%s"', value, soal);
if (!value && !soal) return false;
const vNorm = normalizeText(safeString(value));
const sNorm = normalizeText(safeString(soal));
// exact match
if (vNorm === sNorm) {
console.log('βœ… DEBUG exact match');
return true;
}
// try math evaluation for both sides
const vMath = tryEvaluateMathExpression(vNorm);
const sMath = tryEvaluateMathExpression(sNorm);
if (vMath !== null && sMath !== null && vMath === sMath) {
console.log('βœ… DEBUG math match:', vMath);
return true;
}
if (vMath !== null && sMath === null && vMath === sNorm) {
console.log('βœ… DEBUG math->soal match:', vMath);
return true;
}
if (sMath !== null && vMath === null && sMath === vNorm) {
console.log('βœ… DEBUG soal->math match:', sMath);
return true;
}
// numeric comparison if either is numeric
const vNum = parseFloat(vNorm);
const sNum = parseFloat(sNorm);
if (!isNaN(vNum) && !isNaN(sNum) && Math.abs(vNum - sNum) < 1e-9) {
console.log('βœ… DEBUG numeric equal');
return true;
}
// fuzzy text similarity
const sim = similarity(vNorm, sNorm);
console.log('πŸ“Š DEBUG Similarity:', sim);
if (sim >= fuzzyThreshold) {
console.log('βœ… DEBUG fuzzy match (threshold=', fuzzyThreshold, ')');
return true;
}
// partial match: one contains the other with decent length
if (vNorm && sNorm) {
if (vNorm.includes(sNorm) || sNorm.includes(vNorm)) {
const longer = Math.max(vNorm.length, sNorm.length);
if (longer >= 3) {
console.log('βœ… DEBUG partial contains match');
return true;
}
}
}
console.log('❌ DEBUG: No match found');
return false;
}
function mapAnswer(soalArray, jawaban, botIndex) {
console.log(`πŸ€– DEBUG mapAnswer: Bot ${botIndex}, Jawaban: "${jawaban}"`);
// jawaban can be object or string; normalize to string for now
if (jawaban && typeof jawaban === 'object' && jawaban.response) {
return jawaban.response;
}
return jawaban;
}
// --- Integrated helper from userscript: countPairs ---
function countPairs(s1 = '', s2 = '') {
s1 = safeString(s1).toLowerCase().replace(/[^a-z]/g, '');
s2 = safeString(s2).toLowerCase().replace(/[^a-z]/g, '');
const n1 = s1.length, n2 = s2.length;
const freq1 = Array(26).fill(0);
const freq2 = Array(26).fill(0);
for (let i = 0; i < n1; i++) freq1[s1.charCodeAt(i) - 97] = (freq1[s1.charCodeAt(i) - 97] || 0) + 1;
for (let i = 0; i < n2; i++) freq2[s2.charCodeAt(i) - 97] = (freq2[s2.charCodeAt(i) - 97] || 0) + 1;
let count = 0;
for (let i = 0; i < 26; i++) count += Math.min(freq1[i], freq2[i]);
return count;
}
// Export utilities
module.exports = {
extractTextFromBuffer,
mapAnswer,
normalizeText,
isValueMatch,
levenshtein,
similarity,
NUMBER_WORDS,
LEET_MAP,
countPairs
};