From b20cf1060a8c78b0e5c6532c30b75b947c9ab79c Mon Sep 17 00:00:00 2001 From: Traines Date: Fri, 21 Mar 2025 20:49:38 +0000 Subject: [PATCH] add dbris, moreStops param --- api.js | 2 + format/station-board-req.js | 33 ++------------ lib/api-parsers.js | 85 ++++++++++++++++++++--------------- p/dbris/base.json | 4 ++ p/dbris/index.js | 32 +++++++++++++ p/dbris/station-board-req.js | 34 ++++++++++++++ parse/arrival-or-departure.js | 23 ++++++---- 7 files changed, 139 insertions(+), 74 deletions(-) create mode 100644 p/dbris/base.json create mode 100644 p/dbris/index.js create mode 100644 p/dbris/station-board-req.js diff --git a/api.js b/api.js index 2712e7de..05b83d81 100644 --- a/api.js +++ b/api.js @@ -2,6 +2,7 @@ import {createClient} from './index.js'; import {profile as dbProfile} from './p/db/index.js'; import {profile as dbnavProfile} from './p/dbnav/index.js'; import {profile as dbwebProfile} from './p/dbweb/index.js'; +import {profile as dbrisProfile} from './p/dbris/index.js'; import {mapRouteParsers} from './lib/api-parsers.js'; import {createHafasRestApi as createApi} from 'hafas-rest-api'; @@ -26,6 +27,7 @@ const profiles = { db: dbProfile, dbnav: dbnavProfile, dbweb: dbwebProfile, + dbris: dbrisProfile, }; const start = async () => { diff --git a/format/station-board-req.js b/format/station-board-req.js index acc93fb5..880c879b 100644 --- a/format/station-board-req.js +++ b/format/station-board-req.js @@ -1,6 +1,10 @@ const formatStationBoardReq = (ctx, station, type) => { const {profile, opt} = ctx; + if (opt.moreStops) { + station += ',' + opt.moreStops.join(','); + } + return { endpoint: profile.boardEndpoint, path: (type == 'departures' ? 'departure' : 'arrival') + '/' + station, @@ -15,35 +19,6 @@ const formatStationBoardReq = (ctx, station, type) => { }; }; -/* -TODO separate RIS::Boards profile? -const formatRisStationBoardReq = (ctx, station, type) => { - const {profile, opt} = ctx; - - return { - endpoint: profile.boardEndpoint, - path: type + '/' + station, - query: { - // TODO direction - filterTransports: profile.formatProductsFilter(ctx, opt.products || {}, 'ris'), - timeStart: profile.formatTime(profile, opt.when, true), - timeEnd: profile.formatTime(profile, opt.when.getTime() + opt.duration * 60 * 1000, true), - maxViaStops: opt.stopovers ? undefined : 0, - includeStationGroup: opt.includeRelatedStations, - maxTransportsPerType: opt.results === Infinity ? undefined : opt.results, - includeMessagesDisruptions: opt.remarks, - sortBy: 'TIME_SCHEDULE', - }, - method: 'get', - headers: { - 'Db-Client-Id': process.env.DB_CLIENT_ID, - 'Db-Api-Key': process.env.DB_API_KEY, - 'Accept': 'application/vnd.de.db.ris+json', - }, - }; -}; -*/ - export { formatStationBoardReq, }; diff --git a/lib/api-parsers.js b/lib/api-parsers.js index e49ed529..b2ecd42f 100644 --- a/lib/api-parsers.js +++ b/lib/api-parsers.js @@ -1,5 +1,5 @@ import {data as cards} from '../format/loyalty-cards.js'; -import {parseBoolean, parseInteger} from 'hafas-rest-api/lib/parse.js'; +import {parseBoolean, parseInteger, parseArrayOfStrings} from 'hafas-rest-api/lib/parse.js'; const typesByName = new Map([ ['bahncard-1st-25', {type: cards.BAHNCARD, discount: 25, class: 1}], @@ -42,43 +42,54 @@ const parseArrayOr = (parseEntry) => { }; const mapRouteParsers = (route, parsers) => { - if (route !== 'journeys' && route !== 'refresh-journey') { - return parsers; + if (route.includes('journey')) { + return { + ...parsers, + firstClass: { + description: 'Search for first-class options?', + type: 'boolean', + default: false, + parse: parseBoolean, + }, + loyaltyCard: { + description: 'Type of loyalty card in use.', + type: 'string', + enum: types, + defaultStr: '*none*', + parse: parseArrayOr(parseLoyaltyCard), + }, + age: { + description: 'Age of traveller', + type: 'integer', + defaultStr: '*adult*', + parse: parseArrayOr(parseInteger), + }, + notOnlyFastRoutes: { + description: 'If true, also show routes that are mathematically non-optimal', + type: 'boolean', + default: false, + parse: parseBoolean, + }, + bestprice: { + description: 'Search for lowest prices across the entire day', + type: 'boolean', + default: false, + parse: parseBoolean, + }, + }; } - return { - ...parsers, - firstClass: { - description: 'Search for first-class options?', - type: 'boolean', - default: false, - parse: parseBoolean, - }, - loyaltyCard: { - description: 'Type of loyalty card in use.', - type: 'string', - enum: types, - defaultStr: '*none*', - parse: parseArrayOr(parseLoyaltyCard), - }, - age: { - description: 'Age of traveller', - type: 'integer', - defaultStr: '*adult*', - parse: parseArrayOr(parseInteger), - }, - notOnlyFastRoutes: { - description: 'If true, also show routes that are mathematically non-optimal', - type: 'boolean', - default: false, - parse: parseBoolean, - }, - bestprice: { - description: 'Search for lowest prices across the entire day', - type: 'boolean', - default: false, - parse: parseBoolean, - }, - }; + if (route.includes('departures') || route.includes('arrivals')) { + return { + ...parsers, + moreStops: { + description: 'Also include departures/arrivals for up to nine comma-separated station evaNumbers', + type: 'string', + default: '', + parse: parseArrayOfStrings, + }, + }; + } + return parsers; }; export { diff --git a/p/dbris/base.json b/p/dbris/base.json new file mode 100644 index 00000000..50a2eb1d --- /dev/null +++ b/p/dbris/base.json @@ -0,0 +1,4 @@ +{ + "boardEndpoint": "https://apis.deutschebahn.com/db/apis/ris-boards/v1/public/", + "defaultLanguage": "en" +} diff --git a/p/dbris/index.js b/p/dbris/index.js new file mode 100644 index 00000000..937a231f --- /dev/null +++ b/p/dbris/index.js @@ -0,0 +1,32 @@ +import baseProfile from './base.json' with { type: 'json' }; +import {products} from '../../lib/products.js'; +import {formatStationBoardReq} from './station-board-req.js'; + +const profile = { + ...baseProfile, + locale: 'de-DE', + timezone: 'Europe/Berlin', + + products, + + + formatStationBoardReq, + + journeysOutFrwd: true, + departuresGetPasslist: true, + departuresStbFltrEquiv: true, + trip: false, + radar: false, + refreshJourney: false, + journeysFromTrip: false, + refreshJourneyUseOutReconL: false, + tripsByName: false, + remarks: false, + remarksGetPolyline: false, + reachableFrom: false, + lines: false, +}; + +export { + profile, +}; diff --git a/p/dbris/station-board-req.js b/p/dbris/station-board-req.js new file mode 100644 index 00000000..a4d9bb2b --- /dev/null +++ b/p/dbris/station-board-req.js @@ -0,0 +1,34 @@ +const formatStationBoardReq = (ctx, station, type) => { + const {profile, opt} = ctx; + + const query = { + filterTransports: profile.formatProductsFilter(ctx, opt.products || {}, 'ris_alt'), + timeStart: profile.formatTime(profile, opt.when, true), + timeEnd: profile.formatTime(profile, opt.when.getTime() + opt.duration * 60 * 1000, true), + includeStationGroup: opt.includeRelatedStations, + maxTransportsPerType: opt.results === Infinity ? undefined : opt.results, + includeMessagesDisruptions: opt.remarks, + sortBy: 'TIME_SCHEDULE', + }; + if (!opt.stopovers) { + query.maxViaStops = 0; + } + if (opt.moreStops) { + station += ',' + opt.moreStops.join(','); + } + return { + endpoint: profile.boardEndpoint, + path: type + '/' + station, + query: query, + method: 'get', + headers: { + 'Db-Client-Id': process.env.DB_CLIENT_ID, + 'Db-Api-Key': process.env.DB_API_KEY, + 'Accept': 'application/vnd.de.db.ris+json', + }, + }; +}; + +export { + formatStationBoardReq, +}; diff --git a/parse/arrival-or-departure.js b/parse/arrival-or-departure.js index 2dab416c..27231bb6 100644 --- a/parse/arrival-or-departure.js +++ b/parse/arrival-or-departure.js @@ -40,14 +40,21 @@ const createParseArrOrDep = (prefix) => { res.remarks = profile.parseRemarks(ctx, d); } - if ((opt.stopovers || opt.direction) && Array.isArray(d.ueber)) { - const stopovers = d.ueber - .map(viaName => profile.parseStopover(ctx, {name: viaName}, null)); - - if (prefix === ARRIVAL) { - res.previousStopovers = stopovers; - } else if (prefix === DEPARTURE) { - res.nextStopovers = stopovers; + if (opt.stopovers || opt.direction) { + let stopovers = undefined; + if (Array.isArray(d.ueber)) { + stopovers = d.ueber + .map(viaName => profile.parseStopover(ctx, {name: viaName}, null)); + } else if (Array.isArray(d.transport?.via)) { + stopovers = (d.transport?.via) + .map(via => profile.parseStopover(ctx, via, null)); + } + if (stopovers) { + if (prefix === ARRIVAL) { + res.previousStopovers = stopovers; + } else if (prefix === DEPARTURE) { + res.nextStopovers = stopovers; + } } }