;;; delivery-track.el --- Track packages -*- lexical-binding: t; -*- ;; ;; Copyright (C) 2024 Óscar Nájera ;; ;; Author: Óscar Nájera ;; Maintainer: Óscar Nájera ;; 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--history (events window) "String with all EVENTS and hint of delivery time WINDOW." (with-temp-buffer (insert "** Shipment reverse history\n") (when window (insert "Delivery window: ") (delivery-track-write-time! (car window)) (insert "--") (delivery-track-write-time! (cdr window)) (insert "\n")) (seq-doseq (event events) (seq-let (time status) event (delivery-track-write-time! time) (insert " " status "\n"))) (buffer-string))) (defun delivery-track-org-entry (buffer shipment-id status events &optional window) "Write update on BUFFER the SHIPMENT-ID with STATUS and EVENTS. Optionally give the delivery WINDOW." (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 (delivery-track-org--history events window))))) (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-entry--dhl (response) "Parse DHL RESPONSE into standard delivery-track info for writer." (let* ((shipment-info (seq-find (lambda (item) (eq t (gethash "hasCompleteDetails" item))) (gethash "sendungen" response))) (details (gethash "sendungsdetails" shipment-info)) (history (gethash "sendungsverlauf" details)) (delivery (gethash "zustellung" details))) (list (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)) (cons (gethash "zustellzeitfensterVon" delivery) (gethash "zustellzeitfensterBis" delivery))))) (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--dhl-de (track-id) "Async request to dhl API using TRACK-ID. Write update on the org-node in current 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 #'delivery-track--process-response (list (current-buffer) #'delivery-track-entry--dhl))))) (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-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 )) ("hermes" (delivery-track--hermes-de track-id)))) (provide 'delivery-track) ;;; delivery-track.el ends here