mirror of
https://github.com/public-transport/db-vendo-client.git
synced 2025-06-19 10:42:33 +03:00
Compare commits
213 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 | ||
|
08db80f165 | ||
|
e411a4b60e | ||
|
b45449a20e | ||
|
1d23bef765 | ||
|
52f0dd19bb | ||
|
96f97f245e | ||
|
d2b490a4ff | ||
|
329c89c4d4 | ||
|
3c8bb905f3 | ||
|
d001fcc1a4 | ||
|
8377202866 | ||
|
6da22a0a12 | ||
|
97268d7a8a | ||
|
1a0b595a71 | ||
|
6e74b9ab60 | ||
|
65096a85b6 | ||
|
7f78dc9458 | ||
|
d3bc9d351d | ||
|
191b9abb6d | ||
|
c4966aeca7 | ||
|
784d273adf | ||
|
9365c00aaf | ||
|
248adb5f72 | ||
|
fd90abdeca | ||
|
c826100bc8 | ||
|
336a9ba115 | ||
|
9eeafd0ae8 | ||
|
613609782d | ||
|
90b1140401 | ||
|
66d9fb5194 | ||
|
228c72531b | ||
|
26c56f8dc6 | ||
|
c85f083db5 | ||
|
8a17401693 | ||
|
eec06ba81a | ||
|
a157d0b15f | ||
|
5660f602a7 | ||
|
8520eb3d1c | ||
|
160039df10 | ||
|
f29ced5b2d | ||
|
b12d235bae | ||
|
5287ced44c | ||
|
9a3a1d3b0c | ||
|
244e88dec0 | ||
|
ecc8fccc54 | ||
|
02c781b180 | ||
|
449d2261bd | ||
|
0bc6ba3650 | ||
|
a8401f36e1 | ||
|
45610fc951 | ||
|
581a47510d | ||
|
19cdde0655 | ||
|
8ff945c075 | ||
|
793cc9eee5 | ||
|
5ce0129c36 | ||
|
92bbc63590 | ||
|
f9c24a4a84 | ||
|
4cb7062302 | ||
|
24ad6117b0 | ||
|
b8f0ab0fd6 | ||
|
4116b53e9b | ||
|
9a1ef7c586 | ||
|
dcc01d1413 | ||
|
1e3cbc09a1 | ||
|
f45842d7a3 | ||
|
0e023136b8 | ||
|
02dc6aef12 | ||
|
d6307aa24b | ||
|
ab3f3636ff | ||
|
14c9805ad8 | ||
|
8faf8ba507 | ||
|
9d35d83c97 | ||
|
e7602e6c84 | ||
|
5910d62535 | ||
|
0f3d6ec858 | ||
|
673eb4d6c7 |
581 changed files with 56679 additions and 134157 deletions
2
.dockerignore
Normal file
2
.dockerignore
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.git
|
||||||
|
node_modules
|
|
@ -1,36 +0,0 @@
|
||||||
{
|
|
||||||
"env": {
|
|
||||||
"es2021": true,
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"extends": "eslint:recommended",
|
|
||||||
"globals": {
|
|
||||||
"Atomics": "readonly",
|
|
||||||
"SharedArrayBuffer": "readonly"
|
|
||||||
},
|
|
||||||
"ignorePatterns": ["node_modules", "*example.js"],
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 2021,
|
|
||||||
"sourceType": "module"
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"no-unused-vars": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"vars": "all",
|
|
||||||
"args": "none",
|
|
||||||
"ignoreRestSiblings": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"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 }}
|
74
.github/workflows/test.yml
vendored
74
.github/workflows/test.yml
vendored
|
@ -1,57 +1,101 @@
|
||||||
name: test
|
name: test
|
||||||
|
|
||||||
on: [push, pull_request]
|
on: [push, pull_request, workflow_call]
|
||||||
|
|
||||||
|
env:
|
||||||
|
npm_config_cache: /tmp/npm-cache
|
||||||
|
|
||||||
jobs:
|
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:
|
unit-tests:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [16.x, 18.x]
|
node-version:
|
||||||
|
- 18.x
|
||||||
|
- 20.x
|
||||||
|
- 22.x
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: setup Node.js v${{ matrix.node-version }}
|
- name: setup Node.js v${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
|
|
||||||
|
- id: cache-npm
|
||||||
|
name: restore npm cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
key: npm-cache-${{ github.ref_name }}-${{ matrix.node-version }}-unit-tests
|
||||||
|
path: ${{ env.npm_config_cache }}
|
||||||
- run: npm install
|
- run: npm install
|
||||||
|
|
||||||
- run: npm run lint
|
|
||||||
- run: npm run test-unit
|
- run: npm run test-unit
|
||||||
|
|
||||||
integration-tests:
|
integration-tests:
|
||||||
needs: unit-tests
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [16.x, 18.x]
|
node-version:
|
||||||
|
- 18.x
|
||||||
|
- 20.x
|
||||||
|
- 22.x
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: setup Node.js v${{ matrix.node-version }}
|
- name: setup Node.js v${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
|
|
||||||
|
- id: cache-npm
|
||||||
|
name: restore npm cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
key: npm-cache-${{ github.ref_name }}-${{ matrix.node-version }}-integration-tests
|
||||||
|
path: ${{ env.npm_config_cache }}
|
||||||
- run: npm install
|
- run: npm install
|
||||||
|
|
||||||
- run: npm run lint
|
|
||||||
- run: npm run test-integration
|
- run: npm run test-integration
|
||||||
|
|
||||||
e2e-tests:
|
e2e-tests:
|
||||||
needs: integration-tests
|
needs: [unit-tests, integration-tests]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [16.x]
|
node-version: [18.x]
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: setup Node.js v${{ matrix.node-version }}
|
- name: setup Node.js v${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
|
|
||||||
|
- id: cache-npm
|
||||||
|
name: restore npm cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
key: npm-cache-${{ github.ref_name }}-${{ matrix.node-version }}-e2e-tests
|
||||||
|
path: ${{ env.npm_config_cache }}
|
||||||
- run: npm install
|
- run: npm install
|
||||||
|
|
||||||
- run: npm run lint
|
|
||||||
- run: npm run test-e2e
|
- run: npm run test-e2e
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -5,4 +5,5 @@ Thumbs.db
|
||||||
node_modules
|
node_modules
|
||||||
npm-debug.log
|
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) 2022, 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.
|
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();
|
|
@ -4,11 +4,4 @@ Thanks for helping! 🙏
|
||||||
|
|
||||||
## Adding integration/end-to-end tests
|
## Adding integration/end-to-end tests
|
||||||
|
|
||||||
The [end-to-end/system tests](https://en.wikipedia.org/wiki/System_testing) in [`test/e2e`](test/e2e), executing via `npm run test-e2e`, are querying real HAFAS endpoints, expecting valid & *reasonable* responses.
|
Refer to the [testing docs](docs/tests.md).
|
||||||
|
|
||||||
The [integration tests](https://en.wikipedia.org/wiki/Integration_testing) (`npm run test-integration`) are the same `test/e2e` tests, running against *mocked* HAFAS responses from [`test/e2e/fixtures`](test/e2e/fixtures).
|
|
||||||
|
|
||||||
1. Add a new end-to-end test that tests your feature/bugfix.
|
|
||||||
2. Make sure the test passes. You can run your test exclusively using `test.only`.
|
|
||||||
3. Record the HAFAS responses as [fixtures](https://en.wikipedia.org/wiki/Test_fixture) using `npm run test-integration:record`.
|
|
||||||
4. Make sure the passes against *only* mocked responses using `npm run test-integration`.
|
|
||||||
|
|
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
|
- [`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`
|
- [`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
|
- [`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
|
- [`departures(station, [opt])`](departures.md) – query the next departures at a station
|
||||||
- [`arrivals(station, [opt])`](arrivals.md) – query the next arrivals 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
|
- [`locations(query, [opt])`](locations.md) – find stations, POIs and addresses
|
||||||
- [`stop(id, [opt])`](stop.md) – get details about a stop/station
|
- [`stop(id, [opt])`](stop.md) – get details about a stop/station
|
||||||
- [`nearby(location, [opt])`](nearby.md) – show stations & POIs around
|
- [`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
|
- `radar(north, west, south, east, [opt])` – not supported
|
||||||
- [`reachableFrom(address, [opt])`](reachable-from.md) – get all stations reachable from an address within `n` minutes
|
- `reachableFrom(address, [opt])` – not supported
|
||||||
- [`remarks([opt])`](remarks.md) – get all remarks
|
- `remarks([opt])` – not supported
|
||||||
- [`lines(query, [opt])`](lines.md) – get all lines matching a name
|
- `lines(query, [opt])` – not supported
|
||||||
- [`serverInfo([opt])`](server-info.md) – fetch meta information from HAFAS
|
- `serverInfo([opt])` – not supported
|
||||||
|
|
1020
docs/changelog.md
1020
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,56 +24,48 @@ With `opt`, you can override the default options, which look like this:
|
||||||
```js
|
```js
|
||||||
{
|
{
|
||||||
when: new Date(),
|
when: new Date(),
|
||||||
direction: null, // only show departures heading to this station
|
direction: null, // only supported in `dbweb` and with `enrichStations=true` (experimental)
|
||||||
line: null, // filter by line ID
|
line: null, // not supported
|
||||||
duration: 10, // show departures for the next n minutes
|
duration: 10, // show departures for the next n minutes
|
||||||
results: null, // max. number of results; `null` means "whatever HAFAS wants"
|
results: null, // max. number of results; `null` means "whatever HAFAS wants"
|
||||||
subStops: true, // parse & expose sub-stops of stations?
|
subStops: true, // not supported
|
||||||
entrances: true, // parse & expose entrances of stops/stations?
|
entrances: true, // not supported
|
||||||
linesOfStops: false, // parse & expose lines at the stop/station?
|
linesOfStops: false, // not supported
|
||||||
remarks: true, // parse & expose hints & warnings?
|
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
|
// departures at related stations
|
||||||
// e.g. those that belong together on the metro map.
|
// e.g. those that belong together on the metro map.
|
||||||
includeRelatedStations: true,
|
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
|
language: 'en' // language to get results in
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
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. An example with the [BVG profile](../p/bvg):
|
If you pass an object `opt.products`, its fields will partially override the default products defined in the profile.
|
||||||
|
|
||||||
```js
|
|
||||||
import {createClient} from 'hafas-client'
|
|
||||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
|
||||||
|
|
||||||
const client = createClient(vbbProfile, 'my-awesome-program')
|
|
||||||
|
|
||||||
// will query with these products: suburban, subway, bus, express, regional
|
|
||||||
await client.departures('900000024101', {products: {tram: false, ferry: false}})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Response
|
## 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.
|
*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
|
```js
|
||||||
import {createClient} from 'hafas-client'
|
import {createClient} from 'db-vendo-client'
|
||||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
import {profile as dbnavProfile} from 'db-vendo-client/p/dbnav/index.js'
|
||||||
|
|
||||||
const client = createClient(vbbProfile, 'my-awesome-program')
|
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||||
|
const client = createClient(dbnavProfile, userAgent)
|
||||||
|
|
||||||
// S Charlottenburg
|
// S Charlottenburg
|
||||||
const {
|
const {
|
||||||
departures,
|
departures,
|
||||||
realtimeDataUpdatedAt,
|
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:
|
`departures` may look like this:
|
||||||
|
|
||||||
|
@ -85,11 +77,11 @@ const {
|
||||||
// Depending on the HAFAS endpoint, the destination may be present:
|
// Depending on the HAFAS endpoint, the destination may be present:
|
||||||
destination: {
|
destination: {
|
||||||
type: 'stop',
|
type: 'stop',
|
||||||
id: '900000029101',
|
id: '8089165',
|
||||||
name: 'S Spandau',
|
name: 'S Spandau',
|
||||||
location: {
|
location: {
|
||||||
type: 'location',
|
type: 'location',
|
||||||
id: '900029101',
|
id: '8089165',
|
||||||
latitude: 52.534794,
|
latitude: 52.534794,
|
||||||
longitude: 13.197477
|
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,50 +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 berlinSüdkreuz = '8011113'
|
|
||||||
const münchenHbf = '8000261'
|
|
||||||
const kölnHbf = '8000207'
|
|
||||||
|
|
||||||
const client = createClient(dbProfile, 'my-awesome-program')
|
|
||||||
|
|
||||||
// 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.
|
|
101
docs/journeys.md
101
docs/journeys.md
|
@ -28,7 +28,7 @@
|
||||||
longitude: 3.21
|
longitude: 3.21
|
||||||
}
|
}
|
||||||
|
|
||||||
// an address, which is an FTPF `location` object
|
// an address, which is an FPTF `location` object
|
||||||
{
|
{
|
||||||
type: 'location',
|
type: 'location',
|
||||||
address: 'foo street 1',
|
address: 'foo street 1',
|
||||||
|
@ -53,11 +53,11 @@ With `opt`, you can override the default options, which look like this:
|
||||||
stopovers: false, // return stations on the way?
|
stopovers: false, // return stations on the way?
|
||||||
transfers: -1, // Maximum nr of transfers. Default: Let HAFAS decide.
|
transfers: -1, // Maximum nr of transfers. Default: Let HAFAS decide.
|
||||||
transferTime: 0, // minimum time for a single transfer in minutes
|
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
|
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?
|
// Consider walking to nearby stations at the beginning of a journey?
|
||||||
startWithWalking: true,
|
startWithWalking: true, // always true (?)
|
||||||
products: {
|
products: {
|
||||||
// these entries may vary from profile to profile
|
// these entries may vary from profile to profile
|
||||||
suburban: true,
|
suburban: true,
|
||||||
|
@ -65,15 +65,21 @@ With `opt`, you can override the default options, which look like this:
|
||||||
tram: true,
|
tram: true,
|
||||||
bus: true,
|
bus: true,
|
||||||
ferry: true,
|
ferry: true,
|
||||||
express: true,
|
nationalExpress: true,
|
||||||
|
national: true,
|
||||||
regional: true
|
regional: true
|
||||||
|
regionalExpress: true // this is actually FlixTrain and co.
|
||||||
},
|
},
|
||||||
tickets: false, // return tickets? only available with some profiles
|
tickets: false, // return tickets? only available for [refreshJourney](refresh-journey.md)
|
||||||
polylines: false, // return a shape for each leg?
|
polylines: false, // return a shape for each leg? only available for [refreshJourney](refresh-journey.md)
|
||||||
subStops: true, // parse & expose sub-stops of stations?
|
subStops: true, // not supported
|
||||||
entrances: true, // parse & expose entrances of stops/stations?
|
entrances: true, // not supported
|
||||||
remarks: true, // parse & expose hints & warnings?
|
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
|
language: 'en', // language to get results in
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -82,16 +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.
|
*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
|
```js
|
||||||
import {createClient} 'hafas-client'
|
import {createClient} 'db-vendo-client'
|
||||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
import {profile as dbProfile} from 'db-vendo-client/p/db/index.js'
|
||||||
|
|
||||||
const client = createClient(vbbProfile, 'my-awesome-program')
|
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||||
|
const client = createClient(dbProfile, userAgent)
|
||||||
|
|
||||||
// Hauptbahnhof to Heinrich-Heine-Str.
|
// Frankfurt to Stuttgart
|
||||||
await client.journeys('900000003201', '900000100008', {
|
await client.journeys('8000105', '8000096', {
|
||||||
results: 1,
|
results: 1,
|
||||||
stopovers: true
|
stopovers: true
|
||||||
})
|
})
|
||||||
|
@ -100,7 +106,7 @@ await client.journeys('900000003201', '900000100008', {
|
||||||
`journeys()` will resolve with an object with the following fields:
|
`journeys()` will resolve with an object with the following fields:
|
||||||
- `journeys`
|
- `journeys`
|
||||||
- `earlierRef`/`laterRef` – pass them as `opt.earlierThan`/`opt.laterThan` into another `journeys()` call to retrieve the next "page" of 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:
|
This object might look like this:
|
||||||
|
|
||||||
|
@ -275,44 +281,6 @@ This object might look like this:
|
||||||
realtimeDataUpdatedAt: 1531259400, // 2018-07-10T23:50:00+02
|
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`.
|
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:
|
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:
|
||||||
|
@ -338,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
|
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.
|
## Using the `loyaltyCard` option
|
||||||
|
|
||||||
If you pass `scheduledDays: true`, each journey will have a `scheduledDays` object looking like this:
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
{
|
import {data as loyaltyCards} from 'db-vendo-client/format/loyalty-cards.js' // see there for a list
|
||||||
'2018-01-01': true,
|
|
||||||
'2018-01-02': false,
|
hafas.journeys(from, to, {
|
||||||
// …
|
loyaltyCard: {type: data.BAHNCARD, discount: 25}
|
||||||
'2018-10-12': true,
|
})
|
||||||
'2018-10-13': true,
|
|
||||||
// …
|
|
||||||
'2019-01-02': false,
|
|
||||||
'2019-01-03': false
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 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,69 +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 client = createClient(svvProfile, 'my-awesome-program')
|
|
||||||
|
|
||||||
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,27 +6,27 @@ With `opt`, you can override the default options, which look like this:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
{
|
{
|
||||||
fuzzy: true // find only exact matches?
|
fuzzy: true // not supported
|
||||||
, results: 5 // how many search results?
|
, results: 5 // how many search results?
|
||||||
, stops: true // return stops/stations?
|
, stops: true // return stops/stations?
|
||||||
, addresses: true
|
, addresses: true
|
||||||
, poi: true // points of interest
|
, poi: true // points of interest
|
||||||
, subStops: true // parse & expose sub-stops of stations?
|
, subStops: true // not supported
|
||||||
, entrances: true // parse & expose entrances of stops/stations?
|
, entrances: true // not supported
|
||||||
, linesOfStops: false // parse & expose lines at each stop/station?
|
, linesOfStops: false // not supported
|
||||||
, language: 'en' // language to get results in
|
, language: 'en' // language to get results in
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Response
|
## Response
|
||||||
|
|
||||||
As an example, we're going to use the [VBB profile](../p/vbb):
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import {createClient} from 'hafas-client'
|
import {createClient} from 'db-vendo-client'
|
||||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
import {profile as dbnavProfile} from 'db-vendo-client/p/dbnav/index.js'
|
||||||
|
|
||||||
const client = createClient(vbbProfile, 'my-awesome-program')
|
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||||
|
const client = createClient(dbnavProfile, userAgent)
|
||||||
|
|
||||||
await client.locations('Alexanderplatz', {results: 3})
|
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,24 +10,23 @@ With `opt`, you can override the default options, which look like this:
|
||||||
{
|
{
|
||||||
results: 8, // maximum number of results
|
results: 8, // maximum number of results
|
||||||
distance: null, // maximum walking distance in meters
|
distance: null, // maximum walking distance in meters
|
||||||
poi: false, // return points of interest?
|
poi: false, // not supported
|
||||||
stops: true, // return stops/stations?
|
stops: true, // return stops/stations?
|
||||||
subStops: true, // parse & expose sub-stops of stations?
|
subStops: true, // not supported
|
||||||
entrances: true, // parse & expose entrances of stops/stations?
|
entrances: true, // not supported
|
||||||
linesOfStops: false, // parse & expose lines at each stop/station?
|
linesOfStops: false, // not supported
|
||||||
language: 'en' // language to get results in
|
language: 'en' // language to get results in
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Response
|
## Response
|
||||||
|
|
||||||
As an example, we're going to use the [VBB profile](../p/vbb):
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import {createClient} from 'hafas-client'
|
import {createClient} from 'db-vendo-client'
|
||||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
import {profile as dbProfile} from 'db-vendo-client/p/db/index.js'
|
||||||
|
|
||||||
const client = createClient(vbbProfile, 'my-awesome-program')
|
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||||
|
const client = createClient(dbProfile, userAgent)
|
||||||
|
|
||||||
await client.nearby({
|
await client.nearby({
|
||||||
type: 'location',
|
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,
|
|
||||||
}
|
|
190
docs/radar.md
190
docs/radar.md
|
@ -1,190 +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 client = createClient(vbbProfile, 'my-awesome-program')
|
|
||||||
|
|
||||||
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,97 +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 client = createClient(vbbProfile, 'my-awesome-program')
|
|
||||||
|
|
||||||
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: { /* … */ }
|
|
||||||
}
|
|
||||||
// …
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
103
docs/readme.md
103
docs/readme.md
|
@ -1,33 +1,37 @@
|
||||||
# `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
|
## Throttling requests
|
||||||
|
|
||||||
There's opt-in support for throttling requests to the endpoint.
|
There's opt-in support for throttling requests to the endpoint.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import {createClient} from 'hafas-client'
|
import {createClient} from 'db-vendo-client'
|
||||||
import {withThrottling} from 'hafas-client/throttle.js'
|
import {withThrottling} from 'db-vendo-client/throttle.js'
|
||||||
import {profile as dbProfile} from 'hafas-client/p/db/index.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!
|
||||||
|
|
||||||
// create a throttled HAFAS client with Deutsche Bahn profile
|
// create a throttled HAFAS client with Deutsche Bahn profile
|
||||||
const client = createClient(withThrottling(dbProfile), 'my-awesome-program')
|
const client = createClient(withThrottling(dbProfile), userAgent)
|
||||||
|
|
||||||
// Berlin Jungfernheide to München Hbf
|
// Berlin Jungfernheide to München Hbf
|
||||||
await client.journeys('8011167', '8000261', {results: 1})
|
await client.journeys('8011167', '8000261', {results: 1})
|
||||||
```
|
```
|
||||||
|
|
||||||
You can pass custom values for the nr of requests (`limit`) per interval into `withThrottling`:
|
You can also pass custom values for the nr of requests (`limit`) per interval into `withThrottling`:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// 2 requests per second
|
// 2 requests per second
|
||||||
const throttledDbProfile = withThrottling(dbProfile, 2, 1000)
|
const throttledDbProfile = withThrottling(dbProfile, 2, 1000)
|
||||||
const client = createClient(throttledDbProfile, 'my-awesome-program')
|
const client = createClient(throttledDbProfile, userAgent)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Retrying failed requests
|
## Retrying failed requests
|
||||||
|
@ -35,12 +39,14 @@ const client = createClient(throttledDbProfile, 'my-awesome-program')
|
||||||
There's opt-in support for retrying failed requests to the endpoint.
|
There's opt-in support for retrying failed requests to the endpoint.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import {createClient} from 'hafas-client'
|
import {createClient} from 'db-vendo-client'
|
||||||
import {withRetrying} from 'hafas-client/retry.js'
|
import {withRetrying} from 'db-vendo-client/retry.js'
|
||||||
import {profile as dbProfile} from 'hafas-client/p/db/index.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!
|
||||||
|
|
||||||
// create a client with Deutsche Bahn profile that will retry on HAFAS errors
|
// create a client with Deutsche Bahn profile that will retry on HAFAS errors
|
||||||
const client = createClient(withRetrying(dbProfile), 'my-awesome-program')
|
const client = createClient(withRetrying(dbProfile), userAgent)
|
||||||
```
|
```
|
||||||
|
|
||||||
You can pass custom options into `withRetrying`. They will be passed into [`retry`](https://github.com/tim-kos/node-retry#tutorial).
|
You can pass custom options into `withRetrying`. They will be passed into [`retry`](https://github.com/tim-kos/node-retry#tutorial).
|
||||||
|
@ -52,31 +58,18 @@ const retryingDbProfile = withRetrying(dbProfile, {
|
||||||
minTimeout: 10 * 1000,
|
minTimeout: 10 * 1000,
|
||||||
factor: 3
|
factor: 3
|
||||||
})
|
})
|
||||||
const client = createClient(retryingDbProfile, 'my-awesome-program')
|
const client = createClient(retryingDbProfile, userAgent)
|
||||||
```
|
```
|
||||||
|
|
||||||
## User agent randomization
|
## 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:
|
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
|
|
||||||
import {createClient} from 'hafas-client'
|
|
||||||
// …
|
|
||||||
const client = createClient(someProfile, 'my-awesome-program')
|
|
||||||
|
|
||||||
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`:
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const client = createClient({
|
const client = createClient({
|
||||||
...someProfile,
|
...someProfile,
|
||||||
randomizeUserAgent: false,
|
randomizeUserAgent: true,
|
||||||
}, 'my-awesome-program')
|
}, userAgent)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Logging requests
|
## Logging requests
|
||||||
|
@ -86,8 +79,10 @@ You can use `profile.logRequest` and `profile.logResponse` to process the raw [F
|
||||||
As an example, we can implement a custom logger:
|
As an example, we can implement a custom logger:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import {createClient} from 'hafas-client'
|
import {createClient} from 'db-vendo-client'
|
||||||
import {profile as dbProfile} from 'hafas-client/p/db/index.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!
|
||||||
|
|
||||||
const logRequest = (ctx, fetchRequest, requestId) => {
|
const logRequest = (ctx, fetchRequest, requestId) => {
|
||||||
// ctx looks just like with the other profile.* hooks:
|
// ctx looks just like with the other profile.* hooks:
|
||||||
|
@ -105,7 +100,7 @@ const client = createClient({
|
||||||
...dbProfile,
|
...dbProfile,
|
||||||
logRequest,
|
logRequest,
|
||||||
logResponse,
|
logResponse,
|
||||||
}, 'my-awesome-program')
|
}, userAgent)
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
@ -132,17 +127,14 @@ The default `profile.logRequest` [`console.error`](https://nodejs.org/docs/lates
|
||||||
|
|
||||||
## Error handling
|
## 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.
|
- `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.
|
- `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.
|
- `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.
|
- `hafasCode` – A HAFAS-specific error code, if the HAFAS endpoint returned one; e.g. `H890` when no journeys could be found. `null` otherwise.
|
||||||
|
@ -150,32 +142,13 @@ Each error has the following properties:
|
||||||
- `url` – The URL of the request.
|
- `url` – The URL of the request.
|
||||||
- `response` – The [Fetch API `Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response).
|
- `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
|
## Using `db-vendo-client` from another language
|
||||||
import {HafasError} from 'hafas-client/lib/errors.js'
|
|
||||||
|
|
||||||
try {
|
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).
|
||||||
await client.journeys(/* … */)
|
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.
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof HafasError) {
|
|
||||||
// HAFAS-specific error
|
|
||||||
} else {
|
|
||||||
// different error, e.g. network (ETIMEDOUT, ENETDOWN)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
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.
|
[`db-apis.md`](db-apis.md)
|
||||||
|
|
||||||
## Writing a profile
|
|
||||||
|
|
||||||
Check [the guide](writing-a-profile.md).
|
|
||||||
|
|
||||||
## General documentation for `mgate.exe` APIs
|
|
||||||
|
|
||||||
[`hafas-mgate-api.md`](hafas-mgate-api.md)
|
|
||||||
|
|
|
@ -7,10 +7,10 @@ With `opt`, you can override the default options, which look like this:
|
||||||
```js
|
```js
|
||||||
{
|
{
|
||||||
stopovers: false, // return stations on the way?
|
stopovers: false, // return stations on the way?
|
||||||
polylines: false, // return a shape for each leg?
|
polylines: false, // return a shape for each leg? mutually exclusive with tickets
|
||||||
tickets: false, // return tickets? only available with some profiles
|
tickets: false, // return tickets? mutually exclusive with polylines
|
||||||
subStops: true, // parse & expose sub-stops of stations?
|
subStops: true, // not supported
|
||||||
entrances: true, // parse & expose entrances of stops/stations?
|
entrances: true, // not supported
|
||||||
remarks: true, // parse & expose hints & warnings?
|
remarks: true, // parse & expose hints & warnings?
|
||||||
language: 'en' // language to get results in
|
language: 'en' // language to get results in
|
||||||
}
|
}
|
||||||
|
@ -18,16 +18,14 @@ With `opt`, you can override the default options, which look like this:
|
||||||
|
|
||||||
## Response
|
## Response
|
||||||
|
|
||||||
As an example, we're going to use the [VBB profile](../p/vbb):
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import {createClient} from 'hafas-client'
|
import {createClient} from 'db-vendo-client'
|
||||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
import {profile as dbProfile} from 'db-vendo-client/p/db/index.js'
|
||||||
|
|
||||||
const client = createClient(vbbProfile)
|
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||||
|
const client = createClient(dbProfile, userAgent)
|
||||||
|
|
||||||
// Hauptbahnhof to Heinrich-Heine-Str.
|
const {journeys} = await client.journeys('8000105', '8000096', {results: 1})
|
||||||
const {journeys} = await client.journeys('900000003201', '900000100008', {results: 1})
|
|
||||||
|
|
||||||
// later, fetch up-to-date info on the journey
|
// later, fetch up-to-date info on the journey
|
||||||
const {
|
const {
|
||||||
|
@ -38,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).
|
`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.
|
||||||
|
|
149
docs/remarks.md
149
docs/remarks.md
|
@ -1,149 +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 client = createClient(svvProfile, 'my-awesome-program')
|
|
||||||
|
|
||||||
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,37 +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 client = createClient(svvProfile, 'my-awesome-program')
|
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
```
|
|
32
docs/stop.md
32
docs/stop.md
|
@ -1,5 +1,7 @@
|
||||||
# `stop(id, [opt])`
|
# `stop(id, [opt])`
|
||||||
|
|
||||||
|
This endpoint is not available with `dbweb` profile.
|
||||||
|
|
||||||
`id` must be in one of these formats:
|
`id` must be in one of these formats:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
@ -23,8 +25,8 @@ With `opt`, you can override the default options, which look like this:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
{
|
{
|
||||||
subStops: true, // parse & expose sub-stops of stations?
|
subStops: true, // not supported
|
||||||
entrances: true, // parse & expose entrances of stops/stations?
|
entrances: true, // not supported
|
||||||
linesOfStops: false, // parse & expose lines at the stop/station?
|
linesOfStops: false, // parse & expose lines at the stop/station?
|
||||||
language: 'en' // language to get results in
|
language: 'en' // language to get results in
|
||||||
}
|
}
|
||||||
|
@ -32,13 +34,13 @@ With `opt`, you can override the default options, which look like this:
|
||||||
|
|
||||||
## Response
|
## Response
|
||||||
|
|
||||||
As an example, we're going to use the [VBB profile](../p/vbb):
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import {createClient} from 'hafas-client'
|
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 client = createClient(vbbProfile, 'my-awesome-program')
|
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||||
|
const client = createClient(dbProfile, userAgent)
|
||||||
|
|
||||||
await client.stop('900000042101') // U Spichernstr.
|
await client.stop('900000042101') // U Spichernstr.
|
||||||
```
|
```
|
||||||
|
@ -71,11 +73,6 @@ The result may look like this:
|
||||||
product: 'subway',
|
product: 'subway',
|
||||||
public: true,
|
public: true,
|
||||||
name: 'U1',
|
name: 'U1',
|
||||||
symbol: 'U',
|
|
||||||
nr: 1,
|
|
||||||
metro: false,
|
|
||||||
express: false,
|
|
||||||
night: false
|
|
||||||
},
|
},
|
||||||
// …
|
// …
|
||||||
{
|
{
|
||||||
|
@ -85,21 +82,6 @@ The result may look like this:
|
||||||
product: 'bus',
|
product: 'bus',
|
||||||
public: true,
|
public: true,
|
||||||
name: 'N9',
|
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}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
26
docs/tests.md
Normal file
26
docs/tests.md
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# 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 `db-vendo-client` actually works with HAFAS endpoints *as they currently work*, its testing setup is a bit unusual.
|
||||||
|
|
||||||
|
`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`
|
||||||
|
|
||||||
|
Because the E2E & integration tests are based on the same code, when changing this code, you should also update the integration test fixtures accordingly.
|
||||||
|
|
||||||
|
*Note:* In order to be as reproducible as possible, the tests query transit data for a certain *fixed* point in time on the future, hard-coded in each profile's test suite (a.k.a. each file `test/e2e/*.js`). In combination with the recording & mocking of HTTP requests, this effectively makes the integration tests deterministic.
|
||||||
|
|
||||||
|
## adding integration test fixtures
|
||||||
|
|
||||||
|
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/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.
|
||||||
|
|
||||||
|
Then, check the augmented fixtures (in `test/e2e/fixtures`) into Git, and revert the `tap.only(…)` change. To make sure that everything works, run the entire test suite/file (*without `TAP_ONLY=1`*) with `VCR_MODE=playback`.
|
||||||
|
|
||||||
|
*Note:* It might be that the test suite/file you want to augment hasn't been updated in a while, so that `T_MOCK` is in the past. In this case, recording additional fixtures from actual HTTP requests of your added test is usually not possible because the HAFAS API is unable to serve old transit data. In this case, you will first have to change `T_MOCK` to a future date+time (a weekday as "normal" as possible) and re-record all tests' HTTP requests; Don't hesitate to get in touch with me if you have trouble with this.
|
49
docs/trip.md
49
docs/trip.md
|
@ -2,18 +2,16 @@
|
||||||
|
|
||||||
This method can be used to refetch information about a trip – a vehicle stopping at a set of stops at specific times.
|
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:
|
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
|
```js
|
||||||
import {createClient} from 'hafas-client'
|
import {createClient} from 'db-vendo-client'
|
||||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
import {profile as dbnavProfile} from 'db-vendo-client/p/dbnav/index.js'
|
||||||
|
|
||||||
const client = createClient(vbbProfile, 'my-awesome-program')
|
const userAgent = 'link-to-your-project-or-email' // adapt this to your project!
|
||||||
|
const client = createClient(dbnavProfile, userAgent)
|
||||||
|
|
||||||
// Hauptbahnhof to Heinrich-Heine-Str.
|
const {journeys} = client.journeys('8000096', '8000105', {results: 1})
|
||||||
const {journeys} = client.journeys('900000003201', '900000100008', {results: 1})
|
|
||||||
const leg = journeys[0].legs[0]
|
const leg = journeys[0].legs[0]
|
||||||
|
|
||||||
await client.trip(leg.tripId)
|
await client.trip(leg.tripId)
|
||||||
|
@ -24,9 +22,9 @@ With `opt`, you can override the default options, which look like this:
|
||||||
```js
|
```js
|
||||||
{
|
{
|
||||||
stopovers: true, // return stations on the way?
|
stopovers: true, // return stations on the way?
|
||||||
polyline: false, // return a shape for the trip?
|
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, // parse & expose sub-stops of stations?
|
subStops: true, // not supported
|
||||||
entrances: true, // parse & expose entrances of stops/stations?
|
entrances: true, // not supported
|
||||||
remarks: true, // parse & expose hints & warnings?
|
remarks: true, // parse & expose hints & warnings?
|
||||||
language: 'en' // language to get results in
|
language: 'en' // language to get results in
|
||||||
}
|
}
|
||||||
|
@ -36,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.
|
*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
|
```js
|
||||||
import {createClient} from 'hafas-client'
|
import {createClient} from 'db-vendo-client'
|
||||||
import {profile as vbbProfile} from 'hafas-client/p/vbb/index.js'
|
import {profile as dbnavProfile} from 'db-vendo-client/p/dbnav/index.js'
|
||||||
|
|
||||||
const client = createClient(vbbProfile)
|
const client = createClient(dbnavProfile)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
trip,
|
trip,
|
||||||
|
@ -52,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:
|
When running the code above, `trip` looked like this:
|
||||||
|
|
||||||
|
@ -141,9 +138,9 @@ When running the code above, `trip` looked like this:
|
||||||
|
|
||||||
### `polyline` option
|
### `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
|
```js
|
||||||
{
|
{
|
||||||
|
@ -151,12 +148,6 @@ We'll look at an example for *U6* from *Alt-Mariendorf* to *Alt-Tegel*, taken fr
|
||||||
features: [
|
features: [
|
||||||
{
|
{
|
||||||
type: 'Feature',
|
type: 'Feature',
|
||||||
properties: {
|
|
||||||
type: 'station',
|
|
||||||
id: '900000070301',
|
|
||||||
name: 'U Alt-Mariendorf',
|
|
||||||
/* … */
|
|
||||||
},
|
|
||||||
geometry: {
|
geometry: {
|
||||||
type: 'Point',
|
type: 'Point',
|
||||||
coordinates: [13.3875, 52.43993] // longitude, latitude
|
coordinates: [13.3875, 52.43993] // longitude, latitude
|
||||||
|
@ -165,12 +156,6 @@ We'll look at an example for *U6* from *Alt-Mariendorf* to *Alt-Tegel*, taken fr
|
||||||
/* … */
|
/* … */
|
||||||
{
|
{
|
||||||
type: 'Feature',
|
type: 'Feature',
|
||||||
properties: {
|
|
||||||
type: 'station',
|
|
||||||
id: '900000017101',
|
|
||||||
name: 'U Mehringdamm',
|
|
||||||
/* … */
|
|
||||||
},
|
|
||||||
geometry: {
|
geometry: {
|
||||||
type: 'Point',
|
type: 'Point',
|
||||||
coordinates: [13.38892, 52.49448] // longitude, latitude
|
coordinates: [13.38892, 52.49448] // longitude, latitude
|
||||||
|
@ -188,12 +173,6 @@ We'll look at an example for *U6* from *Alt-Mariendorf* to *Alt-Tegel*, taken fr
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Feature',
|
type: 'Feature',
|
||||||
properties: {
|
|
||||||
type: 'station',
|
|
||||||
id: '900000089301',
|
|
||||||
name: 'U Alt-Tegel',
|
|
||||||
/* … */
|
|
||||||
},
|
|
||||||
geometry: {
|
geometry: {
|
||||||
type: 'Point',
|
type: 'Point',
|
||||||
coordinates: [13.28406, 52.58915] // longitude, latitude
|
coordinates: [13.28406, 52.58915] // longitude, latitude
|
||||||
|
|
|
@ -1,113 +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 client = createClient(vbbProfile, 'my-awesome-program')
|
|
||||||
|
|
||||||
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;
|
|
@ -1,25 +1,27 @@
|
||||||
import {formatLocationIdentifier} from './location-identifier.js'
|
import {formatLocationIdentifier} from './location-identifier.js';
|
||||||
import {formatCoord} from './coord.js'
|
import {formatCoord} from './coord.js';
|
||||||
|
|
||||||
const formatAddress = (a) => {
|
const formatAddress = (a) => {
|
||||||
if (a.type !== 'location' || !a.latitude || !a.longitude || !a.address) {
|
if (a.type !== 'location' || !a.latitude || !a.longitude || !a.address) {
|
||||||
throw new TypeError('invalid address')
|
throw new TypeError('invalid address');
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
A: '2', // address?
|
A: '2', // address?
|
||||||
O: a.address,
|
O: a.address,
|
||||||
X: formatCoord(a.longitude),
|
X: formatCoord(a.longitude),
|
||||||
Y: formatCoord(a.latitude)
|
Y: formatCoord(a.latitude),
|
||||||
|
};
|
||||||
|
if (a.id) {
|
||||||
|
data.L = a.id;
|
||||||
}
|
}
|
||||||
if (a.id) data.L = a.id
|
|
||||||
return {
|
return {
|
||||||
type: 'A', // address
|
type: 'A', // address
|
||||||
name: a.address,
|
name: a.address,
|
||||||
lid: formatLocationIdentifier(data)
|
lid: formatLocationIdentifier(data),
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
formatAddress,
|
formatAddress,
|
||||||
}
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const formatCoord = x => Math.round(x * 1000000)
|
const formatCoord = x => Math.round(x * 1000000);
|
||||||
|
|
||||||
export {
|
export {
|
||||||
formatCoord,
|
formatCoord,
|
||||||
}
|
};
|
||||||
|
|
|
@ -1,21 +1,24 @@
|
||||||
import {DateTime, IANAZone} from 'luxon'
|
import {DateTime, IANAZone} from 'luxon';
|
||||||
import {luxonIANAZonesByProfile as timezones} from '../lib/luxon-timezones.js'
|
import {luxonIANAZonesByProfile as timezones} from '../lib/luxon-timezones.js';
|
||||||
|
|
||||||
// todo: change to `(profile) => (when) => {}`
|
// todo: change to `(profile) => (when) => {}`
|
||||||
const formatDate = (profile, when) => {
|
const formatDate = (profile, when) => {
|
||||||
let timezone
|
let timezone;
|
||||||
if (timezones.has(profile)) timezone = timezones.get(profile)
|
if (timezones.has(profile)) {
|
||||||
else {
|
timezone = timezones.get(profile);
|
||||||
timezone = new IANAZone(profile.timezone)
|
} else {
|
||||||
timezones.set(profile, timezone)
|
timezone = new IANAZone(profile.timezone);
|
||||||
|
timezones.set(profile, timezone);
|
||||||
}
|
}
|
||||||
|
|
||||||
return DateTime.fromMillis(+when, {
|
return DateTime
|
||||||
|
.fromMillis(Number(when), {
|
||||||
locale: profile.locale,
|
locale: profile.locale,
|
||||||
zone: timezone
|
zone: timezone,
|
||||||
}).toFormat('yyyyMMdd')
|
})
|
||||||
}
|
.toFormat('yyyy-MM-dd');
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
formatDate,
|
formatDate,
|
||||||
}
|
};
|
||||||
|
|
|
@ -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,8 +0,0 @@
|
||||||
const formatLocationFilter = (stops, addresses, poi) => {
|
|
||||||
if (stops && addresses && poi) return 'ALL'
|
|
||||||
return (stops ? 'S' : '') + (addresses ? 'A' : '') + (poi ? 'P' : '')
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
formatLocationFilter,
|
|
||||||
}
|
|
|
@ -1,16 +1,18 @@
|
||||||
const sep = '@'
|
const sep = '@';
|
||||||
|
|
||||||
const formatLocationIdentifier = (data) => {
|
const formatLocationIdentifier = (data) => {
|
||||||
let str = ''
|
let str = '';
|
||||||
for (let key in data) {
|
for (let key in data) {
|
||||||
if (!Object.prototype.hasOwnProperty.call(data, key)) continue
|
if (!Object.prototype.hasOwnProperty.call(data, key)) {
|
||||||
|
continue;
|
||||||
str += key + '=' + data[key] + sep // todo: escape, but how?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return str
|
str += key + '=' + data[key] + sep; // todo: escape, but how?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
formatLocationIdentifier,
|
formatLocationIdentifier,
|
||||||
}
|
};
|
||||||
|
|
|
@ -1,17 +1,25 @@
|
||||||
const formatLocation = (profile, l, name = 'location') => {
|
const formatLocation = (profile, l, name = 'location') => {
|
||||||
if ('string' === typeof l) return profile.formatStation(l)
|
if ('string' === typeof l) {
|
||||||
|
return profile.formatStation(l);
|
||||||
|
}
|
||||||
if ('object' === typeof l && !Array.isArray(l)) {
|
if ('object' === typeof l && !Array.isArray(l)) {
|
||||||
if (l.type === 'station' || l.type === 'stop') {
|
if (l.type === 'station' || l.type === 'stop') {
|
||||||
return profile.formatStation(l.id)
|
return profile.formatStation(l.id);
|
||||||
}
|
}
|
||||||
if (l.poi) return profile.formatPoi(l)
|
if (l.poi) {
|
||||||
if ('string' === typeof l.address) return profile.formatAddress(l)
|
return profile.formatPoi(l);
|
||||||
if (!l.type) throw new TypeError(`missing ${name}.type`)
|
|
||||||
throw new TypeError(`invalid ${name}.type: ${l.type}`)
|
|
||||||
}
|
}
|
||||||
throw new TypeError(name + ': valid station, address or poi required.')
|
if ('string' === typeof l.address) {
|
||||||
|
return profile.formatAddress(l);
|
||||||
}
|
}
|
||||||
|
if (!l.type) {
|
||||||
|
throw new TypeError(`missing ${name}.type`);
|
||||||
|
}
|
||||||
|
throw new TypeError(`invalid ${name}.type: ${l.type}`);
|
||||||
|
}
|
||||||
|
throw new TypeError(name + ': valid station, address or poi required.');
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
formatLocation,
|
formatLocation,
|
||||||
}
|
};
|
||||||
|
|
|
@ -1,20 +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: !!opt.poi,
|
|
||||||
getStops: !!opt.stops,
|
|
||||||
maxLoc: opt.results
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
formatNearbyReq,
|
|
||||||
}
|
|
|
@ -1,9 +1,10 @@
|
||||||
import {formatLocationIdentifier} from './location-identifier.js'
|
import {formatLocationIdentifier} from './location-identifier.js';
|
||||||
import {formatCoord} from './coord.js'
|
import {formatCoord} from './coord.js';
|
||||||
|
|
||||||
const formatPoi = (p) => {
|
const formatPoi = (p) => {
|
||||||
|
// todo: use Number.isFinite()!
|
||||||
if (p.type !== 'location' || !p.latitude || !p.longitude || !p.id || !p.name) {
|
if (p.type !== 'location' || !p.latitude || !p.longitude || !p.id || !p.name) {
|
||||||
throw new TypeError('invalid POI')
|
throw new TypeError('invalid POI');
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -14,11 +15,11 @@ const formatPoi = (p) => {
|
||||||
O: p.name,
|
O: p.name,
|
||||||
L: p.id,
|
L: p.id,
|
||||||
X: formatCoord(p.longitude),
|
X: formatCoord(p.longitude),
|
||||||
Y: formatCoord(p.latitude)
|
Y: formatCoord(p.latitude),
|
||||||
})
|
}),
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
formatPoi,
|
formatPoi,
|
||||||
}
|
};
|
||||||
|
|
|
@ -1,35 +1,46 @@
|
||||||
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 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')
|
if (!isObj(filter)) {
|
||||||
const {profile} = ctx
|
throw new TypeError('products filter must be an object');
|
||||||
|
}
|
||||||
|
const {profile} = ctx;
|
||||||
|
|
||||||
const byProduct = {}
|
const byProduct = {};
|
||||||
const defaultProducts = {}
|
const defaultProducts = {};
|
||||||
for (let product of profile.products) {
|
for (let product of profile.products) {
|
||||||
byProduct[product.id] = product
|
byProduct[product.id] = product;
|
||||||
defaultProducts[product.id] = product.default
|
defaultProducts[product.id] = product.default;
|
||||||
}
|
}
|
||||||
filter = Object.assign({}, defaultProducts, filter)
|
filter = Object.assign({}, defaultProducts, filter);
|
||||||
|
|
||||||
let res = 0, products = 0
|
let products = [];
|
||||||
|
let foundDeselected = false;
|
||||||
for (let product in filter) {
|
for (let product in filter) {
|
||||||
if (!hasProp(filter, product) || filter[product] !== true) continue
|
if (!hasProp(filter, product) || filter[product] !== true) {
|
||||||
if (!byProduct[product]) throw new TypeError('unknown product ' + product)
|
foundDeselected = true;
|
||||||
products++
|
continue;
|
||||||
for (let bitmask of byProduct[product].bitmasks) res = res | bitmask
|
}
|
||||||
|
if (!byProduct[product]) {
|
||||||
|
throw new TypeError('unknown product ' + product);
|
||||||
|
}
|
||||||
|
products.push(byProduct[product][key]);
|
||||||
|
}
|
||||||
|
if (products.length === 0) {
|
||||||
|
throw new Error('no products used');
|
||||||
|
}
|
||||||
|
if (!foundDeselected && key == 'ris') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (!foundDeselected && key == 'dbnav') {
|
||||||
|
return ['ALL'];
|
||||||
}
|
}
|
||||||
if (products === 0) throw new Error('no products used')
|
|
||||||
|
|
||||||
return {
|
return products;
|
||||||
type: 'PROD',
|
};
|
||||||
mode: 'INC',
|
|
||||||
value: res + ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
formatProductsFilter,
|
formatProductsFilter,
|
||||||
}
|
};
|
||||||
|
|
|
@ -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,22 +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,25 +0,0 @@
|
||||||
const formatRefreshJourneyReq = (ctx, refreshToken) => {
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const {profile, opt} = ctx
|
|
||||||
|
|
||||||
const req = {
|
|
||||||
getIST: true, // todo: make an option
|
|
||||||
getPasslist: !!opt.stopovers,
|
|
||||||
getPolyline: !!opt.polylines,
|
|
||||||
getTariff: !!opt.tickets
|
|
||||||
}
|
|
||||||
if (profile.refreshJourneyUseOutReconL) {
|
|
||||||
req.outReconL = [{ctx: refreshToken}]
|
|
||||||
} else {
|
|
||||||
req.ctxRecon = refreshToken
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
meth: 'Reconstruction',
|
|
||||||
req,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
formatRefreshJourneyReq,
|
|
||||||
}
|
|
|
@ -1,34 +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 = !!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,
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue