| 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}`)); |
|
|