mirror of
https://github.com/public-transport/db-vendo-client.git
synced 2025-06-07 21:02:33 +03:00
Compare commits
137 commits
Author | SHA1 | Date | |
---|---|---|---|
|
b59d7b3084 | ||
|
db4c03054a | ||
|
eac21d188b | ||
|
ad09f8b1be | ||
|
c4d0a55d41 | ||
|
29aab87cdf | ||
|
883eb8c8de | ||
|
b20cf1060a | ||
|
b887c674d4 | ||
|
b3e0e764e2 | ||
|
2ea47f7792 | ||
|
6c2081c14e | ||
|
f741a13670 | ||
|
bcaad526c7 | ||
|
162b946bac | ||
|
14b80dbf33 | ||
|
1927f98906 | ||
|
0ef3935a35 | ||
|
b04a671b50 | ||
|
9975a6c9ac | ||
|
960371e2ec | ||
|
88acdd1620 | ||
|
25cbb288ca | ||
|
a6e84be2df | ||
|
de63bf0a37 | ||
|
040a8f44e4 | ||
|
6b67a77823 | ||
|
debb45a929 | ||
|
53b385a865 | ||
|
185870db3d | ||
|
16829f839c | ||
|
9fe4972d2b | ||
|
1aeb246622 | ||
|
6d1d0c626f | ||
|
afa99b0742 | ||
|
229dbac93e | ||
|
7a1e513fa2 | ||
|
f1302b0a7b | ||
|
177a3cab3f | ||
|
71d1a4f1a9 | ||
|
6ff406ea79 | ||
|
2a23e1ad9b | ||
|
9314e59053 | ||
|
69c098744a | ||
|
c671e995cb | ||
|
1e7977a8bb | ||
|
ff559c83dd | ||
|
76d6121f88 | ||
|
206e709e6a | ||
|
7d10f409ef | ||
|
179ada6f08 | ||
|
4c8c503e48 | ||
|
3c7227635a | ||
|
22c839847f | ||
|
1b0858a253 | ||
|
a59a3d78dc | ||
|
911a6d371e | ||
|
2b55f7148f | ||
|
715541f060 | ||
|
70f4cdb2b0 | ||
|
bb692f4bc9 | ||
|
977da80885 | ||
|
01b95e74f4 | ||
|
911ac17510 | ||
|
32792507ba | ||
|
60656b0119 | ||
|
232893f2dc | ||
|
41feb41b5a | ||
|
63bc542b1c | ||
|
a624e62172 | ||
|
942972d5f0 | ||
|
8d07b24604 | ||
|
126077582b | ||
|
615c36650e | ||
|
bc676fd0b6 | ||
|
98670d5e08 | ||
|
0e68a375e1 | ||
|
632a29d2aa | ||
|
8026689ee8 | ||
|
d992961421 | ||
|
31df18f4be | ||
|
94e130f0d2 | ||
|
9e69594a36 | ||
|
db12ea036d | ||
|
de78b40da4 | ||
|
b431fe65b7 | ||
|
661024cedb | ||
|
db168b7a35 | ||
|
d2a39b33c7 | ||
|
87a705e966 | ||
|
3d998de41c | ||
|
6538f814aa | ||
|
debc1ee150 | ||
|
ad6c356552 | ||
|
771ab128b3 | ||
|
9f5e1fa6bd | ||
|
ec723b3414 | ||
|
bc56d41fbe | ||
|
d24a341def | ||
|
59bd56e824 | ||
|
73b4d40e02 | ||
|
e14a909ebf | ||
|
2a49495959 | ||
|
318644958a | ||
|
4a932f0329 | ||
|
80195404bb | ||
|
073d33e12f | ||
|
5c5c1acd44 | ||
|
e05b31e19c | ||
|
0e2d0e32ab | ||
|
e4a99d4be3 | ||
|
ed8683e8c2 | ||
|
df81b5600d | ||
|
0aedad5192 | ||
|
43ba0fe0be | ||
|
73d9c88ffb | ||
|
6e0f3d66b9 | ||
|
e87acc3e24 | ||
|
175b166864 | ||
|
30d84352ae | ||
|
5bb4e66c9a | ||
|
e18ac3f8d3 | ||
|
760a1bdb54 | ||
|
f379fba930 | ||
|
c6bb1b468a | ||
|
491348bd3b | ||
|
c663a35711 | ||
|
f8a79834b3 | ||
|
65aae69481 | ||
|
a2de274a90 | ||
|
f5008fc747 | ||
|
0e328aa681 | ||
|
80e633dcb7 | ||
|
2f45f66793 | ||
|
2e094c2b78 | ||
|
e9211e8105 | ||
|
31c4f17b10 |
560 changed files with 53706 additions and 142563 deletions
2
.dockerignore
Normal file
2
.dockerignore
Normal file
|
@ -0,0 +1,2 @@
|
|||
.git
|
||||
node_modules
|
|
@ -1,54 +0,0 @@
|
|||
{
|
||||
"env": {
|
||||
"es2021": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": ["eslint:recommended", "plugin:@stylistic/all-extends"],
|
||||
"ignorePatterns": ["node_modules", "*example.js"],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2021,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"curly": "error",
|
||||
"no-implicit-coercion": "error",
|
||||
"no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"vars": "all",
|
||||
"args": "none",
|
||||
"ignoreRestSiblings": false
|
||||
}
|
||||
],
|
||||
"@stylistic/array-bracket-newline": ["error", "consistent"],
|
||||
"@stylistic/array-element-newline": ["error", "consistent"],
|
||||
"@stylistic/arrow-parens": "off",
|
||||
"@stylistic/comma-dangle": ["error", "always-multiline"],
|
||||
"@stylistic/dot-location": ["error", "property"],
|
||||
"@stylistic/function-call-argument-newline": ["error", "consistent"],
|
||||
"@stylistic/function-paren-newline": "off",
|
||||
"@stylistic/indent": ["error", "tab"],
|
||||
"@stylistic/indent-binary-ops": ["error", "tab"],
|
||||
"@stylistic/max-len": "off",
|
||||
"@stylistic/multiline-ternary": ["error", "always-multiline"],
|
||||
"@stylistic/newline-per-chained-call": ["error", { "ignoreChainWithDepth": 1 }],
|
||||
"@stylistic/no-mixed-operators": "off",
|
||||
"@stylistic/no-tabs": "off",
|
||||
"@stylistic/object-property-newline": "off",
|
||||
"@stylistic/one-var-declaration-per-line": "off",
|
||||
"@stylistic/operator-linebreak": ["error", "before"],
|
||||
"@stylistic/padded-blocks": "off",
|
||||
"@stylistic/quote-props": ["error", "consistent-as-needed"],
|
||||
"@stylistic/quotes": ["error", "single"]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"test/**"
|
||||
],
|
||||
"rules": {
|
||||
"no-unused-vars": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
59
.github/workflows/build.yml
vendored
Normal file
59
.github/workflows/build.yml
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
name: Build NPM Package & Docker
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build-and-push-docker:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
- name: Build and push Docker image
|
||||
id: push
|
||||
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
build-pkg:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
- run: npm ci
|
||||
- run: npm publish --provenance --access public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
37
.github/workflows/test.yml
vendored
37
.github/workflows/test.yml
vendored
|
@ -1,17 +1,34 @@
|
|||
name: test
|
||||
|
||||
on: [push, pull_request]
|
||||
on: [push, pull_request, workflow_call]
|
||||
|
||||
env:
|
||||
npm_config_cache: /tmp/npm-cache
|
||||
|
||||
jobs:
|
||||
lint-and-spellcheck:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@v4
|
||||
- name: "Use Node.js"
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
- run: npm install
|
||||
|
||||
- name: Run lint check
|
||||
run: npm run lint
|
||||
|
||||
- name: Run spell check
|
||||
run: npm run test-spelling
|
||||
|
||||
unit-tests:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version:
|
||||
- 16.x
|
||||
- 18.x
|
||||
- 20.x
|
||||
- 22.x
|
||||
|
@ -25,13 +42,12 @@ jobs:
|
|||
|
||||
- id: cache-npm
|
||||
name: restore npm cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: npm-cache-${{ github.ref_name }}
|
||||
key: npm-cache-${{ github.ref_name }}-${{ matrix.node-version }}-unit-tests
|
||||
path: ${{ env.npm_config_cache }}
|
||||
- run: npm install
|
||||
|
||||
- run: npm run lint
|
||||
- run: npm run test-unit
|
||||
|
||||
integration-tests:
|
||||
|
@ -39,7 +55,6 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
node-version:
|
||||
- 16.x
|
||||
- 18.x
|
||||
- 20.x
|
||||
- 22.x
|
||||
|
@ -53,9 +68,9 @@ jobs:
|
|||
|
||||
- id: cache-npm
|
||||
name: restore npm cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: npm-cache-${{ github.ref_name }}
|
||||
key: npm-cache-${{ github.ref_name }}-${{ matrix.node-version }}-integration-tests
|
||||
path: ${{ env.npm_config_cache }}
|
||||
- run: npm install
|
||||
|
||||
|
@ -66,7 +81,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
node-version: [18.x]
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
@ -77,9 +92,9 @@ jobs:
|
|||
|
||||
- id: cache-npm
|
||||
name: restore npm cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: npm-cache-${{ github.ref_name }}
|
||||
key: npm-cache-${{ github.ref_name }}-${{ matrix.node-version }}-e2e-tests
|
||||
path: ${{ env.npm_config_cache }}
|
||||
- run: npm install
|
||||
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -5,6 +5,5 @@ Thumbs.db
|
|||
node_modules
|
||||
npm-debug.log
|
||||
|
||||
package-lock.json
|
||||
|
||||
/.tap
|
||||
*.ign.*
|
4
.gitmodules
vendored
4
.gitmodules
vendored
|
@ -1,4 +0,0 @@
|
|||
[submodule "tools/transport-apis"]
|
||||
path = tools/transport-apis
|
||||
url = https://github.com/public-transport/transport-apis.git
|
||||
branch = v1
|
22
Dockerfile
Normal file
22
Dockerfile
Normal file
|
@ -0,0 +1,22 @@
|
|||
FROM node:18-alpine
|
||||
LABEL org.opencontainers.image.title="db-vendo-client"
|
||||
LABEL org.opencontainers.image.description="A clean REST API wrapping around the new Deutsche Bahn API."
|
||||
LABEL org.opencontainers.image.authors="Traines <git@traines.eu>"
|
||||
LABEL org.opencontainers.image.documentation="https://github.com/public-transport/db-vendo-client"
|
||||
LABEL org.opencontainers.image.source="https://github.com/public-transport/db-vendo-client"
|
||||
LABEL org.opencontainers.image.licenses="ISC"
|
||||
WORKDIR /app
|
||||
|
||||
# install dependencies
|
||||
#RUN apk add --update git
|
||||
ADD package.json package-lock.json /app/
|
||||
RUN npm install && npm cache clean --force
|
||||
|
||||
# add source code
|
||||
ADD . /app
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
ENV PORT 3000
|
||||
|
||||
CMD ["node", "api.js"]
|
|
@ -1,4 +1,7 @@
|
|||
Copyright (c) 2024, Jannis R
|
||||
# ISC License
|
||||
|
||||
- Copyright © 2024 Jannis R
|
||||
- Copyright © 2025 traines-source
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
||||
|
52
api.js
Normal file
52
api.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
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 {profile as dbbahnhofProfile} from './p/dbbahnhof/index.js';
|
||||
import {profile as dbregioguideProfile} from './p/dbregioguide/index.js';
|
||||
import {mapRouteParsers} from './lib/api-parsers.js';
|
||||
import {createHafasRestApi as createApi} from 'hafas-rest-api';
|
||||
|
||||
const config = {
|
||||
hostname: process.env.HOSTNAME || 'localhost',
|
||||
port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
|
||||
name: 'db-vendo-client',
|
||||
description: 'db-vendo-client',
|
||||
homepage: 'https://github.com/public-transport/db-vendo-client',
|
||||
version: '6',
|
||||
docsLink: 'https://github.com/public-transport/db-vendo-client',
|
||||
openapiSpec: true,
|
||||
logging: true,
|
||||
aboutPage: true,
|
||||
enrichStations: true,
|
||||
etags: 'strong',
|
||||
csp: 'default-src \'none\'; style-src \'self\' \'unsafe-inline\'; img-src https:',
|
||||
mapRouteParsers,
|
||||
};
|
||||
|
||||
const profiles = {
|
||||
db: dbProfile,
|
||||
dbnav: dbnavProfile,
|
||||
dbweb: dbwebProfile,
|
||||
dbris: dbrisProfile,
|
||||
dbbahnhof: dbbahnhofProfile,
|
||||
dbregioguide: dbregioguideProfile,
|
||||
};
|
||||
|
||||
const start = async () => {
|
||||
const vendo = createClient(
|
||||
profiles[process.env.DB_PROFILE] || dbnavProfile,
|
||||
process.env.USER_AGENT || 'link-to-your-project-or-email',
|
||||
config,
|
||||
);
|
||||
const api = await createApi(vendo, config);
|
||||
|
||||
api.listen(config.port, (err) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
start();
|
644
cspell.config.json
Normal file
644
cspell.config.json
Normal file
|
@ -0,0 +1,644 @@
|
|||
{
|
||||
"version": "0.2",
|
||||
"language": "en",
|
||||
"words": [
|
||||
"Abfahrt",
|
||||
"abfahrten",
|
||||
"abfahrts",
|
||||
"abfrage",
|
||||
"abgangs",
|
||||
"Abgelaufen",
|
||||
"Abonnement",
|
||||
"Abschnitte",
|
||||
"abschnitts",
|
||||
"Adelsheim",
|
||||
"Adenauerplatz",
|
||||
"agilis",
|
||||
"Ahorn",
|
||||
"Ahrensfelde",
|
||||
"Aktualisierung",
|
||||
"Alexanderplatz",
|
||||
"alterseingabe",
|
||||
"Altstadt",
|
||||
"Alzey",
|
||||
"andere",
|
||||
"Anfang",
|
||||
"anfrage",
|
||||
"Anfrage",
|
||||
"anfragezeit",
|
||||
"Angebot",
|
||||
"angebote",
|
||||
"angebots",
|
||||
"angebotsbeziehung",
|
||||
"angebotseinholung",
|
||||
"ANGEBOTSINFORMATION",
|
||||
"Angebotsoption",
|
||||
"angefragten",
|
||||
"Angeltürn",
|
||||
"ankuenfte",
|
||||
"ankunft",
|
||||
"ankunfts",
|
||||
"Anmeldung",
|
||||
"ANRUFPFLICHTIG",
|
||||
"ANRUFPFLICHTIGEVERKEHRE",
|
||||
"Anteil",
|
||||
"anzahl",
|
||||
"anzeige",
|
||||
"Anzeigen",
|
||||
"APPLEPAY",
|
||||
"ARGUMENTE",
|
||||
"arrs",
|
||||
"Arverio",
|
||||
"Aschaffenburg",
|
||||
"attribut",
|
||||
"Atze",
|
||||
"Auerbach",
|
||||
"AUSFALL",
|
||||
"ausgegeben",
|
||||
"auslastung",
|
||||
"auslastungs",
|
||||
"Auslastungsinformation",
|
||||
"auslastungsmeldungen",
|
||||
"auslastungstexte",
|
||||
"Ausreserviert",
|
||||
"außergewöhnlich",
|
||||
"Ausserhalb",
|
||||
"außerhalb",
|
||||
"Ausstattung",
|
||||
"Auswahl",
|
||||
"autonome",
|
||||
"bahnbonus",
|
||||
"BAHNCARD",
|
||||
"Bahnhof",
|
||||
"bahnhofs",
|
||||
"Bahnhofsinfo",
|
||||
"bahnhofstafel",
|
||||
"bahnhofstafeln",
|
||||
"BARRIEREFREI",
|
||||
"Bayerischer",
|
||||
"BEFÖRDERER",
|
||||
"begrenzt",
|
||||
"behaviour",
|
||||
"Behindertengerechte",
|
||||
"Behindertengerechtes",
|
||||
"Benoit",
|
||||
"Beratzhausen",
|
||||
"bereits",
|
||||
"besonderer",
|
||||
"Besucherpark",
|
||||
"betrag",
|
||||
"Beusselstraße",
|
||||
"bezeichnung",
|
||||
"Bietigheim",
|
||||
"Bismarckstr",
|
||||
"Bissingen",
|
||||
"bitmasks",
|
||||
"Blaschkoallee",
|
||||
"Blissestr",
|
||||
"BNWNZF",
|
||||
"Böhme",
|
||||
"BONVOYO",
|
||||
"Bordbistro",
|
||||
"Bordrestaurant",
|
||||
"Boxberg",
|
||||
"Breckerfeld",
|
||||
"Britz",
|
||||
"brokentrip",
|
||||
"brutto",
|
||||
"Buch",
|
||||
"Buchbar",
|
||||
"buchbarkeit",
|
||||
"BUCHEN",
|
||||
"BUCHUNG",
|
||||
"buchungs",
|
||||
"Bundesbahnen",
|
||||
"Bundesplatz",
|
||||
"BUSSE",
|
||||
"bzgl",
|
||||
"capacitorjs",
|
||||
"Chaussee",
|
||||
"checkin",
|
||||
"childrens",
|
||||
"CITYTICKET",
|
||||
"clie",
|
||||
"cncl",
|
||||
"codeshares",
|
||||
"conds",
|
||||
"consumability",
|
||||
"Consumability",
|
||||
"Creglingen",
|
||||
"crosssell",
|
||||
"Ctrf",
|
||||
"customisation",
|
||||
"customisations",
|
||||
"Damm",
|
||||
"Daten",
|
||||
"Dauer",
|
||||
"dauerhaft",
|
||||
"dbnav",
|
||||
"dbregioguide",
|
||||
"dbris",
|
||||
"dbweb",
|
||||
"Deldicque",
|
||||
"DELFI",
|
||||
"derhuerst",
|
||||
"Deutz",
|
||||
"dhid",
|
||||
"differenzpreis",
|
||||
"Dinkelsbühl",
|
||||
"distanz",
|
||||
"Dombühl",
|
||||
"echtzeit",
|
||||
"Eggmühl",
|
||||
"ehemals",
|
||||
"Eicholzheim",
|
||||
"einchecken",
|
||||
"eine",
|
||||
"einfache",
|
||||
"Einrichtung",
|
||||
"Einstieg",
|
||||
"einstiegs",
|
||||
"Einstiegshilfe",
|
||||
"Einstiegstyp",
|
||||
"Eisenacher",
|
||||
"emis",
|
||||
"empfehlen",
|
||||
"Ende",
|
||||
"entfällt",
|
||||
"Entgelt",
|
||||
"ereignis",
|
||||
"Erforderlich",
|
||||
"erforderlicher",
|
||||
"Ergoldsbach",
|
||||
"Erlenbach",
|
||||
"ERMAESSIGUNG",
|
||||
"ermaessigungen",
|
||||
"erster",
|
||||
"ERWACHSENER",
|
||||
"erwarten",
|
||||
"erwartet",
|
||||
"Eubigheim",
|
||||
"Eurocity",
|
||||
"eventuell",
|
||||
"externe",
|
||||
"Fahrkarte",
|
||||
"fahrplan",
|
||||
"Fahrplanperiode",
|
||||
"Fahrpreis",
|
||||
"Fahrradbeförderung",
|
||||
"FAHRRADMITNAHME",
|
||||
"Fahrt",
|
||||
"FAHRZEUG",
|
||||
"Fahrzeuggebundene",
|
||||
"FAMILIENKIND",
|
||||
"Fehler",
|
||||
"Fehrbelliner",
|
||||
"Fernbf",
|
||||
"Fernverkehr",
|
||||
"Flexpreis",
|
||||
"Flix",
|
||||
"Fltr",
|
||||
"Flughafen",
|
||||
"FPTF",
|
||||
"Freising",
|
||||
"Friedrichshall",
|
||||
"Friedrichstr",
|
||||
"frueher",
|
||||
"Frwd",
|
||||
"FUSSWEG",
|
||||
"Fußweg",
|
||||
"FVFFLPI",
|
||||
"FVFSPPI",
|
||||
"FVFSSPI",
|
||||
"FVKBACI",
|
||||
"GARE",
|
||||
"Garten",
|
||||
"Gastronomie",
|
||||
"Gattung",
|
||||
"gekauft",
|
||||
"Geltungzeitpunkt",
|
||||
"GENERALABONNEMENT",
|
||||
"geolocation",
|
||||
"geopositions",
|
||||
"Geringe",
|
||||
"gesamt",
|
||||
"Gesundbrunnen",
|
||||
"gleis",
|
||||
"Gneisenaustr",
|
||||
"Goerdelersteg",
|
||||
"grafisch",
|
||||
"Greifswalder",
|
||||
"Grenzallee",
|
||||
"Grünanlagen",
|
||||
"gruppen",
|
||||
"Gütersloh",
|
||||
"GUTSCHEIN",
|
||||
"haben",
|
||||
"hafas",
|
||||
"Hagelstadt",
|
||||
"Halbtax",
|
||||
"HALBTAXABO",
|
||||
"Halemweg",
|
||||
"Halensee",
|
||||
"Hallerstraße",
|
||||
"halte",
|
||||
"Haltestellen",
|
||||
"Hansering",
|
||||
"Hansestadt",
|
||||
"Haselhorst",
|
||||
"Hauptbahnhof",
|
||||
"Heidelberger",
|
||||
"Hennef",
|
||||
"Hermannplatz",
|
||||
"Hermannstraße",
|
||||
"hessen",
|
||||
"Heuchelhof",
|
||||
"HINFAHRT",
|
||||
"Hinweis",
|
||||
"Hinweise",
|
||||
"HOCH",
|
||||
"HOCHGESCHWINDIGKEITSZUEGE",
|
||||
"Hohe",
|
||||
"Hohenstadt",
|
||||
"Hohenzollerndamm",
|
||||
"Hüngheim",
|
||||
"IBNR",
|
||||
"Ihren",
|
||||
"Ihrer",
|
||||
"Informationen",
|
||||
"INKLUSIVE",
|
||||
"Innsbrucker",
|
||||
"Instanz",
|
||||
"INTERCITYUNDEUROCITYZUEGE",
|
||||
"INTERREGIOUNDSCHNELLZUEGE",
|
||||
"inventarsystem",
|
||||
"irregulaere",
|
||||
"Jakob",
|
||||
"Jannis",
|
||||
"Jannowitzbrücke",
|
||||
"jetzt",
|
||||
"Johannisthaler",
|
||||
"journeystop",
|
||||
"JUGENDLICHER",
|
||||
"Jungfernheide",
|
||||
"Kanal",
|
||||
"KATALOG",
|
||||
"kategorie",
|
||||
"Kategorien",
|
||||
"kategorisierung",
|
||||
"kein",
|
||||
"KEINE",
|
||||
"Kennung",
|
||||
"Kennzeichen",
|
||||
"Kirche",
|
||||
"klasse",
|
||||
"KLASSENLOS",
|
||||
"KLEINKIND",
|
||||
"Kleistpark",
|
||||
"Klima",
|
||||
"KLIMATICKET",
|
||||
"Köfering",
|
||||
"Köln",
|
||||
"kombinations",
|
||||
"komfort",
|
||||
"konditionen",
|
||||
"konditions",
|
||||
"Konstanzer",
|
||||
"kontext",
|
||||
"Kontingente",
|
||||
"KRCC",
|
||||
"KREDITKARTE",
|
||||
"Kristjan",
|
||||
"Kurz",
|
||||
"kurztext",
|
||||
"Landsberger",
|
||||
"Landshut",
|
||||
"langtext",
|
||||
"LASTSCHRIFT",
|
||||
"Lauda",
|
||||
"letzte",
|
||||
"letztes",
|
||||
"leuchtturm",
|
||||
"Lichtenberg",
|
||||
"liegt",
|
||||
"linien",
|
||||
"Lipschitzallee",
|
||||
"Loesungs",
|
||||
"Ludwigsburg",
|
||||
"luxon",
|
||||
"materialisierungs",
|
||||
"Maxnet",
|
||||
"MBAAA",
|
||||
"Mehringdamm",
|
||||
"Meidling",
|
||||
"Meldungen",
|
||||
"Merchingen",
|
||||
"Messe",
|
||||
"Mierendorffplatz",
|
||||
"Millis",
|
||||
"Minden",
|
||||
"mitteltext",
|
||||
"mittlere",
|
||||
"Mobilitätseingeschränkte",
|
||||
"Mobilitätsservice",
|
||||
"Möckernbrücke",
|
||||
"Möckmühl",
|
||||
"modul",
|
||||
"moeckmuehl",
|
||||
"Moeglich",
|
||||
"möglich",
|
||||
"Montabaur",
|
||||
"Moosburg",
|
||||
"Mosbach",
|
||||
"Movas",
|
||||
"München",
|
||||
"Musiktheater",
|
||||
"mwst",
|
||||
"mxtxm",
|
||||
"Nachgelagert",
|
||||
"Nachricht",
|
||||
"Nahreisezug",
|
||||
"NAHVERKEHRSONSTIGEZUEGE",
|
||||
"Naturkundemuseum",
|
||||
"Neckarsulm",
|
||||
"netto",
|
||||
"Neufahrn",
|
||||
"Neukölln",
|
||||
"Neumarkt",
|
||||
"NICHT",
|
||||
"Niederbay",
|
||||
"NIEDRIG",
|
||||
"noch",
|
||||
"noopener",
|
||||
"Nord",
|
||||
"noreferrer",
|
||||
"Notiz",
|
||||
"Notizen",
|
||||
"NULLPREIS",
|
||||
"nummer",
|
||||
"nutzungs",
|
||||
"Obereubigheim",
|
||||
"Oberpf",
|
||||
"Oberschefflenz",
|
||||
"Obertraubling",
|
||||
"Oberwittstadt",
|
||||
"Ohne",
|
||||
"orte",
|
||||
"Ostbahnhof",
|
||||
"Osterburken",
|
||||
"Österreichische",
|
||||
"Ostkreuz",
|
||||
"Parchimer",
|
||||
"Parsberg",
|
||||
"passend",
|
||||
"Passlist",
|
||||
"Paulsternstr",
|
||||
"Pauschalpreis",
|
||||
"Pergamonkeller",
|
||||
"Pergamonweg",
|
||||
"Perleberg",
|
||||
"PFLICHT",
|
||||
"PLAETZE",
|
||||
"planungs",
|
||||
"platf",
|
||||
"Platz",
|
||||
"platzbedarfe",
|
||||
"platzprofil",
|
||||
"pollyjs",
|
||||
"polyline",
|
||||
"polylines",
|
||||
"Positionen",
|
||||
"Preis",
|
||||
"preise",
|
||||
"preisunterdrueckung",
|
||||
"Prenzlauer",
|
||||
"prio",
|
||||
"priorisierte",
|
||||
"prioritaet",
|
||||
"produkt",
|
||||
"produkte",
|
||||
"produktgattungen",
|
||||
"Profil",
|
||||
"PRUEFEN",
|
||||
"PUBLICTRANSPORT",
|
||||
"Punkte",
|
||||
"rabatt",
|
||||
"Radzio",
|
||||
"RAILPLUS",
|
||||
"randomised",
|
||||
"Ravenstein",
|
||||
"Referenz",
|
||||
"referenzen",
|
||||
"referenziertes",
|
||||
"Regio",
|
||||
"Regionalbf",
|
||||
"regulaere",
|
||||
"regulaerer",
|
||||
"reise",
|
||||
"REISEANGEBOT",
|
||||
"reiseloesung",
|
||||
"reisende",
|
||||
"reisenden",
|
||||
"REISESTELLENKARTE",
|
||||
"reisetag",
|
||||
"Reisezentrum",
|
||||
"REMENTR",
|
||||
"REMRESR",
|
||||
"Reservierbar",
|
||||
"Reservieren",
|
||||
"Reservierung",
|
||||
"reservierungen",
|
||||
"reservierungs",
|
||||
"RESERVIERUNGSANGEBOT",
|
||||
"RESERVIERUNGSENTGELT",
|
||||
"reservierungspflicht",
|
||||
"Rhein",
|
||||
"richtung",
|
||||
"roehrt",
|
||||
"Rohrdamm",
|
||||
"Roigheim",
|
||||
"Rollstuhl",
|
||||
"rollstuhlgerechte",
|
||||
"Römisches",
|
||||
"Rosenthal",
|
||||
"Rothenburg",
|
||||
"Rudow",
|
||||
"Rueck",
|
||||
"Rüsselsheim",
|
||||
"Saale",
|
||||
"Sammelfaehig",
|
||||
"satz",
|
||||
"SBAHN",
|
||||
"SBAHNEN",
|
||||
"Scharnweberstr",
|
||||
"SCHIFF",
|
||||
"SCHIFFE",
|
||||
"Schlachthof",
|
||||
"schnelle",
|
||||
"Schöneberg",
|
||||
"Schönefeld",
|
||||
"Schönhauser",
|
||||
"Schwedter",
|
||||
"Seckach",
|
||||
"Sennfeld",
|
||||
"servicekategorie",
|
||||
"SHCARD",
|
||||
"sich",
|
||||
"SICT",
|
||||
"Sieg",
|
||||
"Siegburg",
|
||||
"Siemensdamm",
|
||||
"Siglingen",
|
||||
"sitzplatz",
|
||||
"Sitzplatzreservierung",
|
||||
"slugg",
|
||||
"Sonnenallee",
|
||||
"Sören",
|
||||
"spaeter",
|
||||
"Sparpreis",
|
||||
"spezifische",
|
||||
"Spichernstr",
|
||||
"Spittelmarkt",
|
||||
"Stadtgebiet",
|
||||
"Stadtmitte",
|
||||
"Stadtpark",
|
||||
"Steinach",
|
||||
"Storkower",
|
||||
"STRASSENBAHN",
|
||||
"stufe",
|
||||
"stufenfrei",
|
||||
"suchbegriff",
|
||||
"Suche",
|
||||
"Südkreuz",
|
||||
"Südstern",
|
||||
"Sutter",
|
||||
"tage",
|
||||
"täglich",
|
||||
"tapjs",
|
||||
"tarif",
|
||||
"Tarifbereiche",
|
||||
"Tarifgebiet",
|
||||
"Tarifzone",
|
||||
"Tauber",
|
||||
"teilpreis",
|
||||
"teilstrecken",
|
||||
"Tempelhof",
|
||||
"Texte",
|
||||
"Tiergarten",
|
||||
"Toel",
|
||||
"Torfstr",
|
||||
"Torfstraße",
|
||||
"Traines",
|
||||
"traveller",
|
||||
"Travellers",
|
||||
"travelling",
|
||||
"Treptower",
|
||||
"Tsua",
|
||||
"tvlr",
|
||||
"UBAHN",
|
||||
"ueber",
|
||||
"uebergreifende",
|
||||
"ueberschrift",
|
||||
"Uiffingen",
|
||||
"Umbuchungen",
|
||||
"Umstiege",
|
||||
"umstiegs",
|
||||
"Umstiegsdauer",
|
||||
"Umstiegszeit",
|
||||
"unsere",
|
||||
"unter",
|
||||
"Unterwittstadt",
|
||||
"upsell",
|
||||
"ursprungs",
|
||||
"ushrp",
|
||||
"uuidv",
|
||||
"Vaihingen",
|
||||
"vendo",
|
||||
"verbindung",
|
||||
"verbindungen",
|
||||
"verbindungs",
|
||||
"verbindungsabschnitt",
|
||||
"verbindungssuche",
|
||||
"Verfuegbar",
|
||||
"verfügbar",
|
||||
"verkehrmittel",
|
||||
"verkehrsmittel",
|
||||
"Verkehrstage",
|
||||
"VERKNUEPFT",
|
||||
"Verlauf",
|
||||
"vertraglicher",
|
||||
"vlexx",
|
||||
"Voordeelurenabo",
|
||||
"Vorhanden",
|
||||
"VORTEILE",
|
||||
"VORTEILSCARD",
|
||||
"Vorverkaufszeitraum",
|
||||
"waehrung",
|
||||
"wagenreihung",
|
||||
"wählen",
|
||||
"Wannsee",
|
||||
"Wegener",
|
||||
"Weichselstr",
|
||||
"wenden",
|
||||
"wenn",
|
||||
"Westbahn",
|
||||
"Westend",
|
||||
"Westf",
|
||||
"westhafen",
|
||||
"Westkreuz",
|
||||
"wichtig",
|
||||
"Wilmersdorfer",
|
||||
"wird",
|
||||
"Witzleben",
|
||||
"wochentage",
|
||||
"Worringen",
|
||||
"wunsch",
|
||||
"Wunschplatz",
|
||||
"Württemberg",
|
||||
"württembergallee",
|
||||
"Würzburg",
|
||||
"Wutzkyallee",
|
||||
"Yorckstr",
|
||||
"Yureka",
|
||||
"Zahlungsarten",
|
||||
"Zeitpunkt",
|
||||
"Zeitraum",
|
||||
"zgck",
|
||||
"ziel",
|
||||
"Zimmern",
|
||||
"Zitadelle",
|
||||
"Zoologischer",
|
||||
"Zuege",
|
||||
"zugart",
|
||||
"zugattrib",
|
||||
"zugattribute",
|
||||
"Zuges",
|
||||
"zugfahrt",
|
||||
"zuglaeufe",
|
||||
"Zuglauf",
|
||||
"zugnummer",
|
||||
"zulaessige",
|
||||
"Zusammenfassung",
|
||||
"Züttlingen",
|
||||
"Zwickauer",
|
||||
"zwischenhalte",
|
||||
"bestprice",
|
||||
"intervalle",
|
||||
"Intervalle",
|
||||
"tagesbest",
|
||||
"dbbahnhof",
|
||||
"Deutschlandticket",
|
||||
"fahrverguenstigungen",
|
||||
"cancelation"
|
||||
],
|
||||
"ignorePaths": [
|
||||
"docs/dumps/**",
|
||||
"test/e2e/fixtures/**",
|
||||
"test/fixtures/**",
|
||||
"test/parse/remarks.js",
|
||||
"test/parse/dbnav-journey.js"
|
||||
],
|
||||
"dictionaries": [
|
||||
"node"
|
||||
]
|
||||
}
|
18
docs/api.md
18
docs/api.md
|
@ -1,17 +1,19 @@
|
|||
# `hafas-client` API
|
||||
# `db-vendo-client` API
|
||||
|
||||
Also see the [root readme](https://github.com/public-transport/db-vendo-client) for a shortlist of differences of db-vendo-client to hafas-client and of differences between the profiles.
|
||||
|
||||
- [`journeys(from, to, [opt])`](journeys.md) – get journeys between locations
|
||||
- [`refreshJourney(refreshToken, [opt])`](refresh-journey.md) – fetch up-to-date/more details of a `journey`
|
||||
- [`journeysFromTrip(tripId, previousStopover, to, [opt])`](journeys-from-trip.md) – get journeys from a trip to a location
|
||||
- `journeysFromTrip(tripId, previousStopover, to, [opt])` – not supported
|
||||
- [`trip(id, lineName, [opt])`](trip.md) – get details for a trip
|
||||
- [`tripsByName(lineNameOrFahrtNr, [opt])`](trips-by-name.md) – get all trips matching a name
|
||||
- `tripsByName(lineNameOrFahrtNr, [opt])` – not supported
|
||||
- [`departures(station, [opt])`](departures.md) – query the next departures at a station
|
||||
- [`arrivals(station, [opt])`](arrivals.md) – query the next arrivals at a station
|
||||
- [`locations(query, [opt])`](locations.md) – find stations, POIs and addresses
|
||||
- [`stop(id, [opt])`](stop.md) – get details about a stop/station
|
||||
- [`nearby(location, [opt])`](nearby.md) – show stations & POIs around
|
||||
- [`radar(north, west, south, east, [opt])`](radar.md) – find all vehicles currently in a certain area
|
||||
- [`reachableFrom(address, [opt])`](reachable-from.md) – get all stations reachable from an address within `n` minutes
|
||||
- [`remarks([opt])`](remarks.md) – get all remarks
|
||||
- [`lines(query, [opt])`](lines.md) – get all lines matching a name
|
||||
- [`serverInfo([opt])`](server-info.md) – fetch meta information from HAFAS
|
||||
- `radar(north, west, south, east, [opt])` – not supported
|
||||
- `reachableFrom(address, [opt])` – not supported
|
||||
- `remarks([opt])` – not supported
|
||||
- `lines(query, [opt])` – not supported
|
||||
- `serverInfo([opt])` – not supported
|
||||
|
|
1109
docs/changelog.md
1109
docs/changelog.md
File diff suppressed because it is too large
Load diff
90
docs/db-apis.md
Normal file
90
docs/db-apis.md
Normal file
|
@ -0,0 +1,90 @@
|
|||
# New DB Board and Route Planning APIs (beyond HAFAS and IRIS)
|
||||
|
||||
(Beware that a DB journey is what you usually call a trip (a vehicle travelling at a certain time) and a DB trip is what you usually call a journey (result of a route search from A to B).)
|
||||
|
||||
## RIS::Boards
|
||||
https://apis.deutschebahn.com/db/apis/ris-boards/v1/public/
|
||||
|
||||
EPs:
|
||||
* departures/<evaNo>
|
||||
* arrivals/<evaNo>
|
||||
|
||||
Notes:
|
||||
* docs (also helpful for other RIS-based APIs below): https://developers.deutschebahn.com/db-api-marketplace/apis/product/ris-boards-transporteure/api/ris-boards-transporteure#/RISBoards_151/overview
|
||||
* needs an API Key
|
||||
* provides remarks
|
||||
* does not provide loadFactor
|
||||
* no route planning
|
||||
* uses RIS trip IDs
|
||||
* boards up to 12 hours
|
||||
|
||||
## bahnhof.de RIS
|
||||
https://www.bahnhof.de/api/boards/departures?evaNumbers=8000105&filterTransports=BUS&duration=60&locale=de
|
||||
|
||||
Notes:
|
||||
* no API Key needed
|
||||
* provides remarks
|
||||
* uses RIS trip IDs
|
||||
* no route planning
|
||||
* boards up to 6 hours, only from current time (or unknown parameter)
|
||||
|
||||
## Regio Guide RIS
|
||||
https://regio-guide.de/@prd/zupo-travel-information/api/public/ri/
|
||||
|
||||
EPs:
|
||||
* departure/8000105?modeOfTransport=HIGH_SPEED_TRAIN,REGIONAL_TRAIN,CITY_TRAIN,INTER_REGIONAL_TRAIN,UNKNOWN,BUS,TRAM,SUBWAY&timeStart=2024-12-11T15:08:25.678Z&timeEnd=2024-12-12T01:53:25.678&expandTimeFrame=TIME_END&&occupancy=true
|
||||
* board/arrival/<evaNo>
|
||||
* routing-search (with POST body, see regio-guide.de)
|
||||
* trip/<tripId-from-routing-search>
|
||||
* journey/<journeyId-from-trip>
|
||||
|
||||
Notes:
|
||||
* no API Key needed
|
||||
* no remarks in boards (or with unknown param), only some in journey
|
||||
* cancelled trips are completely missing from boards (?)
|
||||
* uses RIS trip IDs, does not expose them directly in the routing-search response
|
||||
* loadFactor for some regional services, not for long distance services
|
||||
* boards up to 12 hours
|
||||
* routing-search returns polylines (!)
|
||||
|
||||
## Vendo/Movas Navigator API
|
||||
https://app.vendo.noncd.db.de/mob/
|
||||
|
||||
EPs:
|
||||
* bahnhofstafel/abfahrt
|
||||
* bahnhofstafel/ankunft
|
||||
* location/search
|
||||
* angebote/fahrplan (for route planning)
|
||||
* zuglauf
|
||||
* zuglaeufe/ICE_947/halte/by-abfahrt/8000207_2024 (coach sequence)
|
||||
* angebote/recon (tickets)
|
||||
* trip/recon (polylines)
|
||||
|
||||
Notes:
|
||||
* see [traffic dumps](dumps/)
|
||||
* no API Key needed
|
||||
* used by new DB Navigator
|
||||
* HAFAS trip IDs
|
||||
* boards only 1 hour (or unknown param)
|
||||
* does not contain machine-readable cancelled info in the boards (only "Halt entfällt" string), but contains relevant remarks
|
||||
* loadFactor only on journeys (?)
|
||||
* polylines only for zuglauf and trip/recon
|
||||
* limited remarks on boards
|
||||
|
||||
## Vendo/Movas bahn.de API
|
||||
https://int.bahn.de/web/api/
|
||||
|
||||
EPs:
|
||||
* angebote/fahrplan (for route planning)
|
||||
* reiseloesung/orte
|
||||
* reiseloesung/orte/nearby
|
||||
* reiseloesung/verbindung
|
||||
* reiseloesung/fahrt
|
||||
* reiseloesung/abfahrten?datum=2024-12-30&zeit=11:55:00&ortExtId=8011160&ortId=A%3D1%40O%3DBerlin+Hbf%40X%3D13369549%40Y%3D52525589%40U%3D80%40L%3D8011160%40i%3DU%C3%97008065969%40&mitVias=true&maxVias=8&verkehrsmittel[]=ICE&verkehrsmittel[]=EC_IC&verkehrsmittel[]=IR&verkehrsmittel[]=REGIONAL
|
||||
* reiseloesung/ankuenfte
|
||||
|
||||
Notes:
|
||||
* no API Key needed
|
||||
* uses HAFAS trip IDs
|
||||
* provides loadFactor
|
||||
* polylines only for /verbindung and /fahrt
|
|
@ -24,58 +24,48 @@ With `opt`, you can override the default options, which look like this:
|
|||
```js
|
||||
{
|
||||
when: new Date(),
|
||||
direction: null, // only show departures heading to this station
|
||||
line: null, // filter by line ID
|
||||
direction: null, // only supported in `dbweb` and with `enrichStations=true` (experimental)
|
||||
line: null, // not supported
|
||||
duration: 10, // show departures for the next n minutes
|
||||
results: null, // max. number of results; `null` means "whatever HAFAS wants"
|
||||
subStops: true, // parse & expose sub-stops of stations?
|
||||
entrances: true, // parse & expose entrances of stops/stations?
|
||||
linesOfStops: false, // parse & expose lines at the stop/station?
|
||||
subStops: true, // not supported
|
||||
entrances: true, // not supported
|
||||
linesOfStops: false, // not supported
|
||||
remarks: true, // parse & expose hints & warnings?
|
||||
stopovers: false, // fetch & parse previous/next stopovers?
|
||||
stopovers: false, // fetch & parse previous/next stopovers?, only supported with `dbweb` profile
|
||||
// departures at related stations
|
||||
// e.g. those that belong together on the metro map.
|
||||
includeRelatedStations: true,
|
||||
moreStops: null // also include departures/arrivals for array of up to nine additional station evaNumbers (not supported with dbnav and dbweb)
|
||||
language: 'en' // language to get results in
|
||||
}
|
||||
```
|
||||
|
||||
If you pass an object `opt.products`, its fields will partially override the default products defined in the profile. An example with the [BVG profile](../p/bvg):
|
||||
|
||||
```js
|
||||
import {createClient} from 'hafas-client'
|
||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
||||
|
||||
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||
const client = createClient(vbbProfile, userAgent)
|
||||
|
||||
// will query with these products: suburban, subway, bus, express, regional
|
||||
await client.departures('900000024101', {products: {tram: false, ferry: false}})
|
||||
```
|
||||
The maximum supported duration depends on the profile (see main readme), e.g. 720 for `db` and 60 for `dbnav`. In order to use the `dbris` profile, you need to pass the env vars `DB_API_KEY` and `DB_CLIENT_ID`.
|
||||
If you pass an object `opt.products`, its fields will partially override the default products defined in the profile.
|
||||
|
||||
## Response
|
||||
|
||||
*Note:* As stated in the [*Friendly Public Transport Format* v2 draft spec](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md), the `when` field includes the current delay. The `delay` field, if present, expresses how much the former differs from the schedule.
|
||||
|
||||
You may pass a departure's `tripId` into [`trip(id, lineName, [opt])`](trip.md) to get details on the whole trip.
|
||||
You may pass a departure's `tripId` into [`trip(id, lineName, [opt])`](trip.md) to get details on the whole trip. For the `dbnav`/`dbweb` profile HAFAS trip ids will be returned, for the `db` profile, RIS trip ids will be returned, then the `trip()` endpoint supports both id types.
|
||||
|
||||
As an example, we're going to use the [VBB profile](../p/vbb):
|
||||
For `db` profile, cancelled trips will not be contained in the response! For the `db` and `dbnav` profile, only the most important remarks will be contained in the boards.
|
||||
|
||||
```js
|
||||
import {createClient} from 'hafas-client'
|
||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
||||
import {createClient} from 'db-vendo-client'
|
||||
import {profile as dbnavProfile} from 'db-vendo-client/p/dbnav/index.js'
|
||||
|
||||
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||
const client = createClient(vbbProfile, userAgent)
|
||||
const client = createClient(dbnavProfile, userAgent)
|
||||
|
||||
// S Charlottenburg
|
||||
const {
|
||||
departures,
|
||||
realtimeDataUpdatedAt,
|
||||
} = await client.departures('900000024101', {duration: 3})
|
||||
} = await client.departures('8089165', {duration: 3})
|
||||
```
|
||||
|
||||
`realtimeDataUpdatedAt` is a UNIX timestamp reflecting the latest moment when (at least some) of the response's realtime data have been updated.
|
||||
`realtimeDataUpdatedAt` is currently not set in db-vendo-client, because the upstream APIs don't provide it.
|
||||
|
||||
`departures` may look like this:
|
||||
|
||||
|
@ -87,11 +77,11 @@ const {
|
|||
// Depending on the HAFAS endpoint, the destination may be present:
|
||||
destination: {
|
||||
type: 'stop',
|
||||
id: '900000029101',
|
||||
id: '8089165',
|
||||
name: 'S Spandau',
|
||||
location: {
|
||||
type: 'location',
|
||||
id: '900029101',
|
||||
id: '8089165',
|
||||
latitude: 52.534794,
|
||||
longitude: 13.197477
|
||||
},
|
||||
|
|
18
docs/dumps/PCAPdroid_02_Jan_15_23_29_moreloyaltycards.txt
Normal file
18
docs/dumps/PCAPdroid_02_Jan_15_23_29_moreloyaltycards.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
POST /mob/angebote/fahrplan HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
x-feature-reiseketten-enabled: false
|
||||
X-Correlation-ID: 68f7ceba-70e7-4a88-b9b0-454809655314_3fbcc823-2a69-46f4-9484-6d94b1c0116a
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: 4779d837-2aa8-4613-9904-8f950367c3c0
|
||||
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
Content-Length: 1120
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
||||
{"autonomeReservierung":false,"einstiegsTypList":["STANDARD"],"klasse":"KLASSE_2","reiseHin":{"wunsch":{"abgangsLocationId":"A\u003d1@O\u003dMünchen Hbf@X\u003d11558339@Y\u003d48140229@U\u003d81@L\u003d8000261@B\u003d1@p\u003d1734722398@i\u003dU×008020347@","verkehrsmittel":["ALL"],"zeitWunsch":{"reiseDatum":"2025-01-02T15:21:31.877957+01:00","zeitPunktArt":"ABFAHRT"},"zielLocationId":"A\u003d1@O\u003dStuttgart Hbf@X\u003d9181636@Y\u003d48784081@U\u003d81@L\u003d8000096@B\u003d1@p\u003d1734722398@i\u003dU×008029034@"}},"reisendenProfil":{"reisende":[{"ermaessigungen":["CH-GENERAL-ABONNEMENT KLASSE_2","CH-GENERAL-ABONNEMENT KLASSE_1","CH-HALBTAXABO_OHNE_RAILPLUS KLASSENLOS","A-VORTEILSCARD KLASSENLOS"],"reisendenTyp":"SENIOR"},{"ermaessigungen":["CH-GENERAL-ABONNEMENT KLASSE_2","CH-GENERAL-ABONNEMENT KLASSE_1","CH-HALBTAXABO_OHNE_RAILPLUS KLASSENLOS","A-VORTEILSCARD KLASSENLOS"],"reisendenTyp":"SENIOR"},{"ermaessigungen":["KLIMATICKET_OE KLASSENLOS","NL-40_OHNE_RAILPLUS KLASSENLOS","BAHNCARD100 KLASSE_1","BAHNCARD100 KLASSE_2"],"reisendenTyp":"ERWACHSENER"}]},"reservierungsKontingenteVorhanden":false}
|
17
docs/dumps/PCAPdroid_02_Jan_15_29_10_alter.txt
Normal file
17
docs/dumps/PCAPdroid_02_Jan_15_29_10_alter.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
POST /mob/angebote/recon/autonomereservierung HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
X-Correlation-ID: 68f7ceba-70e7-4a88-b9b0-454809655314_3fbcc823-2a69-46f4-9484-6d94b1c0116a
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: 7d70a4fc-751f-4893-b2e2-11c925c0538b
|
||||
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
Content-Length: 1872
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
||||
{"einstiegsTypList":["STANDARD"],"klasse":"KLASSE_2","reisendenProfil":{"reisende":[{"alter":63,"ermaessigungen":["KLIMATICKET_OE KLASSENLOS","NL-40_OHNE_RAILPLUS KLASSENLOS"],"reisendenTyp":"ERWACHSENER"}]},"reservierungsKontingenteVorhanden":false,"verbindungHin":{"kontext":"¶HKI¶T$A\u003d1@O\u003dMünchen Hbf@X\u003d11558339@Y\u003d48140229@L\u003d8000261@a\u003d128@$A\u003d1@O\u003dStuttgart Hbf@X\u003d9181636@Y\u003d48784081@L\u003d8000096@a\u003d128@$202501021628$202501021832$ICE 912$$1$$$$$$§T$A\u003d1@O\u003dStuttgart Hbf@X\u003d9181636@Y\u003d48784081@L\u003d8000096@a\u003d128@$A\u003d1@O\u003dParis Est@X\u003d2359120@Y\u003d48876976@L\u003d8700011@a\u003d128@$202501021852$202501022214$TGV 9570$$1$$$$$$¶KC¶#VE#2#CF#100#CA#0#CM#0#SICT#0#AM#81#AM2#0#RT#7#¶KCC¶I1ZFIzEjRVJHIzIjSElOIzAjRUNLIzU2NDAyOHw1NjQwMjh8NTY0Mzc0fDU2NDM3NHwwfDB8NTY1fDU2Mzk1OHwxfDB8MjZ8MHwwfC0yMTQ3NDgzNjQ4I0dBTSMyMDEyNTE2MjgjClojVk4jMSNTVCMxNzM0NzIyMzk4I1BJIzEjWkkjMTk3Mzc3I1RBIzAjREEjMjAxMjUjMVMjODAwMDI2MSMxVCMxNjI4I0xTIzgwMDAwODAjTFQjMjIwMyNQVSM4MSNSVCMxI0NBI0lDRSNaRSM5MTIjWkIjSUNFICA5MTIjUEMjMCNGUiM4MDAwMjYxI0ZUIzE2MjgjVE8jODAwMDA5NiNUVCMxODMyIwpaI1ZOIzEjU1QjMTczNDcyMjM5OCNQSSMxI1pJIzIzOTQ0OSNUQSMwI0RBIzIwMTI1IzFTIzgwMDAwOTYjMVQjMTg1MiNMUyM4NzAwMDExI0xUIzIyMTQjUFUjODEjUlQjMSNDQSNSSFQjWkUjOTU3MCNaQiNUR1YgOTU3MCNQQyMwI0ZSIzgwMDAwOTYjRlQjMTg1MiNUTyM4NzAwMDExI1RUIzIyMTQj¶KRCC¶#VE#1#¶SC¶1_H4sIAAAAAAACA32PS07DMBiEr1J5XarfTtM8JEsmDRWgAhGiCIRYhMZtg/IotlMRRTkHl2HXi/EnEWxA7Dzj8XzjhhykIj6hE8clYyLfDYowmNyHEw+1km/Eb0hR5Qvi2+PuEBAfxqSsTBgbiWEGzAYKjPTmXZp3JrUZBUBr0zec0DF5LepFZtSS+E8NMfW+i0W3NyGG8jLp1MX1HMUhzqq+AphF2ud+03y3HYqRnMj9slwPNVmaYPKUU3HDr46fxXoni9H5y0Y8cEpt27UsTzzyqUunwJgnVtylYsldAGAzKgJ8t+fUsaYOY5bnipSvjh8ALjBAT+AYbYZPLvolsVJ/oqNYpXp0pg1ymWV7lEGPdZ2Z58x+sA5y6T9Yh1IL4Dd2K01UZnWWFugZVcneuiwrVcg6KKsi0cTfxJkeLqJY6yzV5jsr1yUOjHMMNW3bfgFUarsn8gEAAA\u003d\u003d"}}
|
17
docs/dumps/PCAPdroid_02_Jan_15_29_15_intl.txt
Normal file
17
docs/dumps/PCAPdroid_02_Jan_15_29_15_intl.txt
Normal file
File diff suppressed because one or more lines are too long
17
docs/dumps/PCAPdroid_02_Jan_21_59_35_initial_transfer.txt
Normal file
17
docs/dumps/PCAPdroid_02_Jan_21_59_35_initial_transfer.txt
Normal file
File diff suppressed because one or more lines are too long
18
docs/dumps/PCAPdroid_02_Jan_22_47_29_tickets.txt
Normal file
18
docs/dumps/PCAPdroid_02_Jan_22_47_29_tickets.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
POST /mob/angebote/recon HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
x-feature-reiseketten-enabled: false
|
||||
X-Correlation-ID: e1927e98-0d8c-45f2-a161-965622ccd56a_e8cf6ea4-4103-4707-aa44-2287646a87f9
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: dcf2d458-4e11-4e40-a6e3-8922544387e8
|
||||
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
Content-Length: 2749
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
||||
{"einstiegsTypList":["STANDARD"],"klasse":"KLASSE_2","reisendenProfil":{"reisende":[{"ermaessigungen":["KEINE_ERMAESSIGUNG KLASSENLOS"],"reisendenTyp":"ERWACHSENER"}]},"reservierungsKontingenteVorhanden":false,"verbindungHin":{"kontext":"¶HKI¶T$A\u003d1@O\u003dMünchen Hbf@X\u003d11558339@Y\u003d48140229@L\u003d8000261@a\u003d128@$A\u003d1@O\u003dFrankfurt(Main)Hbf@X\u003d8663785@Y\u003d50107149@L\u003d8000105@a\u003d128@$202501030000$202501030438$ICE 618$$1$$$$$$§T$A\u003d1@O\u003dFrankfurt(Main)Hbf@X\u003d8663785@Y\u003d50107149@L\u003d8000105@a\u003d128@$A\u003d1@O\u003dKassel-Wilhelmshöhe@X\u003d9447114@Y\u003d51312558@L\u003d8003200@a\u003d128@$202501030512$202501030644$ICE 1088$$1$$$$$$§T$A\u003d1@O\u003dKassel-Wilhelmshöhe@X\u003d9447114@Y\u003d51312558@L\u003d8003200@a\u003d128@$A\u003d1@O\u003dHamm(Westf)Hbf@X\u003d7807824@Y\u003d51678077@L\u003d8000149@a\u003d128@$202501030703$202501030850$RE 26708$$1$$$$$$§T$A\u003d1@O\u003dHamm(Westf)Hbf@X\u003d7807824@Y\u003d51678077@L\u003d8000149@a\u003d128@$A\u003d1@O\u003dMünster(Westf)Hbf@X\u003d7635716@Y\u003d51956563@L\u003d8000263@a\u003d128@$202501030859$202501030922$RE 32916$$1$$$$$$¶KC¶#VE#2#CF#100#CA#0#CM#0#SICT#0#AM#81#AM2#0#RT#7#¶KCC¶I1ZFIzEjRVJHIzQ1MzE2I0hJTiMwI0VDSyM1NjQ0ODB8NTY0NDgwfDU2NDk1NHw1NjUwNDJ8MHwwfDU2NXw1NjQ0MTN8NHwwfDh8MHwwfC0yMTQ3NDgzNjQ4I0dBTSMzMDEyNTAwMDAjClojVk4jMSNTVCMxNzM0NzIyMzk4I1BJIzEjWkkjMTk1OTUyI1RBIzAjREEjMzAxMjUjMVMjODAwMDI2MSMxVCMwI0xTIzgwMDAxOTkjTFQjMTEyMyNQVSM4MSNSVCMxI0NBI0lDRSNaRSM2MTgjWkIjSUNFICA2MTgjUEMjMCNGUiM4MDAwMjYxI0ZUIzAjVE8jODAwMDEwNSNUVCM0MzgjClojVk4jMSNTVCMxNzM0NzIyMzk4I1BJIzEjWkkjMTkyODc1I1RBIzAjREEjMzAxMjUjMVMjODAwMDEwNSMxVCM1MTIjTFMjODAwMDE5OSNMVCMxMDE4I1BVIzgxI1JUIzEjQ0EjSUNFI1pFIzEwODgjWkIjSUNFIDEwODgjUEMjMCNGUiM4MDAwMTA1I0ZUIzUxMiNUTyM4MDAzMjAwI1RUIzY0NCMKWiNWTiMxI1NUIzE3MzQ3MjIzOTgjUEkjMSNaSSMyMTg2NjIjVEEjMCNEQSMzMDEyNSMxUyM4MDAzMjAwIzFUIzcwMyNMUyM4MDAwMTQ5I0xUIzg1MCNQVSM4MSNSVCMxI0NBI0RQTiNaRSMyNjcwOCNaQiNSRSAyNjcwOCNQQyMzI0ZSIzgwMDMyMDAjRlQjNzAzI1RPIzgwMDAxNDkjVFQjODUwIwpaI1ZOIzEjU1QjMTczNDcyMjM5OCNQSSMxI1pJIzIxNzk5NiNUQSMwI0RBIzMwMTI1IzFTIzgwMDAxNzEjMVQjODQzI0xTIzgwMDAzMTYjTFQjOTUxI1BVIzgxI1JUIzEjQ0EjRFBOI1pFIzMyOTE2I1pCI1JFIDMyOTE2I1BDIzMjRlIjODAwMDE0OSNGVCM4NTkjVE8jODAwMDI2MyNUVCM5MjIj¶KRCC¶#VE#1#¶SC¶1_H4sIAAAAAAACA32P3UrDMACFX0VypVBHfpr+QSB2ZahMV8T5g3hR13SrdO1M0mEpfQ5fxru9mGmLFyJ6l3Nycr6TFuyFBAFAE9cDFhDv2ogonNxFE99oKd5A0IKy3s5AQK3+EIIAWqCqdZRoYcIYYgoRxGAwb/PtYGKbQGisbGg4RRZ4LZtZoeUcBE8t0M2uj8U3i8iEtlXaq4vrqRH7pKh7ZSoJ6J6HTdPNeiw25FTs5tVqrCny1CTPGOILdnX4LFcbUR6dv2T8gSFEqUeIzx+Z7SEbYuzzJfMQnzMPQogdxEPzbseQS2wXY+J7PGfLwweEHsTQeNyMUXr85GxYkkj5J1ppIY/vhdLZyTjAdQh1kWP4FPnUoQ75ySf/8RFE6Dd/LXRcFU2Rl8bTshaDdVnVshRNWNVlqkCQJYUaL+JEqSJX+jsrVlWcyGRrQm3XdV8RCJMF+wEAAA\u003d\u003d"}}
|
17
docs/dumps/PCAPdroid_02_Jan_22_47_34_tickets.txt
Normal file
17
docs/dumps/PCAPdroid_02_Jan_22_47_34_tickets.txt
Normal file
File diff suppressed because one or more lines are too long
17
docs/dumps/PCAPdroid_16_Dec_19_30_43_locsearch.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_30_43_locsearch.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
POST /mob/location/search HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.location.v3+json
|
||||
X-Correlation-ID: 4f2e274e-6e5c-4711-9125-87229046bba3_64466773-556f-4aa8-b128-0948c4d60887
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: 560187ed-f13a-4654-bc62-b9f69f989563
|
||||
Content-Type: application/x.db.vendo.mob.location.v3+json
|
||||
Content-Length: 45
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
||||
{"locationTypes":["ALL"],"searchTerm":"test"}
|
17
docs/dumps/PCAPdroid_16_Dec_19_31_06_locsearch.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_31_06_locsearch.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
HTTP/1.1 200 OK
|
||||
Date: Mon, 16 Dec 2024 18:29:33 GMT
|
||||
Content-Type: application/x.db.vendo.mob.location.v3+json
|
||||
Content-Length: 2994
|
||||
Connection: keep-alive
|
||||
server-timing: intid;desc=a0c42ccdea3bbb18
|
||||
Server-Timing: intid;desc=a0c42ccdea3bbb18
|
||||
Server-Timing: intid;desc=a0c42ccdea3bbb18
|
||||
x-correlation-id: 4f2e274e-6e5c-4711-9125-87229046bba3_64466773-556f-4aa8-b128-0948c4d60887
|
||||
Strict-Transport-Security: max-age=16070400; includeSubDomains
|
||||
X-XSS-Protection: 0
|
||||
server-timing: intid;desc=a0c42ccdea3bbb18
|
||||
Content-Security-Policy: frame-ancestors 'none';
|
||||
X-Content-Type-Options: nosniff
|
||||
Set-Cookie: TS01be2125=01d513bcd1e15efa531633ee310f49108472c2b1a482b002a4d22b5cf935d3d6970510c0b134616b579c639af340fe261746662484; Path=/; Domain=.app.vendo.noncd.db.de; Secure; HTTPOnly
|
||||
|
||||
[{"name":"Tessin West","stationId":"7983","locationId":"A=1@O=Tessin West@X=12442572@Y=54034438@U=81@L=8079604@B=1@p=1734031727@i=U×008030295@","evaNr":"8079604","coordinates":{"latitude":54.0344,"longitude":12.442203},"weight":1489,"products":["NAHVERKEHRSONSTIGEZUEGE","BUSSE","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"},{"name":"Testa Grigia","locationId":"A=1@O=Testa Grigia@X=7707540@Y=45934474@U=81@L=8511303@B=1@p=1734031727@i=U×008511303@","evaNr":"8511303","coordinates":{"latitude":45.934475,"longitude":7.70754},"weight":3825,"products":["NAHVERKEHRSONSTIGEZUEGE"],"locationType":"ST"},{"name":"Teschenhagen","stationId":"6172","locationId":"A=1@O=Teschenhagen@X=13374232@Y=54389368@U=81@L=8013104@B=1@p=1734031727@i=U×008028497@","evaNr":"8013104","coordinates":{"latitude":54.38936,"longitude":13.374196},"weight":3825,"products":["NAHVERKEHRSONSTIGEZUEGE"],"locationType":"ST"},{"name":"Testelt","locationId":"A=1@O=Testelt@X=4946863@Y=51009783@U=81@L=8800244@B=1@p=1734031727@i=U×008833266@","evaNr":"8800244","coordinates":{"latitude":51.009785,"longitude":4.946863},"weight":2627,"products":["INTERCITYUNDEUROCITYZUEGE","NAHVERKEHRSONSTIGEZUEGE"],"locationType":"ST"},{"name":"Tessin","stationId":"6174","locationId":"A=1@O=Tessin@X=12462618@Y=54032020@U=81@L=8013106@B=1@p=1734031727@i=U×008027109@","evaNr":"8013106","coordinates":{"latitude":54.032,"longitude":12.461656},"weight":2402,"products":["NAHVERKEHRSONSTIGEZUEGE","BUSSE"],"locationType":"ST"},{"name":"Chemnitz Zentralhaltestelle","locationId":"A=1@O=Chemnitz Zentralhaltestelle@X=12922263@Y=50831626@U=81@L=8017419@B=1@p=1734031727@i=U×008042918@","evaNr":"8017419","coordinates":{"latitude":50.831627,"longitude":12.922263},"weight":5813,"products":["NAHVERKEHRSONSTIGEZUEGE","BUSSE","STRASSENBAHN","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"},{"name":"Teschow","stationId":"6173","locationId":"A=1@O=Teschow@X=11637956@Y=53994355@U=81@L=8013105@B=1@p=1734031727@i=U×008027118@","evaNr":"8013105","coordinates":{"latitude":53.994175,"longitude":11.637687},"weight":1524,"products":["NAHVERKEHRSONSTIGEZUEGE","BUSSE"],"locationType":"ST"},{"name":"Tesperhude Hudehof, Geesthacht","locationId":"A=1@O=Tesperhude Hudehof, Geesthacht@X=10431253@Y=53404393@U=81@L=694334@B=1@p=1734031727@","evaNr":"694334","coordinates":{"latitude":53.404392,"longitude":10.431253},"weight":912,"products":["BUSSE"],"locationType":"ST"},{"name":"Tesperhude Strandweg, Geesthacht","locationId":"A=1@O=Tesperhude Strandweg, Geesthacht@X=10427424@Y=53402316@U=81@L=694333@B=1@p=1734031727@","evaNr":"694333","coordinates":{"latitude":53.402317,"longitude":10.427424},"weight":912,"products":["BUSSE"],"locationType":"ST"},{"name":"Testorf Umspannwerk, Wangels","locationId":"A=1@O=Testorf Umspannwerk, Wangels@X=10778516@Y=54250161@U=81@L=700240@B=1@p=1734031727@","evaNr":"700240","coordinates":{"latitude":54.25016,"longitude":10.778516},"weight":220,"products":["BUSSE","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"}]
|
18
docs/dumps/PCAPdroid_16_Dec_19_33_04_routesearch.txt
Normal file
18
docs/dumps/PCAPdroid_16_Dec_19_33_04_routesearch.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
POST /mob/angebote/fahrplan HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
x-feature-reiseketten-enabled: false
|
||||
X-Correlation-ID: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: 0d0c6c50-3bea-4cb7-b008-09bc92a0d96a
|
||||
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
Content-Length: 679
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
||||
{"autonomeReservierung":false,"einstiegsTypList":["STANDARD"],"klasse":"KLASSE_2","reiseHin":{"wunsch":{"abgangsLocationId":"A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@U\u003d81@L\u003d8011160@B\u003d1@p\u003d1734031727@i\u003dU×008065969@","verkehrsmittel":["ALL"],"zeitWunsch":{"reiseDatum":"2024-12-16T19:28:48.659812+01:00","zeitPunktArt":"ABFAHRT"},"zielLocationId":"A\u003d1@O\u003dKöln Hbf@X\u003d6958730@Y\u003d50943029@U\u003d81@L\u003d8000207@B\u003d1@p\u003d1734031727@i\u003dU×008015458@"}},"reisendenProfil":{"reisende":[{"ermaessigungen":["KEINE_ERMAESSIGUNG KLASSENLOS"],"reisendenTyp":"ERWACHSENER"}]},"reservierungsKontingenteVorhanden":false}
|
17
docs/dumps/PCAPdroid_16_Dec_19_33_08_routesearch.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_33_08_routesearch.txt
Normal file
File diff suppressed because one or more lines are too long
17
docs/dumps/PCAPdroid_16_Dec_19_35_50_triprecon.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_35_50_triprecon.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
POST /mob/trip/recon HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
X-Correlation-ID: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: 757b9806-028b-4b0e-bdde-2441c2e1e1ee
|
||||
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
Content-Length: 1162
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
||||
{"reconCtx":"¶HKI¶T$A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@L\u003d8011160@a\u003d128@$A\u003d1@O\u003dKöln Hbf@X\u003d6958730@Y\u003d50943029@L\u003d8000207@a\u003d128@$202412161946$202412170057$ICE 842$$1$$$$$$¶KC¶#VE#2#CF#100#CA#0#CM#0#SICT#0#AM#81#AM2#0#RT#7#¶KCC¶I1ZFIzEjRVJHIzEjSElOIzAjRUNLIzUzOTc0Nnw1Mzk3NDZ8NTQwMDU3fDU0MDA1N3wwfDB8NTY1fDUzOTcyNXwxfDB8MTA1MHwwfDB8LTIxNDc0ODM2NDgjR0FNIzE2MTIyNDE5NDYjClojVk4jMSNTVCMxNzM0MDMxNzI3I1BJIzEjWkkjMTc3NjUxI1RBIzAjREEjMTYxMjI0IzFTIzgwMTAyNTUjMVQjMTkzNCNMUyM4MDAwMjA3I0xUIzEwMDU3I1BVIzgxI1JUIzEjQ0EjSUNFI1pFIzg0MiNaQiNJQ0UgIDg0MiNQQyMwI0ZSIzgwMTExNjAjRlQjMTk0NiNUTyM4MDAwMjA3I1RUIzEwMDU3Iw\u003d\u003d¶KRCC¶#VE#1#¶SC¶1_H4sIAAAAAAACA32P7UrDMBiFb0Xyu4436WcKgdiV4cfQIk4U8Udds1lJ25mmw1J6Hd6JN7AbM20ZCIrkT87JyXnet0N7oVCI8MwPkIXEhzYijmb38YwarcQ7CjtUNsUCha41XCIUgoWqRsepFiZMgDiYYA+N5l1eDCamJAAw1mZsOMUWeivbhdRqicKnDul2N8SS25vYhIoqG9TF9dyIfSqbsQKIjfrncab563YqNuRM7JbVeqqReWaSZwzzGxYJJfPy5Pxlwx8Ytm2Pug7lj8wl5rgB5SsWYL5kAWCMPeCR+bVj2LcdsLFPfJ6z1eETIADPpR7lZpRaTysuxjlSpf4EXx2+5JFroIFvw4AF6thAfmABCPj/YLHruMFv7FbopJKt2c14WjVitC6rRpWijaqmzGoUblJZTw9JWtcyr/UxK9ZVkqq0MKGu7/tvvH4lCvABAAA\u003d"}
|
17
docs/dumps/PCAPdroid_16_Dec_19_35_55_triprecon.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_35_55_triprecon.txt
Normal file
File diff suppressed because one or more lines are too long
17
docs/dumps/PCAPdroid_16_Dec_19_36_19_triprecon.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_36_19_triprecon.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
POST /mob/trip/recon HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
X-Correlation-ID: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: f30d7ef5-d9a2-428b-acc2-e8bb68c7d705
|
||||
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
Content-Length: 2507
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
||||
{"reconCtx":"¶HKI¶T$A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@L\u003d8098160@a\u003d128@$A\u003d1@O\u003dHannover Hbf@X\u003d9741017@Y\u003d52376764@L\u003d8000152@a\u003d128@$202412162128$202412162322$ICE 840$$1$$$$$$§T$A\u003d1@O\u003dHannover Hbf@X\u003d9741017@Y\u003d52376764@L\u003d8000152@a\u003d128@$A\u003d1@O\u003dHanau Hbf@X\u003d8929003@Y\u003d50120957@L\u003d8000150@a\u003d128@$202412170030$202412170414$IC 60471$$1$$$$$$§T$A\u003d1@O\u003dHanau Hbf@X\u003d8929003@Y\u003d50120957@L\u003d8000150@a\u003d128@$A\u003d1@O\u003dFrankfurt(Main)Hbf@X\u003d8663785@Y\u003d50107149@L\u003d8000105@a\u003d128@$202412170455$202412170515$RB 15501$$1$$$$$$§T$A\u003d1@O\u003dFrankfurt(Main)Hbf@X\u003d8663785@Y\u003d50107149@L\u003d8000105@a\u003d128@$A\u003d1@O\u003dKöln Hbf@X\u003d6958730@Y\u003d50943029@L\u003d8000207@a\u003d128@$202412170526$202412170633$ICE 222$$1$$$$$$¶KC¶#VE#2#CF#100#CA#0#CM#0#SICT#0#AM#81#AM2#0#RT#7#¶KCC¶I1ZFIzEjRVJHIzI2MCNISU4jNDU1I0VDSyM1Mzk4NDh8NTM5ODQ4fDU0MDM5M3w1NDAzOTN8MHwwfDU2NXw1Mzk4NDh8NHwwfDE4fDB8MHwtMjE0NzQ4MzY0OCNHQU0jMTYxMjI0MjEyOCMKWiNWTiMxI1NUIzE3MzQwMzE3MjcjUEkjMSNaSSMxNzc2MTkjVEEjMCNEQSMxNjEyMjQjMVMjODA5ODE2MCMxVCMyMTI4I0xTIzgwMDAxNTIjTFQjMjMyMiNQVSM4MSNSVCMxI0NBI0lDRSNaRSM4NDAjWkIjSUNFICA4NDAjUEMjMCNGUiM4MDk4MTYwI0ZUIzIxMjgjVE8jODAwMDE1MiNUVCMyMzIyIwpaI1ZOIzEjU1QjMTczNDAzMTcyNyNQSSMxI1pJIzE3MzY1MSNUQSMwI0RBIzE2MTIyNCMxUyM4MDAyNTUzIzFUIzIxNTIjTFMjODUwMzAwMCNMVCMxMTAwNSNQVSM4MSNSVCMxI0NBI0lDI1pFIzYwNDcxI1pCI0lDIDYwNDcxI1BDIzEjRlIjODAwMDE1MiNGVCMxMDAzMCNUTyM4MDAwMTUwI1RUIzEwNDE0IwpaI1ZOIzEjU1QjMTczNDAzMTcyNyNQSSMxI1pJIzE4NzYxOCNUQSMwI0RBIzE3MTIyNCMxUyM4MDA2MTMyIzFUIzQxOCNMUyM4MDAwMTA1I0xUIzUxNSNQVSM4MSNSVCMxI0NBI1JCI1pFIzE1NTAxI1pCI1JCIDE1NTAxI1BDIzMjRlIjODAwMDE1MCNGVCM0NTUjVE8jODAwMDEwNSNUVCM1MTUjClojVk4jMSNTVCMxNzM0MDMxNzI3I1BJIzEjWkkjMjExNDMzI1RBIzAjREEjMTcxMjI0IzFTIzgwMDAxMDUjMVQjNTI2I0xTIzg0MDAwNTgjTFQjOTI5I1BVIzgxI1JUIzEjQ0EjSUNFI1pFIzIyMiNaQiNJQ0UgIDIyMiNQQyMwI0ZSIzgwMDAxMDUjRlQjNTI2I1RPIzgwMDAyMDcjVFQjNjMzIw\u003d\u003d¶KRCC¶#VE#1#¶SC¶1_H4sIAAAAAAACA32P7UrDMBiFb0Xyu4436WcKgdiV4cfQIk4U8Udds1lJ25mmw1J6Hd6JN7AbM20ZCIrkT87JyXnet0N7oVCI8MwPkIXEhzYijmb38YwarcQ7CjtUNsUCha41XCIUgoWqRsepFiZMgDiYYA+N5l1eDCamJAAw1mZsOMUWeivbhdRqicKnDul2N8SS25vYhIoqG9TF9dyIfSqbsQKIjfrncab563YqNuRM7JbVeqqReWaSZwzzGxYJJfPy5Pxlwx8Ytm2Pug7lj8wl5rgB5SsWYL5kAWCMPeCR+bVj2LcdsLFPfJ6z1eETIADPpR7lZpRaTysuxjlSpf4EXx2+5JFroIFvw4AF6thAfmABCPj/YLHruMFv7FbopJKt2c14WjVitC6rRpWijaqmzGoUblJZTw9JWtcyr/UxK9ZVkqq0MKGu7/tvvH4lCvABAAA\u003d"}
|
17
docs/dumps/PCAPdroid_16_Dec_19_36_22_triprecon.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_36_22_triprecon.txt
Normal file
File diff suppressed because one or more lines are too long
15
docs/dumps/PCAPdroid_16_Dec_19_37_19_locationdetails.txt
Normal file
15
docs/dumps/PCAPdroid_16_Dec_19_37_19_locationdetails.txt
Normal file
|
@ -0,0 +1,15 @@
|
|||
GET /mob/location/details/8011160 HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.location.v3+json
|
||||
Content-Type: application/x.db.vendo.mob.location.v3+json
|
||||
X-Correlation-ID: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: b9bd5e93-e6d2-4998-bc5a-17b72ca65ca3
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
17
docs/dumps/PCAPdroid_16_Dec_19_37_22_locationdetails.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_37_22_locationdetails.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
HTTP/1.1 200 OK
|
||||
Date: Mon, 16 Dec 2024 18:36:50 GMT
|
||||
Content-Type: application/x.db.vendo.mob.location.v3+json
|
||||
Content-Length: 1158
|
||||
Connection: keep-alive
|
||||
server-timing: intid;desc=5ae5ce547c4a8b38
|
||||
Server-Timing: intid;desc=5ae5ce547c4a8b38
|
||||
Server-Timing: intid;desc=5ae5ce547c4a8b38
|
||||
x-correlation-id: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
|
||||
Strict-Transport-Security: max-age=16070400; includeSubDomains
|
||||
X-XSS-Protection: 0
|
||||
server-timing: intid;desc=5ae5ce547c4a8b38
|
||||
Content-Security-Policy: frame-ancestors 'none';
|
||||
X-Content-Type-Options: nosniff
|
||||
Set-Cookie: TS01be2125=01d513bcd125629ac814505605565f3e87a8081db30d56beae22efb148df982534183fd830148fa0901fa5a36116fab13d52c5d86a; Path=/; Domain=.app.vendo.noncd.db.de; Secure; HTTPOnly
|
||||
|
||||
{"haltName":"Berlin Hbf","produktGattungen":[{"produktGattung":"HOCHGESCHWINDIGKEITSZUEGE","produkte":[{"name":"ICE"},{"name":"RJ"}]},{"produktGattung":"INTERCITYUNDEUROCITYZUEGE","produkte":[{"name":"EC"},{"name":"IC"},{"name":"NJ"}]},{"produktGattung":"INTERREGIOUNDSCHNELLZUEGE","produkte":[{"name":"BUS"},{"name":"Bus"},{"name":"D"},{"name":"EN"},{"name":"ES"},{"name":"FLX"},{"name":"UEX"}]},{"produktGattung":"NAHVERKEHRSONSTIGEZUEGE","produkte":[{"name":"FEX"},{"name":"HBX"},{"name":"R"},{"name":"RB"},{"name":"RE"},{"name":"RSM"},{"name":"Bus RE3"},{"name":"Bus RE5"},{"name":"Bus RE7"},{"name":"Bus RE8"},{"name":"Bus S7"}]},{"produktGattung":"SBAHNEN","produkte":[{"name":"S 3"},{"name":"S 5"},{"name":"S 7"},{"name":"S 9"},{"name":"S 45"}]},{"produktGattung":"BUSSE","produkte":[{"name":"Bus 120"},{"name":"Bus 123"},{"name":"Bus 142"},{"name":"Bus 147"},{"name":"Bus 245"},{"name":"Bus M41"},{"name":"Bus M85"},{"name":"Bus N5"},{"name":"Bus N20"},{"name":"Bus N40"}]},{"produktGattung":"UBAHN","produkte":[{"name":"U 5"}]},{"produktGattung":"STRASSENBAHN","produkte":[{"name":"STR 12"},{"name":"STR M5"},{"name":"STR M8"},{"name":"STR M10"}]}]}
|
17
docs/dumps/PCAPdroid_16_Dec_19_37_26_departures.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_37_26_departures.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
POST /mob/bahnhofstafel/abfahrt HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.bahnhofstafeln.v2+json
|
||||
X-Correlation-ID: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: b2d3fe71-a8d6-4675-bbe5-a5081368a6ab
|
||||
Content-Type: application/x.db.vendo.mob.bahnhofstafeln.v2+json
|
||||
Content-Length: 197
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
||||
{"anfragezeit":"21:28","datum":"2024-12-16","ursprungsBahnhofId":"A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@U\u003d81@L\u003d8098160@i\u003dU×008031922@","verkehrsmittel":["ALL"]}
|
17
docs/dumps/PCAPdroid_16_Dec_19_37_34_departures.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_37_34_departures.txt
Normal file
File diff suppressed because one or more lines are too long
17
docs/dumps/PCAPdroid_16_Dec_19_37_57_departures.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_37_57_departures.txt
Normal file
File diff suppressed because one or more lines are too long
17
docs/dumps/PCAPdroid_16_Dec_19_38_00_departures.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_38_00_departures.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
POST /mob/bahnhofstafel/abfahrt HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.bahnhofstafeln.v2+json
|
||||
X-Correlation-ID: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: 8c20d0c0-3c0b-40ef-bc95-849c65e375df
|
||||
Content-Type: application/x.db.vendo.mob.bahnhofstafeln.v2+json
|
||||
Content-Length: 378
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
||||
{"anfragezeit":"21:28","datum":"2024-12-16","ursprungsBahnhofId":"A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@U\u003d81@L\u003d8098160@i\u003dU×008031922@","verkehrsmittel":["HOCHGESCHWINDIGKEITSZUEGE","INTERCITYUNDEUROCITYZUEGE","INTERREGIOUNDSCHNELLZUEGE","NAHVERKEHRSONSTIGEZUEGE","SBAHNEN","BUSSE","SCHIFFE","UBAHN","STRASSENBAHN","ANRUFPFLICHTIGEVERKEHRE"]}
|
17
docs/dumps/PCAPdroid_16_Dec_19_38_44_arrivals.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_38_44_arrivals.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
POST /mob/bahnhofstafel/ankunft HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.bahnhofstafeln.v2+json
|
||||
X-Correlation-ID: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: d7e7526d-1d69-41d1-bf48-9c2ed5f4a302
|
||||
Content-Type: application/x.db.vendo.mob.bahnhofstafeln.v2+json
|
||||
Content-Length: 378
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
||||
{"anfragezeit":"21:28","datum":"2024-12-16","ursprungsBahnhofId":"A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@U\u003d81@L\u003d8098160@i\u003dU×008031922@","verkehrsmittel":["HOCHGESCHWINDIGKEITSZUEGE","INTERCITYUNDEUROCITYZUEGE","INTERREGIOUNDSCHNELLZUEGE","NAHVERKEHRSONSTIGEZUEGE","SBAHNEN","BUSSE","SCHIFFE","UBAHN","STRASSENBAHN","ANRUFPFLICHTIGEVERKEHRE"]}
|
17
docs/dumps/PCAPdroid_16_Dec_19_38_47_arrivals.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_38_47_arrivals.txt
Normal file
File diff suppressed because one or more lines are too long
17
docs/dumps/PCAPdroid_16_Dec_19_39_50_locsearch_station.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_39_50_locsearch_station.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
POST /mob/location/search HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.location.v3+json
|
||||
X-Correlation-ID: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: 91946e30-da87-4813-9a13-48dce2cd2cdd
|
||||
Content-Type: application/x.db.vendo.mob.location.v3+json
|
||||
Content-Length: 44
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
||||
{"locationTypes":["ST"],"searchTerm":"test"}
|
17
docs/dumps/PCAPdroid_16_Dec_19_39_53_locsearch_station.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_39_53_locsearch_station.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
HTTP/1.1 200 OK
|
||||
Date: Mon, 16 Dec 2024 18:39:38 GMT
|
||||
Content-Type: application/x.db.vendo.mob.location.v3+json
|
||||
Content-Length: 2994
|
||||
Connection: keep-alive
|
||||
server-timing: intid;desc=6e7cf7863b2dbf7b
|
||||
Server-Timing: intid;desc=6e7cf7863b2dbf7b
|
||||
Server-Timing: intid;desc=6e7cf7863b2dbf7b
|
||||
x-correlation-id: 0564dcdf-edbf-4412-a147-7299f6481470_64466773-556f-4aa8-b128-0948c4d60887
|
||||
Strict-Transport-Security: max-age=16070400; includeSubDomains
|
||||
X-XSS-Protection: 0
|
||||
server-timing: intid;desc=6e7cf7863b2dbf7b
|
||||
Content-Security-Policy: frame-ancestors 'none';
|
||||
X-Content-Type-Options: nosniff
|
||||
Set-Cookie: TS01be2125=01d513bcd109bdf5d864ee9ff6d510d0c0e6688e510fd271d9dba08fe92a99ec7d68b43cb8eddd472ec4074727d7ed3137ea220214; Path=/; Domain=.app.vendo.noncd.db.de; Secure; HTTPOnly
|
||||
|
||||
[{"name":"Tessin West","stationId":"7983","locationId":"A=1@O=Tessin West@X=12442572@Y=54034438@U=81@L=8079604@B=1@p=1734031727@i=U×008030295@","evaNr":"8079604","coordinates":{"latitude":54.0344,"longitude":12.442203},"weight":1489,"products":["NAHVERKEHRSONSTIGEZUEGE","BUSSE","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"},{"name":"Testa Grigia","locationId":"A=1@O=Testa Grigia@X=7707540@Y=45934474@U=81@L=8511303@B=1@p=1734031727@i=U×008511303@","evaNr":"8511303","coordinates":{"latitude":45.934475,"longitude":7.70754},"weight":3825,"products":["NAHVERKEHRSONSTIGEZUEGE"],"locationType":"ST"},{"name":"Teschenhagen","stationId":"6172","locationId":"A=1@O=Teschenhagen@X=13374232@Y=54389368@U=81@L=8013104@B=1@p=1734031727@i=U×008028497@","evaNr":"8013104","coordinates":{"latitude":54.38936,"longitude":13.374196},"weight":3825,"products":["NAHVERKEHRSONSTIGEZUEGE"],"locationType":"ST"},{"name":"Testelt","locationId":"A=1@O=Testelt@X=4946863@Y=51009783@U=81@L=8800244@B=1@p=1734031727@i=U×008833266@","evaNr":"8800244","coordinates":{"latitude":51.009785,"longitude":4.946863},"weight":2627,"products":["INTERCITYUNDEUROCITYZUEGE","NAHVERKEHRSONSTIGEZUEGE"],"locationType":"ST"},{"name":"Tessin","stationId":"6174","locationId":"A=1@O=Tessin@X=12462618@Y=54032020@U=81@L=8013106@B=1@p=1734031727@i=U×008027109@","evaNr":"8013106","coordinates":{"latitude":54.032,"longitude":12.461656},"weight":2402,"products":["NAHVERKEHRSONSTIGEZUEGE","BUSSE"],"locationType":"ST"},{"name":"Chemnitz Zentralhaltestelle","locationId":"A=1@O=Chemnitz Zentralhaltestelle@X=12922263@Y=50831626@U=81@L=8017419@B=1@p=1734031727@i=U×008042918@","evaNr":"8017419","coordinates":{"latitude":50.831627,"longitude":12.922263},"weight":5813,"products":["NAHVERKEHRSONSTIGEZUEGE","BUSSE","STRASSENBAHN","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"},{"name":"Teschow","stationId":"6173","locationId":"A=1@O=Teschow@X=11637956@Y=53994355@U=81@L=8013105@B=1@p=1734031727@i=U×008027118@","evaNr":"8013105","coordinates":{"latitude":53.994175,"longitude":11.637687},"weight":1524,"products":["NAHVERKEHRSONSTIGEZUEGE","BUSSE"],"locationType":"ST"},{"name":"Tesperhude Hudehof, Geesthacht","locationId":"A=1@O=Tesperhude Hudehof, Geesthacht@X=10431253@Y=53404393@U=81@L=694334@B=1@p=1734031727@","evaNr":"694334","coordinates":{"latitude":53.404392,"longitude":10.431253},"weight":912,"products":["BUSSE"],"locationType":"ST"},{"name":"Tesperhude Strandweg, Geesthacht","locationId":"A=1@O=Tesperhude Strandweg, Geesthacht@X=10427424@Y=53402316@U=81@L=694333@B=1@p=1734031727@","evaNr":"694333","coordinates":{"latitude":53.402317,"longitude":10.427424},"weight":912,"products":["BUSSE"],"locationType":"ST"},{"name":"Testorf Umspannwerk, Wangels","locationId":"A=1@O=Testorf Umspannwerk, Wangels@X=10778516@Y=54250161@U=81@L=700240@B=1@p=1734031727@","evaNr":"700240","coordinates":{"latitude":54.25016,"longitude":10.778516},"weight":220,"products":["BUSSE","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"}]
|
18
docs/dumps/PCAPdroid_16_Dec_19_42_53_specialrouting.txt
Normal file
18
docs/dumps/PCAPdroid_16_Dec_19_42_53_specialrouting.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
POST /mob/angebote/fahrplan HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
x-feature-reiseketten-enabled: false
|
||||
X-Correlation-ID: 67b8a500-1983-49f5-a4ff-177d58b395ed_64466773-556f-4aa8-b128-0948c4d60887
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: be862f65-99be-46af-a622-8bc332dc70df
|
||||
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
Content-Length: 1122
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
||||
{"autonomeReservierung":false,"einstiegsTypList":["STANDARD"],"klasse":"KLASSE_2","reiseHin":{"wunsch":{"abgangsLocationId":"A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@U\u003d81@L\u003d8011160@B\u003d1@p\u003d1734031727@i\u003dU×008065969@","economic":true,"minUmstiegsdauer":20,"verkehrsmittel":["NAHVERKEHRSONSTIGEZUEGE","SBAHNEN","BUSSE","SCHIFFE","UBAHN","STRASSENBAHN","ANRUFPFLICHTIGEVERKEHRE"],"viaLocations":[{"locationId":"A\u003d1@O\u003dHannover Hbf@X\u003d9741017@Y\u003d52376764@U\u003d81@L\u003d8000152@B\u003d1@p\u003d1734031727@i\u003dU×008013552@","minUmstiegsdauer":60,"verkehrsmittel":["NAHVERKEHRSONSTIGEZUEGE","SBAHNEN","BUSSE","SCHIFFE","UBAHN","STRASSENBAHN","ANRUFPFLICHTIGEVERKEHRE"]}],"zeitWunsch":{"reiseDatum":"2024-12-16T19:28:48.659+01:00","zeitPunktArt":"ABFAHRT"},"zielLocationId":"A\u003d1@O\u003dKöln Hbf@X\u003d6958730@Y\u003d50943029@U\u003d81@L\u003d8000207@B\u003d1@p\u003d1734031727@i\u003dU×008015458@"}},"reisendenProfil":{"reisende":[{"ermaessigungen":["KEINE_ERMAESSIGUNG KLASSENLOS"],"reisendenTyp":"ERWACHSENER"}]},"reservierungsKontingenteVorhanden":false}
|
17
docs/dumps/PCAPdroid_16_Dec_19_43_07_specialrouting.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_43_07_specialrouting.txt
Normal file
File diff suppressed because one or more lines are too long
18
docs/dumps/PCAPdroid_16_Dec_19_46_29_bahncard.txt
Normal file
18
docs/dumps/PCAPdroid_16_Dec_19_46_29_bahncard.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
POST /mob/angebote/fahrplan HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
x-feature-reiseketten-enabled: false
|
||||
X-Correlation-ID: b8d93b08-71ac-4cc2-836e-cd2683e34478_64466773-556f-4aa8-b128-0948c4d60887
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: c752e01a-e03e-42a5-bcd3-26b1cc2574b3
|
||||
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
Content-Length: 783
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
||||
{"autonomeReservierung":false,"einstiegsTypList":["STANDARD"],"klasse":"KLASSE_2","reiseHin":{"wunsch":{"abgangsLocationId":"A\u003d1@O\u003dKöln Hbf@X\u003d6958730@Y\u003d50943029@U\u003d81@L\u003d8000207@B\u003d1@p\u003d1734031727@i\u003dU×008015458@","minUmstiegsdauer":20,"verkehrsmittel":["NAHVERKEHRSONSTIGEZUEGE","SBAHNEN","BUSSE","SCHIFFE","UBAHN","STRASSENBAHN","ANRUFPFLICHTIGEVERKEHRE"],"zeitWunsch":{"reiseDatum":"2024-12-16T19:45:47.459239+01:00","zeitPunktArt":"ABFAHRT"},"zielLocationId":"A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@U\u003d81@L\u003d8011160@B\u003d1@p\u003d1734031727@i\u003dU×008065969@"}},"reisendenProfil":{"reisende":[{"ermaessigungen":["BAHNCARD25 KLASSE_2"],"reisendenTyp":"SENIOR"}]},"reservierungsKontingenteVorhanden":false}
|
17
docs/dumps/PCAPdroid_16_Dec_19_46_32_bahncard.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_46_32_bahncard.txt
Normal file
File diff suppressed because one or more lines are too long
15
docs/dumps/PCAPdroid_16_Dec_19_47_54_zuglauf.txt
Normal file
15
docs/dumps/PCAPdroid_16_Dec_19_47_54_zuglauf.txt
Normal file
|
@ -0,0 +1,15 @@
|
|||
GET /mob/zuglauf/2%7C%23VN%231%23ST%231734031727%23PI%231%23ZI%23178229%23TA%230%23DA%23161224%231S%238000207%231T%231926%23LS%238098160%23LT%2310012%23PU%2381%23RT%231%23CA%23ICE%23ZE%23947%23ZB%23ICE%20%20947%23PC%230%23FR%238000207%23FT%231926%23TO%238098160%23TT%2310012%23 HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.zuglauf.v2+json
|
||||
Content-Type: application/x.db.vendo.mob.zuglauf.v2+json
|
||||
X-Correlation-ID: b8d93b08-71ac-4cc2-836e-cd2683e34478_64466773-556f-4aa8-b128-0948c4d60887
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: 57c6969b-e937-4591-accf-0850e7b72278
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
17
docs/dumps/PCAPdroid_16_Dec_19_47_58_zuglauf.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_47_58_zuglauf.txt
Normal file
File diff suppressed because one or more lines are too long
15
docs/dumps/PCAPdroid_16_Dec_19_48_15_coachseq.txt
Normal file
15
docs/dumps/PCAPdroid_16_Dec_19_48_15_coachseq.txt
Normal file
|
@ -0,0 +1,15 @@
|
|||
GET /mob/zuglaeufe/ICE_947/halte/by-abfahrt/8000207_2024-12-16T19:26:00+01:00/wagenreihung HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.wagenreihung.v3+json
|
||||
Content-Type: application/x.db.vendo.mob.wagenreihung.v3+json
|
||||
X-Correlation-ID: b8d93b08-71ac-4cc2-836e-cd2683e34478_64466773-556f-4aa8-b128-0948c4d60887
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: 4ce19a5d-9da2-480d-a278-543fade2f485
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
15
docs/dumps/PCAPdroid_16_Dec_19_48_25_coachseq.txt
Normal file
15
docs/dumps/PCAPdroid_16_Dec_19_48_25_coachseq.txt
Normal file
|
@ -0,0 +1,15 @@
|
|||
GET /mob/zuglaeufe/ICE_947/halte/by-abfahrt/8000207_2024-12-16T19:26:00+01:00/wagenreihung HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.wagenreihung.v3+json
|
||||
Content-Type: application/x.db.vendo.mob.wagenreihung.v3+json
|
||||
X-Correlation-ID: b8d93b08-71ac-4cc2-836e-cd2683e34478_64466773-556f-4aa8-b128-0948c4d60887
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: 4ce19a5d-9da2-480d-a278-543fade2f485
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
17
docs/dumps/PCAPdroid_16_Dec_19_49_08_coachseq.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_19_49_08_coachseq.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
HTTP/1.1 200 OK
|
||||
Date: Mon, 16 Dec 2024 18:48:06 GMT
|
||||
Content-Type: application/x.db.vendo.mob.wagenreihung.v3+json
|
||||
Content-Length: 4538
|
||||
Connection: keep-alive
|
||||
server-timing: intid;desc=8f64b8dc41cc78fe
|
||||
Server-Timing: intid;desc=8f64b8dc41cc78fe
|
||||
Server-Timing: intid;desc=8f64b8dc41cc78fe
|
||||
x-correlation-id: b8d93b08-71ac-4cc2-836e-cd2683e34478_64466773-556f-4aa8-b128-0948c4d60887
|
||||
Strict-Transport-Security: max-age=16070400; includeSubDomains
|
||||
X-XSS-Protection: 0
|
||||
server-timing: intid;desc=8f64b8dc41cc78fe
|
||||
Content-Security-Policy: frame-ancestors 'none';
|
||||
X-Content-Type-Options: nosniff
|
||||
Set-Cookie: TS01be2125=01d513bcd1b784b8e477d277c1a5b018395e42eab8d1fee3cb97bf2b9ec80a7dd3763f495c187c41e167ab4fcf87709c96c07cb192; Path=/; Domain=.app.vendo.noncd.db.de; Secure; HTTPOnly
|
||||
|
||||
{"status":"MATCHES_SCHEDULE","gleis":{"start":{"position":0},"ende":{"position":487},"sektoren":[{"bezeichnung":"G","start":{"position":0},"ende":{"position":96.75},"gleisabschnittswuerfelPosition":58,"ersteKlasse":false},{"bezeichnung":"F","start":{"position":96.75},"ende":{"position":161.75},"gleisabschnittswuerfelPosition":135.5,"ersteKlasse":false},{"bezeichnung":"E","start":{"position":161.75},"ende":{"position":216.4},"gleisabschnittswuerfelPosition":188,"ersteKlasse":false},{"bezeichnung":"D","start":{"position":216.4},"ende":{"position":275.525},"gleisabschnittswuerfelPosition":244.8,"ersteKlasse":false},{"bezeichnung":"C","start":{"position":275.525},"ende":{"position":327.4},"gleisabschnittswuerfelPosition":306.25,"ersteKlasse":false},{"bezeichnung":"B","start":{"position":327.4},"ende":{"position":371.5},"gleisabschnittswuerfelPosition":348.55,"ersteKlasse":false},{"bezeichnung":"A","start":{"position":371.5},"ende":{"position":487},"gleisabschnittswuerfelPosition":394.45,"ersteKlasse":true}],"bezeichnung":"5"},"fahrzeuggruppen":[{"fahrzeuge":[{"fahrzeugtyp":{"fahrzeugkategorie":"POWERCAR","baureihe":"I4020","ersteKlasse":false,"zweiteKlasse":false},"status":"OPEN","orientierung":"FORWARDS","positionAmGleis":{"start":{"position":427.44},"ende":{"position":448},"sektor":"A"},"ausstattungsmerkmale":[]},{"fahrzeugtyp":{"fahrzeugkategorie":"PASSENGERCARRIAGE_FIRST_CLASS","baureihe":"Apmz","ersteKlasse":true,"zweiteKlasse":false},"status":"OPEN","orientierung":"BACKWARDS","positionAmGleis":{"start":{"position":401.04},"ende":{"position":427.44},"sektor":"A"},"ausstattungsmerkmale":[{"art":"SEATS_SEVERELY_DISABLED","status":"UNDEFINED"},{"art":"AIR_CONDITION","status":"UNDEFINED"},{"art":"ZONE_QUIET","status":"UNDEFINED"}],"ordnungsnummer":27},{"fahrzeugtyp":{"fahrzeugkategorie":"PASSENGERCARRIAGE_FIRST_CLASS","baureihe":"Apmz","ersteKlasse":true,"zweiteKlasse":false},"status":"OPEN","orientierung":"FORWARDS","positionAmGleis":{"start":{"position":374.64},"ende":{"position":401.04},"sektor":"A"},"ausstattungsmerkmale":[{"art":"SEATS_BAHN_COMFORT","status":"UNDEFINED"},{"art":"AIR_CONDITION","status":"UNDEFINED"}],"ordnungsnummer":26},{"fahrzeugtyp":{"fahrzeugkategorie":"DININGCAR","baureihe":"WRmbsz","ersteKlasse":false,"zweiteKlasse":false},"status":"OPEN","orientierung":"BACKWARDS","positionAmGleis":{"start":{"position":348.24},"ende":{"position":374.64},"sektor":"B"},"ausstattungsmerkmale":[{"art":"INFO","status":"UNDEFINED"},{"art":"TOILET_WHEELCHAIR","status":"UNDEFINED"},{"art":"WHEELCHAIR_SPACE","status":"AVAILABLE"}],"ordnungsnummer":25},{"fahrzeugtyp":{"fahrzeugkategorie":"PASSENGERCARRIAGE_ECONOMY_CLASS","baureihe":"Bpmbz","ersteKlasse":false,"zweiteKlasse":true},"status":"OPEN","orientierung":"BACKWARDS","positionAmGleis":{"start":{"position":321.84},"ende":{"position":348.24},"sektor":"B"},"ausstattungsmerkmale":[{"art":"SEATS_SEVERELY_DISABLED","status":"UNDEFINED"},{"art":"ZONE_FAMILY","status":"UNDEFINED"},{"art":"CABIN_INFANT","status":"UNDEFINED"},{"art":"AIR_CONDITION","status":"UNDEFINED"},{"art":"TOILET_WHEELCHAIR","status":"UNDEFINED"},{"art":"WHEELCHAIR_SPACE","status":"AVAILABLE"}],"ordnungsnummer":24},{"fahrzeugtyp":{"fahrzeugkategorie":"PASSENGERCARRIAGE_ECONOMY_CLASS","baureihe":"Bpmz","ersteKlasse":false,"zweiteKlasse":true},"status":"OPEN","orientierung":"BACKWARDS","positionAmGleis":{"start":{"position":295.44},"ende":{"position":321.84},"sektor":"C"},"ausstattungsmerkmale":[{"art":"SEATS_BAHN_COMFORT","status":"UNDEFINED"},{"art":"AIR_CONDITION","status":"UNDEFINED"}],"ordnungsnummer":23},{"fahrzeugtyp":{"fahrzeugkategorie":"PASSENGERCARRIAGE_ECONOMY_CLASS","baureihe":"Bpmz","ersteKlasse":false,"zweiteKlasse":true},"status":"OPEN","orientierung":"BACKWARDS","positionAmGleis":{"start":{"position":269.04},"ende":{"position":295.44},"sektor":"C"},"ausstattungsmerkmale":[{"art":"AIR_CONDITION","status":"UNDEFINED"},{"art":"ZONE_QUIET","status":"UNDEFINED"}],"ordnungsnummer":22},{"fahrzeugtyp":{"fahrzeugkategorie":"CONTROLCAR_ECONOMY_CLASS","baureihe":"Bpmzf","ersteKlasse":false,"zweiteKlasse":true},"status":"OPEN","orientierung":"FORWARDS","positionAmGleis":{"start":{"position":242.64},"ende":{"position":269.04},"sektor":"D"},"ausstattungsmerkmale":[{"art":"AIR_CONDITION","status":"UNDEFINED"}],"ordnungsnummer":21}],"fahrtreferenz":{"typ":"HIGH_SPEED_TRAIN","linie":"","ziel":{"bezeichnung":"Berlin Hbf"},"gattung":"ICE","fahrtnummer":947},"bezeichnung":"ICE0225"}],"fahrtrichtung":"RECHTS","gleisSoll":"4","gleisVorschau":"5"}
|
17
docs/dumps/PCAPdroid_16_Dec_20_18_17_reservierung.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_20_18_17_reservierung.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
POST /mob/angebote/recon/autonomereservierung HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
X-Correlation-ID: b8d93b08-71ac-4cc2-836e-cd2683e34478_64466773-556f-4aa8-b128-0948c4d60887
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: c88c72b9-3515-4acf-b052-3e71578c9461
|
||||
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
Content-Length: 2276
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
||||
{"einstiegsTypList":["STANDARD"],"klasse":"KLASSE_2","reisendenProfil":{"reisende":[{"ermaessigungen":["BAHNCARD25 KLASSE_2"],"reisendenTyp":"SENIOR"}]},"reservierungsKontingenteVorhanden":false,"verbindungHin":{"kontext":"¶HKI¶T$A\u003d1@O\u003dKöln Hbf@X\u003d6958730@Y\u003d50943029@L\u003d8000207@a\u003d128@$A\u003d1@O\u003dFrankfurt(Main)Hbf@X\u003d8663785@Y\u003d50107149@L\u003d8000105@a\u003d128@$202412162342$202412170220$IC 60403$$1$$$$$$§T$A\u003d1@O\u003dFrankfurt(Main)Hbf@X\u003d8663785@Y\u003d50107149@L\u003d8000105@a\u003d128@$A\u003d1@O\u003dErfurt Hbf@X\u003d11037989@Y\u003d50972352@L\u003d8010101@a\u003d128@$202412170249$202412170518$ICE 698$$1$$$$$$§T$A\u003d1@O\u003dErfurt Hbf@X\u003d11037989@Y\u003d50972352@L\u003d8010101@a\u003d128@$A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@L\u003d8098160@a\u003d128@$202412170527$202412170729$ICE 1606$$1$$$$$$¶KC¶#VE#2#CF#100#CA#0#CM#0#SICT#0#AM#81#AM2#0#RT#7#¶KCC¶I1ZFIzEjRVJHIzQ1MzE1I0hJTiMwI0VDSyM1Mzk5OTV8NTM5OTgyfDU0MDQ0OXw1NDA0NDl8MHwwfDU2NXw1Mzk5NTN8M3wwfDh8MHwwfC0yMTQ3NDgzNjQ4I0dBTSMxNjEyMjQyMzQyIwpaI1ZOIzEjU1QjMTczNDAzMTcyNyNQSSMxI1pJIzE3MzU2MyNUQSMwI0RBIzE2MTIyNCMxUyM4NDAwMDU4IzFUIzIwMTUjTFMjODUwMzAwMCNMVCMxMDgwNSNQVSM4MSNSVCMxI0NBI0lDI1pFIzYwNDAzI1pCI0lDIDYwNDAzI1BDIzEjRlIjODAwMDIwNyNGVCMyMzQyI1RPIzgwMDAxMDUjVFQjMTAyMjAjClojVk4jMSNTVCMxNzM0MDMxNzI3I1BJIzEjWkkjMTc3MTQ3I1RBIzAjREEjMTYxMjI0IzFTIzgwMDAyNjEjMVQjMjE1MSNMUyM4MDk4MTYwI0xUIzEwNzU1I1BVIzgxI1JUIzEjQ0EjSUNFI1pFIzY5OCNaQiNJQ0UgIDY5OCNQQyMwI0ZSIzgwMDAxMDUjRlQjMTAyNDkjVE8jODAxMDEwMSNUVCMxMDUxOCMKWiNWTiMxI1NUIzE3MzQwMzE3MjcjUEkjMSNaSSMxNzQ1MzgjVEEjMCNEQSMxNzEyMjQjMVMjODAxMDEwMSMxVCM1MjcjTFMjODAwMjU1MyNMVCM5MzkjUFUjODEjUlQjMSNDQSNJQ0UjWkUjMTYwNiNaQiNJQ0UgMTYwNiNQQyMwI0ZSIzgwMTAxMDEjRlQjNTI3I1RPIzgwOTgxNjAjVFQjNzI5Iw\u003d\u003d¶KRCC¶#VE#1#¶SC¶1_H4sIAAAAAAACA32P7UrDMBiFb0Xyu443adM2hUDsyvBjaBEnivijrtms9GOm6bCUXod34g3sxkxbBoIi+ZNz3pPzvOnQXioUIDzzfGQh+aGNiMLZfTRjRiv5joIOlU2xQAG1hkuIArBQ1ego0dKECRAHE+yi0bzLisHEzKEAxtqMDafYQm9lu8i1WqLgqUO63Q2x+PYmMqGiSgd1cT03Yp/kzVgBxEb987jT/HU7FRtyKnfLaj3V5Flqkmccixt+dfjKy5Pzl4144C6jvmeDeOQUmGMDYWLFfSyW3AcAAp4IzZsdx57tgI094omMrw6fAD5g6lBfmEVqPX1wMW6RKPUnNpQqz45cbNsG7bABTMyh/g8wxtiFf8AuZS77Dd5KHVd5ayDG06qRo3VZNaqUbVg1ZVqjYJPk9TSIk7rOs1ofs3JdxYlKChPq+r7/Bvf6c1bwAQAA"}}
|
17
docs/dumps/PCAPdroid_16_Dec_20_18_20_reservierung.txt
Normal file
17
docs/dumps/PCAPdroid_16_Dec_20_18_20_reservierung.txt
Normal file
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,17 @@
|
|||
POST /mob/bahnhofstafel/abfahrt HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.bahnhofstafeln.v2+json
|
||||
X-Correlation-ID: b8d93b08-71ac-4cc2-836e-cd2683e34478_64466773-556f-4aa8-b128-0948c4d60887
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: 2b250673-6250-4bcd-8dc3-8cf83c0a7686
|
||||
Content-Type: application/x.db.vendo.mob.bahnhofstafeln.v2+json
|
||||
Content-Length: 376
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
||||
{"anfragezeit":"20:35","datum":"2024-12-16","ursprungsBahnhofId":"A\u003d1@O\u003dKöln Hbf@X\u003d6958730@Y\u003d50943029@U\u003d81@L\u003d8000207@i\u003dU×008015458@","verkehrsmittel":["HOCHGESCHWINDIGKEITSZUEGE","INTERCITYUNDEUROCITYZUEGE","INTERREGIOUNDSCHNELLZUEGE","NAHVERKEHRSONSTIGEZUEGE","SBAHNEN","BUSSE","SCHIFFE","UBAHN","STRASSENBAHN","ANRUFPFLICHTIGEVERKEHRE"]}
|
File diff suppressed because one or more lines are too long
17
docs/dumps/PCAPdroid_18_Dec_22_39_50_departures_ondemand.txt
Normal file
17
docs/dumps/PCAPdroid_18_Dec_22_39_50_departures_ondemand.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
HTTP/1.1 200 OK
|
||||
Date: Wed, 18 Dec 2024 21:38:58 GMT
|
||||
Content-Type: application/x.db.vendo.mob.bahnhofstafeln.v2+json
|
||||
Content-Length: 590
|
||||
Connection: keep-alive
|
||||
server-timing: intid;desc=bf069d97495f8dad
|
||||
Server-Timing: intid;desc=bf069d97495f8dad
|
||||
Server-Timing: intid;desc=bf069d97495f8dad
|
||||
x-correlation-id: faddeefb-53f2-41a9-9879-e1c4edd7845f_5a24d491-8e2e-4cd8-85b5-ddd1d7d322c8
|
||||
Strict-Transport-Security: max-age=16070400; includeSubDomains
|
||||
X-XSS-Protection: 0
|
||||
server-timing: intid;desc=bf069d97495f8dad
|
||||
Content-Security-Policy: frame-ancestors 'none';
|
||||
X-Content-Type-Options: nosniff
|
||||
Set-Cookie: TS01be2125=01d513bcd1e76575d2a4e5c74d05675d588d2a35d6e7b8b0e5cad991641ebd4a05f4c1b051774f52d1a60eac0d3365250488a0be85; Path=/; Domain=.app.vendo.noncd.db.de; Secure; HTTPOnly
|
||||
|
||||
{"bahnhofstafelAbfahrtPositionen":[{"zuglaufId":"2|#VN#1#ST#1734378327#PI#1#ZI#514236#TA#0#DA#181224#1S#541958#1T#1944#LS#548569#LT#2108#PU#81#RT#1#CA#rfb#ZE#8175#ZB#RUF 8175#PC#9#FR#541958#FT#1944#TO#548569#TT#2108#","kurztext":"RUF","mitteltext":"RUF 8175","abfrageOrt":{"name":"Penzing Ortsmitte, Aidenbach","locationId":"A=1@O=Penzing Ortsmitte, Aidenbach@X=13063430@Y=48562182@U=81@L=548572@","evaNr":"548572"},"richtung":"Köching Abzw. Haidenburg Wartehäuschen, Aidenbach","echtzeitNotizen":[],"abgangsDatum":"2024-12-18T21:03:00+01:00","produktGattung":"ANRUFPFLICHTIGEVERKEHRE"}]}
|
17
docs/dumps/PCAPdroid_18_Dec_22_42_29_departures_ferry.txt
Normal file
17
docs/dumps/PCAPdroid_18_Dec_22_42_29_departures_ferry.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
HTTP/1.1 200 OK
|
||||
Date: Wed, 18 Dec 2024 21:42:04 GMT
|
||||
Content-Type: application/x.db.vendo.mob.bahnhofstafeln.v2+json
|
||||
Content-Length: 2498
|
||||
Connection: keep-alive
|
||||
server-timing: intid;desc=4e6f4ce0b26b14cb
|
||||
Server-Timing: intid;desc=4e6f4ce0b26b14cb
|
||||
Server-Timing: intid;desc=4e6f4ce0b26b14cb
|
||||
x-correlation-id: faddeefb-53f2-41a9-9879-e1c4edd7845f_5a24d491-8e2e-4cd8-85b5-ddd1d7d322c8
|
||||
Strict-Transport-Security: max-age=16070400; includeSubDomains
|
||||
X-XSS-Protection: 0
|
||||
server-timing: intid;desc=4e6f4ce0b26b14cb
|
||||
Content-Security-Policy: frame-ancestors 'none';
|
||||
X-Content-Type-Options: nosniff
|
||||
Set-Cookie: TS01be2125=01d513bcd1b5c4411e233c8956b40fa37e9050070c341e1782b80282d570eb1ed0cb63a2fab4db66435d0bad351471a04f949cac0d; Path=/; Domain=.app.vendo.noncd.db.de; Secure; HTTPOnly
|
||||
|
||||
{"bahnhofstafelAbfahrtPositionen":[{"zuglaufId":"2|#VN#1#ST#1734378327#PI#1#ZI#989203#TA#17#DA#181224#1S#368702#1T#930#LS#936350#LT#935#PU#81#RT#1#CA#FAE#ZE#FÄ1#ZB#Fähre #PC#6#FR#368702#FT#930#TO#936350#TT#935#","kurztext":"Fähre","mitteltext":"Fähre","abfrageOrt":{"name":"Hohe Düne Fähre, Rostock","locationId":"A=1@O=Hohe Düne Fähre, Rostock@X=12097404@Y=54176710@U=81@L=368702@","evaNr":"368702"},"richtung":"Warnemünde","echtzeitNotizen":[],"abgangsDatum":"2024-12-18T09:30:00+01:00","produktGattung":"SCHIFF"},{"zuglaufId":"2|#VN#1#ST#1734378327#PI#1#ZI#417851#TA#1#DA#181224#1S#936250#1T#947#LS#939255#LT#1021#PU#81#RT#1#CA#Bus#ZE#18#ZB#Bus 18#PC#5#FR#936250#FT#947#TO#939255#TT#1021#","kurztext":"Bus","mitteltext":"Bus 18","abfrageOrt":{"name":"Hohe Düne Fähre, Rostock","locationId":"A=1@O=Hohe Düne Fähre, Rostock@X=12098627@Y=54176485@U=81@L=936250@","evaNr":"936250"},"richtung":"Dierkower Kreuz","echtzeitNotizen":[],"abgangsDatum":"2024-12-18T09:47:00+01:00","produktGattung":"BUS"},{"zuglaufId":"2|#VN#1#ST#1734378327#PI#1#ZI#989203#TA#18#DA#181224#1S#368702#1T#950#LS#936350#LT#955#PU#81#RT#1#CA#FAE#ZE#FÄ1#ZB#Fähre #PC#6#FR#368702#FT#950#TO#936350#TT#955#","kurztext":"Fähre","mitteltext":"Fähre","abfrageOrt":{"name":"Hohe Düne Fähre, Rostock","locationId":"A=1@O=Hohe Düne Fähre, Rostock@X=12097404@Y=54176710@U=81@L=368702@","evaNr":"368702"},"richtung":"Warnemünde","echtzeitNotizen":[],"abgangsDatum":"2024-12-18T09:50:00+01:00","produktGattung":"SCHIFF"},{"zuglaufId":"2|#VN#1#ST#1734378327#PI#1#ZI#420342#TA#0#DA#181224#1S#936250#1T#1007#LS#842059#LT#1018#PU#81#RT#1#CA#Bus#ZE#17#ZB#Bus 17#PC#5#FR#936250#FT#1007#TO#842059#TT#1018#","kurztext":"Bus","mitteltext":"Bus 17","abfrageOrt":{"name":"Hohe Düne Fähre, Rostock","locationId":"A=1@O=Hohe Düne Fähre, Rostock@X=12098627@Y=54176485@U=81@L=936250@","evaNr":"936250"},"richtung":"Markgrafenheide Ost","echtzeitNotizen":[],"abgangsDatum":"2024-12-18T10:07:00+01:00","produktGattung":"BUS"},{"zuglaufId":"2|#VN#1#ST#1734378327#PI#1#ZI#989203#TA#19#DA#181224#1S#368702#1T#1010#LS#936350#LT#1015#PU#81#RT#1#CA#FAE#ZE#FÄ1#ZB#Fähre #PC#6#FR#368702#FT#1010#TO#936350#TT#1015#","kurztext":"Fähre","mitteltext":"Fähre","abfrageOrt":{"name":"Hohe Düne Fähre, Rostock","locationId":"A=1@O=Hohe Düne Fähre, Rostock@X=12097404@Y=54176710@U=81@L=368702@","evaNr":"368702"},"richtung":"Warnemünde","echtzeitNotizen":[],"abgangsDatum":"2024-12-18T10:10:00+01:00","produktGattung":"SCHIFF"}]}
|
|
@ -0,0 +1,17 @@
|
|||
HTTP/1.1 200 OK
|
||||
Date: Wed, 18 Dec 2024 21:43:02 GMT
|
||||
Content-Type: application/x.db.vendo.mob.bahnhofstafeln.v2+json
|
||||
Content-Length: 1030
|
||||
Connection: keep-alive
|
||||
server-timing: intid;desc=3007cc29ce998300
|
||||
Server-Timing: intid;desc=3007cc29ce998300
|
||||
Server-Timing: intid;desc=3007cc29ce998300
|
||||
x-correlation-id: faddeefb-53f2-41a9-9879-e1c4edd7845f_5a24d491-8e2e-4cd8-85b5-ddd1d7d322c8
|
||||
Strict-Transport-Security: max-age=16070400; includeSubDomains
|
||||
X-XSS-Protection: 0
|
||||
server-timing: intid;desc=3007cc29ce998300
|
||||
Content-Security-Policy: frame-ancestors 'none';
|
||||
X-Content-Type-Options: nosniff
|
||||
Set-Cookie: TS01be2125=01d513bcd1778d8a6954cb4376d998b52e8b4b9edfa2de0eed859c823593d6a880ffc23b5ab8f107642d7ccfb36b5be2d8c9d6632f; Path=/; Domain=.app.vendo.noncd.db.de; Secure; HTTPOnly
|
||||
|
||||
{"bahnhofstafelAbfahrtPositionen":[{"gleis":"2","zuglaufId":"2|#VN#1#ST#1734378327#PI#1#ZI#338816#TA#0#DA#181224#1S#8000096#1T#712#LS#8100003#LT#1352#PU#81#RT#1#CA#DPF#ZE#963#ZB#WB 963#PC#2#FR#8000096#FT#712#TO#8100003#TT#1352#","kurztext":"WB","mitteltext":"WB 963","abfrageOrt":{"name":"Stuttgart Hbf","locationId":"A=1@O=Stuttgart Hbf@X=9181636@Y=48784081@U=81@L=8000096@i=U×008029034@","evaNr":"8000096","stationId":"6071"},"echtzeitNotizen":[],"abgangsDatum":"2024-12-18T07:12:00+01:00","produktGattung":"IR"},{"gleis":"8","zuglaufId":"2|#VN#1#ST#1734378327#PI#1#ZI#203510#TA#0#DA#181224#1S#8000096#1T#717#LS#8098160#LT#1315#PU#81#RT#1#CA#DPF#ZE#1240#ZB#FLX 1240#PC#2#FR#8000096#FT#717#TO#8098160#TT#1315#","kurztext":"FLX","mitteltext":"FLX 1240","abfrageOrt":{"name":"Stuttgart Hbf","locationId":"A=1@O=Stuttgart Hbf@X=9181636@Y=48784081@U=81@L=8000096@i=U×008029034@","evaNr":"8000096","stationId":"6071"},"richtung":"Berlin Hbf","echtzeitNotizen":[],"abgangsDatum":"2024-12-18T07:17:00+01:00","produktGattung":"IR"}]}
|
17
docs/dumps/PCAPdroid_18_Dec_23_19_54_locsearch_addr.txt
Normal file
17
docs/dumps/PCAPdroid_18_Dec_23_19_54_locsearch_addr.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
POST /mob/location/search HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.location.v3+json
|
||||
X-Correlation-ID: 0079a47f-6208-4507-9d9a-0e02802ef52c_5a24d491-8e2e-4cd8-85b5-ddd1d7d322c8
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: 205f5fd7-f96a-4e07-a47d-bb3edb8c443e
|
||||
Content-Type: application/x.db.vendo.mob.location.v3+json
|
||||
Content-Length: 52
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
||||
{"locationTypes":["ALL"],"searchTerm":"schillerstr"}
|
17
docs/dumps/PCAPdroid_18_Dec_23_19_57_locsearch_addr.txt
Normal file
17
docs/dumps/PCAPdroid_18_Dec_23_19_57_locsearch_addr.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
HTTP/1.1 200 OK
|
||||
Date: Wed, 18 Dec 2024 22:19:20 GMT
|
||||
Content-Type: application/x.db.vendo.mob.location.v3+json
|
||||
Content-Length: 2877
|
||||
Connection: keep-alive
|
||||
server-timing: intid;desc=8a52db25dd785bfe
|
||||
Server-Timing: intid;desc=8a52db25dd785bfe
|
||||
Server-Timing: intid;desc=8a52db25dd785bfe
|
||||
x-correlation-id: 0079a47f-6208-4507-9d9a-0e02802ef52c_5a24d491-8e2e-4cd8-85b5-ddd1d7d322c8
|
||||
Strict-Transport-Security: max-age=16070400; includeSubDomains
|
||||
X-XSS-Protection: 0
|
||||
server-timing: intid;desc=8a52db25dd785bfe
|
||||
Content-Security-Policy: frame-ancestors 'none';
|
||||
X-Content-Type-Options: nosniff
|
||||
Set-Cookie: TS01be2125=01d513bcd1de3bcb4b755a3859cad31611f802372756db9780547d536b115fb805b4cb7fe4f2aefad293244318a0660f18cf234872; Path=/; Domain=.app.vendo.noncd.db.de; Secure; HTTPOnly
|
||||
|
||||
[{"name":"Schillerstr., Ofterdingen","locationId":"A=1@O=Schillerstr., Ofterdingen@X=9030635@Y=48420917@U=81@L=750308@B=1@p=1734378327@","evaNr":"750308","coordinates":{"latitude":48.420918,"longitude":9.030635},"weight":995,"products":["BUSSE","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"},{"name":"Schillerstr., Lutherstadt Wittenberg","locationId":"A=1@O=Schillerstr., Lutherstadt Wittenberg@X=12660057@Y=51875984@U=81@L=963037@B=1@p=1734378327@","evaNr":"963037","coordinates":{"latitude":51.875984,"longitude":12.660057},"weight":943,"products":["BUSSE","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"},{"name":"Schillerstr., Geithain","locationId":"A=1@O=Schillerstr., Geithain@X=12691420@Y=51058568@U=81@L=200268@B=1@p=1734378327@","evaNr":"200268","coordinates":{"latitude":51.058567,"longitude":12.69142},"weight":941,"products":["BUSSE"],"locationType":"ST"},{"name":"Schillerstr., Zwickau","locationId":"A=1@O=Schillerstr., Zwickau@X=12493001@Y=50715018@U=81@L=983776@B=1@p=1734378327@","evaNr":"983776","coordinates":{"latitude":50.71502,"longitude":12.493001},"weight":941,"products":["NAHVERKEHRSONSTIGEZUEGE","BUSSE","STRASSENBAHN","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"},{"name":"Schillerstr., Überherrn","locationId":"A=1@O=Schillerstr., Überherrn@X=6705494@Y=49239322@U=81@L=835123@B=1@p=1734378327@","evaNr":"835123","coordinates":{"latitude":49.239323,"longitude":6.705494},"weight":941,"products":["BUSSE"],"locationType":"ST"},{"name":"Schillerstr./Forstweg, Brieselang","locationId":"A=1@O=Schillerstr./Forstweg, Brieselang@X=12988073@Y=52583830@U=81@L=734788@B=1@p=1734378327@","evaNr":"734788","coordinates":{"latitude":52.58383,"longitude":12.988073},"weight":941,"products":["BUSSE"],"locationType":"ST"},{"name":"Schillerstr., Berlin","locationId":"A=1@O=Schillerstr., Berlin@X=13410584@Y=52591948@U=81@L=732759@B=1@p=1734378327@","evaNr":"732759","coordinates":{"latitude":52.59195,"longitude":13.410584},"weight":412,"products":["STRASSENBAHN"],"locationType":"ST"},{"name":"Schillerstr., Schöneiche b. Berlin","locationId":"A=1@O=Schillerstr., Schöneiche b. Berlin@X=13709386@Y=52476958@U=81@L=738128@B=1@p=1734378327@","evaNr":"738128","coordinates":{"latitude":52.47696,"longitude":13.709386},"weight":412,"products":["STRASSENBAHN"],"locationType":"ST"},{"name":"Schillerstr., Markdorf","locationId":"A=1@O=Schillerstr., Markdorf@X=9386033@Y=47727336@U=81@L=801101@B=1@p=1734378327@","evaNr":"801101","coordinates":{"latitude":47.727337,"longitude":9.386033},"weight":272,"products":["BUSSE","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"},{"name":"Schillerstr., Salzwedel","locationId":"A=1@O=Schillerstr., Salzwedel@X=11160072@Y=52850918@U=81@L=957938@B=1@p=1734378327@","evaNr":"957938","coordinates":{"latitude":52.850918,"longitude":11.160072},"weight":272,"products":["BUSSE","ANRUFPFLICHTIGEVERKEHRE"],"locationType":"ST"}]
|
|
@ -0,0 +1,18 @@
|
|||
POST /mob/angebote/fahrplan HTTP/1.1
|
||||
Accept: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
x-feature-reiseketten-enabled: false
|
||||
X-Correlation-ID: 84f2ab9b-d4a7-4e98-9a29-113783f0d431_5a24d491-8e2e-4cd8-85b5-ddd1d7d322c8
|
||||
X-Device-Os-Name: Android
|
||||
X-Device-Os-Version: 32
|
||||
X-Device-Model: Google Pixel 3a
|
||||
X-App-Version: 24.32.2
|
||||
Accept-Language: en,de
|
||||
X-INSTANA-ANDROID: a33cf0fb-5fd8-4c2f-befa-d4c1963cf3b7
|
||||
Content-Type: application/x.db.vendo.mob.verbindungssuche.v8+json
|
||||
Content-Length: 686
|
||||
Host: app.vendo.noncd.db.de
|
||||
Connection: Keep-Alive
|
||||
Accept-Encoding: gzip
|
||||
User-Agent: okhttp/4.12.0
|
||||
|
||||
{"autonomeReservierung":false,"einstiegsTypList":["STANDARD"],"klasse":"KLASSE_2","reiseHin":{"wunsch":{"abgangsLocationId":"A\u003d2@O\u003dBerlin - Bohnsdorf, Schillerstraße 10@H\u003d10@X\u003d13585128@Y\u003d52398257@U\u003d92@L\u003d980126874@B\u003d1@p\u003d1706613073@","verkehrsmittel":["ALL"],"zeitWunsch":{"reiseDatum":"2024-12-18T22:38:09.928078+01:00","zeitPunktArt":"ABFAHRT"},"zielLocationId":"A\u003d1@O\u003dBerlin Hbf@X\u003d13369549@Y\u003d52525589@U\u003d81@L\u003d8011160@B\u003d1@p\u003d1734031727@i\u003dU×008065969@"}},"reisendenProfil":{"reisende":[{"ermaessigungen":["BAHNCARD25 KLASSE_2"],"reisendenTyp":"SENIOR"}]},"reservierungsKontingenteVorhanden":false}
|
File diff suppressed because one or more lines are too long
15
docs/dumps/readme.md
Normal file
15
docs/dumps/readme.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Dumps from DB Navigator API app.vendo.noncd.db.de
|
||||
|
||||
In this directory, some intercepted traffic from DB Navigator. (Repo license does not apply to this directory.)
|
||||
|
||||
You can browse some responses of the bahn.de API and others in the [fixtures](https://github.com/public-transport/db-vendo-client/tree/main/test/fixtures) and [e2e fixtures](https://github.com/public-transport/db-vendo-client/tree/main/test/e2e/fixtures).
|
||||
|
||||
How to intercept DB Navigator traffic:
|
||||
|
||||
1. Download/extract Split APK
|
||||
2. Merge APK (e.g. using [APKEditor](https://github.com/REAndroid/APKEditor))
|
||||
3. decompile using apktool
|
||||
4. edit [res/xml/network_security_config.xml](https://developer.android.com/privacy-and-security/security-config) to allow user CAs not just in debug
|
||||
5. recompile using apktool, sign
|
||||
6. install on an Android
|
||||
7. intercept with a mitm decryption tool of your choice by installing CA cert into Android store (e.g. [PCAPdroid](https://github.com/emanuele-f/PCAPdroid) with [mitm addon](https://github.com/emanuele-f/PCAPdroid-mitm), no root needed)
|
|
@ -1,160 +0,0 @@
|
|||
# HAFAS `mgate.exe` protocol
|
||||
|
||||
The protocol of `mgate.exe` HAFAS endpoints is not openly (and freely) documented. The following documentation is based on general observations and reverse-engineering.
|
||||
|
||||
*Note:* There are also `rest.exe` (a.k.a. "open API", a.k.a. "REST API") endpoints. This documentation is *not* about them.
|
||||
|
||||
## date & time format
|
||||
|
||||
Dates are encoded as `YYYYMMDD`, time strings as `HHMMSS`. These are in the timezone configured on the HAFAS/server side, *per endpoint*.
|
||||
|
||||
Whenever HAFAS returns a time string that exceeds the day the response describes, it will add a "day offset". As an example, when you query departures at `2019-12-12T23:50+01:00` for the next 30 minutes, it will encode the departure at `2019-12-13T00:13+01:00` as `20191212` & `01001300`.
|
||||
|
||||
For working code, check out [`parseDateTime()`](../parse/date-time.js).
|
||||
|
||||
## coordinate format
|
||||
|
||||
All endpoints I've seen so far use [WGS84](http://wiki.gis.com/wiki/index.php/WGS84). Values are multiplied by `10^6` though, so you would encode `{latitude: 1.23, longitude: -2.34}` as `{Y: 1230000: X: -2340000}`. There's an optional parameter `z` with the elevation.
|
||||
|
||||
For working code, check out [`formatAddress()`](../format/address.js).
|
||||
|
||||
## querying the API
|
||||
|
||||
In many aspects, the API looks and feels like [RPCs](https://en.wikipedia.org/wiki/Remote_procedure_call). You must send queries via HTTP `POST`, with the minimal JSON body looking like this:
|
||||
|
||||
```js
|
||||
{
|
||||
"auth": {
|
||||
"type": "AID",
|
||||
"aid": "…" // endpoint-specific authentication token, e.g. `1Rxs112shyHLatUX4fofnmdxK`
|
||||
},
|
||||
"ver": "…", // endpoint-specific string, e.g. `1.15`
|
||||
"ext": "…", // endpoint-specific string, e.g. `BVG.1`
|
||||
"client": {
|
||||
"type": "IPA", // might also be `IPH` for "iPhone" or `WEB` for "web client"
|
||||
"id": "…", // endpoint-specific string, e.g. `BVG`
|
||||
"name": "…", // endpoint-specific string, e.g. `FahrInfo`
|
||||
"v": "…" // endpoint-specific string, e.g. `4070700`
|
||||
},
|
||||
"lang": "…", // language, sometimes 2-digit (e.g. `de`), sometimes 3-digit (e.g. `deu`)
|
||||
"svcReqL": [
|
||||
{
|
||||
"meth": "…", // name of the API call, supported values depend on the endpoint
|
||||
"req": {
|
||||
// actual request parameters…
|
||||
}
|
||||
// some endpoints also require this:
|
||||
"cfg": {
|
||||
"cfgGrpL": [],
|
||||
"cfgHash": "…" // endpoint-specific string
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- The data in `client` must be correct, otherwise HAFAS will reject your request.
|
||||
- HAFAS will return slightly different response formats (and slightly different levels of detail) for different `ver`, `ext` and `client.v` values.
|
||||
- All endpoints known support JSON & UTF-8, so make sure to send `Accept: application/json` & `Accept-Charset: utf-8` headers.
|
||||
- Most endpoints support at least GZIP compression, so make sure to send a `Accept-Encoding: gzip` header.
|
||||
|
||||
For working code, check out [`request()`](lib/request.js).
|
||||
|
||||
## Authentication
|
||||
|
||||
There are three known types of authentication used among `mgate.exe` endpoints.
|
||||
|
||||
For working code, check out [`hafas-client`'s `request()`](lib/request.js), [`public-transport-enabler`'s Java implementation](https://github.com/schildbach/public-transport-enabler/blob/69614c87af627e2feafc576882f2ccccdbf4b7e6/src/de/schildbach/pte/AbstractHafasClientInterfaceProvider.java#L845-L860), [`TripKit`'s Swift implementation](https://github.com/alexander-albers/tripkit/blob/724b6cd8c258c9c61e7443c81e914618b79393cb/TripKit/AbstractHafasClientInterfaceProvider.swift#L1473-L1495) or [`marudor.de`'s TypeScript implementation](https://github.com/marudor/BahnhofsAbfahrten/blob/cf64d53c6902981ec529d3952253b2c83bff9221/src/server/HAFAS/profiles.ts#L30-L54).
|
||||
|
||||
### unprotected endpoints
|
||||
|
||||
You can just query these, as long as you send a formally correct request.
|
||||
|
||||
### endpoints using the `checksum` query parameter
|
||||
|
||||
`checksum` is a [message authentication code](https://en.wikipedia.org/wiki/Message_authentication_code): You can compute it by [hashing](https://en.wikipedia.org/wiki/Hash_function) the request body and a secret *salt*.
|
||||
|
||||
This secret can be read from the config file inside the accompanying client app. There is no guide for this yet, so please [open an issue](https://github.com/public-transport/hafas-client/issues/new).
|
||||
|
||||
### endpoints using the `mic` & `mac` query parameters
|
||||
|
||||
`mic` is a [message integrity code](https://en.wikipedia.org/wiki/Message_authentication_code), the [hash](https://en.wikipedia.org/wiki/Hash_function) of the request body.
|
||||
|
||||
`mac` is a [message authentication code](https://en.wikipedia.org/wiki/Message_authentication_code), the hash of `mic` and a secret *salt*.
|
||||
|
||||
This secret can be read from the config file inside the accompanying client app. There is no guide for this yet, so please [open an issue](https://github.com/public-transport/hafas-client/issues/new).
|
||||
|
||||
## API responses
|
||||
|
||||
A minimal valid response looks like this:
|
||||
|
||||
```js
|
||||
{
|
||||
"ver": "…", // endpoint-specific string, e.g. `1.15`
|
||||
"lang": "…", // language
|
||||
"ext": "…", // endpoint-specific string, e.g. `BVG.1`
|
||||
"id": "…", // unique ID for each response?
|
||||
"svcResL": [
|
||||
{
|
||||
"meth": "StationBoard",
|
||||
"err": "OK",
|
||||
"res": {
|
||||
// result of the API call
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
For working code, check out [`request()`](lib/request.js).
|
||||
|
||||
### parse error
|
||||
|
||||
todo: generic server error
|
||||
|
||||
```js
|
||||
{
|
||||
"ver": "…", // endpoint-specific string, e.g. `1.15`
|
||||
"lang": "…", // language, sometimes 2-digit (e.g. `de`), sometimes 3-digit (e.g. `deu`)
|
||||
"err": "PARSE", // error code
|
||||
"errTxt": "…", // error message, not always present
|
||||
"svcResL": []
|
||||
}
|
||||
```
|
||||
|
||||
### authentication error
|
||||
|
||||
```js
|
||||
{
|
||||
"ver": "…", // endpoint-specific string, e.g. `1.15`
|
||||
"lang": "…", // language
|
||||
"ext": "…", // endpoint-specific string, e.g. `BVG.1`
|
||||
"err": "AUTH", // error code
|
||||
"errTxt": "…", // error message, not always present
|
||||
"svcResL": []
|
||||
}
|
||||
```
|
||||
|
||||
### API-call-specific error
|
||||
|
||||
```js
|
||||
{
|
||||
"ver": "…", // endpoint-specific string, e.g. `1.15`
|
||||
"lang": "…", // language
|
||||
"ext": "…", // endpoint-specific string, e.g. `BVG.1`
|
||||
"svcResL": [
|
||||
{
|
||||
"meth": "StationBoard",
|
||||
"err": "…", // error code, e.g. `H9300`
|
||||
"errTxt": "…", // error message, e.g. `Unknown arrival station`
|
||||
"res": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## more ressources
|
||||
|
||||
- [@Nakaner's `strecken.info` API docs](https://github.com/Nakaner/bahnstoerungen/tree/62a72b1e0f0255668500b438187ff65aef39242a/api-doc/db-strecken-info)
|
||||
- [unfinished HAFAS glossary](https://gist.github.com/derhuerst/74b703e2a0fc64e4a0fa8fbb1f3a61b4)
|
||||
- [various `mgate.exe` HTTP traffic recordings](https://gist.github.com/search?q=post+mgate.exe&ref=searchresults)
|
|
@ -1,51 +0,0 @@
|
|||
# `journeysFromTrip(tripId, previousStopover, to, [opt])`
|
||||
|
||||
`to` must be an [*Friendly Public Transport Format* (FPTF) `stop`](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md#stop) or [`station`](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md#station). See [`journeys()`](journeys.md) for details.
|
||||
|
||||
With `opt`, you can override the default options, which look like this:
|
||||
|
||||
```js
|
||||
{
|
||||
accessibility: 'none', // 'none', 'partial' or 'complete'
|
||||
stopovers: false, // return stations on the way?
|
||||
polylines: false, // return leg shapes?
|
||||
transferTime: 0, // minimum time for a single transfer in minutes
|
||||
tickets: false, // return tickets?
|
||||
remarks: true // parse & expose hints & warnings?
|
||||
}
|
||||
```
|
||||
|
||||
## Response
|
||||
|
||||
*Note:* The returned `departure` and `arrival` times include the current delay. The `departureDelay`/`arrivalDelay` fields express how much they differ from `plannedDeparture`/`plannedArrival`, respectively.
|
||||
|
||||
As an example, we're going to use the [*Deutsche Bahn* profile](../p/db):
|
||||
|
||||
```js
|
||||
import {createClient} from 'hafas-client'
|
||||
import {profile as dbProfile} from 'hafas-client/p/db/index.js'
|
||||
|
||||
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||
const client = createClient(dbProfile, userAgent)
|
||||
|
||||
const berlinSüdkreuz = '8011113'
|
||||
const münchenHbf = '8000261'
|
||||
const kölnHbf = '8000207'
|
||||
|
||||
// find any journey from Berlin Südkreuz to München Hbf
|
||||
const [journey] = await client.journeys(berlinSüdkreuz, münchenHbf, {results: 1, stopovers: true})
|
||||
// find the ICE leg
|
||||
const leg = journey.legs.find(l => l.line.product === 'nationalExpress')
|
||||
// find the stopover at the stop you've just passed
|
||||
const previousStopover = leg.stopovers.find(st => st.departure && new Date(st.departure) < Date.now())
|
||||
|
||||
// find journeys from the ICE train to Köln Hbf
|
||||
const {
|
||||
journeys,
|
||||
realtimeDataUpdatedAt,
|
||||
} = await client.journeysFromTrip(leg.id, previousStopover, kölnHbf)
|
||||
```
|
||||
|
||||
`journeys` is an array of [FPTF `journey`s](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md#journey), as documented in [`journeys()`](journeys.md).
|
||||
|
||||
`realtimeDataUpdatedAt` is a UNIX timestamp reflecting the latest moment when (at least some of) the response's realtime data have been updated.
|
|
@ -53,11 +53,11 @@ With `opt`, you can override the default options, which look like this:
|
|||
stopovers: false, // return stations on the way?
|
||||
transfers: -1, // Maximum nr of transfers. Default: Let HAFAS decide.
|
||||
transferTime: 0, // minimum time for a single transfer in minutes
|
||||
accessibility: 'none', // 'none', 'partial' or 'complete'
|
||||
accessibility: 'none', // not supported
|
||||
bike: false, // only bike-friendly journeys
|
||||
walkingSpeed: 'normal', // 'slow', 'normal', 'fast'
|
||||
walkingSpeed: 'normal', // not supported
|
||||
// Consider walking to nearby stations at the beginning of a journey?
|
||||
startWithWalking: true,
|
||||
startWithWalking: true, // always true (?)
|
||||
products: {
|
||||
// these entries may vary from profile to profile
|
||||
suburban: true,
|
||||
|
@ -65,15 +65,21 @@ With `opt`, you can override the default options, which look like this:
|
|||
tram: true,
|
||||
bus: true,
|
||||
ferry: true,
|
||||
express: true,
|
||||
nationalExpress: true,
|
||||
national: true,
|
||||
regional: true
|
||||
regionalExpress: true // this is actually FlixTrain and co.
|
||||
},
|
||||
tickets: false, // return tickets? only available with some profiles
|
||||
polylines: false, // return a shape for each leg?
|
||||
subStops: true, // parse & expose sub-stops of stations?
|
||||
entrances: true, // parse & expose entrances of stops/stations?
|
||||
tickets: false, // return tickets? only available for [refreshJourney](refresh-journey.md)
|
||||
polylines: false, // return a shape for each leg? only available for [refreshJourney](refresh-journey.md)
|
||||
subStops: true, // not supported
|
||||
entrances: true, // not supported
|
||||
remarks: true, // parse & expose hints & warnings?
|
||||
scheduledDays: false, // parse which days each journey is valid on
|
||||
scheduledDays: false, // returns a field `serviceDays` (instead of `scheduledDays` in hafas-client!) with a different, human-readable structure
|
||||
notOnlyFastRoutes: false, // if true, also show journeys that are mathematically non-optimal
|
||||
bestprice: false, // search for lowest prices across the entire day, returns list of journeys sorted by price
|
||||
firstClass: false, // first or second class for tickets
|
||||
loyaltyCard: null, // BahnCards etc., see below
|
||||
language: 'en', // language to get results in
|
||||
}
|
||||
```
|
||||
|
@ -82,17 +88,16 @@ With `opt`, you can override the default options, which look like this:
|
|||
|
||||
*Note:* As stated in the [*Friendly Public Transport Format* v2 draft spec](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md), the returned `departure` and `arrival` times include the current delay. The `departureDelay`/`arrivalDelay` fields express how much they differ from the schedule.
|
||||
|
||||
As an example, we're going to use the [VBB profile](../p/vbb):
|
||||
|
||||
```js
|
||||
import {createClient} 'hafas-client'
|
||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
||||
import {createClient} 'db-vendo-client'
|
||||
import {profile as dbProfile} from 'db-vendo-client/p/db/index.js'
|
||||
|
||||
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||
const client = createClient(vbbProfile, userAgent)
|
||||
const client = createClient(dbProfile, userAgent)
|
||||
|
||||
// Hauptbahnhof to Heinrich-Heine-Str.
|
||||
await client.journeys('900000003201', '900000100008', {
|
||||
// Frankfurt to Stuttgart
|
||||
await client.journeys('8000105', '8000096', {
|
||||
results: 1,
|
||||
stopovers: true
|
||||
})
|
||||
|
@ -101,7 +106,7 @@ await client.journeys('900000003201', '900000100008', {
|
|||
`journeys()` will resolve with an object with the following fields:
|
||||
- `journeys`
|
||||
- `earlierRef`/`laterRef` – pass them as `opt.earlierThan`/`opt.laterThan` into another `journeys()` call to retrieve the next "page" of journeys
|
||||
- `realtimeDataUpdatedAt` – a UNIX timestamp reflecting the latest moment when (at least some of) the response's realtime data have been updated
|
||||
- `realtimeDataUpdatedAt` – is currently not set in db-vendo-client, because the upstream APIs don't provide it.
|
||||
|
||||
This object might look like this:
|
||||
|
||||
|
@ -276,44 +281,6 @@ This object might look like this:
|
|||
realtimeDataUpdatedAt: 1531259400, // 2018-07-10T23:50:00+02
|
||||
}
|
||||
```
|
||||
|
||||
Some [profiles](../p) are able to parse the ticket information, if returned by the API. For example, if you pass `tickets: true` with the [VBB profile](../p/vbb), each `journey` will have a tickets array that looks like this:
|
||||
|
||||
```js
|
||||
[ {
|
||||
name: 'Berlin Tarifgebiet A-B: Einzelfahrausweis – Regeltarif',
|
||||
price: 2.8,
|
||||
tariff: 'Berlin',
|
||||
coverage: 'AB',
|
||||
variant: 'adult',
|
||||
amount: 1
|
||||
}, {
|
||||
name: 'Berlin Tarifgebiet A-B: Einzelfahrausweis – Ermäßigungstarif',
|
||||
price: 1.7,
|
||||
tariff: 'Berlin',
|
||||
coverage: 'AB',
|
||||
variant: 'reduced',
|
||||
amount: 1,
|
||||
reduced: true
|
||||
}, /* … */ {
|
||||
name: 'Berlin Tarifgebiet A-B: Tageskarte – Ermäßigungstarif',
|
||||
price: 4.7,
|
||||
tariff: 'Berlin',
|
||||
coverage: 'AB',
|
||||
variant: '1 day, reduced',
|
||||
amount: 1,
|
||||
reduced: true,
|
||||
fullDay: true
|
||||
}, /* … */ {
|
||||
name: 'Berlin Tarifgebiet A-B: 4-Fahrten-Karte – Regeltarif',
|
||||
price: 9,
|
||||
tariff: 'Berlin',
|
||||
coverage: 'AB',
|
||||
variant: '4x adult',
|
||||
amount: 4
|
||||
} ]
|
||||
```
|
||||
|
||||
If a journey leg has been cancelled, a `cancelled: true` will be added. Also, `departure`/`departureDelay`/`departurePlatform` and `arrival`/`arrivalDelay`/`arrivalPlatform` will be `null`.
|
||||
|
||||
To get more journeys earlier/later than the current set of results, pass `earlierRef`/`laterRef` into `opt.earlierThan`/`opt.laterThan`. For example, query *later* journeys as follows:
|
||||
|
@ -339,19 +306,16 @@ departure of last journey 2017-12-17T19:07:00+01:00
|
|||
departure of first (later) journey 2017-12-17T19:19:00+01:00
|
||||
```
|
||||
|
||||
If you pass `polylines: true`, each journey leg will have a `polyline` field. Refer to [the section in the `trip()` docs](trip.md#polyline-option) for details.
|
||||
|
||||
If you pass `scheduledDays: true`, each journey will have a `scheduledDays` object looking like this:
|
||||
## Using the `loyaltyCard` option
|
||||
|
||||
```js
|
||||
{
|
||||
'2018-01-01': true,
|
||||
'2018-01-02': false,
|
||||
// …
|
||||
'2018-10-12': true,
|
||||
'2018-10-13': true,
|
||||
// …
|
||||
'2019-01-02': false,
|
||||
'2019-01-03': false
|
||||
}
|
||||
import {data as loyaltyCards} from 'db-vendo-client/format/loyalty-cards.js' // see there for a list
|
||||
|
||||
hafas.journeys(from, to, {
|
||||
loyaltyCard: {type: data.BAHNCARD, discount: 25}
|
||||
})
|
||||
```
|
||||
|
||||
## The `routingMode` option
|
||||
|
||||
The `routingMode` option is not supported by db-vendo-client. The behavior will be the same as the [`HYBRID` mode of hafas-client](https://github.com/public-transport/hafas-client/blob/main/p/db/readme.md#using-the-routingmode-option), i.e. cancelled trains/infeasible journeys will also be contained for informational purpose.
|
|
@ -1,70 +0,0 @@
|
|||
# `lines([opt])`
|
||||
|
||||
**Fetches all lines known to the HAFAS endpoint**, e.g. warnings about disruptions, planned construction work, and general notices about the operating situation.
|
||||
|
||||
## Example
|
||||
|
||||
As an example, we're going to use the [SVV profile](../p/svv):
|
||||
|
||||
```js
|
||||
import {createClient} from 'hafas-client'
|
||||
import {profile as svvProfile} from 'hafas-client/p/svv/index.js'
|
||||
|
||||
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||
const client = createClient(svvProfile, userAgent)
|
||||
|
||||
const {
|
||||
lines,
|
||||
realtimeDataUpdatedAt,
|
||||
} = await client.lines('S1')
|
||||
```
|
||||
|
||||
`realtimeDataUpdatedAt` is a UNIX timestamp reflecting the latest moment when (at least some of) the response's realtime data have been updated.
|
||||
|
||||
`lines` may look like this:
|
||||
|
||||
```js
|
||||
[
|
||||
{
|
||||
"id": "obb-1-S1-V-j20-1",
|
||||
"type": "line",
|
||||
"name": "S1",
|
||||
"public": true,
|
||||
"mode": "train",
|
||||
"product": "bahn-s-bahn",
|
||||
"operator": {
|
||||
"type": "operator",
|
||||
"id": "montafonerbahn-ag",
|
||||
"name": "Montafonerbahn AG"
|
||||
},
|
||||
"directions": [
|
||||
"Bludenz Bahnhof",
|
||||
"Bregenz Hafen Bahnhof",
|
||||
"Lindau Hbf",
|
||||
"Bregenz Bahnhof",
|
||||
"Schruns Bahnhof",
|
||||
"Lochau Bahnhof"
|
||||
],
|
||||
},
|
||||
// …
|
||||
{
|
||||
"id": "svv-42-50-j20-2",
|
||||
"type": "line",
|
||||
"name": "S1",
|
||||
"public": true,
|
||||
"mode": "train",
|
||||
"product": "bahn-s-bahn",
|
||||
"operator": {
|
||||
"type": "operator",
|
||||
"id": "salzburg-ag-salzburger-lokalbahn",
|
||||
"name": "Salzburg AG - Salzburger Lokalbahn"
|
||||
},
|
||||
"directions": [
|
||||
"Lamprechtshausen Bahnhof",
|
||||
"Salzburg Hauptbahnhof",
|
||||
"Acharting S-Bahn",
|
||||
"Weitwörth-Nussdorf Bahnhof"
|
||||
],
|
||||
},
|
||||
]
|
||||
```
|
|
@ -6,28 +6,27 @@ With `opt`, you can override the default options, which look like this:
|
|||
|
||||
```js
|
||||
{
|
||||
fuzzy: true // find only exact matches?
|
||||
fuzzy: true // not supported
|
||||
, results: 5 // how many search results?
|
||||
, stops: true // return stops/stations?
|
||||
, addresses: true
|
||||
, poi: true // points of interest
|
||||
, subStops: true // parse & expose sub-stops of stations?
|
||||
, entrances: true // parse & expose entrances of stops/stations?
|
||||
, linesOfStops: false // parse & expose lines at each stop/station?
|
||||
, subStops: true // not supported
|
||||
, entrances: true // not supported
|
||||
, linesOfStops: false // not supported
|
||||
, language: 'en' // language to get results in
|
||||
}
|
||||
```
|
||||
|
||||
## Response
|
||||
|
||||
As an example, we're going to use the [VBB profile](../p/vbb):
|
||||
|
||||
```js
|
||||
import {createClient} from 'hafas-client'
|
||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
||||
import {createClient} from 'db-vendo-client'
|
||||
import {profile as dbnavProfile} from 'db-vendo-client/p/dbnav/index.js'
|
||||
|
||||
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||
const client = createClient(vbbProfile, userAgent)
|
||||
const client = createClient(dbnavProfile, userAgent)
|
||||
|
||||
await client.locations('Alexanderplatz', {results: 3})
|
||||
```
|
||||
|
|
|
@ -1,108 +0,0 @@
|
|||
# Migrating to `hafas-client@5`
|
||||
|
||||
## If you use Node `8` ("Carbon")…
|
||||
|
||||
…migrate to Node `10` ("Dubnium"), sorry. [Node `8` is out of maintenance now](https://nodejs.org/en/about/releases/). 83f43c6
|
||||
|
||||
## new fields for departure/arrival time & delays
|
||||
|
||||
An arrival/departure now looks like this:
|
||||
|
||||
```js
|
||||
{
|
||||
when: null, // realtime/prognosed
|
||||
plannedWhen: '2019-10-10T10:10+10:00',
|
||||
platform: '3', // realtime/prognosed
|
||||
plannedPlatform: '4'
|
||||
}
|
||||
```
|
||||
|
||||
A stopover/journey leg now will look like this:
|
||||
|
||||
```js
|
||||
{
|
||||
arrival: null, // realtime/prognosed
|
||||
plannedArrival: '2019-10-10T10:10+10:00',
|
||||
arrivalDelay: null,
|
||||
arrivalPlatform: '3', // realtime/prognosed
|
||||
plannedArrivalPlatform: '4',
|
||||
|
||||
departure: '2019-10-10T10:12+10:00', // realtime/prognosed
|
||||
plannedDeparture: '2019-10-10T10:10+10:00',
|
||||
departureDelay: 120, // seconds
|
||||
departurePlatform: null, // realtime/prognosed
|
||||
plannedDeparturePlatform: null
|
||||
}
|
||||
```
|
||||
|
||||
If the same stopover/journey leg is `cancelled: true`, it will look like this:
|
||||
|
||||
```js
|
||||
{
|
||||
arrival: null,
|
||||
prognosedArrival: null,
|
||||
plannedArrival: '2019-10-10T10:10+10:00',
|
||||
arrivalDelay: null,
|
||||
arrivalPlatform: null,
|
||||
prognosedArrivalPlatform: '3',
|
||||
plannedArrivalPlatform: '4',
|
||||
|
||||
departure: null,
|
||||
prognosedDeparture: '2019-10-10T10:12+10:00',
|
||||
plannedDeparture: '2019-10-10T10:10+10:00',
|
||||
departureDelay: 120, // seconds
|
||||
departurePlatform: null,
|
||||
prognosedDeparturePlatform: null,
|
||||
plannedDeparturePlatform: null
|
||||
}
|
||||
```
|
||||
|
||||
## If you use `journeys()`…
|
||||
|
||||
…with the `walkingSpeed` option and a custom profile, add `journeysWalkingSpeed` to your profile. 937583e
|
||||
…without the `results` option, but *expect* a certain number of results, you must pass `results` now. 0045587
|
||||
|
||||
## If you use `departures()`/`arrivals()` with the [BVG profile](../p/bvg)…
|
||||
|
||||
With the latest protocol version, the BVG endpoint doesn't support these options anymore:
|
||||
|
||||
- `stopovers` – Fetch & parse previous/next stopovers? Default: `false`
|
||||
- `includeRelatedStations` – Fepartures at related stations, e.g. those that belong together on the metro map? Default: `true`
|
||||
|
||||
2d72391
|
||||
|
||||
## If you use a custom profile…
|
||||
|
||||
Let's assume you have parse function looking like this:
|
||||
|
||||
```js
|
||||
const createParseLine = (profile, opt, data) => (rawLine) => {
|
||||
const operator = data.operators[rawLine.oprX] || null
|
||||
if (operator && operator.name === 'foo') {
|
||||
return {
|
||||
type: 'line',
|
||||
name: 'really special tram line',
|
||||
mode: 'tram',
|
||||
product: 'special-tram',
|
||||
operator
|
||||
}
|
||||
}
|
||||
return defaultParseLine(rawLine)
|
||||
}
|
||||
```
|
||||
|
||||
Adapt your parse function like this:
|
||||
|
||||
```diff
|
||||
const createParseLine = (profile, opt, _) => (rawLine) => {
|
||||
- const operator = data.operators[rawLine.oprX] || null
|
||||
+ const operator = rawLine.operator || null
|
||||
```
|
||||
|
||||
See also [`#127`](https://github.com/public-transport/hafas-client/pull/127).
|
||||
|
||||
If you use `icons` in `parseWarning`/`parseHint`, adapt the function(s) to take an object `data` as the first argument. You can access the list of *parsed* icons via `data.icons`, *parsed* warnings via `data.warnings`, etc. a229205 b36f0e3
|
||||
|
||||
## Other breaking changes
|
||||
|
||||
- `journey.price` will be `null` if there's no pricing data returned by the endpoint, instead of `{amount: null}`. 8fe277d
|
|
@ -1,93 +0,0 @@
|
|||
# Migrating to `hafas-client@6`
|
||||
|
||||
## If you use Node.js <16 …
|
||||
|
||||
… migrate to Node `16` ("Gallium"), sorry. [Node `10`, `12` & `14` are out of (active) LTS now](https://nodejs.org/en/about/releases/).
|
||||
|
||||
## If you use `hafas-client` via [CommonJS](https://en.wikipedia.org/wiki/CommonJS) …
|
||||
|
||||
… you'll have to either
|
||||
- migrate your code to ECMAScript Modules (ESM), or
|
||||
- use [dynamic `import()`](https://nodejs.org/docs/latest-v16.x/api/esm.html#import-expressions), or
|
||||
- use a (somewhat hacky) tool like [`esm`](https://www.npmjs.com/package/esm).
|
||||
|
||||
For more background information, check out [MDN's ESM explainer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) and [Node.js's ESM docs](https://nodejs.org/docs/latest-v16.x/api/esm.html).
|
||||
|
||||
## If you use `departures()` or `arrivals()` …
|
||||
|
||||
… adapt your code as follows:
|
||||
- `departures()` now returns an object `{departures: […], realtimeDataUpdatedAt: …}`
|
||||
- `arrivals()` now returns an object `{arrivals: […], realtimeDataUpdatedAt: …}`
|
||||
|
||||
### … with `opt.stopovers: true` …
|
||||
|
||||
… check if this still works. If `hafas-client` throws "`opt.stopovers` is not supported by this endpoint", you'll have to use `trip()` for each departure/arrival to get its trip's stopovers.
|
||||
|
||||
Most profiles had to be upgraded to a newer HAFAS protocol version to still work, and newer HAFAS protocol versions don't support this flag anymore.
|
||||
|
||||
## If you use `journeys()`, `refreshJourney()` or `journeysFromTrip()` …
|
||||
|
||||
… use `res.realtimeDataUpdatedAt` instead of `res.realtimeDataFrom`, it has been renamed.
|
||||
|
||||
## If you use `refreshJourney()` …
|
||||
|
||||
… adapt your code as follows: it now returns an object `{journey: …, realtimeDataUpdatedAt: …}`.
|
||||
|
||||
## If you use `trip()` …
|
||||
|
||||
… adapt your code as follows: it now returns an object `{trip: …, realtimeDataUpdatedAt: …}`.
|
||||
|
||||
… don't pass the `lineName` parameter anymore, it is not needed anymore and has been removed.
|
||||
|
||||
## If you use `tripsByName()` …
|
||||
|
||||
… adapt your code as follows: it now returns an object `{trips: […], realtimeDataUpdatedAt: …}`.
|
||||
|
||||
## If you use `radar()` …
|
||||
|
||||
… adapt your code as follows: it now returns an object `{movements: […], realtimeDataUpdatedAt: …}`.
|
||||
|
||||
## If you use `reachableFrom()` …
|
||||
|
||||
… and it sometimes fails with a server error (a.k.a. HAFAS is unable to process the request), wrap it in a retry logic ([open an Issue](https://github.com/public-transport/hafas-client/issues/new) to get help). Automatic retries have been removed.
|
||||
|
||||
## If you use `remarks()` …
|
||||
|
||||
… adapt your code as follows: it now returns an object `{remarks: […], realtimeDataUpdatedAt: …}`.
|
||||
|
||||
## If you use `lines()` …
|
||||
|
||||
… adapt your code as follows: it now returns an object `{lines: […], realtimeDataUpdatedAt: …}`.
|
||||
|
||||
## If you use the DB profile …
|
||||
|
||||
… be aware that the `regionalExp` product has been renamed to `regionalExpress`. Among other places, you will notice this in `line.product`.
|
||||
|
||||
## If you use the BVG or VBB profile …
|
||||
|
||||
### … and rely on `stop.weight` …
|
||||
|
||||
… use [`vbb-stations`](https://npmjs.com/package/vbb-stations) to get it instead. It has been removed from `hafas-client`.
|
||||
|
||||
### … and rely on 12-digit stop IDs …
|
||||
|
||||
… adapt your code to handle 9-digit (and sometimes 6-digit?) stop IDs. The translation logic has been removed from `hafas-client`.
|
||||
|
||||
## If you rely on `line.adminCode` …
|
||||
|
||||
… be aware that `hafas-client` now doesn't remove trailing `-` characters anymore (e.g. `DBS---` instead of `DBS`).
|
||||
|
||||
## If you use the VBB profile …
|
||||
|
||||
### … and rely on `line.{symbol,nr,metro,express,night}` …
|
||||
|
||||
… use [`vbb-parse-line`](https://npmjs.com/package/vbb-parse-line) with `line.name` by yourself. It has been removed from `hafas-client`.
|
||||
|
||||
### … and rely on `ticket.{amount,fullDay,group,tariff,coverage,variant}` …
|
||||
|
||||
… use [`vbb-parse-ticket`](https://npmjs.com/package/vbb-parse-ticket) to parse details from the ticket identifier ([open an Issue](https://github.com/public-transport/hafas-client/issues/new) to get help). It has been removed from `hafas-client`.
|
||||
|
||||
## Other breaking changes
|
||||
|
||||
- `warning.fromLoc`/`warning.toLoc` are now called `warning.fromLocation`/`warning.toLocation`
|
||||
- `trip()`/`tripsByName()`: remove `trip.reachable` (it didn't make sense anyways)
|
|
@ -10,25 +10,23 @@ With `opt`, you can override the default options, which look like this:
|
|||
{
|
||||
results: 8, // maximum number of results
|
||||
distance: null, // maximum walking distance in meters
|
||||
poi: false, // return points of interest?
|
||||
poi: false, // not supported
|
||||
stops: true, // return stops/stations?
|
||||
subStops: true, // parse & expose sub-stops of stations?
|
||||
entrances: true, // parse & expose entrances of stops/stations?
|
||||
linesOfStops: false, // parse & expose lines at each stop/station?
|
||||
subStops: true, // not supported
|
||||
entrances: true, // not supported
|
||||
linesOfStops: false, // not supported
|
||||
language: 'en' // language to get results in
|
||||
}
|
||||
```
|
||||
|
||||
## Response
|
||||
|
||||
As an example, we're going to use the [VBB profile](../p/vbb):
|
||||
|
||||
```js
|
||||
import {createClient} from 'hafas-client'
|
||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
||||
import {createClient} from 'db-vendo-client'
|
||||
import {profile as dbProfile} from 'db-vendo-client/p/db/index.js'
|
||||
|
||||
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||
const client = createClient(vbbProfile, userAgent)
|
||||
const client = createClient(dbProfile, userAgent)
|
||||
|
||||
await client.nearby({
|
||||
type: 'location',
|
||||
|
|
2492
docs/openapi.yaml
Normal file
2492
docs/openapi.yaml
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,45 +0,0 @@
|
|||
// Refer to the the ./writing-a-profile.md guide.
|
||||
|
||||
const products = [
|
||||
{
|
||||
id: 'commuterTrain',
|
||||
mode: 'train',
|
||||
bitmasks: [16],
|
||||
name: 'ACME Commuter Rail',
|
||||
short: 'CR',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: 'metro',
|
||||
mode: 'train',
|
||||
bitmasks: [8],
|
||||
name: 'Foo Bar Metro',
|
||||
short: 'M',
|
||||
default: true,
|
||||
},
|
||||
];
|
||||
|
||||
const transformReqBody = (body) => {
|
||||
// get these from the recorded app requests
|
||||
// body.client = { … }
|
||||
// body.ver = …
|
||||
// body.auth = { … }
|
||||
// body.lang = …
|
||||
return body;
|
||||
};
|
||||
|
||||
const insaProfile = {
|
||||
// locale: …,
|
||||
// timezone: …,
|
||||
// endpoint: …,
|
||||
transformReqBody,
|
||||
|
||||
products: products,
|
||||
|
||||
trip: false,
|
||||
radar: false,
|
||||
};
|
||||
|
||||
export {
|
||||
insaProfile,
|
||||
};
|
191
docs/radar.md
191
docs/radar.md
|
@ -1,191 +0,0 @@
|
|||
# `radar({north, west, south, east}, [opt])`
|
||||
|
||||
Use this method to find all vehicles currently in an area. Note that it is not supported by every profile/endpoint.
|
||||
|
||||
`north`, `west`, `south` and `eath` must be numbers (e.g. `52.52411`). Together, they form a [bounding box](https://en.wikipedia.org/wiki/Minimum_bounding_box).
|
||||
|
||||
With `opt`, you can override the default options, which look like this:
|
||||
|
||||
```js
|
||||
{
|
||||
results: 256, // maximum number of vehicles
|
||||
duration: 30, // compute frames for the next n seconds
|
||||
frames: 3, // nr of frames to compute
|
||||
polylines: true, // return a track shape for each vehicle?
|
||||
subStops: true, // parse & expose sub-stops of stations?
|
||||
entrances: true, // parse & expose entrances of stops/stations?
|
||||
language: 'en' // language to get results in
|
||||
}
|
||||
```
|
||||
|
||||
## Response
|
||||
|
||||
*Note:* As stated in the [*Friendly Public Transport Format* v2 draft spec](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md), the returned `departure` and `arrival` times include the current delay. The `departureDelay`/`arrivalDelay` fields express how much they differ from the schedule.
|
||||
|
||||
As an example, we're going to use the [VBB profile](../p/vbb):
|
||||
|
||||
```js
|
||||
import {createClient} from 'hafas-client'
|
||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
||||
|
||||
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||
const client = createClient(vbbProfile, userAgent)
|
||||
|
||||
const {
|
||||
movements,
|
||||
realtimeDataUpdatedAt,
|
||||
} = await client.radar({
|
||||
north: 52.52411,
|
||||
west: 13.41002,
|
||||
south: 52.51942,
|
||||
east: 13.41709
|
||||
}, {results: 5})
|
||||
```
|
||||
|
||||
`realtimeDataUpdatedAt` is a UNIX timestamp reflecting the latest moment when (at least some of) the response's realtime data have been updated.
|
||||
|
||||
`movements` may look like this:
|
||||
|
||||
```js
|
||||
[ {
|
||||
location: {
|
||||
type: 'location',
|
||||
latitude: 52.521508,
|
||||
longitude: 13.411267
|
||||
},
|
||||
|
||||
line: {
|
||||
type: 'line',
|
||||
id: 's9',
|
||||
fahrtNr: '12345',
|
||||
name: 'S9',
|
||||
public: true,
|
||||
mode: 'train',
|
||||
product: 'suburban',
|
||||
symbol: 'S',
|
||||
nr: 9,
|
||||
metro: false,
|
||||
express: false,
|
||||
night: false,
|
||||
operator: {
|
||||
type: 'operator',
|
||||
id: 's-bahn-berlin-gmbh',
|
||||
name: 'S-Bahn Berlin GmbH'
|
||||
}
|
||||
},
|
||||
direction: 'S Flughafen Berlin-Schönefeld',
|
||||
trip: 31463, // todo: outdated, should be tripId!
|
||||
|
||||
nextStopovers: [ {
|
||||
stop: {
|
||||
type: 'stop',
|
||||
id: '900000029101',
|
||||
name: 'S Spandau',
|
||||
location: {
|
||||
type: 'location',
|
||||
latitude: 52.534794,
|
||||
longitude: 13.197477
|
||||
},
|
||||
products: {
|
||||
suburban: true,
|
||||
subway: false,
|
||||
tram: false,
|
||||
bus: true,
|
||||
ferry: false,
|
||||
express: true,
|
||||
regional: true
|
||||
}
|
||||
},
|
||||
arrival: null,
|
||||
plannedArrival: null,
|
||||
arrivalDelay: null,
|
||||
arrivalPlatform: null,
|
||||
plannedArrivalPlatform: null,
|
||||
departure: null,
|
||||
plannedDeparture: '2017-12-17T19:16:00+01:00',
|
||||
departureDelay: null,
|
||||
departurePlatform: null,
|
||||
plannedDeparturePlatform: '1'
|
||||
} /* … */ ],
|
||||
frames: [ {
|
||||
origin: {
|
||||
type: 'stop',
|
||||
id: '900000100003',
|
||||
name: 'S+U Alexanderplatz',
|
||||
location: { /* … */ },
|
||||
products: { /* … */ }
|
||||
},
|
||||
destination: {
|
||||
type: 'stop',
|
||||
id: '900000100004',
|
||||
name: 'S+U Jannowitzbrücke',
|
||||
location: { /* … */ },
|
||||
products: { /* … */ }
|
||||
},
|
||||
t: 0
|
||||
}, /* … */ {
|
||||
origin: { /* Alexanderplatz */ },
|
||||
destination: { /* Jannowitzbrücke */ },
|
||||
t: 30000
|
||||
} ]
|
||||
}, {
|
||||
location: {
|
||||
type: 'location',
|
||||
latitude: 52.523297,
|
||||
longitude: 13.411151
|
||||
},
|
||||
line: {
|
||||
type: 'line',
|
||||
id: 'm2',
|
||||
fahrtNr: '54321',
|
||||
name: 'M2',
|
||||
public: true,
|
||||
mode: 'train',
|
||||
product: 'tram',
|
||||
symbol: 'M',
|
||||
nr: 2,
|
||||
metro: true,
|
||||
express: false,
|
||||
night: false,
|
||||
operator: {
|
||||
type: 'operator',
|
||||
id: 'berliner-verkehrsbetriebe',
|
||||
name: 'Berliner Verkehrsbetriebe'
|
||||
}
|
||||
},
|
||||
direction: 'Heinersdorf',
|
||||
trip: 26321,
|
||||
nextStopovers: [ {
|
||||
stop: { /* S+U Alexanderplatz/Dircksenstr. */ },
|
||||
arrival: null,
|
||||
plannedArrival: null,
|
||||
arrivalDelay: null,
|
||||
departure: null,
|
||||
plannedAeparture: '2017-12-17T19:52:00+01:00',
|
||||
departureDelay: null
|
||||
}, {
|
||||
stop: { /* Memhardstr. */ },
|
||||
arrival: null,
|
||||
plannedArrival: '2017-12-17T19:54:00+01:00',
|
||||
arrivalDelay: null,
|
||||
arrivalPlatform: null,
|
||||
plannedArrivalPlatform: null,
|
||||
departure: null,
|
||||
plannedDeparture: '2017-12-17T19:54:00+01:00',
|
||||
departureDelay: null,
|
||||
departurePlatform: null,
|
||||
plannedDeparturePlatform: '1'
|
||||
}, /* … */ ],
|
||||
frames: [ {
|
||||
origin: { /* S+U Alexanderplatz/Dircksenstr. */ },
|
||||
destination: { /* Memhardstr. */ },
|
||||
t: 0
|
||||
}, /* … */ {
|
||||
origin: { /* Memhardstr. */ },
|
||||
destination: { /* Mollstr./Prenzlauer Allee */ },
|
||||
t: 30000
|
||||
} ]
|
||||
}, /* … */ ]
|
||||
```
|
||||
|
||||
If you pass `polylines: true`, each movement will have a `polyline` field, as documented in [the corresponding section in the `trip()` docs](trip.md#polyline-option), with the exception that station info is missing.
|
|
@ -1,98 +0,0 @@
|
|||
# `reachableFrom(address, [opt])`
|
||||
|
||||
This method can be used to get stations reachable within a certain time from an address. This concept is called [isochrone diagram](https://en.wikipedia.org/wiki/Isochrone_map#Transportation_planning).
|
||||
|
||||
*Note*: It appears that HAFAS cannot generate actual isochrones, but only the list of reachable stations, which you can estimate the isochrone(s) from.
|
||||
|
||||
`address` must be an [*FPTF* `location` object](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md#location-objects).
|
||||
|
||||
With `opt`, you can override the default options, which look like this:
|
||||
|
||||
```js
|
||||
{
|
||||
when: new Date(),
|
||||
maxTransfers: 5, // maximum of 5 transfers
|
||||
maxDuration: 20, // maximum travel duration in minutes, pass `null` for infinite
|
||||
products: {
|
||||
// These entries may vary from profile to profile!
|
||||
suburban: true,
|
||||
subway: true
|
||||
// …
|
||||
},
|
||||
subStops: true, // parse & expose sub-stops of stations?
|
||||
entrances: true, // parse & expose entrances of stops/stations?
|
||||
}
|
||||
```
|
||||
|
||||
## Response
|
||||
|
||||
`reachableFrom(address, [opt])` returns an array, in which each item has a `duration` and an array of [*Friendly Public Transport Format* `station`s](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md#station).
|
||||
|
||||
As an example, we're going to use the [VBB profile](../p/vbb):
|
||||
|
||||
```js
|
||||
import {createClient} from 'hafas-client'
|
||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
||||
|
||||
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||
const client = createClient(vbbProfile, userAgent)
|
||||
|
||||
const {
|
||||
reachable,
|
||||
realtimeDataUpdatedAt,
|
||||
} = await client.reachableFrom({
|
||||
type: 'location',
|
||||
address: '13353 Berlin-Wedding, Torfstr. 17',
|
||||
latitude: 52.541797,
|
||||
longitude: 13.350042
|
||||
}, {
|
||||
maxDuration: 10 // minutes
|
||||
})
|
||||
```
|
||||
|
||||
`realtimeDataUpdatedAt` is a UNIX timestamp reflecting the latest moment when (at least some of) the response's realtime data have been updated.
|
||||
|
||||
`reachable` may look like this:
|
||||
|
||||
```js
|
||||
[
|
||||
{
|
||||
duration: 2,
|
||||
stations: [
|
||||
{
|
||||
type: 'stop',
|
||||
id: '900000009101',
|
||||
name: 'U Amrumer Str.',
|
||||
location: {type: 'location', latitude: 52.542201, longitude: 13.34953},
|
||||
products: { /* … */ }
|
||||
}
|
||||
]
|
||||
}, {
|
||||
duration: 3,
|
||||
stations: [
|
||||
{
|
||||
type: 'stop',
|
||||
id: '900000001201',
|
||||
name: 'S+U Westhafen',
|
||||
location: {type: 'location', latitude: 52.536179, longitude: 13.343839},
|
||||
products: { /* … */ }
|
||||
}
|
||||
// …
|
||||
]
|
||||
},
|
||||
// …
|
||||
{
|
||||
duration: 10,
|
||||
stations: [
|
||||
{
|
||||
type: 'stop',
|
||||
id: '900000001203',
|
||||
name: 'Döberitzer Str.',
|
||||
location: {type: 'location', latitude: 52.530668, longitude: 13.36811},
|
||||
products: { /* … */ }
|
||||
}
|
||||
// …
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
|
@ -1,19 +1,21 @@
|
|||
# `hafas-client` documentation
|
||||
# `db-vendo-client` documentation
|
||||
|
||||
**[API documentation](api.md)**
|
||||
**[JS API documentation](api.md)**
|
||||
|
||||
## Migrating from an old `hafas-client` version
|
||||
[REST API OpenAPI schema](openapi.yaml) ([open in Swagger Editor](https://editor.swagger.io/?url=https://raw.githubusercontent.com/public-transport/db-vendo-client/refs/heads/main/docs/openapi.yaml))
|
||||
|
||||
- [`4` → `5` migration guide](migrating-to-5.md)
|
||||
## Migrating from an old (v5) `hafas-client` version
|
||||
|
||||
`db-vendo-client` tries to be as compatible as possible with `hafas-client` v6. If you were still on v5 or earlier, see the [`5` → `6` migration guide](https://github.com/public-transport/hafas-client/blob/main/docs/migrating-to-6.md) of `hafas-client`.
|
||||
|
||||
## Throttling requests
|
||||
|
||||
There's opt-in support for throttling requests to the endpoint.
|
||||
|
||||
```js
|
||||
import {createClient} from 'hafas-client'
|
||||
import {withThrottling} from 'hafas-client/throttle.js'
|
||||
import {profile as dbProfile} from 'hafas-client/p/db/index.js'
|
||||
import {createClient} from 'db-vendo-client'
|
||||
import {withThrottling} from 'db-vendo-client/throttle.js'
|
||||
import {profile as dbProfile} from 'db-vendo-client/p/db/index.js'
|
||||
|
||||
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||
|
||||
|
@ -37,9 +39,9 @@ const client = createClient(throttledDbProfile, userAgent)
|
|||
There's opt-in support for retrying failed requests to the endpoint.
|
||||
|
||||
```js
|
||||
import {createClient} from 'hafas-client'
|
||||
import {withRetrying} from 'hafas-client/retry.js'
|
||||
import {profile as dbProfile} from 'hafas-client/p/db/index.js'
|
||||
import {createClient} from 'db-vendo-client'
|
||||
import {withRetrying} from 'db-vendo-client/retry.js'
|
||||
import {profile as dbProfile} from 'db-vendo-client/p/db/index.js'
|
||||
|
||||
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||
|
||||
|
@ -61,27 +63,12 @@ const client = createClient(retryingDbProfile, userAgent)
|
|||
|
||||
## User agent randomization
|
||||
|
||||
By default, `hafas-client` randomizes the client name that you pass into `createClient`, and sends it as [`User-Agent`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent) in a randomized form:
|
||||
|
||||
```js
|
||||
import {createClient} from 'hafas-client'
|
||||
// …
|
||||
|
||||
const userAgent = 'my-awesome-program'
|
||||
const client = createClient(someProfile, userAgent)
|
||||
|
||||
await client.journeys(/* … */)
|
||||
// User-Agent: my-awee70429some-pre70429ogram
|
||||
await client.journeys(/* … */)
|
||||
// User-Agent: my-awesom9bb8e2e-prog9bb8e2ram
|
||||
```
|
||||
|
||||
You can turn this off by setting `profile.randomizeUserAgent` to `false`:
|
||||
By default, `db-vendo-client` does not randomize the client name that you pass into `createClient`, and sends it as [`User-Agent`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent) as it is. At least DB Navigator always sends the same user agent as well (cf. `dbnav` profile). You can turn on randomization by setting `profile.randomizeUserAgent` to `false`:
|
||||
|
||||
```js
|
||||
const client = createClient({
|
||||
...someProfile,
|
||||
randomizeUserAgent: false,
|
||||
randomizeUserAgent: true,
|
||||
}, userAgent)
|
||||
```
|
||||
|
||||
|
@ -92,8 +79,8 @@ You can use `profile.logRequest` and `profile.logResponse` to process the raw [F
|
|||
As an example, we can implement a custom logger:
|
||||
|
||||
```js
|
||||
import {createClient} from 'hafas-client'
|
||||
import {profile as dbProfile} from 'hafas-client/p/db/index.js'
|
||||
import {createClient} from 'db-vendo-client'
|
||||
import {profile as dbProfile} from 'db-vendo-client/p/db/index.js'
|
||||
|
||||
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||
|
||||
|
@ -140,17 +127,14 @@ The default `profile.logRequest` [`console.error`](https://nodejs.org/docs/lates
|
|||
|
||||
## Error handling
|
||||
|
||||
Unexpected errors – e.g. due to bugs in `hafas-client` itself – aside, its methods may reject with the following errors:
|
||||
Unexpected errors – e.g. due to bugs in `db-vendo-client` itself – aside, its methods may reject with the following errors:
|
||||
|
||||
- `Error` – A generic error, e.g. if the DB backend returned a HTTP error.
|
||||
- `HafasError` – A generic error to signal that something HAFAS-related went wrong, either in the client, or in the HAFAS endpoint.
|
||||
- `HafasAccessDeniedError` – The HAFAS endpoint has rejected your request because you're not allowed to access it (or the specific API call). Subclass of `HafasError`.
|
||||
- `HafasInvalidRequestError` – The HAFAS endpoint reports that an invalid request has been sent. Subclass of `HafasError`.
|
||||
- `HafasNotFoundError` – The HAFAS endpoint does not know about such stop/trip/etc. Subclass of `HafasError`.
|
||||
- `HafasServerError` – An error occured within the HAFAS endpoint, so that it can't fulfill the request; For example, this happens when HAFAS' internal backend is unavailable. Subclass of `HafasError`.
|
||||
|
||||
Each error has the following properties:
|
||||
Each `HafasError` error has the following properties:
|
||||
|
||||
- `isHafasError` – Always `true`. Allows you to differente HAFAS-related errors from e.g. network errors.
|
||||
- `isHafasError` – Always `true`. Allows you to distinguish HAFAS-related errors from e.g. network errors.
|
||||
- `code` – A string representing the error type for all other error classes, e.g. `INVALID_REQUEST` for `HafasInvalidRequestError`. `null` for plain `HafasError`s.
|
||||
- `isCausedByServer` – Boolean, telling you if the HAFAS endpoint says that it couldn't process your request because *it* is unavailable/broken.
|
||||
- `hafasCode` – A HAFAS-specific error code, if the HAFAS endpoint returned one; e.g. `H890` when no journeys could be found. `null` otherwise.
|
||||
|
@ -158,32 +142,13 @@ Each error has the following properties:
|
|||
- `url` – The URL of the request.
|
||||
- `response` – The [Fetch API `Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response).
|
||||
|
||||
To check **if an error from `hafas-client` is HAFAS-specific, use `error instanceof HafasError`**:
|
||||
|
||||
```js
|
||||
import {HafasError} from 'hafas-client/lib/errors.js'
|
||||
## Using `db-vendo-client` from another language
|
||||
|
||||
try {
|
||||
await client.journeys(/* … */)
|
||||
} catch (err) {
|
||||
if (err instanceof HafasError) {
|
||||
// HAFAS-specific error
|
||||
} else {
|
||||
// different error, e.g. network (ETIMEDOUT, ENETDOWN)
|
||||
}
|
||||
}
|
||||
```
|
||||
If you want to use `db-vendo-client` to access DB APIs but work with non-Node.js environments, you can use it together with [hafas-rest-api](https://github.com/public-transport/hafas-rest-api) to create a REST API (see the [root readme](https://github.com/public-transport/db-vendo-client/tree/main#usage) and the Docker image).
|
||||
Or use [`hafas-client-rpc`](https://github.com/derhuerst/hafas-client-rpc) to create a [JSON-RPC](https://www.jsonrpc.org) interface that you can send commands to.
|
||||
|
||||
To determine **if you should automatically retry an error, use `!error.causedByServer`**.
|
||||
|
||||
## Using `hafas-client` from another language
|
||||
## General documentation and notes for DB APIs
|
||||
|
||||
If you want to use `hafas-client` to access HAFAS APIs but work with non-Node.js environments, you can use [`hafas-client-rpc`](https://github.com/derhuerst/hafas-client-rpc) to create a [JSON-RPC](https://www.jsonrpc.org) interface that you can send commands to.
|
||||
|
||||
## Writing a profile
|
||||
|
||||
Check [the guide](writing-a-profile.md).
|
||||
|
||||
## General documentation for `mgate.exe` APIs
|
||||
|
||||
[`hafas-mgate-api.md`](hafas-mgate-api.md)
|
||||
[`db-apis.md`](db-apis.md)
|
||||
|
|
|
@ -7,10 +7,10 @@ With `opt`, you can override the default options, which look like this:
|
|||
```js
|
||||
{
|
||||
stopovers: false, // return stations on the way?
|
||||
polylines: false, // return a shape for each leg?
|
||||
tickets: false, // return tickets? only available with some profiles
|
||||
subStops: true, // parse & expose sub-stops of stations?
|
||||
entrances: true, // parse & expose entrances of stops/stations?
|
||||
polylines: false, // return a shape for each leg? mutually exclusive with tickets
|
||||
tickets: false, // return tickets? mutually exclusive with polylines
|
||||
subStops: true, // not supported
|
||||
entrances: true, // not supported
|
||||
remarks: true, // parse & expose hints & warnings?
|
||||
language: 'en' // language to get results in
|
||||
}
|
||||
|
@ -18,17 +18,14 @@ With `opt`, you can override the default options, which look like this:
|
|||
|
||||
## Response
|
||||
|
||||
As an example, we're going to use the [VBB profile](../p/vbb):
|
||||
|
||||
```js
|
||||
import {createClient} from 'hafas-client'
|
||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
||||
import {createClient} from 'db-vendo-client'
|
||||
import {profile as dbProfile} from 'db-vendo-client/p/db/index.js'
|
||||
|
||||
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||
const client = createClient(vbbProfile, userAgent)
|
||||
const client = createClient(dbProfile, userAgent)
|
||||
|
||||
// Hauptbahnhof to Heinrich-Heine-Str.
|
||||
const {journeys} = await client.journeys('900000003201', '900000100008', {results: 1})
|
||||
const {journeys} = await client.journeys('8000105', '8000096', {results: 1})
|
||||
|
||||
// later, fetch up-to-date info on the journey
|
||||
const {
|
||||
|
@ -39,4 +36,4 @@ const {
|
|||
|
||||
`journey` is a *single* [*Friendly Public Transport Format* v2 draft](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md) `journey`, in the same format as returned by [`journeys()`](journeys.md).
|
||||
|
||||
`realtimeDataUpdatedAt` is a UNIX timestamp reflecting the latest moment when (at least some of) the response's realtime data have been updated.
|
||||
`realtimeDataUpdatedAt` is currently not set in db-vendo-client, because the upstream APIs don't provide it.
|
||||
|
|
150
docs/remarks.md
150
docs/remarks.md
|
@ -1,150 +0,0 @@
|
|||
# `remarks([opt])`
|
||||
|
||||
**Fetches all remarks known to the HAFAS endpoint**, e.g. warnings about disruptions, planned construction work, and general notices about the operating situation.
|
||||
|
||||
With `opt`, you can override the default options, which look like this:
|
||||
|
||||
```js
|
||||
{
|
||||
results: 100, // maximum number of remarks
|
||||
// filter by time
|
||||
from: Date.now(),
|
||||
to: null,
|
||||
products: null, // filter by affected products
|
||||
language: 'en', // depends on the profile
|
||||
}
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
As an example, we're going to use the [SVV profile](../p/svv):
|
||||
|
||||
```js
|
||||
import {createClient} from 'hafas-client'
|
||||
import {profile as svvProfile} from 'hafas-client/p/svv/index.js'
|
||||
|
||||
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||
const client = createClient(svvProfile, userAgent)
|
||||
|
||||
const {
|
||||
remarks,
|
||||
realtimeDataUpdatedAt,
|
||||
} = await client.remarks()
|
||||
```
|
||||
|
||||
`realtimeDataUpdatedAt` is a UNIX timestamp reflecting the latest moment when (at least some of) the response's realtime data have been updated.
|
||||
|
||||
`remarks` may look like this:
|
||||
|
||||
```js
|
||||
[
|
||||
{
|
||||
id: 'HIM_FREETEXT_110342',
|
||||
type: 'warning',
|
||||
summary: 'Bus will be running at different times',
|
||||
text: 'Due to operational changes, this bus will be running at different times. We apologise for any inconvenience this may cause.',
|
||||
priority: 50,
|
||||
company: 'KGÖVV',
|
||||
validFrom: '2020-07-04T00:00:00+02:00',
|
||||
validUntil: '2020-08-09T23:59:00+02:00',
|
||||
modified: '2020-07-01T14:39:12+02:00',
|
||||
products: {
|
||||
'bahn-s-bahn': true,
|
||||
'u-bahn': true,
|
||||
strassenbahn: true,
|
||||
fernbus: true,
|
||||
regionalbus: true,
|
||||
stadtbus: true,
|
||||
'seilbahn-zahnradbahn': true,
|
||||
schiff: true,
|
||||
},
|
||||
categories: [1],
|
||||
icon: {type: 'HIM1', title: null},
|
||||
},
|
||||
// …
|
||||
{
|
||||
id: 'HIM_FREETEXT_110235',
|
||||
type: 'warning',
|
||||
summary: 'Linie 7 - Umleitungen',
|
||||
text: 'Aufgrund einer Baustelle gibt es bei der Linie 7 umfangreiche Umleitungen.',
|
||||
priority: 100,
|
||||
company: 'VOR',
|
||||
validFrom: '2020-07-13T00:00:00+02:00',
|
||||
validUntil: '2020-08-31T23:59:00+02:00',
|
||||
modified: '2020-06-30T12:37:38+02:00',
|
||||
affectedLines: [{
|
||||
type: 'line',
|
||||
id: '7',
|
||||
name: '7',
|
||||
public: true,
|
||||
}],
|
||||
products: {
|
||||
'bahn-s-bahn': true,
|
||||
'u-bahn': true,
|
||||
strassenbahn: true,
|
||||
fernbus: true,
|
||||
regionalbus: true,
|
||||
stadtbus: true,
|
||||
'seilbahn-zahnradbahn': false,
|
||||
schiff: true,
|
||||
},
|
||||
categories: [1],
|
||||
icon: {type: 'HIM1', title: null},
|
||||
},
|
||||
// …
|
||||
{
|
||||
id: 'HIM_FREETEXT_106619',
|
||||
type: 'warning',
|
||||
summary: 'Stop Bad Hall Bahnhofstraße can not be approached',
|
||||
text: 'The stop at Bad Hall Bahnhofstraße can not be approached during 21.04.-24.07.2020. Please use alternatively the stop at Bad Hall Busterminal (Abzw Bahnhofstraße).',
|
||||
priority: 100,
|
||||
company: 'OÖVG',
|
||||
validFrom: '2020-04-21T00:00:00+02:00',
|
||||
validUntil: '2020-07-24T23:59:00+02:00',
|
||||
modified: '2020-07-08T12:52:13+02:00',
|
||||
affectedLines: [{
|
||||
type: 'line',
|
||||
id: '452',
|
||||
name: '452',
|
||||
public: true,
|
||||
}],
|
||||
products: {
|
||||
'bahn-s-bahn': false,
|
||||
'u-bahn': false,
|
||||
strassenbahn: false,
|
||||
fernbus: false,
|
||||
regionalbus: true,
|
||||
stadtbus: false,
|
||||
'seilbahn-zahnradbahn': false,
|
||||
schiff: false
|
||||
},
|
||||
categories: [1],
|
||||
icon: {type: 'HIM1', title: null},
|
||||
},
|
||||
// …
|
||||
{
|
||||
id: 'HIM_FREETEXT_106671',
|
||||
type: 'warning',
|
||||
summary: 'Neue Haltestellennamen',
|
||||
text: 'Im Zuge der Neuordnung der Regionalbusverkehre werden mit 6.7.2020 neue Fahrpläne und Liniennummern wirksam und dadurch können sich mitunter die Haltestellennamen verändern.',
|
||||
priority: 100,
|
||||
company: 'VOR',
|
||||
validFrom: '2020-04-21T00:00:00+02:00',
|
||||
validUntil: '2020-09-30T23:59:00+02:00',
|
||||
modified: '2020-04-21T13:20:41+02:00',
|
||||
products: {
|
||||
'bahn-s-bahn': true,
|
||||
'u-bahn': true,
|
||||
strassenbahn: true,
|
||||
fernbus: true,
|
||||
regionalbus: true,
|
||||
stadtbus: true,
|
||||
'seilbahn-zahnradbahn': false,
|
||||
schiff: true,
|
||||
},
|
||||
categories: [4],
|
||||
icon: {type: 'HIM4', title: null},
|
||||
},
|
||||
// …
|
||||
]
|
||||
```
|
|
@ -1,38 +0,0 @@
|
|||
# `serverInfo([opt])`
|
||||
|
||||
**Fetches meta information from the HAFAS endpoint.**
|
||||
|
||||
With `opt`, you can override the default options, which look like this:
|
||||
|
||||
```js
|
||||
{
|
||||
versionInfo: true, // query HAFAS versions?
|
||||
language: 'en', // depends on the profile
|
||||
}
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
As an example, we're going to use the [SVV profile](../p/svv):
|
||||
|
||||
```js
|
||||
import {createClient} from 'hafas-client'
|
||||
import {profile as svvProfile} from 'hafas-client/p/svv/index.js'
|
||||
|
||||
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||
const client = createClient(svvProfile, userAgent)
|
||||
|
||||
await client.serverInfo()
|
||||
```
|
||||
|
||||
```js
|
||||
{
|
||||
// version of the HAFAS Connection Interface (HCI), the API that hafas-client uses
|
||||
hciVersion: '1.23',
|
||||
|
||||
timetableStart: '20200517',
|
||||
timetableEnd: '20201212',
|
||||
serverTime: '2020-07-19T21:32:12+02:00',
|
||||
realtimeDataUpdatedAt: 1595187102,
|
||||
}
|
||||
```
|
31
docs/stop.md
31
docs/stop.md
|
@ -1,5 +1,7 @@
|
|||
# `stop(id, [opt])`
|
||||
|
||||
This endpoint is not available with `dbweb` profile.
|
||||
|
||||
`id` must be in one of these formats:
|
||||
|
||||
```js
|
||||
|
@ -23,8 +25,8 @@ With `opt`, you can override the default options, which look like this:
|
|||
|
||||
```js
|
||||
{
|
||||
subStops: true, // parse & expose sub-stops of stations?
|
||||
entrances: true, // parse & expose entrances of stops/stations?
|
||||
subStops: true, // not supported
|
||||
entrances: true, // not supported
|
||||
linesOfStops: false, // parse & expose lines at the stop/station?
|
||||
language: 'en' // language to get results in
|
||||
}
|
||||
|
@ -32,14 +34,13 @@ With `opt`, you can override the default options, which look like this:
|
|||
|
||||
## Response
|
||||
|
||||
As an example, we're going to use the [VBB profile](../p/vbb):
|
||||
|
||||
```js
|
||||
import {createClient} from 'hafas-client'
|
||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
||||
import {profile as dbProfile} from 'hafas-client/p/db/index.js'
|
||||
|
||||
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||
const client = createClient(vbbProfile, userAgent)
|
||||
const client = createClient(dbProfile, userAgent)
|
||||
|
||||
await client.stop('900000042101') // U Spichernstr.
|
||||
```
|
||||
|
@ -72,11 +73,6 @@ The result may look like this:
|
|||
product: 'subway',
|
||||
public: true,
|
||||
name: 'U1',
|
||||
symbol: 'U',
|
||||
nr: 1,
|
||||
metro: false,
|
||||
express: false,
|
||||
night: false
|
||||
},
|
||||
// …
|
||||
{
|
||||
|
@ -86,21 +82,6 @@ The result may look like this:
|
|||
product: 'bus',
|
||||
public: true,
|
||||
name: 'N9',
|
||||
symbol: 'N',
|
||||
nr: 9,
|
||||
metro: false,
|
||||
express: false,
|
||||
night: true
|
||||
} ]
|
||||
}
|
||||
```
|
||||
|
||||
If the endpoint returns a list of entrances for a station, the resulting station object will have an `entrances` array looking similar to this:
|
||||
|
||||
```js
|
||||
[
|
||||
{type: 'location', latitude: 47.411069, longitude: 10.277412},
|
||||
{type: 'location', latitude: 47.410493, longitude: 10.277223},
|
||||
{type: 'location', latitude: 47.410754, longitude: 10.278023}
|
||||
]
|
||||
```
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# automated tests in `hafas-client`
|
||||
# automated tests in `db-vendo-client`
|
||||
|
||||
Because transit data is inherently dynamic (e.g. a different set of departures being returned for a stop now than in 10 minutes), and because it is of paramount importance that `hafas-client` actually works with HAFAS endpoints *as they currently work*, its testing setup is a bit unusual.
|
||||
Because transit data is inherently dynamic (e.g. a different set of departures being returned for a stop now than in 10 minutes), and because it is of paramount importance that `db-vendo-client` actually works with HAFAS endpoints *as they currently work*, its testing setup is a bit unusual.
|
||||
|
||||
`hafas-client` has three kinds of automated tests:
|
||||
`db-vendo-client` has three kinds of automated tests:
|
||||
- unit tests, which test individual aspects of the case base in isolation (e.g. the parsing of HAFAS-formatted dates & times) – run via `npm run test-unit`
|
||||
- end-to-end (E2E) tests, which run actual HTTP requests against their respective profile's HAFAS endpoint – run via `npm run test-e2e`
|
||||
- integration tests, which are the E2E tests running against pre-recorded (and checked-in) HTTP request fixtures – run via `npm run test-integration`
|
||||
|
@ -16,7 +16,7 @@ Because the E2E & integration tests are based on the same code, when changing th
|
|||
As an example, let's assume that we have added an entirely new test to [the *DB* profile's E2E tests](../test/e2e/db.js).
|
||||
|
||||
The behaviour of the HTTP request recording (into fixtures) and mocking (using the recorded fixtures) is controlled via an environment variable `$VCR_MODE`:
|
||||
- By running the test(s) with `VCR_MODE=record`, we can record the HTTP requests being made. The tests will run just like without `$VCR_MODE`, except that they will query data for date+time specified in `T_MOCK` (e.g. [here](https://github.com/public-transport/hafas-client/blob/8ff945c07515155380de0acb33584e474d6d547c/test/e2e/db.js#L33)).
|
||||
- By running the test(s) with `VCR_MODE=record`, we can record the HTTP requests being made. The tests will run just like without `$VCR_MODE`, except that they will query data for date+time specified in `T_MOCK` (e.g. [here](https://github.com/public-transport/db-vendo-client/blob/8ff945c07515155380de0acb33584e474d6d547c/test/e2e/db.js#L33)).
|
||||
- Then, by running the test(s) with `VCR_MODE=playback`, because their HTTP requests match the pre-recorded fixtures, they work on the corresponding mocked HTTP responses.
|
||||
|
||||
Usually, you would not want to update all *already existing* recorded HTTP request fixtures of the test suite you have made changes in, as they are unrelated to the test you have added. To only record your *added* test, temporarily change `tap.test(…)` to read `tap.only(…)`, and run with `TAP_ONLY=1 VCR_MODE=record`; This will skip all unrelated tests entirely.
|
||||
|
|
48
docs/trip.md
48
docs/trip.md
|
@ -2,19 +2,16 @@
|
|||
|
||||
This method can be used to refetch information about a trip – a vehicle stopping at a set of stops at specific times.
|
||||
|
||||
*Note*: This method is not supported by every profile/endpoint.
|
||||
|
||||
Let's say you used [`journeys`](journeys.md) and now want to get more up-to-date data about the arrival/departure of a leg. You'd pass in the trip ID from `leg.tripId`, e.g. `'1|24983|22|86|18062017'`, and the name of the line from `leg.line.name` like this:
|
||||
|
||||
```js
|
||||
import {createClient} from 'hafas-client'
|
||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
||||
import {createClient} from 'db-vendo-client'
|
||||
import {profile as dbnavProfile} from 'db-vendo-client/p/dbnav/index.js'
|
||||
|
||||
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||
const client = createClient(vbbProfile, userAgent)
|
||||
const client = createClient(dbnavProfile, userAgent)
|
||||
|
||||
// Hauptbahnhof to Heinrich-Heine-Str.
|
||||
const {journeys} = client.journeys('900000003201', '900000100008', {results: 1})
|
||||
const {journeys} = client.journeys('8000096', '8000105', {results: 1})
|
||||
const leg = journeys[0].legs[0]
|
||||
|
||||
await client.trip(leg.tripId)
|
||||
|
@ -25,9 +22,9 @@ With `opt`, you can override the default options, which look like this:
|
|||
```js
|
||||
{
|
||||
stopovers: true, // return stations on the way?
|
||||
polyline: false, // return a shape for the trip?
|
||||
subStops: true, // parse & expose sub-stops of stations?
|
||||
entrances: true, // parse & expose entrances of stops/stations?
|
||||
polyline: false, // return a shape for the trip? only supported with HAFAS trip id (i.e. not with a trip id from a departure/arrival board of the `db` profile)
|
||||
subStops: true, // not supported
|
||||
entrances: true, // not supported
|
||||
remarks: true, // parse & expose hints & warnings?
|
||||
language: 'en' // language to get results in
|
||||
}
|
||||
|
@ -37,13 +34,12 @@ With `opt`, you can override the default options, which look like this:
|
|||
|
||||
*Note:* As stated in the [*Friendly Public Transport Format* v2 draft spec](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md), the returned `departure` and `arrival` times include the current delay. The `departureDelay`/`arrivalDelay` fields express how much they differ from the schedule.
|
||||
|
||||
As an example, we're going to use the [VBB profile](../p/vbb):
|
||||
|
||||
```js
|
||||
import {createClient} from 'hafas-client'
|
||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
||||
import {createClient} from 'db-vendo-client'
|
||||
import {profile as dbnavProfile} from 'db-vendo-client/p/dbnav/index.js'
|
||||
|
||||
const client = createClient(vbbProfile)
|
||||
const client = createClient(dbnavProfile)
|
||||
|
||||
const {
|
||||
trip,
|
||||
|
@ -53,7 +49,7 @@ const {
|
|||
})
|
||||
```
|
||||
|
||||
`realtimeDataUpdatedAt` is a UNIX timestamp reflecting the latest moment when (at least some of) the response's realtime data have been updated.
|
||||
`realtimeDataUpdatedAt` is currently not set in db-vendo-client, because the upstream APIs don't provide it.
|
||||
|
||||
When running the code above, `trip` looked like this:
|
||||
|
||||
|
@ -142,9 +138,9 @@ When running the code above, `trip` looked like this:
|
|||
|
||||
### `polyline` option
|
||||
|
||||
If you pass `polyline: true`, the trip will have a `polyline` field, containing a [GeoJSON](http://geojson.org) [`FeatureCollection`](https://tools.ietf.org/html/rfc7946#section-3.3) of [`Point`s](https://tools.ietf.org/html/rfc7946#appendix-A.1). Every `Point` next to a station will have `properties` containing the station's metadata.
|
||||
Only supported with HAFAS trip id (i.e. not with a trip id from a departure/arrival board of the `db` profile).
|
||||
|
||||
We'll look at an example for *U6* from *Alt-Mariendorf* to *Alt-Tegel*, taken from the [VBB profile](../p/vbb):
|
||||
If you pass `polyline: true`, the trip will have a `polyline` field, containing a [GeoJSON](http://geojson.org) [`FeatureCollection`](https://tools.ietf.org/html/rfc7946#section-3.3) of [`Point`s](https://tools.ietf.org/html/rfc7946#appendix-A.1).
|
||||
|
||||
```js
|
||||
{
|
||||
|
@ -152,12 +148,6 @@ We'll look at an example for *U6* from *Alt-Mariendorf* to *Alt-Tegel*, taken fr
|
|||
features: [
|
||||
{
|
||||
type: 'Feature',
|
||||
properties: {
|
||||
type: 'station',
|
||||
id: '900000070301',
|
||||
name: 'U Alt-Mariendorf',
|
||||
/* … */
|
||||
},
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [13.3875, 52.43993] // longitude, latitude
|
||||
|
@ -166,12 +156,6 @@ We'll look at an example for *U6* from *Alt-Mariendorf* to *Alt-Tegel*, taken fr
|
|||
/* … */
|
||||
{
|
||||
type: 'Feature',
|
||||
properties: {
|
||||
type: 'station',
|
||||
id: '900000017101',
|
||||
name: 'U Mehringdamm',
|
||||
/* … */
|
||||
},
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [13.38892, 52.49448] // longitude, latitude
|
||||
|
@ -189,12 +173,6 @@ We'll look at an example for *U6* from *Alt-Mariendorf* to *Alt-Tegel*, taken fr
|
|||
},
|
||||
{
|
||||
type: 'Feature',
|
||||
properties: {
|
||||
type: 'station',
|
||||
id: '900000089301',
|
||||
name: 'U Alt-Tegel',
|
||||
/* … */
|
||||
},
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [13.28406, 52.58915] // longitude, latitude
|
||||
|
|
|
@ -1,114 +0,0 @@
|
|||
# `tripsByName([lineNameOrFahrtNr], [opt])`
|
||||
|
||||
Get all trips matching one or more criteria, e.g. a specific name.
|
||||
|
||||
## Response
|
||||
|
||||
As an example, we're going to use the [VBB profile](../p/vbb):
|
||||
|
||||
```js
|
||||
import {createClient} from 'hafas-client'
|
||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
||||
|
||||
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||
const client = createClient(vbbProfile, userAgent)
|
||||
|
||||
const {
|
||||
trips,
|
||||
realtimeDataUpdatedAt,
|
||||
} = await client.tripsByName('S1')
|
||||
```
|
||||
|
||||
With `opt`, you can override the default options, which look like this:
|
||||
|
||||
```js
|
||||
{
|
||||
// use either this
|
||||
when: null,
|
||||
// or these
|
||||
fromWhen: null, untilWhen: null,
|
||||
|
||||
onlyCurrentlyRunning: true,
|
||||
products: {
|
||||
// these entries may vary from profile to profile
|
||||
suburban: true,
|
||||
subway: true,
|
||||
tram: true,
|
||||
bus: true,
|
||||
ferry: true,
|
||||
express: true,
|
||||
regional: true,
|
||||
},
|
||||
|
||||
currentlyStoppingAt: null, // only show trips currently stopping at a stop/station, string
|
||||
lineName: null, // only show trips with this line name, string
|
||||
operatorNames: null, // only show trips with these operator names, array of strings
|
||||
}
|
||||
```
|
||||
|
||||
`realtimeDataUpdatedAt` is a UNIX timestamp reflecting the latest moment when (at least some of) the response's realtime data have been updated.
|
||||
|
||||
`trips` may look like this:
|
||||
|
||||
```js
|
||||
[
|
||||
{
|
||||
id: '1|1214|0|86|16092020'
|
||||
direction: null,
|
||||
line: {
|
||||
type: 'line',
|
||||
id: 's1',
|
||||
fahrtNr: '325',
|
||||
name: 'S1',
|
||||
mode: 'train',
|
||||
product: 'suburban',
|
||||
// …
|
||||
},
|
||||
|
||||
origin: {
|
||||
type: 'stop',
|
||||
id: '900000550239',
|
||||
name: 'Warnemünde, Bhf',
|
||||
location: { /* … */ },
|
||||
products: { /* … */ },
|
||||
},
|
||||
departure: '2020-09-16T04:03:00+02:00',
|
||||
plannedDeparture: '2020-09-16T04:03:00+02:00',
|
||||
departureDelay: null,
|
||||
departurePlatform: null,
|
||||
plannedDeparturePlatform: null,
|
||||
|
||||
destination: {
|
||||
type: 'stop',
|
||||
id: '900000550002',
|
||||
name: 'Rostock, Hbf',
|
||||
location: { /* … */ },
|
||||
products: { /* … */ },
|
||||
},
|
||||
arrival: '2020-09-16T04:24:00+02:00',
|
||||
plannedArrival: '2020-09-16T04:24:00+02:00',
|
||||
arrivalDelay: null,
|
||||
arrivalPlatform: null,
|
||||
plannedArrivalPlatform: null,
|
||||
},
|
||||
// …
|
||||
{
|
||||
id: '1|62554|0|86|16092020'
|
||||
direction: null,
|
||||
line: {
|
||||
type: 'line',
|
||||
id: 's1',
|
||||
fahrtNr: '2001',
|
||||
name: 'S1',
|
||||
public: true,
|
||||
mode: 'train',
|
||||
product: 'suburban',
|
||||
// …
|
||||
},
|
||||
|
||||
origin: { /* … */ },
|
||||
destination: { /* … */ },
|
||||
// …
|
||||
}
|
||||
]
|
||||
```
|
|
@ -1,163 +0,0 @@
|
|||
# Writing a profile
|
||||
|
||||
**Per HAFAS endpoint, `hafas-client` has an endpoint-specific customisation called *profile*.** A profile may, for example, do the following:
|
||||
|
||||
- handle the additional requirements of the endpoint (e.g. authentication),
|
||||
- extract additional information from the data provided by the endpoint,
|
||||
- guard against triggering bugs of certain endpoints (e.g. time limits).
|
||||
|
||||
This guide is about writing such a profile. If you just want to use an already supported endpoint, refer to the [main readme](../readme.md) instead.
|
||||
|
||||
*Note*: **If you get stuck, ask for help by [creating an issue](https://github.com/public-transport/hafas-client/issues/new)**; We're happy to help you expand the scope of this library!
|
||||
|
||||
## 0. How do the profiles work?
|
||||
|
||||
A profile may consist of three things:
|
||||
|
||||
- **mandatory details about the HAFAS endpoint**
|
||||
- `endpoint`: The protocol, host and path of the endpoint.
|
||||
- `locale`: The [BCP 47](https://en.wikipedia.org/wiki/IETF_language_tag) [locale](https://en.wikipedia.org/wiki/Locale_(computer_software)) of your endpoint (or the area that your endpoint covers).
|
||||
- `timezone`: An [IANA-time-zone](https://www.iana.org/time-zones)-compatible [timezone](https://en.wikipedia.org/wiki/Time_zone) of your endpoint.
|
||||
- **flags indicating which features are supported by the endpoint** – e.g. `trip`
|
||||
- **methods overriding the [default profile](../lib/default-profile.js)**
|
||||
|
||||
Let's use a fictional endpoint for [Austria](https://en.wikipedia.org/wiki/Austria) as an example:
|
||||
|
||||
```js
|
||||
const myProfile = {
|
||||
endpoint: 'https://example.org/bin/mgate.exe',
|
||||
locale: 'de-AT',
|
||||
timezone: 'Europe/Vienna'
|
||||
}
|
||||
```
|
||||
|
||||
Assuming their HAFAS endpoint returns all line names prefixed with `foo `, we can adapt our profile to clean them:
|
||||
|
||||
```js
|
||||
// get the default line parser
|
||||
import {parseLine} from 'hafas-client/parse/line.js'
|
||||
|
||||
// wrapper function with additional logic
|
||||
const parseLineWithoutFoo = (ctx, rawLine) => {
|
||||
const line = parseLine(ctx, rawLine)
|
||||
line.name = line.name.replace(/foo /g, '')
|
||||
return line
|
||||
}
|
||||
|
||||
myProfile.parseLine = parseLineWithoutFoo
|
||||
```
|
||||
|
||||
If you pass this profile into `hafas-client`, the `parseLine` method will override [the default one](../parse/line.js).
|
||||
|
||||
You can also use the `parseHook` helper to reduce boilerplate:
|
||||
|
||||
```js
|
||||
import {parseHook} from 'hafas-client/lib/profile-hooks.js'
|
||||
|
||||
const removeFoo = (ctx, rawLine) => ({
|
||||
...ctx.parsed,
|
||||
name: line.name.replace(/foo /g, '')
|
||||
})
|
||||
|
||||
myProfile.parseLine = parseHook(parseLine, removeFoo)
|
||||
```
|
||||
|
||||
## 1. Setup
|
||||
|
||||
*Note*: There are many ways to find the required values. This way is rather easy and works with most endpoints by now.
|
||||
|
||||
1. **Find the journey planning webapp** corresponding to the API endpoint; Usually, you can find it on the public transport provider's website.
|
||||
2. **Open your [browser's devtools](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools)**, switch to the "Network" tab, and **inspect the requests to the HAFAS API**.
|
||||
|
||||
If you can't find the webapp or your public transport provider doesn't have one, you can inspect their mobile app's traffic instead:
|
||||
|
||||
1. Get an iOS or Android device and **download the "official" app.**
|
||||
2. **Configure a [man-in-the-middle HTTP proxy](https://docs.mitmproxy.org/stable/concepts-howmitmproxyworks/)** like [mitmproxy](https://mitmproxy.org).
|
||||
- Configure your device to trust the self-signed SSL certificate, [as outlined in the mitmproxy docs](https://docs.mitmproxy.org/stable/concepts-certificates/).
|
||||
- *Note*: This method does not work if the app uses [public key pinning](https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning). In this case (the app won't be able to query data), please [create an issue](https://github.com/public-transport/hafas-client/issues/new), so we can discuss other techniques.
|
||||
3. **Record requests of the app.**
|
||||
- [There's a video showing this step](https://stuff.jannisr.de/how-to-record-hafas-requests.mp4).
|
||||
- Make sure to cover all relevant sections of the app, e.g. "journeys", "departures", "live map". Better record more than less!
|
||||
- To help others in the future, post the requests (in their entirety!) on GitHub, e.g. in as format like [this](https://gist.github.com/derhuerst/5fa86ed5aec63645e5ae37e23e555886). This will also let us help you if you have any questions.
|
||||
|
||||
## 2. Basic profile
|
||||
|
||||
*Note:* You should have read the [general documentation on `mgate.exe` APIs](hafas-mgate-api.md) to make sense of the terminology used below.
|
||||
|
||||
You may want to start with the [profile boilerplate](profile-boilerplate.js).
|
||||
|
||||
- **Identify the `endpoint`.** The protocol, host and path of the endpoint, *but not* the query string.
|
||||
- *Note*: **`hafas-client` for now only supports the interface providing JSON** (generated from XML), which is being used by the corresponding iOS/Android apps. It supports neither the JSONP, nor the XML, nor the HTML interface. If the endpoint does not end in `mgate.exe`, it mostly likely won't work.
|
||||
- **Identify the `locale`.** Basically guess work; Use the date & time formats as an indicator.
|
||||
- **Identify the `timezone`.** This may be tricky, a for example [Deutsche Bahn](https://en.wikipedia.org/wiki/Deutsche_Bahn) returns departures for Moscow as `+01:00` instead of `+03:00`.
|
||||
- **Copy the authentication** and other meta fields, namely `ver`, `ext`, `client` and `lang`.
|
||||
- You can find these fields in the root of each request JSON. Check [a VBB request](https://gist.github.com/derhuerst/ea5d6482b61aeb7384a2c788f43dc11d#file-0-serverinfo-http-L11-L33) and [the corresponding VBB profile](https://github.com/public-transport/hafas-client/blob/2baf2f6f0444ffc67317f8bafe0fe05f687e5fae/p/vbb/base.json#L2-L11) for an example.
|
||||
- Add a function `transformReqBody(ctx, body)` to your profile, which adds the fields to `body`. todo: adapt this
|
||||
- Some profiles have a `checksum` parameter (like [here](https://gist.github.com/derhuerst/2a735268bd82a0a6779633f15dceba33#file-journey-details-1-http-L1)) or two `mic` & `mac` parameters (like [here](https://gist.github.com/derhuerst/5fa86ed5aec63645e5ae37e23e555886#file-1-http-L1)). If you see one of them in your requests, jump to the [*Authentication* section of the `mgate.exe` docs](hafas-mgate-api.md#authentication). Unfortunately, this is necessary to get the profile working.
|
||||
|
||||
## 3. Products
|
||||
|
||||
In `hafas-client`, there's a distinction between the `mode` and the `product` fields:
|
||||
|
||||
- The `mode` field describes the mode of transport in general. [Standardised by the *Friendly Public Transport Format*](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md#modes), it is on purpose limited to a very small number of possible values, e.g. `train` or `bus`.
|
||||
- The value for `product` relates to how a means of transport "works" *in local context*. Example: Even though [*S-Bahn*](https://en.wikipedia.org/wiki/Berlin_S-Bahn) and [*U-Bahn*](https://en.wikipedia.org/wiki/Berlin_U-Bahn) in Berlin are both `train`s, they have different operators, service patterns, stations and look different. Therefore, they are two distinct `product`s `subway` and `suburban`.
|
||||
|
||||
**Specify `product`s that appear in the app** you recorded requests of. For a fictional transit network, this may look like this:
|
||||
|
||||
```js
|
||||
const products = [
|
||||
{
|
||||
id: 'commuterTrain',
|
||||
mode: 'train',
|
||||
bitmasks: [16],
|
||||
name: 'ACME Commuter Rail',
|
||||
short: 'CR',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
id: 'metro',
|
||||
mode: 'train',
|
||||
bitmasks: [8],
|
||||
name: 'Foo Bar Metro',
|
||||
short: 'M',
|
||||
default: true
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Let's break this down:
|
||||
|
||||
- `id`: A sensible, [camelCased](https://en.wikipedia.org/wiki/Camel_case#Variations_and_synonyms), alphanumeric identifier. Use it for the key in the `products` array as well.
|
||||
- `mode`: A [valid *Friendly Public Transport Format* mode](https://github.com/public-transport/friendly-public-transport-format/blob/3bd36faa721e85d9f5ca58fb0f38cdbedb87bbca/spec/readme.md#modes).
|
||||
- `bitmasks`: HAFAS endpoints work with a [bitmask](https://en.wikipedia.org/wiki/Mask_(computing)#Arguments_to_functions) that toggles the individual products. It should be an array of values that toggle the appropriate bit(s) in the bitmask (see below).
|
||||
- `name`: A short, but distinct name for the means of transport, *just precise enough in local context*, and in the local language. In Berlin, `S-Bahn-Schnellzug` would be too much, because everyone knows what `S-Bahn` means.
|
||||
- `short`: The shortest possible symbol that identifies the product.
|
||||
- `default`: Should the product be used for queries (e.g. journeys) by default?
|
||||
|
||||
If you want, you can now **verify that the profile works**; We've prepared [a script](https://runkit.com/derhuerst/hafas-client-profile-example/0.2.1) for that. Alternatively, [submit a Pull Request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) and we will help you out with testing and improvements.
|
||||
|
||||
### Finding the right values for the `bitmasks` field
|
||||
|
||||
As shown in [the video](https://stuff.jannisr.de/how-to-record-hafas-requests.mp4), search for a journey and toggle off one product at a time, recording the requests. After extracting the products bitmask ([example](https://gist.github.com/derhuerst/193ef489f8aa50c2343f8bf1f2a22069#file-via-http-L34)) you will end up with values looking like these:
|
||||
|
||||
```
|
||||
toggles value binary subtraction bit(s)
|
||||
all products 31 11111 31 - 0
|
||||
all but ACME Commuter Rail 15 01111 31 - 2^4 2^4
|
||||
all but Foo Bar Metro 23 10111 31 - 2^3 2^3
|
||||
all but product E 25 11001 31 - 2^2 - 2^1 2^2, 2^1
|
||||
all but product F 30 11110 31 - 2^0 2^0
|
||||
```
|
||||
|
||||
## 4. Additional info
|
||||
|
||||
We consider these improvements to be *optional*:
|
||||
- **Check if the endpoint supports the `trip()` call.**
|
||||
- In the app, check if you can re-fetch details for the status of a single journey leg. It should load realtime delays and the current progress.
|
||||
- If this feature is supported, add `trip: true` to the profile.
|
||||
- **Check if the endpoint supports the live map call.** Does the app have a "live map" showing all vehicles within an area? If so, add `radar: true` to the profile.
|
||||
- **Consider transforming station & line names** into the formats that's most suitable for *local users*. This is just an optimal optimisation that makes it easier for users of the profile to use the data. Some examples:
|
||||
- `M13 (Tram)` -> `M13`. With Berlin context, it is obvious that `M13` is a tram.
|
||||
- `Berlin Jungfernheide Bhf` -> `Berlin Jungfernheide`. With local context, it's obvious that *Jungfernheide* is a train station.
|
||||
- **Check if the endpoint has non-obvious limitations** and let use know about these. Examples:
|
||||
- Some endpoints have a time limit, after which they won't return more departures, but silently discard them.
|
62
eslint.config.js
Normal file
62
eslint.config.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
import eslintPluginJs from '@eslint/js';
|
||||
import eslintPluginStylistic from '@stylistic/eslint-plugin';
|
||||
import globals from 'globals';
|
||||
|
||||
|
||||
const config = [
|
||||
eslintPluginJs.configs.recommended,
|
||||
eslintPluginStylistic.configs['all-flat'],
|
||||
{
|
||||
files: ['**/*.js'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
globals: {
|
||||
...globals.node,
|
||||
},
|
||||
sourceType: 'module',
|
||||
},
|
||||
rules: {
|
||||
'@stylistic/array-bracket-newline': ['error', 'consistent'],
|
||||
'@stylistic/array-element-newline': ['error', 'consistent'],
|
||||
'@stylistic/arrow-parens': 'off',
|
||||
'@stylistic/comma-dangle': ['error', 'always-multiline'],
|
||||
'@stylistic/dot-location': ['error', 'property'],
|
||||
'@stylistic/function-call-argument-newline': ['error', 'consistent'],
|
||||
'@stylistic/function-paren-newline': 'off',
|
||||
'@stylistic/indent': ['error', 'tab'],
|
||||
'@stylistic/indent-binary-ops': ['error', 'tab'],
|
||||
'@stylistic/max-len': 'off',
|
||||
'@stylistic/multiline-comment-style': 'off',
|
||||
'@stylistic/multiline-ternary': ['error', 'always-multiline'],
|
||||
'@stylistic/newline-per-chained-call': ['error', {ignoreChainWithDepth: 1}],
|
||||
'@stylistic/no-extra-parens': 'off',
|
||||
'@stylistic/no-mixed-operators': 'off',
|
||||
'@stylistic/no-tabs': 'off',
|
||||
'@stylistic/object-property-newline': 'off',
|
||||
'@stylistic/one-var-declaration-per-line': 'off',
|
||||
'@stylistic/operator-linebreak': ['error', 'before'],
|
||||
'@stylistic/padded-blocks': 'off',
|
||||
'@stylistic/quote-props': ['error', 'consistent-as-needed'],
|
||||
'@stylistic/quotes': ['error', 'single'],
|
||||
'curly': 'error',
|
||||
'no-implicit-coercion': 'error',
|
||||
'no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
vars: 'all',
|
||||
args: 'none',
|
||||
ignoreRestSiblings: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['test/**', '**/example.js'],
|
||||
rules: {
|
||||
'no-unused-vars': 'off',
|
||||
'@stylistic/semi': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default config;
|
|
@ -16,7 +16,7 @@ const formatDate = (profile, when) => {
|
|||
locale: profile.locale,
|
||||
zone: timezone,
|
||||
})
|
||||
.toFormat('yyyyMMdd');
|
||||
.toFormat('yyyy-MM-dd');
|
||||
};
|
||||
|
||||
export {
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
const bike = {type: 'BC', mode: 'INC'};
|
||||
|
||||
const accessibility = {
|
||||
none: {type: 'META', mode: 'INC', meta: 'notBarrierfree'},
|
||||
partial: {type: 'META', mode: 'INC', meta: 'limitedBarrierfree'},
|
||||
complete: {type: 'META', mode: 'INC', meta: 'completeBarrierfree'},
|
||||
};
|
||||
|
||||
export {
|
||||
bike,
|
||||
accessibility,
|
||||
};
|
|
@ -1,12 +0,0 @@
|
|||
const formatLinesReq = (ctx, query) => {
|
||||
return {
|
||||
meth: 'LineMatch',
|
||||
req: {
|
||||
input: query,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
formatLinesReq,
|
||||
};
|
|
@ -1,14 +0,0 @@
|
|||
const formatLocationFilter = (stops, addresses, poi) => {
|
||||
if (stops && addresses && poi) {
|
||||
return 'ALL';
|
||||
}
|
||||
return (
|
||||
(stops ? 'S' : '')
|
||||
+ (addresses ? 'A' : '')
|
||||
+ (poi ? 'P' : '')
|
||||
);
|
||||
};
|
||||
|
||||
export {
|
||||
formatLocationFilter,
|
||||
};
|
|
@ -1,22 +0,0 @@
|
|||
const formatLocationsReq = (ctx, query) => {
|
||||
const {profile, opt} = ctx;
|
||||
|
||||
return {
|
||||
cfg: {polyEnc: 'GPA'},
|
||||
meth: 'LocMatch',
|
||||
req: {input: {
|
||||
loc: {
|
||||
type: profile.formatLocationFilter(opt.stops, opt.addresses, opt.poi),
|
||||
name: opt.fuzzy
|
||||
? query + '?'
|
||||
: query,
|
||||
},
|
||||
maxLoc: opt.results,
|
||||
field: 'S', // todo: what is this?
|
||||
}},
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
formatLocationsReq,
|
||||
};
|
65
format/loyalty-cards.js
Normal file
65
format/loyalty-cards.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
const c = {
|
||||
NONE: Symbol('no loyalty card'),
|
||||
BAHNCARD: Symbol('Bahncard'),
|
||||
VORTEILSCARD: Symbol('VorteilsCard'),
|
||||
HALBTAXABO: Symbol('HalbtaxAbo'),
|
||||
VOORDEELURENABO: Symbol('Voordeelurenabo'),
|
||||
SHCARD: Symbol('SH-Card'),
|
||||
GENERALABONNEMENT: Symbol('General-Abonnement'),
|
||||
NL_40: Symbol('NL-40%'),
|
||||
AT_KLIMATICKET: Symbol('AT-KlimaTicket'),
|
||||
};
|
||||
|
||||
const formatLoyaltyCard = (data) => {
|
||||
if (!data) {
|
||||
return {
|
||||
art: 'KEINE_ERMAESSIGUNG',
|
||||
klasse: 'KLASSENLOS',
|
||||
};
|
||||
}
|
||||
const cls = data.class === 1 ? 'KLASSE_1' : 'KLASSE_2';
|
||||
if (data.type.toString() === c.BAHNCARD.toString()) {
|
||||
return {
|
||||
art: 'BAHNCARD' + (data.business ? 'BUSINESS' : '') + data.discount,
|
||||
klasse: cls,
|
||||
};
|
||||
}
|
||||
if (data.type.toString() === c.VORTEILSCARD.toString()) {
|
||||
return {
|
||||
art: 'A-VORTEILSCARD',
|
||||
klasse: 'KLASSENLOS',
|
||||
};
|
||||
}
|
||||
if (data.type.toString() === c.HALBTAXABO.toString()) {
|
||||
return {
|
||||
art: 'CH-HALBTAXABO_OHNE_RAILPLUS',
|
||||
klasse: 'KLASSENLOS',
|
||||
};
|
||||
}
|
||||
if (data.type.toString() === c.GENERALABONNEMENT.toString()) {
|
||||
return {
|
||||
art: 'CH-GENERAL-ABONNEMENT',
|
||||
klasse: cls,
|
||||
};
|
||||
}
|
||||
if (data.type.toString() === c.NL_40.toString()) {
|
||||
return {
|
||||
art: 'NL-40_OHNE_RAILPLUS',
|
||||
klasse: 'KLASSENLOS',
|
||||
};
|
||||
}
|
||||
if (data.type.toString() === c.AT_KLIMATICKET.toString()) {
|
||||
return {
|
||||
art: 'KLIMATICKET_OE',
|
||||
klasse: 'KLASSENLOS',
|
||||
};
|
||||
}
|
||||
return {
|
||||
art: 'KEINE_ERMAESSIGUNG',
|
||||
klasse: 'KLASSENLOS',
|
||||
};
|
||||
};
|
||||
export {
|
||||
c as data,
|
||||
formatLoyaltyCard,
|
||||
};
|
|
@ -1,28 +0,0 @@
|
|||
const formatNearbyReq = (ctx, location) => {
|
||||
const {profile, opt} = ctx;
|
||||
|
||||
return {
|
||||
cfg: {polyEnc: 'GPA'},
|
||||
meth: 'LocGeoPos',
|
||||
req: {
|
||||
ring: {
|
||||
cCrd: {
|
||||
x: profile.formatCoord(location.longitude),
|
||||
y: profile.formatCoord(location.latitude),
|
||||
},
|
||||
maxDist: opt.distance || -1,
|
||||
minDist: 0,
|
||||
},
|
||||
locFltrL: [
|
||||
profile.formatProductsFilter(ctx, opt.products || {}),
|
||||
],
|
||||
getPOIs: Boolean(opt.poi),
|
||||
getStops: Boolean(opt.stops),
|
||||
maxLoc: opt.results,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
formatNearbyReq,
|
||||
};
|
|
@ -1,8 +1,8 @@
|
|||
import isObj from 'lodash/isObject.js';
|
||||
const isObj = element => element !== null && 'object' === typeof element && !Array.isArray(element);
|
||||
|
||||
const hasProp = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
|
||||
|
||||
const formatProductsFilter = (ctx, filter) => {
|
||||
const formatProductsFilter = (ctx, filter, key = 'vendo') => {
|
||||
if (!isObj(filter)) {
|
||||
throw new TypeError('products filter must be an object');
|
||||
}
|
||||
|
@ -16,28 +16,29 @@ const formatProductsFilter = (ctx, filter) => {
|
|||
}
|
||||
filter = Object.assign({}, defaultProducts, filter);
|
||||
|
||||
let res = 0, products = 0;
|
||||
let products = [];
|
||||
let foundDeselected = false;
|
||||
for (let product in filter) {
|
||||
if (!hasProp(filter, product) || filter[product] !== true) {
|
||||
foundDeselected = true;
|
||||
continue;
|
||||
}
|
||||
if (!byProduct[product]) {
|
||||
throw new TypeError('unknown product ' + product);
|
||||
}
|
||||
products++;
|
||||
for (let bitmask of byProduct[product].bitmasks) {
|
||||
res = res | bitmask;
|
||||
}
|
||||
products.push(byProduct[product][key]);
|
||||
}
|
||||
if (products === 0) {
|
||||
if (products.length === 0) {
|
||||
throw new Error('no products used');
|
||||
}
|
||||
if (!foundDeselected && key == 'ris') {
|
||||
return undefined;
|
||||
}
|
||||
if (!foundDeselected && key == 'dbnav') {
|
||||
return ['ALL'];
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'PROD',
|
||||
mode: 'INC',
|
||||
value: String(res),
|
||||
};
|
||||
return products;
|
||||
};
|
||||
|
||||
export {
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
const formatRadarReq = (ctx, north, west, south, east) => {
|
||||
const {profile, opt} = ctx;
|
||||
|
||||
return {
|
||||
meth: 'JourneyGeoPos',
|
||||
req: {
|
||||
maxJny: opt.results,
|
||||
onlyRT: false, // todo: does this mean "only realtime"?
|
||||
date: profile.formatDate(profile, opt.when),
|
||||
time: profile.formatTime(profile, opt.when),
|
||||
// todo: would a ring work here as well?
|
||||
rect: profile.formatRectangle(profile, north, west, south, east),
|
||||
perSize: opt.duration * 1000,
|
||||
perStep: Math.round(opt.duration / Math.max(opt.frames, 1) * 1000),
|
||||
ageOfReport: true, // todo: what is this?
|
||||
jnyFltrL: [
|
||||
profile.formatProductsFilter(ctx, opt.products || {}),
|
||||
],
|
||||
// todo: what is this? what about realtime?
|
||||
// - CALC
|
||||
// - CALC_REPORT (as seen in the INSA Young app)
|
||||
trainPosMode: 'CALC',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
formatRadarReq,
|
||||
};
|
|
@ -1,24 +0,0 @@
|
|||
const formatReachableFromReq = (ctx, address) => {
|
||||
const {profile, opt} = ctx;
|
||||
|
||||
return {
|
||||
meth: 'LocGeoReach',
|
||||
req: {
|
||||
loc: profile.formatLocation(profile, address, 'address'),
|
||||
maxDur: opt.maxDuration === null
|
||||
? -1
|
||||
: opt.maxDuration,
|
||||
maxChg: opt.maxTransfers,
|
||||
date: profile.formatDate(profile, opt.when),
|
||||
time: profile.formatTime(profile, opt.when),
|
||||
period: 120, // todo: what is this?
|
||||
jnyFltrL: [
|
||||
profile.formatProductsFilter(ctx, opt.products || {}),
|
||||
],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
formatReachableFromReq,
|
||||
};
|
|
@ -1,16 +0,0 @@
|
|||
const formatRectangle = (profile, north, west, south, east) => {
|
||||
return {
|
||||
llCrd: {
|
||||
x: profile.formatCoord(west),
|
||||
y: profile.formatCoord(south),
|
||||
},
|
||||
urCrd: {
|
||||
x: profile.formatCoord(east),
|
||||
y: profile.formatCoord(north),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
formatRectangle,
|
||||
};
|
|
@ -1,24 +0,0 @@
|
|||
const formatRefreshJourneyReq = (ctx, refreshToken) => {
|
||||
const {profile, opt} = ctx;
|
||||
|
||||
const req = {
|
||||
getIST: true, // todo: make an option
|
||||
getPasslist: Boolean(opt.stopovers),
|
||||
getPolyline: Boolean(opt.polylines),
|
||||
getTariff: Boolean(opt.tickets),
|
||||
};
|
||||
if (profile.refreshJourneyUseOutReconL) {
|
||||
req.outReconL = [{ctx: refreshToken}];
|
||||
} else {
|
||||
req.ctxRecon = refreshToken;
|
||||
}
|
||||
|
||||
return {
|
||||
meth: 'Reconstruction',
|
||||
req,
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
formatRefreshJourneyReq,
|
||||
};
|
|
@ -1,38 +0,0 @@
|
|||
const formatRemarksReq = (ctx) => {
|
||||
const {profile, opt} = ctx;
|
||||
|
||||
const himFltrL = [];
|
||||
// todo: https://github.com/marudor/BahnhofsAbfahrten/blob/95fef0217d01344642dd423457473fe9b8b6056e/src/types/HAFAS/index.ts#L76-L91
|
||||
if (opt.products) {
|
||||
himFltrL.push(profile.formatProductsFilter(ctx, opt.products));
|
||||
}
|
||||
|
||||
const req = {
|
||||
himFltrL,
|
||||
};
|
||||
if (profile.remarksGetPolyline) {
|
||||
req.getPolyline = Boolean(opt.polylines);
|
||||
}
|
||||
// todo: stLoc, dirLoc
|
||||
// todo: comp, dept, onlyHimId, onlyToday
|
||||
// todo: dailyB, dailyE
|
||||
// see https://github.com/marudor/BahnhofsAbfahrten/blob/46a74957d68edc15713112df44e1a25150f5a178/src/types/HAFAS/HimSearch.ts#L3-L18
|
||||
|
||||
if (opt.results !== null) {
|
||||
req.maxNum = opt.results;
|
||||
}
|
||||
if (opt.from !== null) {
|
||||
req.dateB = profile.formatDate(profile, opt.from);
|
||||
req.timeB = profile.formatTime(profile, opt.from);
|
||||
}
|
||||
if (opt.to !== null) {
|
||||
req.dateE = profile.formatDate(profile, opt.to);
|
||||
req.timeE = profile.formatTime(profile, opt.to);
|
||||
}
|
||||
|
||||
return {meth: 'HimSearch', req};
|
||||
};
|
||||
|
||||
export {
|
||||
formatRemarksReq,
|
||||
};
|
|
@ -1,42 +0,0 @@
|
|||
const formatStationBoardReq = (ctx, station, type) => {
|
||||
const {profile, opt} = ctx;
|
||||
|
||||
const jnyFltrL = [
|
||||
profile.formatProductsFilter(ctx, opt.products || {}),
|
||||
];
|
||||
if (opt.line !== null) {
|
||||
jnyFltrL.push({type: 'LINEID', mode: 'INC', value: opt.line});
|
||||
}
|
||||
|
||||
const req = {
|
||||
type,
|
||||
date: profile.formatDate(profile, opt.when),
|
||||
time: profile.formatTime(profile, opt.when),
|
||||
stbLoc: station,
|
||||
dirLoc: opt.direction
|
||||
? profile.formatStation(opt.direction)
|
||||
: undefined,
|
||||
jnyFltrL,
|
||||
dur: opt.duration,
|
||||
};
|
||||
if (opt.results !== null) {
|
||||
req.maxJny = opt.results === Infinity
|
||||
? 10000
|
||||
: opt.results;
|
||||
}
|
||||
if (profile.departuresGetPasslist) {
|
||||
req.getPasslist = Boolean(opt.stopovers);
|
||||
}
|
||||
if (profile.departuresStbFltrEquiv) {
|
||||
req.stbFltrEquiv = !opt.includeRelatedStations;
|
||||
}
|
||||
|
||||
return {
|
||||
meth: 'StationBoard',
|
||||
req,
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
formatStationBoardReq,
|
||||
};
|
|
@ -1,6 +1,11 @@
|
|||
import {formatLocationIdentifier} from './location-identifier.js';
|
||||
|
||||
const isIBNR = /^\d{6,}$/;
|
||||
|
||||
const formatStation = (id) => {
|
||||
if (!isIBNR.test(id)) {
|
||||
throw new Error('station ID must be an IBNR.');
|
||||
}
|
||||
return {
|
||||
type: 'S', // station
|
||||
// todo: name necessary?
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
const formatStopReq = (ctx, stopRef) => {
|
||||
return {
|
||||
// todo: there's also `StationDetails`, are there differences?
|
||||
meth: 'LocDetails',
|
||||
req: {
|
||||
locL: [stopRef],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
formatStopReq,
|
||||
};
|
|
@ -1,8 +1,7 @@
|
|||
import {DateTime, IANAZone} from 'luxon';
|
||||
import {luxonIANAZonesByProfile as timezones} from '../lib/luxon-timezones.js';
|
||||
|
||||
// todo: change to `(profile) => (when) => {}`
|
||||
const formatTime = (profile, when) => {
|
||||
const getTimezone = (profile) => {
|
||||
let timezone;
|
||||
if (timezones.has(profile)) {
|
||||
timezone = timezones.get(profile);
|
||||
|
@ -10,15 +9,35 @@ const formatTime = (profile, when) => {
|
|||
timezone = new IANAZone(profile.timezone);
|
||||
timezones.set(profile, timezone);
|
||||
}
|
||||
return timezone;
|
||||
};
|
||||
|
||||
const formatTime = (profile, when, includeOffset = false) => {
|
||||
const timezone = getTimezone(profile);
|
||||
|
||||
return DateTime
|
||||
.fromMillis(Number(when), {
|
||||
locale: profile.locale,
|
||||
zone: timezone,
|
||||
})
|
||||
.toFormat('HHmmss');
|
||||
.startOf('second')
|
||||
.toISO({includeOffset: includeOffset, suppressMilliseconds: true});
|
||||
};
|
||||
|
||||
const formatTimeOfDay = (profile, when) => {
|
||||
const timezone = getTimezone(profile);
|
||||
|
||||
return DateTime
|
||||
.fromMillis(Number(when), {
|
||||
locale: profile.locale,
|
||||
zone: timezone,
|
||||
})
|
||||
.toFormat('HH:mm');
|
||||
};
|
||||
|
||||
export {
|
||||
formatTime,
|
||||
formatTimeOfDay,
|
||||
};
|
||||
|
||||
|
||||
|
|
10
format/transfers.js
Normal file
10
format/transfers.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
const formatTransfers = (transfers) => {
|
||||
if (transfers === -1) { // profiles may not accept -1: https://github.com/public-transport/db-vendo-client/issues/5
|
||||
return undefined;
|
||||
}
|
||||
return transfers;
|
||||
};
|
||||
|
||||
export {
|
||||
formatTransfers,
|
||||
};
|
48
format/travellers.js
Normal file
48
format/travellers.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
const formatTraveller = ({profile}, ageGroup, age, loyaltyCard) => {
|
||||
const tvlrAgeGroup = age
|
||||
? profile.ageGroupFromAge(age)
|
||||
: ageGroup;
|
||||
let r = {
|
||||
typ: profile.ageGroupLabel[tvlrAgeGroup || profile.ageGroup.ADULT],
|
||||
anzahl: 1,
|
||||
alter: age
|
||||
? [String(age)]
|
||||
: [],
|
||||
ermaessigungen: [profile.formatLoyaltyCard(loyaltyCard)],
|
||||
};
|
||||
return r;
|
||||
};
|
||||
|
||||
const validateArr = (field, length) => {
|
||||
return !field || Array.isArray(field) && field.length == length;
|
||||
};
|
||||
|
||||
const formatTravellers = ({profile, opt}) => {
|
||||
if ('age' in opt && 'ageGroup' in opt) {
|
||||
throw new TypeError(`\
|
||||
opt.age and opt.ageGroup are mutually exclusive.
|
||||
Pass in just opt.age, and the age group will calculated automatically.`);
|
||||
}
|
||||
let travellers = [];
|
||||
if (Array.isArray(opt.loyaltyCard) || Array.isArray(opt.age) || Array.isArray(opt.ageGroup)) {
|
||||
const len = opt.loyaltyCard?.length || opt.age?.length || opt.ageGroup?.length;
|
||||
if (!validateArr(opt.loyaltyCard, len) || !validateArr(opt.age, len) || !validateArr(opt.ageGroup, len)) {
|
||||
throw new TypeError('If any of loyaltyCard, age or ageGroup are an array, all given must be an array of the same length.');
|
||||
}
|
||||
for (let i = 0; i < len; i++) {
|
||||
travellers.push(formatTraveller({profile}, opt.ageGroup && opt.ageGroup[i], opt.age && opt.age[i], opt.loyaltyCard && opt.loyaltyCard[i]));
|
||||
}
|
||||
} else {
|
||||
travellers.push(formatTraveller({profile}, opt.ageGroup, opt.age, opt.loyaltyCard));
|
||||
}
|
||||
|
||||
const basicCtrfReq = {
|
||||
klasse: opt.firstClass === true ? 'KLASSE_1' : 'KLASSE_2',
|
||||
reisende: travellers,
|
||||
};
|
||||
return basicCtrfReq;
|
||||
};
|
||||
|
||||
export {
|
||||
formatTravellers,
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue