Dans cet article je raconte comment avec un peu de lisp j’envoie les articles des mes abonnements rss, lus dans emacs et elfeed, vers mon déploiement de readeck et qui seront envoyés à ma tablette remarkable.
Depuis quelques temps j’utilise emacs et elfeed pour lire mes abonnements RSS. Certains articles peuvent être longs et je souhaitais les lire sur ma tablette remarkable. J’ai un script1 qui synchronise mes bookmarks readeck vers la tablette et il ne manquait qu’une solution pour envoyer les articles de elfeed vers readeck.
Auparavant j’utilisais le défunt pocket-reader pour lequel il existait un client pour emacs. J’ai donc repris du code du projet pour, avec mes maigres compétences lisp, utiliser le service readeck.
Readeck a une api, documentée, et accessible sur les déploiements à l’adresse https://mon-readeck.example.org/docs/api
Mes besoins sont simples envoyer le lien d’un article depuis le buffer de recherche de elfeed (page d’accueil) ou alors depuis le buffer de l’article lui même.
Et comme des fois je veux simplement sauvegarder un article, je veux un moyen d’ajouter le tag “remarkable” que j’utilise pour l’envoi vers la tablette.
Pour ajouter le tag j’utilise le “prefix argument”, C-u
, avant d’appeler la fonction de sauvegarde du lien.
Je peux donc sauvegarder un arcticle avec M-x readeck-send-link
en étant dans le buffer de recherche ou le buffer de l’article.
La commande C-u M-x readeck-send-link
permet de faire la même chose en ajoutant le tag remarkable
.
Le code ci-dessous fait tout ça.
(defgroup readeck nil
"Envoyer des urls vers le service readeck."
:group 'external)
(defcustom readeck-api-endpoint ""
"Adresse du service readeck (https://mon-readeck.example.org/api/bookmarks)."
:type 'string
:group 'readeck)
(defcustom readeck-api-key ""
"Clef d'api du service readeck"
:type 'string
:group 'readeck)
(defun readeck-send-url (url &optional title tags)
"Envoyer une url vers le service readeck avec éventuellement un titre et des tags.
Les variables de endpoint et api doivent être définies"
(interactive "sURL: ")
(unless (and (stringp readeck-api-endpoint)
(> (length readeck-api-endpoint) 0))
(user-error "Vous devez définir `readeck-api-endpoint'"))
(unless (and (stringp readeck-api-key)
(> (length readeck-api-key) 0))
(user-error "Voud devez définir `readeck-api-key'"))
(require 'request)
(request readeck-api-endpoint
:type "POST"
:headers `(("Authorization" . ,(concat "Bearer " readeck-api-key))
("Content-Type" . "application/json"))
:data (json-encode `(("url" . ,url)
("title" . ,title)
("labels" . ,tags)))
:parser 'json-read
:success (cl-function
(lambda (&key data response &allow-other-keys)
(message "URL envoyée à Readeck.")))
:error (cl-function
(lambda (&key error-thrown response &allow-other-keys)
(message "Erreur pour l'envoi à Readeck : %S" error-thrown)))))
(cl-defun readeck-add-urls (urls &optional tags)
"Ajoute une ou plusieurs urls à readeck.
Les urls peuvent être une string ou une liste de string.
Les tags sont optionnels et peuvent être une string ou liste de string"
(when (atom urls)
(setq urls (list urls)))
(when current-prefix-arg
(setq tags (cons "remarkable" tags)))
(dolist (url
(--map (list
:url it
:tags tags)
urls))
;; (with-current-buffer (get-buffer "*scratch*")
;; (save-excursion
;; (goto-char (point-max))
;; (insert (format "url : %s | Tags : %s\n"
;; (plist-get url :url)
;; (or (plist-get url :tags) "")))))
;; ))
(readeck-send-url (plist-get url :url) nil (or (plist-get url :tags) '("")))))
(defun readeck-elfeed-search-send-link ()
"Envoie à readeck les liens de l'entrée elfeed sélectionnée dans le buffer elfeed de recherche."
(interactive)
(when-let ((entries (elfeed-search-selected))
(links (mapcar #'elfeed-entry-link entries)))
(when (readeck-add-urls links)
(message "Ajout : %s" (s-join ", " links))
(elfeed-search-untag-all-unread)
)))
(defun readeck-elfeed-entry-send-link ()
"Envoie à readeck les liens de l'entrée elfeed affichée dans le buffer elfeed (en lecture)."
(interactive)
(when-let ((link (elfeed-entry-link elfeed-show-entry)))
(when (readeck-add-urls link)
(message "Ajout : %s" link))))
(defun readeck-send-link ()
"Envoie à readeck le lien sélectionné.
En fonction du mode majeur appelle la bonne fonction."
(interactive)
(cl-case major-mode
('elfeed-search-mode (readeck-elfeed-search-send-link))
('elfeed-show-mode (readeck-elfeed-entry-send-link))))
(provide 'readeck)
On peut ajouter des raccourcis clavier :
(with-eval-after-load 'elfeed
(define-key elfeed-search-mode-map (kbd "B") 'readeck-send-link)
(define-key elfeed-search-mode-map (kbd "R") (lambda ()
(interactive)
(let ((current-prefix-arg 1))
(call-interactively #'readeck-send-link))))
(define-key elfeed-show-mode-map (kbd "B") 'readeck-send-link)
(define-key elfeed-show-mode-map (kbd "R") (lambda ()
(interactive)
(let ((current-prefix-arg 1))
(call-interactively #'readeck-send-link))))
)
Dans les deux buffers la commande B
sauvegarde le lien, la commande R
sauvegarde le lien avec le tag remarkable
.
Il faut améliorer le code, en particulier le nom des fonctions trop générique car cela ne fonctionne que pour elfeed.
Mais pour l’instant, en peu de temps, cela répond à mon besoin !
-
Il faudra que je fasse un article sur le code rust qui synchronise les bookmarks de readeck vers la tablette remarkable. C’est un fork d’un projet et il faudrait que je publie le code et fasse une merge request. Le code permet d’envoyer des articles avec un tag vers la remarkable et de supprimer les articles lus sur la tablette. Simple et efficace (du moins je trouve). ↩︎