Skip to content

Commit f424520

Browse files
committed
Switch to totem from Divia website
1 parent 71b3703 commit f424520

File tree

8 files changed

+51
-118
lines changed

8 files changed

+51
-118
lines changed

README.md

+7-19
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Version 2 de l'API Divia.
44

5-
Puisque [l'ancienne API de Keolis](http://timeo3.keolis.com/relais/217.php) n'est plus disponible, j'ai décompilé puis récupéré dans le code source de l'application Android les différentes méthodes pour accéder aux prochains passages Totem.
5+
Puisque [l'ancienne API de Keolis](http://timeo3.keolis.com/relais/217.php) n'est plus disponible, celle-ci utilise l'API du [site de Divia](https://www.divia.fr/bus-tram) qui renvoie un extrait de page HTML dans lequel se trouve les horaires des prochains passages.
66

77
## Utilisation
88

@@ -28,7 +28,7 @@ const api = new DiviaAPI();
2828
const stop = api.findStop('T1', 'Grésilles', 'A');
2929

3030
// Récupère les prochains passages :
31-
console.log(await stop.totem('username', 'password'));
31+
console.log(await stop.totem());
3232
})();
3333
```
3434

@@ -40,23 +40,11 @@ Pour chaque Line ou Stop, vous pouvez récupérer les données fournies par Divi
4040

4141
L'API récupère dans un premier temps les données du réseau Divia à cette adresse : https://bo-api.divia.fr/api/reseau/type/json (méthode `api#init`) afin de pouvoir récupérer les identifiants et informations des lignes et arrêts. Vous pouvez donc si vous le souhaitez mettre en cache la variable JSON `api.reseau` afin d'éviter de refaire la requête à chaque démarrage de votre application.
4242

43-
Pour récupérer les prochains passages Totem, il faut faire une requête HTTP GET à cette adresse : https://tim.divia.fr/api/get/totem, avec les [query params](https://en.wikipedia.org/wiki/Query_string) suivants :
44-
- `source_type=bo_divia_utilisateur`
45-
- `source_uuid=<uuid>`
46-
- `source_id=`
47-
- `ligne=<id_ligne>`
48-
- `arret=<id_arrêt>`
49-
- `token=<access_token>`
50-
avec :
51-
- `uuid` : un UUID v4 généré aléatoirement
52-
- `id_ligne` : identifiant de la ligne
53-
- `id_arrêt` : identifiant de la l'arrêt
54-
- `access_token` : [jeton d'accès JWT](https://jwt.io/) généré comme ci-dessous
55-
56-
Pour générer le jeton d'accès JWT, il faut faire une requête HTTP POST à cette adresse : https://tim.divia.fr/api/login_check, avec les entêtes HTTP suivant :
57-
- `Accept: application/json`
58-
- `Content-Type: application/x-www-form-urlencoded`
59-
Et avec le body suivant : `_username=<username>&_password=<password>`
43+
Pour récupérer les prochains passages Totem, il faut faire une requête HTTP POST à cette adresse : https://www.divia.fr/bus-tram?type=479, avec le contenu application/x-www-form-urlencoded suivant :
44+
- `requete=arret_prochainpassage`
45+
- `requete_val[id_ligne]=<id_ligne>`
46+
- `requete_val[id_arret]=<id_arrêt>`
47+
Note : bien penser à encoder les crochets avec URL encode (par exemple : `requete_val%5Bid_ligne%5D`).
6048

6149
## Licence
6250

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "divia-api",
3-
"version": "2.1.1",
4-
"description": "Divia API v2",
3+
"version": "2.2.0",
4+
"description": "Dvia API v2",
55
"main": "src/index.js",
66
"scripts": {
77
"ts": "npx typescript src/index.js --declaration --allowJs --emitDeclarationOnly --outDir types",

src/Stop.js

+29-20
Original file line numberDiff line numberDiff line change
@@ -28,32 +28,41 @@ class Stop {
2828
}
2929

3030
/**
31-
* @param {string} username
32-
* @param {string} password
3331
* @returns {Promise.<{
34-
* '@id': number,
35-
* duree: string,
36-
* destination: string,
37-
* minutes: string,
38-
* duree2: string,
39-
* departure_date_time: string,
40-
* now_date_time: string
32+
* text: string,
33+
* date: Date
4134
* }[]>}
4235
*/
43-
async totem(username, password) {
44-
const token = await this.api._getToken(username, password);
45-
const response = await fetch(`${this.api.baseURL}get/totem?source_type=bo_divia_utilisateur&source_uuid=${uuidv4()}&source_id=&ligne=${this.line.data.id}&arret=${this.data.id}&token=${token}`).then(res => res.json());
46-
return response.result_infos.totem;
36+
async totem() {
37+
const result = [];
38+
try {
39+
const html = await fetch('https://www.divia.fr/bus-tram?type=479', {
40+
method: 'POST',
41+
headers: {
42+
'Content-Type': 'application/x-www-form-urlencoded',
43+
'X-Requested-With': 'XMLHttpRequest'
44+
},
45+
body: `requete=arret_prochainpassage&requete_val%5Bid_ligne%5D=${this.line.data.id}&requete_val%5Bid_arret%5D=${this.data.id}`
46+
}).then(res => res.text());
47+
let matches;
48+
const regex = /<span class="uk-badge">\s*(((0?|[12])\d):(\d{2}))<\/span>/gi;
49+
while (matches = regex.exec(html)) {
50+
const hours = matches[2];
51+
const minutes = matches[4];
52+
const date = new Date();
53+
date.setHours(hours, minutes, 0, 0);
54+
result.push({
55+
text: matches[1],
56+
date
57+
});
58+
}
59+
}
60+
finally {
61+
return result;
62+
}
4763
}
4864

4965
}
5066

51-
function uuidv4() {
52-
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
53-
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
54-
return v.toString(16);
55-
});
56-
}
57-
5867
module.exports = Stop;
5968
module.exports.default = Stop;

