aboutsummaryrefslogtreecommitdiffstats
path: root/elisp/delivery-track.el
blob: 809e1f27cafabbb16638e5a163e6d3c3c8deac8e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
;;; delivery-track.el --- Track packages -*- lexical-binding: t; -*-
;;
;; Copyright (C) 2024 Óscar Nájera
;;
;; Author: Óscar Nájera <hi@oscarnajera.com>
;; Maintainer: Óscar Nájera <hi@oscarnajera.com>
;; Created: August 28, 2024
;; Modified: August 28, 2024
;; Version: 0.0.1
;; Keywords: abbrev bib c calendar comm convenience data docs emulations extensions faces files frames games hardware help hypermedia i18n internal languages lisp local maint mail matching mouse multimedia news outlines processes terminals tex tools unix vc wp
;; Homepage: https://github.com/titan/delivery-track
;; Package-Requires: ((emacs "29.1"))
;;
;; This file is not part of GNU Emacs.
;;
;;; Commentary:
;;
;;  Track packages
;;
;;; Code:

(require 'org)
(require 'org-id)
(require 'url)

(defvar url-http-end-of-headers)

(defun delivery-track-write-time! (timestring)
  "Insert an org inactive timestamp from a parse-able TIMESTRING."
  (thread-first
    timestring
    (parse-time-string)
    (encode-time)
    (org-insert-timestamp t t)))

(defun delivery-track-org-entry (buffer shipment-id status events)
  "Write update on BUFFER the SHIPMENT-ID with STATUS and EVENTS."
  (with-current-buffer buffer
    (goto-char (org-find-entry-with-id shipment-id))
    (org-entry-put nil "status" status)
    (org-next-visible-heading 1)
    (seq-let (level _rlevel _todo _prio headline)
        (org-heading-components)
      (when (and (= level 2)
                 (string= "Shipment reverse history" headline))
        (org-cut-subtree))

      (insert "** Shipment reverse history\n")
      (seq-doseq (event events)
        (seq-let (time status) event
          (delivery-track-write-time! time)
          (insert " " status "\n"))))))

(defun delivery-track--process-response (_request-status buffer provider-parser)
  "Process API response with PROVIDER-PARSER and update into BUFFER."
  (goto-char url-http-end-of-headers)
  (apply #'delivery-track-org-entry
         buffer
         (funcall provider-parser (json-parse-buffer))))

(defun delivery-track--dhl-de (track-id buffer callback)
  "Async request to API using TRACK-ID and process with CALLBACK.
It writes into the org-node in BUFFER."
  (let* ((url-request-method "GET")
         (params `((piececode ,track-id)
                   ("noRedirect" true)
                   (language "en"))))
    (thread-first
      "https://www.dhl.de/int-verfolgen/data/search?"
      (concat (url-build-query-string params))
      (url-retrieve callback (list buffer)))))

(defun delivery-track-entry--hermes (response)
  "Parse hermes RESPONSE into standard delivery-track info for writer."
  (list
   (gethash "barcode" response)
   (thread-last response (gethash "status") (gethash "parcelStatus"))
   (thread-last
     (gethash "parcelHistory" response)
     (seq-keep (lambda (event)
                 (when-let ((time (gethash "timestamp" event)))
                   (list time (gethash "statusHistoryText" event)))))
     (reverse))))

(defun delivery-track--hermes-de (track-id)
  "Async request to hermes API using TRACK-ID.
Write update on the org-node in current buffer."
  (let* ((url-request-method "GET")
         (url-request-extra-headers
          '(("User-Agent" . "Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0"))))
    (thread-first
      "https://api.my-deliveries.de/tnt/parcelservice/parceldetails/"
      (concat track-id)
      (url-retrieve #'delivery-track--process-response
                    (list (current-buffer) #'delivery-track-entry--hermes)))))


(defun delivery-track-entry--dhl (_request-status buffer)
  "Parse response from DHL and write in in BUFFER."
  (goto-char url-http-end-of-headers)
  (let* ((shipment-info (thread-last
                          (json-parse-buffer)
                          (gethash "sendungen")
                          (seq-find
                           (lambda (item)
                             (eq t (gethash "hasCompleteDetails" item))))))
         (history (thread-last
                    shipment-info
                    (gethash "sendungsdetails")
                    (gethash "sendungsverlauf"))))
    (delivery-track-org-entry
     buffer
     (gethash "id" shipment-info)
     (gethash "kurzStatus" history)
     (thread-last
       (gethash "events" history)
       (seq-keep (lambda (event)
                   (when-let ((time (gethash "datum" event)))
                     (list time (gethash "status" event)))))
       (reverse)))))

(defun delivery-track-update (track-id provider)
  "Update tracking information for TRACK-ID under PROVIDER.
Interactive defaults to current buffer's org-node id and provider properties."
  (interactive (list (read-string "What is the tracking id? " (org-id-get))
                     (completing-read "Which service provider? "
                                      '(dhl hermes) nil t (org-entry-get nil "provider"))))
  (pcase provider
    ("dhl" (delivery-track--dhl-de track-id (current-buffer) #'delivery-track-entry--dhl))
    ("hermes" (delivery-track--hermes-de track-id))))


(provide 'delivery-track)
;;; delivery-track.el ends here