+84
| 'use strict' | ||
| const moment = require('moment-timezone') | ||
| const { stringify } = require('query-string') | ||
| const { fetch } = require('fetch-ponyfill')() | ||
| const parse = require('./parse') | ||
| const formatDate = (date) => moment(date).tz('Europe/Berlin').format('DD.MM.YY') | ||
| const formatTime = (date) => moment(date).tz('Europe/Berlin').format('HH:mm') | ||
| const defaults = { | ||
| class: 2, // 1st class or 2nd class? | ||
| travellers: [{ // one or more | ||
| bc: 0, // bahncard; 13, 12, 11, 9, 4, 3, 2, 1, 0 | ||
| typ: 'E', // E: adult: K: child; B: baby | ||
| alter: 30 | ||
| }], | ||
| noICETrains: false, | ||
| // todo: can this have any value? the DB app just offers 0, 10, 15, 20, 25 | ||
| transferTime: 0, // in minutes | ||
| duration: 1440, // search for routes in the next n minutes | ||
| preferFastRoutes: true | ||
| } | ||
| const endpoint = 'https://ps.bahn.de/preissuche/preissuche/psc_service.go' | ||
| const queryPrices = (start, dest, date, opt) => { | ||
| opt = Object.assign({}, defaults, opt || {}) | ||
| date = date || new Date() | ||
| const query = { | ||
| lang: 'en', | ||
| service: 'pscangebotsuche', | ||
| data: JSON.stringify({ | ||
| s: start, | ||
| d: dest, | ||
| dt: formatDate(date), | ||
| t: formatTime(date), | ||
| c: opt.class, | ||
| ohneICE: opt.noICETrains, | ||
| tct: opt.transferTime, | ||
| dur: opt.duration, | ||
| travellers: opt.travellers, | ||
| sv: opt.preferFastRoutes, | ||
| v: '16040000', // client version (direved from date?) | ||
| dir: '1', // ??? | ||
| bic: false, // ??? | ||
| device: 'HANDY', // todo: is this necessary? | ||
| os: 'iOS_9.3.1' // todo: is this necessary? | ||
| }) | ||
| } | ||
| return fetch(endpoint + '?' + stringify(query), { | ||
| headers: { accept: 'application/json' }, | ||
| mode: 'cors', | ||
| redirect: 'follow' | ||
| }) | ||
| .then((res) => { | ||
| if (!res.ok) { | ||
| const err = new Error(res.statusText) | ||
| err.statusCode = res.status | ||
| throw err | ||
| } | ||
| return res.json() | ||
| }) | ||
| .then((body) => { | ||
| const notes = parse.notes(body.peTexte) | ||
| const offers = [] | ||
| for (let id in body.angebote) { | ||
| offers.push(parse.offer(body.angebote[id], notes)) | ||
| } | ||
| const journeys = [] | ||
| for (let id in body.verbindungen) { | ||
| journeys.push(parse.journey(body.verbindungen[id], offers)) | ||
| } | ||
| return journeys | ||
| }) | ||
| } | ||
| module.exports = queryPrices |
+100
| 'use strict' | ||
| const moment = require('moment-timezone') | ||
| const slugg = require('slugg') | ||
| const minBy = require('lodash.minby') | ||
| const price = (string) => parseFloat(string.replace(',', '.')) | ||
| const offer = (data, notes) => { | ||
| const offer = { | ||
| // todo: sel, t, c, arq, ff, aix, risids | ||
| name: null, | ||
| description: null, | ||
| discount: data.tt === 'SP', // are there others than SP & NP? | ||
| price: price(data.p), | ||
| routes: data.sids, | ||
| anyTrain: data.zb !== 'Y' | ||
| } | ||
| if (notes[data.pky]) Object.assign(offer, notes[data.pky]) | ||
| return offer | ||
| } | ||
| const when = (data) => { | ||
| // todo: timezone | ||
| return moment.tz(parseInt(data.m), 'Europe/Berlin').toISOString() | ||
| } | ||
| // see https://github.com/public-transport/friendly-public-transport-format/blob/5ba55e2/docs/readme.md#modes | ||
| const modesByProduct = { | ||
| S: 'train', | ||
| IC: 'train', | ||
| ICE: 'train', | ||
| RE: 'train', | ||
| VBG: 'train', // http://www.laenderbahn.com/vogtlandbahn/ | ||
| ALX: 'train' // http://www.laenderbahn.com/alex | ||
| } | ||
| const leg = (data) => ({ | ||
| // todo: rp, re, sp | ||
| origin: { | ||
| type: 'station', | ||
| id: data.s, | ||
| name: data.sn | ||
| // todo: coordinates | ||
| }, | ||
| departure: when(data.dep), | ||
| departurePlatform: data.pd || null, | ||
| destination: { | ||
| type: 'station', | ||
| id: data.d, | ||
| name: data.dn | ||
| // todo: coordinates | ||
| }, | ||
| arrival: when(data.arr), | ||
| arrivalPlatform: data.pa || null, | ||
| line: { | ||
| type: 'line', | ||
| id: slugg(data.tn), | ||
| name: data.tn, | ||
| mode: modesByProduct[data.eg] || null, | ||
| product: data.eg | ||
| } | ||
| }) | ||
| const journey = (_, offers) => { | ||
| const legs = _.trains.map(leg) | ||
| let offer = minBy(offers.filter((o) => o.routes.includes(_.sid)), (o) => o.price) | ||
| const price = offer ? { | ||
| currency: 'EUR', | ||
| amount: offer.price, | ||
| discount: offer.discount, | ||
| name: offer.name, | ||
| description: offer.description, | ||
| anyTrain: offer.anyTrain | ||
| } : null | ||
| return { | ||
| // todo: sel, dir | ||
| type: 'journey', | ||
| id: _.sid, | ||
| origin: legs[0].origin, | ||
| destination: legs[legs.length - 1].destination, | ||
| legs, | ||
| price, | ||
| nightTrain: _.NZVerb // todo: why here? | ||
| } | ||
| } | ||
| const notes = (data) => { | ||
| const notes = {} | ||
| for (let ref in data) { | ||
| notes[ref] = { name: data[ref].name, description: data[ref].hinweis } | ||
| } | ||
| return notes | ||
| } | ||
| module.exports = { notes, offer, journey } |
+36
-28
| { | ||
| "name": "db-prices", | ||
| "version": "3.0.2", | ||
| "description": "Find the cheapest routes using the DB Sparpreise API.", | ||
| "version": "3.0.1", | ||
| "keywords": [ | ||
| "train", | ||
| "railway", | ||
| "fare", | ||
| "sparpreis", | ||
| "preis", | ||
| "DB", | ||
| "allemand", | ||
| "bahn", | ||
| "deutsch", | ||
| "allemand", | ||
| "fare", | ||
| "german", | ||
| "bahn" | ||
| "preis", | ||
| "railway", | ||
| "sparpreis", | ||
| "train" | ||
| ], | ||
| "homepage": "https://github.com/juliuste/db-prices", | ||
| "bugs": "https://github.com/juliuste/db-prices/issues", | ||
| "repository": "juliuste/db-prices", | ||
| "license": "ISC", | ||
| "author": "Julius Tens <mail@juliustens.eu>", | ||
@@ -21,32 +25,36 @@ "contributors": [ | ||
| ], | ||
| "homepage": "https://github.com/juliuste/db-prices", | ||
| "repository": "juliuste/db-prices", | ||
| "bugs": "https://github.com/juliuste/db-prices/issues", | ||
| "main": "index.js", | ||
| "files": [ | ||
| "index.js", | ||
| "parse.js" | ||
| "lib/*" | ||
| ], | ||
| "main": "lib/index.js", | ||
| "scripts": { | ||
| "check-deps": "depcheck", | ||
| "fix": "eslint --fix lib test.js", | ||
| "lint": "eslint lib test.js", | ||
| "prepublishOnly": "npm test", | ||
| "test": "npm run lint && npm run check-deps && node test" | ||
| }, | ||
| "dependencies": { | ||
| "fetch-ponyfill": "^6.0.2", | ||
| "fetch-ponyfill": "^6.1.0", | ||
| "lodash.minby": "^4.6.0", | ||
| "moment-timezone": "^0.5.21", | ||
| "query-string": "^6.1.0", | ||
| "moment-timezone": "^0.5.25", | ||
| "query-string": "^6.7.0", | ||
| "slugg": "^1.2.1" | ||
| }, | ||
| "devDependencies": { | ||
| "db-stations": "^2.8.0", | ||
| "db-stations": "^2.11.0", | ||
| "depcheck": "^0.8.1", | ||
| "eslint": "^5.16.0", | ||
| "eslint-config-standard": "^12.0.0", | ||
| "eslint-plugin-import": "^2.17.3", | ||
| "eslint-plugin-node": "^9.1.0", | ||
| "eslint-plugin-promise": "^4.1.1", | ||
| "eslint-plugin-standard": "^4.0.0", | ||
| "is-roughly-equal": "^0.1.0", | ||
| "tap-spec": "^5.0.0", | ||
| "tape": "^4.9.1", | ||
| "tape-promise": "^3.0.0" | ||
| "tape": "^4.10.2", | ||
| "tape-promise": "^4.0.0" | ||
| }, | ||
| "scripts": { | ||
| "test": "node test.js | tap-spec", | ||
| "prepareOnly": "npm test" | ||
| }, | ||
| "engine": { | ||
| "engines": { | ||
| "node": ">=8" | ||
| }, | ||
| "license": "ISC" | ||
| } | ||
| } |
-84
| 'use strict' | ||
| const moment = require('moment-timezone') | ||
| const {stringify} = require('query-string') | ||
| const {fetch} = require('fetch-ponyfill')() | ||
| const parse = require('./parse') | ||
| const formatDate = (date) => moment(date).tz('Europe/Berlin').format('DD.MM.YY') | ||
| const formatTime = (date) => moment(date).tz('Europe/Berlin').format('HH:mm') | ||
| const defaults = { | ||
| class: 2, // 1st class or 2nd class? | ||
| travellers: [{ // one or more | ||
| bc: 0, // bahncard; 13, 12, 11, 9, 4, 3, 2, 1, 0 | ||
| typ: "E", // E: adult: K: child; B: baby | ||
| alter: 30 | ||
| }], | ||
| noICETrains: false, | ||
| // todo: can this have any value? the DB app just offers 0, 10, 15, 20, 25 | ||
| transferTime: 0, // in minutes | ||
| duration: 1440, // search for routes in the next n minutes | ||
| preferFastRoutes: true | ||
| } | ||
| const endpoint = 'http://ps.bahn.de/preissuche/preissuche/psc_service.go' | ||
| const queryPrices = (start, dest, date, opt) => { | ||
| opt = Object.assign({}, defaults, opt || {}) | ||
| date = date || new Date() | ||
| const query = { | ||
| lang: 'en', | ||
| service: 'pscangebotsuche', | ||
| data: JSON.stringify({ | ||
| s: start, | ||
| d: dest, | ||
| dt: formatDate(date), | ||
| t: formatTime(date), | ||
| c: opt.class, | ||
| ohneICE: opt.noICETrains, | ||
| tct: opt.transferTime, | ||
| dur: opt.duration, | ||
| travellers: opt.travellers, | ||
| sv: opt.preferFastRoutes, | ||
| v: "16040000", // client version (direved from date?) | ||
| dir: "1", // ??? | ||
| bic: false, // ??? | ||
| device: "HANDY", // todo: is this necessary? | ||
| os: "iOS_9.3.1" // todo: is this necessary? | ||
| }) | ||
| } | ||
| return fetch(endpoint + '?' + stringify(query), { | ||
| headers: {accept: 'application/json'}, | ||
| mode: 'cors', | ||
| redirect: 'follow' | ||
| }) | ||
| .then((res) => { | ||
| if (!res.ok) { | ||
| const err = new Error(res.statusText) | ||
| err.statusCode = res.status | ||
| throw err | ||
| } | ||
| return res.json() | ||
| }) | ||
| .then((body) => { | ||
| const notes = parse.notes(body.peTexte) | ||
| const offers = [] | ||
| for (let id in body.angebote) { | ||
| offers.push(parse.offer(body.angebote[id], notes)) | ||
| } | ||
| const journeys = [] | ||
| for (let id in body.verbindungen) { | ||
| journeys.push(parse.journey(body.verbindungen[id], offers)) | ||
| } | ||
| return journeys | ||
| }) | ||
| } | ||
| module.exports = queryPrices |
-100
| 'use strict' | ||
| const moment = require('moment-timezone') | ||
| const slugg = require('slugg') | ||
| const minBy = require('lodash.minby') | ||
| const price = (string) => parseFloat(string.replace(',', '.')) | ||
| const offer = (data, notes) => { | ||
| const offer = { | ||
| // todo: sel, t, c, arq, ff, aix, risids | ||
| name: null, | ||
| description: null, | ||
| discount: data.tt === 'SP', // are there others than SP & NP? | ||
| price: price(data.p), | ||
| routes: data.sids, | ||
| anyTrain: data.zb !== 'Y' | ||
| } | ||
| if (notes[data.pky]) Object.assign(offer, notes[data.pky]) | ||
| return offer | ||
| } | ||
| const when = (data) => { | ||
| // todo: timezone | ||
| return moment.tz(parseInt(data.m), 'Europe/Berlin').toISOString() | ||
| } | ||
| // see https://github.com/public-transport/friendly-public-transport-format/blob/5ba55e2/docs/readme.md#modes | ||
| const modesByProduct = { | ||
| S: 'train', | ||
| IC: 'train', | ||
| ICE: 'train', | ||
| RE: 'train', | ||
| VBG: 'train', // http://www.laenderbahn.com/vogtlandbahn/ | ||
| ALX: 'train' // http://www.laenderbahn.com/alex | ||
| } | ||
| const leg = (data) => ({ | ||
| // todo: rp, re, sp | ||
| origin: { | ||
| type: 'station', | ||
| id: data.s, | ||
| name: data.sn | ||
| // todo: coordinates | ||
| }, | ||
| departure: when(data.dep), | ||
| departurePlatform: data.pd || null, | ||
| destination: { | ||
| type: 'station', | ||
| id: data.d, | ||
| name: data.dn | ||
| // todo: coordinates | ||
| }, | ||
| arrival: when(data.arr), | ||
| arrivalPlatform: data.pa || null, | ||
| line: { | ||
| type: 'line', | ||
| id: slugg(data.tn), | ||
| name: data.tn, | ||
| mode: modesByProduct[data.eg] || null, | ||
| product: data.eg | ||
| } | ||
| }) | ||
| const journey = (_, offers) => { | ||
| const legs = _.trains.map(leg) | ||
| let offer = minBy(offers.filter((o) => o.routes.includes(_.sid)), (o) => o.price) | ||
| const price = offer ? { | ||
| currency: 'EUR', | ||
| amount: offer.price, | ||
| discount: offer.discount, | ||
| name: offer.name, | ||
| description: offer.description, | ||
| anyTrain: offer.anyTrain | ||
| } : null | ||
| return { | ||
| // todo: sel, dir | ||
| type: 'journey', | ||
| id: _.sid, | ||
| origin: legs[0].origin, | ||
| destination: legs[legs.length - 1].destination, | ||
| legs, | ||
| price, | ||
| nightTrain: _.NZVerb // todo: why here? | ||
| } | ||
| } | ||
| const notes = (data) => { | ||
| const notes = {} | ||
| for (let ref in data) { | ||
| notes[ref] = {name: data[ref].name, description: data[ref].hinweis} | ||
| } | ||
| return notes | ||
| } | ||
| module.exports = {notes, offer, journey} |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
10026
3.65%11
120%2
100%Updated
Updated
Updated