文件预览

arrivals.mjs

查看 NS Trains 技能包中的文件内容。

文件内容

scripts/arrivals.mjs

#!/usr/bin/env node
/**
 * NS Station Arrivals
 * Usage: node arrivals.mjs --station "Station Name"
 */

import { nsFetch, requireNsSubscriptionKey } from './ns-api.mjs';

const NS_SUBSCRIPTION_KEY = (() => {
  try { return requireNsSubscriptionKey(); }
  catch (e) { console.error(`❌ ${e.message}`); process.exit(1); }
})();

const STATIONS_URL = 'https://gateway.apiportal.ns.nl/reisinformatie-api/api/v2/stations';
const ARRIVALS_URL = 'https://gateway.apiportal.ns.nl/reisinformatie-api/api/v2/arrivals';

const args = process.argv.slice(2);
const getArg = (flag) => {
  const idx = args.indexOf(flag);
  return idx !== -1 ? args[idx + 1] : null;
};

const station = getArg('--station') || getArg('-s');
const limit = parseInt(getArg('--limit') || '10');

if (!station) {
  console.log(`
🚉 NS Station Arrivals

Usage: node arrivals.mjs --station "Station Name" [--limit 10]

Examples:
  node arrivals.mjs --station "Amsterdam Zuid"
  node arrivals.mjs -s "Almere Centrum" --limit 5
`);
  process.exit(1);
}

async function getStationCode(name) {
  const res = await nsFetch(`${STATIONS_URL}?q=${encodeURIComponent(name)}`, {
    subscriptionKey: NS_SUBSCRIPTION_KEY,
  });
  const data = await res.json();
  return data.payload?.[0]?.code || null;
}

async function getArrivals() {
  try {
    // Resolve station name to code
    const code = await getStationCode(station);
    if (!code) {
      console.error(`❌ Station not found: ${station}`);
      process.exit(1);
    }

    const res = await nsFetch(`${ARRIVALS_URL}?station=${code}&maxJourneys=${limit}`, {
      subscriptionKey: NS_SUBSCRIPTION_KEY,
    });

    if (!res.ok) {
      console.error(`❌ API Error: ${res.status}`);
      process.exit(1);
    }

    const data = await res.json();
    const arrivals = data.payload?.arrivals || [];

    if (arrivals.length === 0) {
      console.log(`❌ No arrivals found for "${station}"`);
      process.exit(0);
    }

    console.log(`\n🚉 Arrivals at ${station}`);
    console.log('═'.repeat(50));

    arrivals.forEach(arr => {
      const planned = new Date(arr.plannedDateTime);
      const actual = arr.actualDateTime ? new Date(arr.actualDateTime) : planned;
      const delay = Math.round((actual - planned) / 60000);

      const time = planned.toLocaleTimeString('nl-NL', { hour: '2-digit', minute: '2-digit' });
      const delayStr = delay > 0 ? ` (+${delay})` : '';
      const status = arr.cancelled ? '❌' : (delay > 0 ? '⚠️' : '✅');

      const origin = arr.origin || 'Unknown';
      const platform = arr.actualTrack || arr.plannedTrack || '?';
      const trainType = arr.product?.shortCategoryName || '?';

      console.log(`\n${status} ${time}${delayStr} ← ${origin}`);
      console.log(`   🚆 ${trainType} | 🚏 Platform ${platform}`);

      if (arr.cancelled) console.log(`   ❌ CANCELLED`);
    });

    console.log('\n' + '─'.repeat(50));
    console.log(`⏱️  Updated: ${new Date().toLocaleTimeString('nl-NL')}`);

  } catch (err) {
    console.error(`❌ Error: ${err.message}`);
    process.exit(1);
  }
}

getArrivals();