#!/usr/bin/env node // tj-snapshot.mjs — capture a live snapshot of the TransJakarta bus feed (MQTT over WSS). // // The broker (wss://mqtt.tj.co.id:8084/mqtt) is anonymous. This tool connects, // subscribes, and records the last known state of every bus it sees. // // Setup: npm install mqtt (in this directory, or a parent dir) // Usage: node tj-snapshot.mjs [--topic /mobile_armada/#] [--seconds 60] [--out snapshot.json] // // Output: summary on stdout; with --out, a JSON file: // { capturedAt, durationSec, topic, messages, busCount, buses: { : {...} } } // // Notes: undocumented infrastructure — be gentle; one connection is enough. // Whole fleet: '/mobile_armada/#'. One area: '/mobile_armada/0018-0022/#'. import mqtt from 'mqtt'; import fs from 'fs'; const args = process.argv.slice(2); const getArg = (name, fallback) => { const i = args.indexOf(name); return i >= 0 && args[i + 1] ? args[i + 1] : fallback; }; const topic = getArg('--topic', '/mobile_armada/#'); const seconds = parseInt(getArg('--seconds', '60'), 10) || 60; const outFile = getArg('--out', ''); const out = { capturedAt: new Date().toISOString(), durationSec: seconds, topic, messages: 0, buses: {} }; const client = mqtt.connect('wss://mqtt.tj.co.id:8084/mqtt', { protocolVersion: 4, keepalive: 30, clean: true, connectTimeout: 15000, reconnectPeriod: 0 }); client.on('connect', () => client.subscribe(topic, { qos: 0 })); client.on('error', (e) => console.error('MQTT error:', e.message)); client.on('message', (t, payload) => { out.messages++; try { const d = JSON.parse(payload.toString()); if (!d.bus_body_no) return; out.buses[d.bus_body_no] = { lat: d.latitude, lng: d.longitude, bearing: d.bearing, route: d.route_code, trip: d.trip_id, headsign: d.trip_headsign, type: d.type, direction: d.direction, currStop: d.curr_stops, nextStop: d.next_stops, etaNextStopMin: d.estimated_time_next_stops, lastUpdate: d.timestamp, mqttTopic: t }; } catch { /* skip malformed */ } }); setTimeout(() => { out.busCount = Object.keys(out.buses).length; console.log(`Captured ${out.messages} messages, ${out.busCount} unique buses in ${seconds}s`); if (outFile) { fs.writeFileSync(outFile, JSON.stringify(out)); console.log(`Snapshot written to ${outFile}`); } client.end(true); process.exit(0); }, seconds * 1000);