emacs-opml-to-elfeed-feeds

Unnamed repository; edit this file 'description' to name the repository.
git clone https://kaka.farm/~git/emacs-opml-to-elfeed-feeds/
Log | Files | Refs | README | LICENSE

commit 04e09341c0cfbc66a9618a58f6af8b3026b410eb
parent 80245f41431d8859a388f07a130081782b573bb9
Author: Yuval Langer <yuval.langer@gmail.com>
Date:   Sat, 29 Jun 2024 05:52:27 +0300

Fix the o2e-elfeed-feeds updating function and add a ton of docstrings.

Diffstat:
Mopml-to-elfeed-feeds.el | 161++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
1 file changed, 118 insertions(+), 43 deletions(-)

diff --git a/opml-to-elfeed-feeds.el b/opml-to-elfeed-feeds.el @@ -37,23 +37,33 @@ Each member is either a string or the URL, or a list with the URL as the first member and optional tags. The tags would be attached to each feed in the OPML file." + :type '(repeat (choice string (cons string (repeat symbol))))) (defcustom o2e-elfeed-feeds '() - "The GCO `elfeed-feeds'." + "`o2e-elfeeds' is the `o2e'-specific `elfeed-feeds' of all OPMLs after running the function `o2e--update-o2e-efleed-feeds'." + :type '(repeat (choice string (cons string (repeat symbol))))) -(defun o2e--blogger-name-to-symbol (blogger-name) +(defun o2e--text-element-value-to-tag (text-element-value) + "Convert TEXT-ELEMENT-VALUE of an OPML's \"outline\" into a symbol. + +It is used as an elfeed-feeds tag. + +In the Craftering OPML it represents the author of the blog's name." + (intern (downcase (replace-regexp-in-string "[ .()]+" "-" - blogger-name)))) + text-element-value)))) (defun o2e--response-body-position (response-buffer) + "Return the starting position of the body within the HTTP RESPONSE-BUFFER." + (with-current-buffer response-buffer (named-let loop ((position 1)) (cond @@ -68,25 +78,44 @@ The tags would be attached to each feed in the OPML file." (loop (1+ position))))))) (defun o2e--parse-response-buffer-to-xml (response-buffer) + "Return the parsed XML contents of the HTTP RESPONSE-BUFFER." + (with-current-buffer response-buffer (let ((start (o2e--response-body-position response-buffer)) (end (buffer-end 1))) + ;; XXX: Last time I've tried using it, the builtin + ;; `xml-parse-regin' function returned some weird crap: + ;; ;; (xml-parse-region beg ;; end ;; response-buffer) + ;; + ;; So I'm using the libxml library instead: (libxml-parse-xml-region start end)))) -(defun o2e--xml-to-opml-outlines (xml) - (pcase (assq 'body xml) - ;; TODO: What the hell is this? +(defun o2e--xml-to-opml-outlines (opml) + "Return the outlines of the parsed OPML." + + (pcase (assq 'body opml) + ;; TODO: What the hell is this? Does anybody know? (`(body ,TODO-WHAT-THE-HELL-IS-THIS? . ,outlines) outlines))) (cl-defun o2e--response-buffer-to-elfeed-feed (response-buffer &key (tags '()) - (tag-blogger-name? '())) + (text-element-value-to-tag '())) + "Convert the raw OPML data within RESPONSE-BUFFER to an `elfeed-feeds' and return it. + +The optional named arguments: + +- TAGS - A list of symbols that would be added to every + resulting feed. + +- TEXT-ELEMENT-VALUE-TO-TAG - When non-nil, would append the + munged \"text\" entry of each \"outline\" within the OPML." + (let* ((xml (o2e--parse-response-buffer-to-xml response-buffer)) (outlines (o2e--xml-to-opml-outlines xml))) (named-let loop ((outlines outlines) @@ -99,15 +128,18 @@ The tags would be attached to each feed in the OPML file." 'outline)) (let* ((outline-values (cadar outlines)) (rest-of-outlines (cdr outlines)) + ;; XXX: In Craftering OPML uses to represent the name + ;; of the author. (text (cdr (assq 'text outline-values))) (xml-url (cdr (assq 'xmlUrl outline-values))) - (blogger-name-tag (o2e--blogger-name-to-symbol text)) (all-tags (elfeed-normalize-tags - (if tag-blogger-name? - (list blogger-name-tag) - '()) + ;; If user provided a "text" munging + ;; function, use it to create a tag. + (when text-element-value-to-tag + (list (funcall text-element-value-to-tag + text))) tags)) (elfeed-feed-entry (append (list xml-url) all-tags))) @@ -124,7 +156,10 @@ The tags would be attached to each feed in the OPML file." accumulator)))))) (defun o2e--elfeed-feeds-entry-url (elfeed-feeds-entry) - "Takes an `elfeed-feeds' kind of entry as ELFEED-FEEDS-ENTRY and return its URL." + "Return the URL of ELFEED-FEEDS-ENTRY. + +ELFEED-FEEDS-ENTRY is of the `elfeed-feeds' type." + (pcase elfeed-feeds-entry (`(,url . ,tags) url) @@ -132,26 +167,54 @@ The tags would be attached to each feed in the OPML file." url))) (defun o2e--elfeed-feeds-entry-tags (elfeed-feeds-entry) - "Takes an `elfeed-feeds' kind of entry as ELFEED-FEEDS-ENTRY and return its list of tags." + "Return the tags of ELFEED-FEEDS-ENTRY. + +ELFEED-FEEDS-ENTRY is of the `elfeed-feeds' type." + (pcase elfeed-feeds-entry (`(,url . ,tags) tags) (url '()))) -(defun o2e--hash-table-to-alist (hash-table) - "Convert an hash-table HASH-TABLE to a list. +(cl-defun o2e--hash-table-to-alist (hash-table &key (ordp 'string<)) + "Return HASH-TABLE represented as a list. + +The optional named argument ORDP decides how to order the +returned list. Its default value is the symbol `string<'. TODO: There MUST be a standard function for that, right? Send a patch if you know." + + '(unless ordp + (setq ordp 'string<)) + (mapcar (lambda (key) (cons key (gethash key hash-table))) (sort (hash-table-keys hash-table) - 'string<))) + ordp))) + +(cl-defun o2e--normalise-elfeed-feeds (elfeed-feeds &key (ordp 'string<)) + "Return ELFEED-FEEDS with duplicate entries merged. + +\"Merged\", in this case, means that calling the function with an +ELFEED-FEEDS with the value of: + +'((\"https://example.com/blog.xml\" a b c) + (\"https://example.com/blog.xml\" b c d)) + +would result with: + +'((\"https://example.com/blog.xml\" a b c d)) + +The order of the original elfeed-feeds is destroyed but it is +ordered according to the optional named argument ORDP." + + '(unless ordp + (setq ordp 'string<)) -(defun o2e--normalise-elfeed-feeds (elfeed-feeds) (let (;; XXX: Pass ":test 'equal" into make-hash-table so that ;; string comparisons work, otherwise it'll put many of the ;; same string as keys, each with its own values, like: @@ -177,34 +240,46 @@ patch if you know." (o2e--hash-table-to-alist hash))) (defun o2e--update-o2e-efleed-feeds () - "TODO: This here supposed to take the opmls and add and dedup the GCO elfeed-feeds" - (let ((our-elfeed-feeds o2e-elfeed-feeds)) - (dolist (opml-list-entry - o2e-opml-list - (setq o2e-elfeed-feeds (o2e--normalise-elfeed-feeds - (apply 'append - our-elfeed-feeds)))) - (let* ((url+tags (cond - ;; If we have a simple string entry, - ((stringp opml-list-entry) - ;; convert to a tagged entry with empty tag - ;; list. - (list opml-list-entry)) - ;; If we have a tagged entry, - ((and (consp opml-list-entry) - (stringp (car opml-list-entry))) - ;; just evaluate to the entry. - (message "%S" opml-list-entry) - opml-list-entry))) - (url (car url+tags)) - (tags (cdr url+tags)) - (response-buffer (url-retrieve-synchronously url))) - (setq our-elfeed-feeds - (cons (o2e--response-buffer-to-elfeed-feed response-buffer - :tags tags) - our-elfeed-feeds)))))) + "Retrieve the feed list for each of the OPML lists in `o2e-opml-list' and merge them into `o2e-elfeed-feeds'." + + (let ((new-elfeed-feeds + (apply 'append + (mapcar (lambda (opml-list-entry) + (let* ((url+tags (cond + ;; If we have a simple string entry, + ((stringp opml-list-entry) + ;; convert to a tagged entry with empty tag + ;; list. + (list opml-list-entry)) + ;; If we have a tagged entry, + ((and (consp opml-list-entry) + (stringp (car opml-list-entry))) + ;; just evaluate to the entry. + opml-list-entry))) + (url (car url+tags)) + (tags (cdr url+tags)) + (response-buffer (url-retrieve-synchronously url))) + (o2e--response-buffer-to-elfeed-feed response-buffer + :tags tags))) + o2e-opml-list)))) + (message "%S" new-elfeed-feeds) + (customize-save-variable 'o2e-elfeed-feeds + (o2e--normalise-elfeed-feeds + (append new-elfeed-feeds + o2e-elfeed-feeds))))) + +(defun o2e--merge-o2e-elfeed-feeds-with-elfeed-elfeed-feeds () + "Merge the two variables `elfeed-feeds' and `o2e-elfeed-feeds' and +save the result in `elfeed-feeds' as the default value. + +TODO: Do you really call the saved customization \"default value\"?" + + (customize-save-variable 'elfeed-feeds + (o2e--normalise-elfeed-feeds (append elfeed-feeds + o2e-elfeed-feeds)))) ;;; XXX: Manual testing AHOY! +;; (customize-save-variable 'o2e-elfeed-feeds '()) ;; o2e-elfeed-feeds ;; (o2e--update-o2e-efleed-feeds) ;; (setq craftering (url-retrieve-synchronously (caar o2e-opml-list)))