universal-parcel-scraper 0.654.0-main.3113 → 0.654.2-main.3118
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/carriers/correos-chile/status.js +19 -2
- package/dist/carriers/correos-chile/statuses.json +77 -0
- package/dist/carriers/dhl/adapter.d.ts +2 -2
- package/dist/carriers/dhl/adapter.js +6 -4
- package/dist/places/index.js +13 -2
- package/dist/server/openapi.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -155,7 +155,7 @@ createTracker({ providers: ['ParcelsApp', 'Ship24', '17TRACK', 'Postal Ninja', '
|
|
|
155
155
|
<!-- GENERATED:stages -->
|
|
156
156
|
<img src="docs/assets/stages.svg" alt="DHL: Die Sendung wurde in das Zustellfahrzeug geladen.; Mondial Relay: En cours de livraison; Correios Brazil: Objeto saiu para entrega ao destinatário; Correos Express: EN REPARTO; Yamato Transport: 配達中; La Poste / Colissimo: DISTOU. All are filed under out_for_delivery." width="760">
|
|
157
157
|
|
|
158
|
-
The carrier folders record 16,
|
|
158
|
+
The carrier folders record 16,496 such statuses, each filed under one stage.
|
|
159
159
|
<!-- /GENERATED:stages -->
|
|
160
160
|
|
|
161
161
|
Wording nobody recorded yet goes through a classifier that reads seven European languages.
|
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
function comparable(value) {
|
|
2
|
-
return value.normalize('NFD').replace(/[
|
|
2
|
+
return value.normalize('NFD').replace(/[̀-ͯ]/g, '').toUpperCase().replace(/\s+/g, ' ').trim();
|
|
3
3
|
}
|
|
4
|
+
const TRANSIT = { status: 'in_transit', stage: 'in_transit' };
|
|
5
|
+
// Scans of a parcel leaving Chile and of the legs before delivery, as providers and an
|
|
6
|
+
// open-source client relay them, without the portal's code. The whole wording must match.
|
|
7
|
+
const WORDINGS = new Map([
|
|
8
|
+
['RECIBIDO POR CORREOSCHILE', { status: 'in_transit', stage: 'accepted' }],
|
|
9
|
+
['RECIBIDO EN PLANTA DE ORIGEN', TRANSIT],
|
|
10
|
+
['RECIBIDO EN PLANTA ORIGEN', TRANSIT],
|
|
11
|
+
['ENVIO DESPACHADO', TRANSIT],
|
|
12
|
+
['DESPACHADO A OFICINA DE CORREOSCHILE', TRANSIT],
|
|
13
|
+
['RECIBIDO EN OFICINA DE CORREOSCHILE', TRANSIT],
|
|
14
|
+
['DISPONIBLE PARA SALIR AL PAIS DE DESTINO', TRANSIT],
|
|
15
|
+
['ENVIO DESPACHADO AL PAIS DE DESTINO', TRANSIT],
|
|
16
|
+
['ENVIO INGRESA A PAIS DE DESTINO', TRANSIT],
|
|
17
|
+
['RECEPCION INTERNACIONAL POR CORREOSCHILE', TRANSIT],
|
|
18
|
+
// The destination country's import, after the parcel entered it.
|
|
19
|
+
['ENVIO EN PROCESO DE INTERNACION', { status: 'in_transit', stage: 'customs' }],
|
|
20
|
+
]);
|
|
4
21
|
/** Only actual scan wording establishes progress; the portal also sends future rail markers. */
|
|
5
22
|
export function classifyCorreosChileScan(code, wording) {
|
|
6
23
|
const label = comparable(wording);
|
|
@@ -13,5 +30,5 @@ export function classifyCorreosChileScan(code, wording) {
|
|
|
13
30
|
if ((code === '006' || code === '010') && label === 'ENVIO ENTREGADO') {
|
|
14
31
|
return { status: 'delivered', stage: 'delivered' };
|
|
15
32
|
}
|
|
16
|
-
return null;
|
|
33
|
+
return WORDINGS.get(label) ?? null;
|
|
17
34
|
}
|
|
@@ -7,6 +7,83 @@
|
|
|
7
7
|
"language": "es",
|
|
8
8
|
"stage": "customs",
|
|
9
9
|
"confirmedBy": "live"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"wording": "RECIBIDO POR CORREOSCHILE",
|
|
13
|
+
"language": "es",
|
|
14
|
+
"stage": "accepted",
|
|
15
|
+
"confirmedBy": "live",
|
|
16
|
+
"note": "A parcel leaving Chile, as a universal provider relays it without the portal code."
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"wording": "RECIBIDO EN PLANTA DE ORIGEN",
|
|
20
|
+
"language": "es",
|
|
21
|
+
"stage": "in_transit",
|
|
22
|
+
"confirmedBy": "live",
|
|
23
|
+
"note": "A parcel leaving Chile, as a universal provider relays it without the portal code."
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"wording": "ENVIO DESPACHADO",
|
|
27
|
+
"language": "es",
|
|
28
|
+
"stage": "in_transit",
|
|
29
|
+
"confirmedBy": "live",
|
|
30
|
+
"note": "A parcel leaving Chile, as a universal provider relays it without the portal code."
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"wording": "DISPONIBLE PARA SALIR AL PAIS DE DESTINO",
|
|
34
|
+
"language": "es",
|
|
35
|
+
"stage": "in_transit",
|
|
36
|
+
"confirmedBy": "live",
|
|
37
|
+
"note": "A parcel leaving Chile, as a universal provider relays it without the portal code."
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"wording": "ENVIO DESPACHADO AL PAIS DE DESTINO",
|
|
41
|
+
"language": "es",
|
|
42
|
+
"stage": "in_transit",
|
|
43
|
+
"confirmedBy": "live",
|
|
44
|
+
"note": "A parcel leaving Chile, as a universal provider relays it without the portal code."
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"wording": "ENVIO INGRESA A PAIS DE DESTINO",
|
|
48
|
+
"language": "es",
|
|
49
|
+
"stage": "in_transit",
|
|
50
|
+
"confirmedBy": "live",
|
|
51
|
+
"note": "A parcel leaving Chile, as a universal provider relays it without the portal code."
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"wording": "ENVIO EN PROCESO DE INTERNACION",
|
|
55
|
+
"language": "es",
|
|
56
|
+
"stage": "customs",
|
|
57
|
+
"confirmedBy": "live",
|
|
58
|
+
"note": "A parcel leaving Chile, as a universal provider relays it without the portal code."
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"wording": "RECIBIDO EN PLANTA ORIGEN",
|
|
62
|
+
"language": "es",
|
|
63
|
+
"stage": "in_transit",
|
|
64
|
+
"confirmedBy": "prior-art",
|
|
65
|
+
"note": "In the example reply of an open-source Correos de Chile client, without the portal code."
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"wording": "DESPACHADO A OFICINA DE CORREOSCHILE",
|
|
69
|
+
"language": "es",
|
|
70
|
+
"stage": "in_transit",
|
|
71
|
+
"confirmedBy": "prior-art",
|
|
72
|
+
"note": "In the example reply of an open-source Correos de Chile client, without the portal code."
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"wording": "RECIBIDO EN OFICINA DE CORREOSCHILE",
|
|
76
|
+
"language": "es",
|
|
77
|
+
"stage": "in_transit",
|
|
78
|
+
"confirmedBy": "prior-art",
|
|
79
|
+
"note": "In the example reply of an open-source Correos de Chile client, without the portal code."
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"wording": "RECEPCION INTERNACIONAL POR CORREOSCHILE",
|
|
83
|
+
"language": "es",
|
|
84
|
+
"stage": "in_transit",
|
|
85
|
+
"confirmedBy": "prior-art",
|
|
86
|
+
"note": "In the example reply of an open-source Correos de Chile client, without the portal code."
|
|
10
87
|
}
|
|
11
88
|
]
|
|
12
89
|
}
|
|
@@ -61,8 +61,8 @@ export declare class DHLTracker {
|
|
|
61
61
|
/**
|
|
62
62
|
* Whether DHL Paket knows the number, over HTTP alone: the direct session,
|
|
63
63
|
* renewed as for a lookup, and never the browser. DHL's explicit no-data
|
|
64
|
-
* answer is unknown; a
|
|
65
|
-
*
|
|
64
|
+
* answer is unknown; a postcode request for a parcel DHL holds is known; a
|
|
65
|
+
* rejected session or another DHL service stays inconclusive.
|
|
66
66
|
*/
|
|
67
67
|
recognizes(trackingNumber: string, context?: TrackingContext): Promise<Recognition>;
|
|
68
68
|
private lookup;
|
|
@@ -97,10 +97,12 @@ export function parseDHLTrackingResponse(payload, trackingNumber) {
|
|
|
97
97
|
if (matches.length !== 1)
|
|
98
98
|
throw new SchemaError(PROVIDER, 'DHL did not return one matching shipment');
|
|
99
99
|
const shipment = matches[0];
|
|
100
|
+
const missing = isRecord(shipment.sendungNichtGefunden) ? shipment.sendungNichtGefunden : {};
|
|
100
101
|
if (shipment.plzBenoetigt === true || shipment.versandDatumBenoetigt === true) {
|
|
101
|
-
|
|
102
|
+
// DHL asks the recipient's postcode to show a parcel it holds; a record it also calls not found names none.
|
|
103
|
+
const held = shipment.plzBenoetigt === true && !Object.values(missing).some((flag) => flag === true);
|
|
104
|
+
throw new InputRequiredError(PROVIDER, 'a postcode or shipping date', 'DHL requires additional verification on its tracking website', held ? { reason: 'parcel_found' } : undefined);
|
|
102
105
|
}
|
|
103
|
-
const missing = isRecord(shipment.sendungNichtGefunden) ? shipment.sendungNichtGefunden : {};
|
|
104
106
|
// DHL Paket's tracker answered that it holds no Paket shipment under the number. Another DHL
|
|
105
107
|
// service may still know it, so it stays inconclusive, but it is an answer, not an outage.
|
|
106
108
|
if (missing.keineDhlPaketSendung === true || missing.sendungsnummerNichtSuchbar === true) {
|
|
@@ -274,8 +276,8 @@ export class DHLTracker {
|
|
|
274
276
|
/**
|
|
275
277
|
* Whether DHL Paket knows the number, over HTTP alone: the direct session,
|
|
276
278
|
* renewed as for a lookup, and never the browser. DHL's explicit no-data
|
|
277
|
-
* answer is unknown; a
|
|
278
|
-
*
|
|
279
|
+
* answer is unknown; a postcode request for a parcel DHL holds is known; a
|
|
280
|
+
* rejected session or another DHL service stays inconclusive.
|
|
279
281
|
*/
|
|
280
282
|
async recognizes(trackingNumber, context = {}) {
|
|
281
283
|
return takeTurn(this.serialize, 'dhl', context, (left) => {
|
package/dist/places/index.js
CHANGED
|
@@ -411,7 +411,8 @@ function candidates(data, key, carriers) {
|
|
|
411
411
|
export function locatePlace(location, hints = {}) {
|
|
412
412
|
return locate(location, hints)?.place ?? null;
|
|
413
413
|
}
|
|
414
|
-
|
|
414
|
+
/** `split` reads a country the text names apart from its town; without it the whole text is the town's. */
|
|
415
|
+
function locate(location, hints, split = true) {
|
|
415
416
|
const text = location?.trim();
|
|
416
417
|
// A military post office's base can be anywhere in its region (`militaryPostOffice`).
|
|
417
418
|
if (!text || militaryPostOffice(text))
|
|
@@ -420,7 +421,7 @@ function locate(location, hints) {
|
|
|
420
421
|
if (facility)
|
|
421
422
|
return { place: facility, text: true };
|
|
422
423
|
const data = gazetteer();
|
|
423
|
-
const explicit = trackingPlace(text);
|
|
424
|
+
const explicit = split ? trackingPlace(text) : { country: null, place: text };
|
|
424
425
|
let country = explicit.country && data.countries.has(explicit.country) ? explicit.country : null;
|
|
425
426
|
let rest = explicit.country ? explicit.place : text;
|
|
426
427
|
const context = hints.countries ?? [];
|
|
@@ -569,6 +570,16 @@ function locate(location, hints) {
|
|
|
569
570
|
},
|
|
570
571
|
};
|
|
571
572
|
}
|
|
573
|
+
// A hub named after a city that shares a country's name: GIG Logistics' "BENIN HUB"
|
|
574
|
+
// is in Benin City. On a parcel in the city's country, and not in the named one, the
|
|
575
|
+
// city it is, when it is a large one.
|
|
576
|
+
const facilityWords = tokens(rest);
|
|
577
|
+
if (country && !context.includes(country) && text.endsWith(rest) && facilityWords.length
|
|
578
|
+
&& facilityWords.every((token) => FACILITY_WORDS.has(token.key))) {
|
|
579
|
+
const town = locate(text, hints, false);
|
|
580
|
+
if (town?.place.precision === 'city' && context.includes(town.place.country) && (town.thousands ?? 0) >= NAMESAKE_THOUSANDS)
|
|
581
|
+
return town;
|
|
582
|
+
}
|
|
572
583
|
// A two-letter code with no town of its country before it, on a parcel nothing else puts
|
|
573
584
|
// there, may be no country at all: Royal Mail ends its mail centres with MC and its
|
|
574
585
|
// delivery offices with DO, India's states are written like countries. A town the text
|
package/dist/server/openapi.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "universal-parcel-scraper",
|
|
3
|
-
"version": "0.654.
|
|
3
|
+
"version": "0.654.2-main.3118",
|
|
4
4
|
"description": "Self-hosted parcel tracking: carrier detection, dedicated scrapers and optional universal providers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|