Spaces:
Paused
Paused
Update endpoints/antibot.js
Browse files- endpoints/antibot.js +26 -12
endpoints/antibot.js
CHANGED
|
@@ -57,7 +57,7 @@ function evaluateMath(exp) {
|
|
| 57 |
try {
|
| 58 |
const cleanExp = exp.replace(/x/g, '*');
|
| 59 |
|
| 60 |
-
if (!/^\d+[\+\-\*\/]\d+/.test(cleanExp)) {
|
| 61 |
return null;
|
| 62 |
}
|
| 63 |
|
|
@@ -88,7 +88,6 @@ async function preprocessSoal(b) {
|
|
| 88 |
}
|
| 89 |
|
| 90 |
async function preprocessBot(b) {
|
| 91 |
-
// Preprocessing agresif untuk tombol anti-bot (berbagai warna)
|
| 92 |
return sharp(b)
|
| 93 |
.resize({ width: 800 })
|
| 94 |
.grayscale()
|
|
@@ -101,12 +100,10 @@ async function preprocessBot(b) {
|
|
| 101 |
// ==========================
|
| 102 |
// FUNGSI OCR
|
| 103 |
// ==========================
|
| 104 |
-
// OCR Umum untuk Soal & Tombol Bot (karena tombol bisa Romawi atau Arab)
|
| 105 |
async function ocr(buf) {
|
| 106 |
const tmp = path.join(__dirname, "tmp_" + Date.now() + ".png");
|
| 107 |
fs.writeFileSync(tmp, buf);
|
| 108 |
|
| 109 |
-
// Whitelist diperluas: termasuk huruf Romawi I,V,X
|
| 110 |
const whitelist = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@$&+-*/,.;";
|
| 111 |
|
| 112 |
try {
|
|
@@ -116,7 +113,7 @@ async function ocr(buf) {
|
|
| 116 |
tessedit_ocr_engine_mode: "1"
|
| 117 |
});
|
| 118 |
fs.unlinkSync(tmp);
|
| 119 |
-
// Hapus spasi dan simbol
|
| 120 |
return r.data.text.trim().replace(/\s+/g, '').replace(/[^-+*/,;.\w\d]/g, '');
|
| 121 |
} catch (error) {
|
| 122 |
fs.unlinkSync(tmp);
|
|
@@ -148,7 +145,7 @@ module.exports = async data => {
|
|
| 148 |
|
| 149 |
// Langkah B: Fallback ke Regex Pola
|
| 150 |
if (rawParts.length < 3) {
|
| 151 |
-
const regex = /(\d[\+\*x\/\-]\d|\d{1,3}(?:[.,]\d{1,3})?|one|two|three|four|five|six|seven|eight|nine)/gi;
|
| 152 |
let match;
|
| 153 |
|
| 154 |
rawParts = [];
|
|
@@ -157,9 +154,9 @@ module.exports = async data => {
|
|
| 157 |
}
|
| 158 |
}
|
| 159 |
|
| 160 |
-
const wordToDigit = { one: '1', two: '2', three: '3', four: '4', five: '5', six: '6', seven: '7', eight: '8', nine: '9' };
|
| 161 |
|
| 162 |
-
// Langkah C: Proses 3 Bagian Pertama yang Ditemukan
|
| 163 |
for (let i = 0; i < rawParts.length && soalRaw.length < 3; i++) {
|
| 164 |
let expression = rawParts[i].toLowerCase();
|
| 165 |
|
|
@@ -167,8 +164,12 @@ module.exports = async data => {
|
|
| 167 |
expression = wordToDigit[expression];
|
| 168 |
}
|
| 169 |
|
| 170 |
-
|
|
|
|
|
|
|
|
|
|
| 171 |
soalRaw.push(cleanExpression);
|
|
|
|
| 172 |
soalValues.push(evaluateMath(cleanExpression) ?? parseInt(cleanExpression) ?? null);
|
| 173 |
}
|
| 174 |
|
|
@@ -187,7 +188,7 @@ module.exports = async data => {
|
|
| 187 |
const d = buf(b.img);
|
| 188 |
const p = await preprocessBot(d);
|
| 189 |
|
| 190 |
-
const t = await ocr(p);
|
| 191 |
|
| 192 |
const clean = t.replace(/[^A-Za-z0-9@$&]/g, "");
|
| 193 |
const leet = leetize(clean);
|
|
@@ -213,10 +214,18 @@ module.exports = async data => {
|
|
| 213 |
const botIds = botResults.map(b => b.id);
|
| 214 |
|
| 215 |
// PRIORITAS 1: Pencocokan Nilai Numerik (Angka Arab = Angka Romawi)
|
|
|
|
| 216 |
for (let i = 0; i < soalValues.length; i++) {
|
| 217 |
const targetValue = soalValues[i];
|
| 218 |
|
| 219 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 220 |
let bestBot = null;
|
| 221 |
|
| 222 |
for (const bot of botResults) {
|
|
@@ -235,9 +244,14 @@ module.exports = async data => {
|
|
| 235 |
}
|
| 236 |
}
|
| 237 |
|
| 238 |
-
// PRIORITAS 2: Pencocokan Levenshtein
|
| 239 |
for (let i = 0; i < soalLeet.length; i++) {
|
| 240 |
if (result[i] !== null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
|
| 242 |
const s = soalLeet[i];
|
| 243 |
let bestBot = null;
|
|
|
|
| 57 |
try {
|
| 58 |
const cleanExp = exp.replace(/x/g, '*');
|
| 59 |
|
| 60 |
+
if (!/^\d+[\+\-\*\/]\d+$/.test(cleanExp)) { // Pastikan ada 2 angka dan 1 operator
|
| 61 |
return null;
|
| 62 |
}
|
| 63 |
|
|
|
|
| 88 |
}
|
| 89 |
|
| 90 |
async function preprocessBot(b) {
|
|
|
|
| 91 |
return sharp(b)
|
| 92 |
.resize({ width: 800 })
|
| 93 |
.grayscale()
|
|
|
|
| 100 |
// ==========================
|
| 101 |
// FUNGSI OCR
|
| 102 |
// ==========================
|
|
|
|
| 103 |
async function ocr(buf) {
|
| 104 |
const tmp = path.join(__dirname, "tmp_" + Date.now() + ".png");
|
| 105 |
fs.writeFileSync(tmp, buf);
|
| 106 |
|
|
|
|
| 107 |
const whitelist = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@$&+-*/,.;";
|
| 108 |
|
| 109 |
try {
|
|
|
|
| 113 |
tessedit_ocr_engine_mode: "1"
|
| 114 |
});
|
| 115 |
fs.unlinkSync(tmp);
|
| 116 |
+
// Hapus spasi dan simbol non-esensial, pertahankan delimiter
|
| 117 |
return r.data.text.trim().replace(/\s+/g, '').replace(/[^-+*/,;.\w\d]/g, '');
|
| 118 |
} catch (error) {
|
| 119 |
fs.unlinkSync(tmp);
|
|
|
|
| 145 |
|
| 146 |
// Langkah B: Fallback ke Regex Pola
|
| 147 |
if (rawParts.length < 3) {
|
| 148 |
+
const regex = /(\d[\+\*x\/\-]\d|\d{1,3}(?:[.,]\d{1,3})?|one|two|three|four|five|six|seven|eight|nine|ten)/gi;
|
| 149 |
let match;
|
| 150 |
|
| 151 |
rawParts = [];
|
|
|
|
| 154 |
}
|
| 155 |
}
|
| 156 |
|
| 157 |
+
const wordToDigit = { one: '1', two: '2', three: '3', four: '4', five: '5', six: '6', seven: '7', eight: '8', nine: '9', ten: '10' };
|
| 158 |
|
| 159 |
+
// Langkah C: Proses 3 Bagian Pertama yang Ditemukan & Pembersihan Akhir
|
| 160 |
for (let i = 0; i < rawParts.length && soalRaw.length < 3; i++) {
|
| 161 |
let expression = rawParts[i].toLowerCase();
|
| 162 |
|
|
|
|
| 164 |
expression = wordToDigit[expression];
|
| 165 |
}
|
| 166 |
|
| 167 |
+
// Pembersihan Akhir: Hapus semua karakter yang tidak valid (selain angka dan operator)
|
| 168 |
+
let cleanExpression = expression.replace(/[^0-9\+\-\*\/]/g, '');
|
| 169 |
+
cleanExpression = cleanExpression.replace(/x/g, '*');
|
| 170 |
+
|
| 171 |
soalRaw.push(cleanExpression);
|
| 172 |
+
// Cek nilai numerik: evaluasi jika operasi, parse jika angka murni
|
| 173 |
soalValues.push(evaluateMath(cleanExpression) ?? parseInt(cleanExpression) ?? null);
|
| 174 |
}
|
| 175 |
|
|
|
|
| 188 |
const d = buf(b.img);
|
| 189 |
const p = await preprocessBot(d);
|
| 190 |
|
| 191 |
+
const t = await ocr(p);
|
| 192 |
|
| 193 |
const clean = t.replace(/[^A-Za-z0-9@$&]/g, "");
|
| 194 |
const leet = leetize(clean);
|
|
|
|
| 214 |
const botIds = botResults.map(b => b.id);
|
| 215 |
|
| 216 |
// PRIORITAS 1: Pencocokan Nilai Numerik (Angka Arab = Angka Romawi)
|
| 217 |
+
const numericSoalIndices = [];
|
| 218 |
for (let i = 0; i < soalValues.length; i++) {
|
| 219 |
const targetValue = soalValues[i];
|
| 220 |
|
| 221 |
+
// Tandai soal sebagai numerik jika mengandung operator atau angka murni
|
| 222 |
+
const isMathOrPureNumber = /^\d+[\+\-\*\/]\d+$/.test(soalRaw[i]) || /^\d+$/.test(soalRaw[i]);
|
| 223 |
+
|
| 224 |
+
if (isMathOrPureNumber) {
|
| 225 |
+
numericSoalIndices.push(i);
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
if (targetValue !== null) {
|
| 229 |
let bestBot = null;
|
| 230 |
|
| 231 |
for (const bot of botResults) {
|
|
|
|
| 244 |
}
|
| 245 |
}
|
| 246 |
|
| 247 |
+
// PRIORITAS 2: Pencocokan Levenshtein (Hanya untuk soal BUKAN angka/matematika murni)
|
| 248 |
for (let i = 0; i < soalLeet.length; i++) {
|
| 249 |
if (result[i] !== null) continue;
|
| 250 |
+
|
| 251 |
+
// Abaikan Levenshtein jika soal adalah numerik (kita ingin Auto-fill di P3, bukan Leet)
|
| 252 |
+
if (numericSoalIndices.includes(i)) {
|
| 253 |
+
continue;
|
| 254 |
+
}
|
| 255 |
|
| 256 |
const s = soalLeet[i];
|
| 257 |
let bestBot = null;
|