mirror of
https://github.com/public-transport/db-vendo-client.git
synced 2025-04-20 23:23:56 +03:00
support for bestprice, notOnlyFastRoutes, serviceDays, fix param default handling
This commit is contained in:
parent
14b80dbf33
commit
162b946bac
12 changed files with 53 additions and 15 deletions
14
index.js
14
index.js
|
@ -85,10 +85,10 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
throw new TypeError('type must be a non-empty string.');
|
||||
}
|
||||
|
||||
if (!profile.departuresGetPasslist && 'stopovers' in opt) {
|
||||
if (!profile.departuresGetPasslist && opt.stopovers) {
|
||||
throw new Error('opt.stopovers is not supported by this endpoint');
|
||||
}
|
||||
if (!profile.departuresStbFltrEquiv && 'includeRelatedStations' in opt) { // TODO artificially filter?
|
||||
if (!profile.departuresStbFltrEquiv && 'includeRelatedStations' in opt) {
|
||||
throw new Error('opt.includeRelatedStations is not supported by this endpoint');
|
||||
}
|
||||
|
||||
|
@ -185,6 +185,8 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
entrances: true, // parse & expose entrances of stops/stations?
|
||||
remarks: true, // parse & expose hints & warnings?
|
||||
scheduledDays: false, // parse & expose dates each journey is valid on?
|
||||
notOnlyFastRoutes: false, // if true, also show routes that are mathematically non-optimal
|
||||
bestprice: false, // search for lowest prices across the entire day
|
||||
}, opt);
|
||||
|
||||
if (opt.when !== undefined) {
|
||||
|
@ -210,9 +212,15 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
const req = profile.formatJourneysReq({profile, opt}, from, to, when, outFrwd, journeysRef);
|
||||
const {res} = await profile.request({profile, opt}, userAgent, req);
|
||||
const ctx = {profile, opt, common, res};
|
||||
const verbindungen = Number.isInteger(opt.results) ? res.verbindungen.slice(0, opt.results) : res.verbindungen;
|
||||
if (opt.bestprice) {
|
||||
res.verbindungen = (res.intervalle || res.tagesbestPreisIntervalle).flatMap(i => i.verbindungen.map(v => ({...v, ...v.verbindung})));
|
||||
}
|
||||
const verbindungen = Number.isInteger(opt.results) && opt.results != 3 ? res.verbindungen.slice(0, opt.results) : res.verbindungen; // TODO remove default from hafas-rest-api
|
||||
const journeys = verbindungen
|
||||
.map(j => profile.parseJourney(ctx, j));
|
||||
if (opt.bestprice) {
|
||||
journeys.sort((a, b) => a.price?.amount - b.price?.amount);
|
||||
}
|
||||
|
||||
return {
|
||||
earlierRef: res.verbindungReference?.earlier || res.frueherContext || null,
|
||||
|
|
|
@ -50,7 +50,7 @@ const mapRouteParsers = (route, parsers) => {
|
|||
firstClass: {
|
||||
description: 'Search for first-class options?',
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
default: false,
|
||||
parse: parseBoolean,
|
||||
},
|
||||
loyaltyCard: {
|
||||
|
@ -66,6 +66,18 @@ const mapRouteParsers = (route, parsers) => {
|
|||
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,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"journeysEndpoint": "https://app.vendo.noncd.db.de/mob/angebote/fahrplan",
|
||||
"bestpriceEndpoint": "https://app.vendo.noncd.db.de/mob/angebote/tagesbestpreis",
|
||||
"refreshJourneysEndpointTickets": "https://app.vendo.noncd.db.de/mob/angebote/recon",
|
||||
"refreshJourneysEndpointPolyline": "https://app.vendo.noncd.db.de/mob/trip/recon",
|
||||
"locationsEndpoint": "https://app.vendo.noncd.db.de/mob/location/search",
|
||||
|
|
|
@ -55,8 +55,11 @@ const formatJourneysReq = (ctx, from, to, when, outFrwd, journeysRef) => {
|
|||
if (journeysRef) {
|
||||
query.reiseHin.wunsch.context = journeysRef;
|
||||
}
|
||||
if (opt.notOnlyFastRoutes) {
|
||||
query.reiseHin.wunsch.economic = true;
|
||||
}
|
||||
return {
|
||||
endpoint: ctx.profile.journeysEndpoint,
|
||||
endpoint: opt.bestprice ? profile.bestpriceEndpoint : profile.journeysEndpoint,
|
||||
body: query,
|
||||
headers: getHeaders('application/x.db.vendo.mob.verbindungssuche.v8+json'),
|
||||
method: 'post',
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"journeysEndpoint": "https://int.bahn.de/web/api/angebote/fahrplan",
|
||||
"bestpriceEndpoint": "https://int.bahn.de/web/api/angebote/tagesbestpreis",
|
||||
"refreshJourneysEndpointTickets": "https://int.bahn.de/web/api/angebote/recon",
|
||||
"refreshJourneysEndpointPolyline": "https://int.bahn.de/web/api/reiseloesung/verbindung",
|
||||
"locationsEndpoint": "https://int.bahn.de/web/api/reiseloesung/orte",
|
||||
|
|
|
@ -13,7 +13,7 @@ const formatJourneysReq = (ctx, from, to, when, outFrwd, journeysRef) => {
|
|||
deutschlandTicketVorhanden: false,
|
||||
nurDeutschlandTicketVerbindungen: false,
|
||||
reservierungsKontingenteVorhanden: false,
|
||||
schnelleVerbindungen: true,
|
||||
schnelleVerbindungen: !opt.notOnlyFastRoutes,
|
||||
sitzplatzOnly: false,
|
||||
abfahrtsHalt: from.lid,
|
||||
zwischenhalte: opt.via
|
||||
|
@ -35,14 +35,14 @@ const formatJourneysReq = (ctx, from, to, when, outFrwd, journeysRef) => {
|
|||
if (opt.results !== null) {
|
||||
// TODO query.numF = opt.results;
|
||||
}
|
||||
query = Object.assign(query, ctx.profile.formatTravellers(ctx));
|
||||
query = Object.assign(query, profile.formatTravellers(ctx));
|
||||
return {
|
||||
endpoint: ctx.profile.journeysEndpoint,
|
||||
endpoint: opt.bestprice ? profile.bestpriceEndpoint : profile.journeysEndpoint,
|
||||
body: query,
|
||||
method: 'post',
|
||||
};
|
||||
};
|
||||
// TODO poly conditional other endpoint
|
||||
|
||||
const formatRefreshJourneyReq = (ctx, refreshToken) => {
|
||||
const {profile, opt} = ctx;
|
||||
if (opt.tickets) {
|
||||
|
|
|
@ -69,7 +69,15 @@ const parseJourney = (ctx, jj) => { // j = raw journey
|
|||
// TODO
|
||||
if (opt.scheduledDays && j.serviceDays) {
|
||||
// todo [breaking]: rename to scheduledDates
|
||||
// res.scheduledDays = profile.parseScheduledDays(ctx, j.serviceDays);
|
||||
// TODO parse scheduledDays as before
|
||||
res.serviceDays = j.serviceDays.map(d => ({
|
||||
irregular: d.irregular,
|
||||
lastDateInPeriod: d.lastDateInPeriod || d.letztesDatumInZeitraum,
|
||||
planningPeriodBegin: d.planningPeriodBegin || d.planungsZeitraumAnfang,
|
||||
planningPeriodEnd: d.planningPeriodEnd || d.planungsZeitraumEnde,
|
||||
regular: d.regular,
|
||||
weekdays: d.weekdays || d.wochentage,
|
||||
}));
|
||||
}
|
||||
|
||||
res.price = profile.parsePrice(ctx, jj);
|
||||
|
|
|
@ -9,7 +9,7 @@ const parseLoadFactor = (opt, auslastung) => {
|
|||
if (!auslastung) {
|
||||
return null;
|
||||
}
|
||||
const cls = opt.firstClass
|
||||
const cls = opt.firstClass === true
|
||||
? 'KLASSE_1'
|
||||
: 'KLASSE_2';
|
||||
const load = auslastung.find(a => a.klasse === cls)?.stufe;
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
const PARTIAL_FARE_HINT = 'Teilpreis / partial fare';
|
||||
|
||||
const parsePrice = (ctx, raw) => {
|
||||
const p = raw.angebotsPreis || raw.angebote?.preise?.gesamt?.ab; // TODO teilpreis
|
||||
const p = raw.angebotsPreis || raw.angebote?.preise?.gesamt?.ab || raw.abPreis;
|
||||
if (p?.betrag) {
|
||||
const partialFare = raw.hasTeilpreis ?? raw.angebote?.preise?.istTeilpreis ?? raw.teilpreis;
|
||||
return {
|
||||
amount: p.betrag,
|
||||
currency: p.waehrung,
|
||||
hint: null,
|
||||
hint: partialFare ? PARTIAL_FARE_HINT : null,
|
||||
partialFare: partialFare,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
|
@ -45,7 +48,7 @@ const parseTickets = (ctx, j) => {
|
|||
partialFare: s.teilpreis,
|
||||
};
|
||||
if (s.teilpreis) {
|
||||
p.addData = 'Teilpreis / partial fare';
|
||||
p.addData = PARTIAL_FARE_HINT;
|
||||
}
|
||||
const conds = s.konditionsAnzeigen || s.konditionen;
|
||||
if (conds) {
|
||||
|
|
1
test/fixtures/dbnav-refresh-journey.js
vendored
1
test/fixtures/dbnav-refresh-journey.js
vendored
|
@ -212,6 +212,7 @@ const dbNavJourney = {
|
|||
amount: 43.99,
|
||||
currency: 'EUR',
|
||||
hint: null,
|
||||
partialFare: false,
|
||||
},
|
||||
tickets: [
|
||||
{
|
||||
|
|
2
test/fixtures/dbweb-journey.js
vendored
2
test/fixtures/dbweb-journey.js
vendored
|
@ -292,7 +292,7 @@ const dbwebJourney = {
|
|||
},
|
||||
],
|
||||
refreshToken: '¶HKI¶T$A=1@O=Köln Hbf@X=6958730@Y=50943029@L=8000207@a=128@$A=1@O=Köln Messe/Deutz@X=6975000@Y=50940872@L=8003368@a=128@$202504110511$202504110512$S 12$$1$$$$$$§W$A=1@O=Köln Messe/Deutz@X=6975000@Y=50940872@L=8003368@a=128@$A=1@O=Köln Messe/Deutz Gl.11-12@X=6974065@Y=50941717@L=8073368@a=128@$202504110512$202504110519$$$1$$$$$$§T$A=1@O=Köln Messe/Deutz Gl.11-12@X=6974065@Y=50941717@L=8073368@a=128@$A=1@O=Nürnberg Hbf@X=11082989@Y=49445615@L=8000284@a=128@$202504110520$202504110858$ICE 523$$1$$$$$$',
|
||||
price: {amount: 31.49, currency: 'EUR', hint: null},
|
||||
price: {amount: 31.49, currency: 'EUR', hint: null, partialFare: false},
|
||||
tickets: [{
|
||||
name: 'from',
|
||||
priceObj: {
|
||||
|
|
1
test/fixtures/dbweb-refresh-journey.js
vendored
1
test/fixtures/dbweb-refresh-journey.js
vendored
|
@ -210,6 +210,7 @@ const dbJourney = {
|
|||
amount: 27.99,
|
||||
currency: 'EUR',
|
||||
hint: null,
|
||||
partialFare: false,
|
||||
},
|
||||
tickets: [
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue