Arsitektur Aggregator Live Draw — Bagaimana 14 Pasaran Di-update Otomatis
Behind the scenes: cron 30-menit, single-fetch scraper, deduplication, dan arsip 10 tahun yang accumulating.
Behind the scenes: cron 30-menit, single-fetch scraper, deduplication, dan arsip 10 tahun yang accumulating.
togel.to bukan operator lottery — kami adalah aggregator data publik. Setiap pasaran (Hongkong Pools, Singapore Pools, dst) memiliki situs aggregator pihak ketiga yang sudah scrape dari sumber resmi. Kami pilih satu sumber master (grab4dofficial.org) untuk efisiensi + konsistensi.
Pipeline arsitektur: launchd (macOS cron) → tsx scrape-grab4d.ts → fetch HTML → cheerio parse → merge dengan data/paito/{slug}.json → write file → Next.js ISR pickup pada request berikutnya.
Initial design pakai per-pasaran scraping (14 fetch berbeda). Issue: rate limiting risk + inconsistency timestamp + complexity. Refactor ke single-fetch strategy — 1 HTTP request ke grab4dofficial.org homepage yang udah display semua 14 pasaran sekaligus.
// Single fetch — 1 request, 14 markets
const html = await fetch("https://grab4dofficial.org/").then(r => r.text());
const $ = cheerio.load(html);
const MARKET_MAP = {
hk: { name: "Hongkong", code: "HKG", gid: "hongkong-pools" },
sgp: { name: "Singapore", code: "SGP", gid: "singapore-pools" },
// ... 14 entries total
};
for (const [slug, info] of Object.entries(MARKET_MAP)) {
const block = $(`#${info.gid}`);
if (!block.length) continue;
const result = parseMarketBlock(block, info);
await mergeIntoArchive(slug, result);
}Setiap blok pasaran di grab4d punya struktur HTML yang konsisten: header pasaran + tabel hasil terbaru. Parser extract date (format Indonesian 'Kamis, 30-Apr-2026') + 4 digit hasil per row.
Indonesian date parser handle locale-specific format yang tidak bisa di-parse oleh native Date() JavaScript: bulan disingkat dalam bahasa Indonesia (Jan, Feb, Mar, Apr, Mei, Jun, Jul, Agu, Sep, Okt, Nov, Des).
Critical insight: scrape hanya dapat data terbaru (~7-30 baris terakhir). Tapi arsip kami punya 10 tahun (~3650 baris per pasaran). Kalau langsung overwrite, history hilang. Solusi: merge logic yang preserve historis + dedupe berdasarkan tanggal.
async function mergeIntoArchive(slug: string, fresh: PaitoRow[]) {
const filePath = `data/paito/${slug}.json`;
const existing: PaitoRow[] = JSON.parse(
await fs.readFile(filePath, "utf-8").catch(() => "[]")
);
// Merge: dedupe berdasarkan tanggal
const map = new Map<string, PaitoRow>();
for (const r of [...existing, ...fresh]) {
const key = r.date.toISOString().slice(0, 10); // YYYY-MM-DD
map.set(key, r); // overwrite — fresh data wins kalau sama tanggal
}
const merged = [...map.values()].sort(
(a, b) => b.date.getTime() - a.date.getTime() // newest first
);
await fs.writeFile(filePath, JSON.stringify(merged, null, 2));
}Dengan logic ini, setiap scrape hanya menambah baris baru atau update baris yang sudah ada. Arsip 10 tahun selalu intact, dan data terbaru selalu fresh.
Server togel.to jalan di macOS (Mac Mini sebagai always-on dev server). Untuk scheduling pakai launchd — native macOS service manager, lebih reliable dibanding crontab.
<!-- ~/Library/LaunchAgents/to.togel.scrape-grab4d.plist -->
<plist version="1.0">
<dict>
<key>Label</key>
<string>to.togel.scrape-grab4d</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/npx</string>
<string>tsx</string>
<string>scripts/scrape-grab4d.ts</string>
</array>
<key>WorkingDirectory</key>
<string>/Users/jarvis/Documents/togel-to</string>
<key>StartInterval</key>
<integer>1800</integer>
<key>RunAtLoad</key>
<true/>
<key>StandardOutPath</key>
<string>/tmp/togel-scrape.log</string>
</dict>
</plist>StartInterval 1800 = 30 menit. RunAtLoad true berarti scrape jalan immediately saat plist di-load. Log output ke /tmp/togel-scrape.log untuk debugging.
Tidak semua pasaran memiliki tingkat verifikasi yang sama. Kami klasifikasi 3 tier: AUDITED (HK Pools, SGP Pools — WLA-audited operator resmi), VERIFIED (Sydney, Cambodia, Taiwan, Korea, Malaysia, Vietnam, China — cross-referenced multiple aggregator), AGGREGATOR (Japan, Mongolia, Nusantara, HK Night, Nex4D — single feed bandar-curated).
Tier ini transparan ditampilkan di setiap card pasaran di /prediksi dengan badge berwarna. Tujuannya: user paham tingkat trust per pasaran sebelum baseline keputusan apapun.