src/index.js

-23
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
const fetch = require('isomorphic-unfetch');
2-
const jwt_decode = require('jwt-decode');
32
const Line = require('./Line');
43
const Stop = require('./Stop');
54

6-
const baseURL = 'https://tim.divia.fr/api/';
7-
85
class DiviaAPI {
96

107
constructor() {
118
/** @type {object} */
129
this.reseau = null;
13-
this.baseURL = baseURL;
1410
/** @type {string} */
1511
this._token = null;
1612
}
@@ -68,25 +64,6 @@ class DiviaAPI {
6864
return Object.values(this.reseau.arrets);
6965
}
7066

71-
/**
72-
* @param {string} username
73-
* @param {string} password
74-
*/
75-
async _getToken(username, password) {
76-
if (!this._token || jwt_decode(this._token).exp <= Math.round(Date.now() / 1000)) {
77-
const response = await fetch(`${baseURL}login_check`, {
78-
method: 'POST',
79-
headers: {
80-
Accept: 'application/json',
81-
'Content-Type': 'application/x-www-form-urlencoded'
82-
},
83-
body: `_username=${username}&_password=${password}`
84-
}).then(res => res.json());
85-
this._token = response.token;
86-
}
87-
return this._token;
88-
}
89-
9067
}
9168

9269
module.exports = DiviaAPI;

test/test.js

+8-37
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,7 @@
11
const assert = require('assert');
2-
const jwt_decode = require('jwt-decode');
32
const DiviaAPI = require('../src');
43

54
describe('DiviaAPI', async function() {
6-
this.timeout(60000);
7-
8-
let ids;
9-
describe('enter divia creditentials', () => {
10-
it('creditentials input', async () => {
11-
ids = await getIds();
12-
});
13-
});
14-
155
this.timeout(5000);
166

177
const instance = new DiviaAPI();
@@ -34,13 +24,6 @@ describe('DiviaAPI', async function() {
3424
)
3525
});
3626
});
37-
describe('#getToken', () => {
38-
it('should return a valid jwt access token', async () => {
39-
const token = await instance._getToken().catch(() => assert.fail());
40-
if (token)
41-
jwt_decode(token);
42-
});
43-
});
4427
describe('#getLine', () => {
4528
it('should find a tram line by its ID', () => {
4629
const line = instance.getLine('81');
@@ -89,30 +72,18 @@ describe('DiviaAPI', async function() {
8972
assert.fail();
9073
else
9174
assert.ok(true);
92-
await stop.totem(ids.username, ids.password).then(passages => {
93-
if (Array.isArray(passages))
75+
await stop.totem().then(passages => {
76+
console.log(passages);
77+
if (Array.isArray(passages)) {
78+
for (let passage of passages) {
79+
if (!passage.text.match(/^(0?|[12])\d:\d{2}$/ || !(passage.date instanceof Date)))
80+
return assert.fail();
81+
}
9482
assert.ok(true);
83+
}
9584
else
9685
assert.fail();
9786
}).catch(() => assert.fail());
9887
});
9988
});
10089
});
101-
102-
/**
103-
* @returns {Promise<{ username: string, password: string }>}
104-
*/
105-
function getIds() {
106-
return new Promise((resolve) => {
107-
const readline = require('readline').createInterface({
108-
input: process.stdin,
109-
output: process.stdout
110-
});
111-
readline.question('username: ', username => {
112-
readline.question('password: ', password => {
113-
readline.close();
114-
resolve({ username, password });
115-
});
116-
});
117-
});
118-
}

types/Stop.d.ts

+4-14
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,13 @@ declare class Stop {
2222
data: any;
2323
/**
2424
* @returns {Promise.<{
25-
* '@id': number,
26-
* duree: string,
27-
* destination: string,
28-
* minutes: string,
29-
* duree2: string,
30-
* departure_date_time: string,
31-
* now_date_time: string
25+
* text: string,
26+
* date: Date
3227
* }[]>}
3328
*/
3429
totem(): Promise<{
35-
'@id': number;
36-
duree: string;
37-
destination: string;
38-
minutes: string;
39-
duree2: string;
40-
departure_date_time: string;
41-
now_date_time: string;
30+
text: string;
31+
date: Date;
4232
}[]>;
4333
}
4434
declare namespace Stop {

types/index.d.ts

-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ export = DiviaAPI;
22
declare class DiviaAPI {
33
/** @type {object} */
44
reseau: object;
5-
baseURL: string;
65
/** @type {string} */
76
_token: string;
87
init(): Promise<void>;
@@ -32,7 +31,6 @@ declare class DiviaAPI {
3231
* @returns {Stop.StopObject[]}
3332
*/
3433
get stops(): any[];
35-
_getToken(): Promise<string>;
3634
}
3735
declare namespace DiviaAPI {
3836
export { DiviaAPI as default };

0 commit comments

Comments
 (0)