| | const express = require('express'); |
| | const puppeteer = require('puppeteer-extra'); |
| | const StealthPlugin = require('puppeteer-extra-plugin-stealth'); |
| | const Tesseract = require('tesseract.js'); |
| | const sharp = require('sharp'); |
| | const crypto = require('crypto'); |
| |
|
| | puppeteer.use(StealthPlugin()); |
| |
|
| | const app = express(); |
| | const port = process.env.PORT || 7860; |
| |
|
| | |
| | async function getImageHash(buffer) { |
| | try { |
| | const processed = await sharp(buffer) |
| | .grayscale() |
| | .resize(32, 32, { fit: 'fill' }) |
| | .threshold(120) |
| | .toBuffer(); |
| | return crypto.createHash('md5').update(processed).digest('hex'); |
| | } catch (e) { |
| | return null; |
| | } |
| | } |
| |
|
| | |
| | async function preprocessForOCR(buffer) { |
| | return await sharp(buffer) |
| | .grayscale() |
| | .threshold(120) |
| | .resize(400) |
| | .toBuffer(); |
| | } |
| |
|
| | app.get('/solve', async (req, res) => { |
| | const targetUrl = req.query.url; |
| | if (!targetUrl) return res.status(400).send({ error: "URL-na mana?" }); |
| |
|
| | |
| | const browser = await puppeteer.launch({ |
| | headless: "new", |
| | args: [ |
| | '--no-sandbox', |
| | '--disable-setuid-sandbox', |
| | '--disable-dev-shm-usage', |
| | '--disable-gpu', |
| | '--no-zygote', |
| | '--single-process', |
| | '--hide-scrollbars' |
| | ] |
| | }); |
| |
|
| | try { |
| | const page = await browser.newPage(); |
| | await page.setViewport({ width: 1280, height: 800 }); |
| | |
| | console.log(`Maju ka: ${targetUrl}`); |
| | await page.goto(targetUrl, { waitUntil: 'networkidle2', timeout: 60000 }); |
| |
|
| | |
| | await page.waitForSelector('.antibotlinks', { timeout: 15000 }); |
| |
|
| | |
| | const instructionImg = await page.$('#atb-instruction img'); |
| | let instrBuffer = await instructionImg.screenshot(); |
| | instrBuffer = await preprocessForOCR(instrBuffer); |
| | |
| | const { data: { text: rawInstruction } } = await Tesseract.recognize(instrBuffer, 'eng'); |
| | const order = rawInstruction.toLowerCase() |
| | .replace(/[^a-z0-9, ]/g, '') |
| | .split(/[, ]+/) |
| | .filter(t => t.length > 0); |
| | |
| | console.log("Urutan nu dideteksi:", order); |
| |
|
| | |
| | const links = await page.$$('.antibotlinks a'); |
| | let linkMap = []; |
| |
|
| | for (const link of links) { |
| | const img = await link.$('img'); |
| | const imgBuffer = await img.screenshot(); |
| | |
| | const cleanImg = await preprocessForOCR(imgBuffer); |
| | const { data: { text: label } } = await Tesseract.recognize(cleanImg, 'eng'); |
| | |
| | linkMap.push({ |
| | element: link, |
| | label: label.toLowerCase().replace(/[^a-z0-9]/g, ''), |
| | done: false |
| | }); |
| | } |
| |
|
| | |
| | let clickedItems = []; |
| | for (const target of order) { |
| | const match = linkMap.find(item => |
| | (item.label.includes(target) || target.includes(item.label)) && !item.done |
| | ); |
| |
|
| | if (match) { |
| | console.log(`KLIK: ${match.label} (Target: ${target})`); |
| | await match.element.click(); |
| | match.done = true; |
| | clickedItems.push(target); |
| | await new Promise(r => setTimeout(r, 1500)); |
| | } |
| | } |
| |
|
| | res.send({ |
| | status: clickedItems.length === order.length ? "Success" : "Partial", |
| | order: order, |
| | clicked: clickedItems |
| | }); |
| |
|
| | } catch (err) { |
| | console.error("Fatal Error:", err.message); |
| | res.status(500).send({ error: err.message }); |
| | } finally { |
| | |
| | await browser.close(); |
| | } |
| | }); |
| |
|
| | app.listen(port, () => console.log(`DAN Solver Engine up on port ${port}`)); |
| |
|