]> git.bundesbrandschatzamt.de Git - emacsconfig.git/commitdiff
update elfeed-org
authorAndreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
Wed, 21 May 2025 12:02:00 +0000 (14:02 +0200)
committerAndreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
Wed, 21 May 2025 12:02:00 +0000 (14:02 +0200)
bba.org
elpa/elfeed-org-20231009.1125/elfeed-org-autoloads.el [deleted file]
elpa/elfeed-org-20231009.1125/elfeed-org-pkg.el [deleted file]
elpa/elfeed-org-20231009.1125/elfeed-org.el [deleted file]
elpa/elfeed-org-20250219.950/elfeed-org-autoloads.el [new file with mode: 0644]
elpa/elfeed-org-20250219.950/elfeed-org-pkg.el [new file with mode: 0644]
elpa/elfeed-org-20250219.950/elfeed-org.el [new file with mode: 0644]

diff --git a/bba.org b/bba.org
index ffec4ae6aa0f40a9422ee4faa218415f600f62e0..8cce2bfc48877f6e39cc2f5a035076f9f67b82be 100644 (file)
--- a/bba.org
+++ b/bba.org
@@ -1133,7 +1133,7 @@ necessary.
   (if (>= emacs-major-version 24)
       (progn
         (add-to-list 'load-path "~/.emacs.d/elpa/elfeed-20210822.2129")
-        (add-to-list 'load-path "~/.emacs.d/elpa/elfeed-org-20221118.852")
+        (add-to-list 'load-path "~/.emacs.d/elpa/elfeed-org-20250219.950")
         (require 'elfeed-org)
         (elfeed-org)
         (setq rmh-elfeed-org-files (list "~/org/elfeed.org"))))
diff --git a/elpa/elfeed-org-20231009.1125/elfeed-org-autoloads.el b/elpa/elfeed-org-20231009.1125/elfeed-org-autoloads.el
deleted file mode 100644 (file)
index ac9aaa0..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-;;; elfeed-org-autoloads.el --- automatically extracted autoloads (do not edit)   -*- lexical-binding: t -*-
-;; Generated by the `loaddefs-generate' function.
-
-;; This file is part of GNU Emacs.
-
-;;; Code:
-
-(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
-
-
-\f
-;;; Generated autoloads from elfeed-org.el
-
-(autoload 'elfeed-org "elfeed-org" "\
-Hook up rmh-elfeed-org to read the `org-mode' configuration when elfeed is run." t)
-(register-definition-prefixes "elfeed-org" '("elfeed-org-" "rmh-elfeed-"))
-\f
-;;; End of scraped data
-
-(provide 'elfeed-org-autoloads)
-
-;; Local Variables:
-;; version-control: never
-;; no-byte-compile: t
-;; no-update-autoloads: t
-;; no-native-compile: t
-;; coding: utf-8-emacs-unix
-;; End:
-
-;;; elfeed-org-autoloads.el ends here
diff --git a/elpa/elfeed-org-20231009.1125/elfeed-org-pkg.el b/elpa/elfeed-org-20231009.1125/elfeed-org-pkg.el
deleted file mode 100644 (file)
index 9b6069a..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-;; -*- no-byte-compile: t; lexical-binding: nil -*-
-(define-package "elfeed-org" "20231009.1125"
-  "Configure elfeed with one or more org-mode files."
-  '((elfeed "1.1.1")
-    (org    "8.2.7")
-    (cl-lib "0.5"))
-  :url "https://github.com/remyhonig/elfeed-org"
-  :commit "fe59a96969bd321f5f9ec7317a4bc80943b94c86"
-  :revdesc "fe59a96969bd"
-  :keywords '("news")
-  :authors '(("Remy Honig" . "remyhonigNOSPAMgmail.com"))
-  :maintainers '(("Remy Honig" . "remyhonigNOSPAMgmail.com")))
diff --git a/elpa/elfeed-org-20231009.1125/elfeed-org.el b/elpa/elfeed-org-20231009.1125/elfeed-org.el
deleted file mode 100644 (file)
index 6bd1af1..0000000
+++ /dev/null
@@ -1,448 +0,0 @@
-;;; elfeed-org.el --- Configure elfeed with one or more org-mode files -*- lexical-binding: t; -*-
-
-;; Copyright (C) 2014  Remy Honig
-
-;; Author           : Remy Honig <remyhonigNOSPAMgmail.com>
-;; Package-Requires : ((elfeed "1.1.1") (org "8.2.7") (cl-lib "0.5"))
-;; URL              : https://github.com/remyhonig/elfeed-org
-;; Version          : 20170423.1
-;; Keywords         : news
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-;; Package-Version: 20231009.1125
-;; Package-Revision: fe59a96969bd
-
-;;; Commentary:
-;; Maintaining tags for all rss feeds is cumbersome using the regular
-;; flat list where there is no hierarchy and tag names are duplicated
-;; a lot.  Org-mode makes the book keeping of tags and feeds much
-;; easier.  Tags get inherited from parent headlines.  Multiple files
-;; can be specified to separate your private from your work feeds for
-;; example.  You may also use tagging rules to tag feeds by entry-title
-;; keywords.  See https://github.com/remyhonig/elfeed-org for usage.
-
-;;; Code:
-
-(require 'elfeed)
-(require 'org)
-(require 'org-element)
-(require 'cl-lib)
-(require 'xml)
-
-
-(defgroup elfeed-org nil
-  "Configure the Elfeed RSS reader with an Orgmode file"
-  :group 'comm)
-
-
-(defcustom rmh-elfeed-org-tree-id "elfeed"
-  "The tag or ID property on the trees containing the RSS feeds."
-  :group 'elfeed-org
-  :type 'string)
-
-(defcustom rmh-elfeed-org-ignore-tag "ignore"
-  "The tag on the feed trees that will be ignored."
-  :group 'elfeed-org
-  :type 'string)
-
-(defcustom rmh-elfeed-org-auto-ignore-invalid-feeds nil
-  "Tag feeds to ignore them when a feed could not loaded."
-  :group 'elfeed-org
-  :type 'bool)
-
-(defcustom rmh-elfeed-org-files (list (locate-user-emacs-file "elfeed.org"))
-  "The files where we look to find trees with the `rmh-elfeed-org-tree-id'.
-In this file paths can be given relative to `org-directory'."
-  :group 'elfeed-org
-  :type '(repeat (file :tag "org-mode file")))
-
-(defvar elfeed-org-new-entry-hook nil
-  "List of new-entry tagger hooks created by elfeed-org.")
-
-(defun rmh-elfeed-org-check-configuration-file (file)
-  "Make sure FILE exists."
-  (when (not (file-exists-p (expand-file-name file org-directory)))
-    (error "Elfeed-org cannot open %s.  Make sure it exists or customize the variable \'rmh-elfeed-org-files\'"
-           (abbreviate-file-name file))))
-
-(defun rmh-elfeed-org-is-headline-contained-in-elfeed-tree ()
-  "Is any ancestor a headline with the elfeed tree id.
-Return t if it does or nil if it does not."
-  (let ((result nil))
-    (save-excursion
-      (while (and (not result) (org-up-heading-safe))
-        (setq result (member rmh-elfeed-org-tree-id (org-get-tags))))
-    result)))
-
-(defun rmh-elfeed-org-mark-feed-ignore (url)
-  "Set tag `rmh-elfeed-org-ignore-tag' to headlines containing the feed URL."
-  (dolist (org-file rmh-elfeed-org-files)
-    (with-temp-buffer
-      (insert-file-contents org-file)
-      (let ((org-inhibit-startup t)
-            (org-mode-hook nil))
-        (org-mode))
-      (goto-char (point-min))
-      (while (and
-              (search-forward url nil t)
-              (org-at-heading-p t)
-              (rmh-elfeed-org-is-headline-contained-in-elfeed-tree))
-        (org-toggle-tag rmh-elfeed-org-ignore-tag 'on))
-      (elfeed-log 'info "elfeed-org tagged '%s' in file '%s' with '%s' to be ignored" url org-file rmh-elfeed-org-ignore-tag))))
-
-
-(defun rmh-elfeed-org-import-trees (tree-id)
-  "Get trees with \":ID:\" property or tag of value TREE-ID.
-Return trees with TREE-ID as the value of the id property or
-with a tag of the same value.  Setting an \":ID:\" property is not
-recommended but I support it for backward compatibility of
-current users."
-  (org-element-map
-      (org-element-parse-buffer)
-      'headline
-    (lambda (h)
-      (when (or (member tree-id (org-element-property :tags h))
-                (equal tree-id (org-element-property :ID h))) h))))
-
-
-(defun rmh-elfeed-org-convert-tree-to-headlines (parsed-org)
-  "Get the inherited tags from PARSED-ORG structure if MATCH-FUNC is t.
-The algorithm to gather inherited tags depends on the tree being
-visited depth first by `org-element-map'.  The reason I don't use
-`org-get-tags-at' for this is that I can reuse the parsed org
-structure and I am not dependent on the setting of
-`org-use-tag-inheritance' or an org buffer being present at
-all.  Which in my opinion makes the process more traceable."
-  (let* ((tags '())
-         (level 1))
-    (org-element-map parsed-org 'headline
-      (lambda (h)
-        (pcase-let*
-            ((current-level (org-element-property :level h))
-             (delta-level (- current-level level))
-             (delta-tags (mapcar (lambda (tag)
-                                   (intern (substring-no-properties tag)))
-                                 (org-element-property :tags h)))
-             (heading (org-element-property :raw-value h))
-             (`(,link ,description)
-              (org-element-map (org-element-property :title h) 'link
-                (lambda (link)
-                  (list
-                   (org-element-property :raw-link link)
-                   (when (and (org-element-property :contents-begin link)
-                              (org-element-property :contents-end link))
-                     (buffer-substring
-                      (org-element-property :contents-begin link)
-                      (org-element-property :contents-end link)))))
-                nil t)))
-          ;; update the tags stack when we visit a parent or sibling
-          (unless (> delta-level 0)
-            (let ((drop-num (+ 1 (- delta-level))))
-              (setq tags (nthcdr drop-num tags))))
-          ;; save current level to compare with next heading that will be visited
-          (setq level current-level)
-          ;; save the tags that might apply to potential children of the current heading
-          (push (append (car tags) delta-tags) tags)
-          ;; return the heading and inherited tags
-          (if (and link description)
-              (append (list link)
-                       (car tags)
-                       (list description))
-            (append (list (if link link heading))
-                     (car tags))))))))
-
-;; TODO: mark wrongly formatted feeds (PoC for unretrievable feeds)
-(defun rmh-elfeed-org-flag-headlines (parsed-org)
-  "Flag headlines in PARSED-ORG if they don't have a valid value."
-  (org-element-map parsed-org 'headline
-    (lambda (h)
-      (let ((tags (org-element-property :tags h)))
-        (org-element-put-property h :tags (push "_flag_" tags))))))
-
-
-(defun rmh-elfeed-org-filter-relevant (list)
-  "Filter relevant entries from the LIST."
-  (cl-remove-if-not
-   (lambda (entry)
-     (and
-      (string-match-p "\\(http\\|gopher\\|file\\|entry-title\\)" (car entry))
-      (not (member (intern rmh-elfeed-org-ignore-tag) entry))))
-   list))
-
-
-(defun rmh-elfeed-org-cleanup-headlines (headlines tree-id)
-  "In all HEADLINES given remove the TREE-ID."
-  (mapcar (lambda (e) (delete tree-id e)) headlines))
-
-
-(defun rmh-elfeed-org-import-headlines-from-files (files tree-id)
-  "Visit all FILES and return the headlines stored under tree tagged TREE-ID or with the \":ID:\" TREE-ID in one list."
-  (cl-remove-duplicates
-   (mapcan (lambda (file)
-             (with-temp-buffer
-               (insert-file-contents (expand-file-name file org-directory))
-               (let ((org-inhibit-startup t)
-                     (org-mode-hook nil))
-                 (org-mode))
-               (rmh-elfeed-org-cleanup-headlines
-                (rmh-elfeed-org-filter-relevant
-                 (rmh-elfeed-org-convert-tree-to-headlines
-                  (rmh-elfeed-org-import-trees tree-id)))
-                (intern tree-id))))
-           files)
-   :test #'equal))
-
-
-(defun rmh-elfeed-org-convert-headline-to-tagger-params (tagger-headline)
-  "Add new entry hooks for tagging configured with the found headline in TAGGER-HEADLINE."
-  (list
-   (string-clean-whitespace
-    (string-remove-prefix "entry-title:" (car tagger-headline)))
-   (cdr tagger-headline)))
-
-
-(defun rmh-elfeed-org-export-entry-hook (tagger-params)
-  "Export TAGGER-PARAMS to the proper `elfeed' structure."
-  (add-hook 'elfeed-org-new-entry-hook
-            (elfeed-make-tagger
-             :entry-title (nth 0 tagger-params)
-             :add (nth 1 tagger-params))))
-
-(defun rmh-elfeed-org-export-feed (headline)
-  "Export HEADLINE to the proper `elfeed' structure."
-  (if (and (stringp (car (last headline)))
-           (> (length headline) 1))
-      (progn
-        (add-to-list 'elfeed-feeds (butlast headline))
-        (let ((feed (elfeed-db-get-feed (car headline)))
-              (title (substring-no-properties (car (last headline)))))
-          (setf (elfeed-meta feed :title) title)
-          (elfeed-meta feed :title)))
-    (add-to-list 'elfeed-feeds headline)))
-
-(defun rmh-elfeed-org-process (files tree-id)
-  "Process headlines and taggers from FILES with org headlines with TREE-ID."
-
-  ;; Warn if configuration files are missing
-  (mapc #'rmh-elfeed-org-check-configuration-file files)
-
-  ;; Clear elfeed structures
-  (setq elfeed-feeds nil)
-  (setq elfeed-org-new-entry-hook nil)
-
-  ;; Convert org structure to elfeed structure and register taggers and subscriptions
-  (let* ((headlines (rmh-elfeed-org-import-headlines-from-files files tree-id))
-         (subscriptions (rmh-elfeed-org-filter-subscriptions headlines))
-         (taggers (rmh-elfeed-org-filter-taggers headlines))
-         (elfeed-taggers (mapcar #'rmh-elfeed-org-convert-headline-to-tagger-params
-                                 (rmh-elfeed-org-filter-taggers headlines))))
-    (mapc #'rmh-elfeed-org-export-feed subscriptions)
-    (mapc #'rmh-elfeed-org-export-entry-hook elfeed-taggers))
-
-  ;; Tell user what we did
-  (elfeed-log 'info "elfeed-org loaded %i feeds, %i rules"
-           (length elfeed-feeds)
-           (length elfeed-org-new-entry-hook)))
-
-(defun elfeed-org-run-new-entry-hook (entry)
-  "Run ENTRY through elfeed-org taggers."
-  (dolist (hook elfeed-org-new-entry-hook)
-    (funcall hook entry)))
-
-(defun rmh-elfeed-apply-autotags-now-advice ()
-  "Make entry title matching rules works with `elfeed-apply-autotags-now'."
-  (interactive)
-  (let* ((headlines (rmh-elfeed-org-import-headlines-from-files
-                     rmh-elfeed-org-files rmh-elfeed-org-tree-id))
-         (taggers (rmh-elfeed-org-filter-taggers headlines))
-         (elfeed-taggers (mapcar #'rmh-elfeed-org-convert-headline-to-tagger-params taggers))
-         (entry-match-taggers (mapcar (lambda (tagger-params)
-                                        (elfeed-make-tagger
-                                         :entry-title (nth 0 tagger-params)
-                                         :add (nth 1 tagger-params)))
-                                      elfeed-taggers)))
-    (with-elfeed-db-visit (entry feed)
-                          (dolist (tagger entry-match-taggers)
-                            (funcall tagger entry)))))
-
-(defun rmh-elfeed-org-filter-taggers (headlines)
-  "Filter tagging rules from the HEADLINES in the tree."
-  (cl-remove-if-not #'identity
-                    (mapcar
-                     (lambda (headline)
-                       (when (string-prefix-p "entry-title" (car headline))
-                         headline))
-                     headlines)))
-
-(defun rmh-elfeed-org-filter-subscriptions (headlines)
-  "Filter subscriptions to rss feeds from the HEADLINES in the tree."
-  (cl-remove-if-not #'identity
-                    (mapcar
-                     (lambda (headline)
-                       (let* ((text (car headline))
-                              (link-and-title (and (string-match "^\\[\\[\\(http.+?\\)\\]\\[\\(.+?\\)\\]\\]" text)
-                                                   (list (match-string-no-properties 0 text)
-                                                         (match-string-no-properties 1 text)
-                                                         (match-string-no-properties 2 text))))
-                              (hyperlink (and (string-match "^\\[\\[\\(http.+?\\)\\]\\(?:\\[.+?\\]\\)?\\]" text)
-                                              (list (match-string-no-properties 0 text)
-                                                    (match-string-no-properties 1 text)))))
-                         (cond ((string-prefix-p "http" text) headline)
-                               (link-and-title (append (list (nth 1 hyperlink))
-                                                        (cdr headline)
-                                                        (list (nth 2 link-and-title))))
-                               (hyperlink (append (list (nth 1 hyperlink)) (cdr headline))))))
-                     headlines)))
-
-
-(defun rmh-elfeed-org-convert-opml-to-org (xml level)
-  "Convert OPML content to Org format.
-Argument XML content of the OPML file.
-Argument LEVEL current level in the tree."
-  (cl-loop for (tag attr . content) in (cl-remove-if-not #'listp xml)
-           when (and (not (assoc 'xmlUrl attr)) (assoc 'title attr))
-           concat (format "%s %s\n" (make-string level ?*) (cdr it))
-           when (assoc 'xmlUrl attr)
-           concat (format "%s [[%s][%s]]\n" (make-string level ?*)
-                          (cdr it) (cdr (assoc 'title attr)))
-           concat (rmh-elfeed-org-convert-opml-to-org content (+ 1 level))))
-
-(defun elfeed-org-import-opml (opml-file)
-  "Import feeds from OPML file to a temporary Org buffer.
-Argument OPML-FILE filename of the OPML file."
-  (interactive "FInput OPML file: ")
-  (let* ((xml (xml-parse-file opml-file))
-         (content (rmh-elfeed-org-convert-opml-to-org xml 0)))
-    (with-current-buffer (get-buffer-create "*Imported Org Feeds*")
-      (erase-buffer)
-      (insert (format "* Imported Feeds            :%s:\n" rmh-elfeed-org-tree-id))
-      (insert content)
-      (let ((org-inhibit-startup t))
-        (org-mode))
-      (pop-to-buffer (current-buffer)))))
-
-
-(defun rmh-elfeed-org-convert-org-to-opml (org-buffer)
-  "Convert Org buffer content to OPML format.
-Argument ORG-BUFFER the buffer to write the OPML content to."
-  (let (need-ends
-        opml-body)
-    (with-current-buffer org-buffer
-      (let ((org-inhibit-startup t)
-            (org-mode-hook nil))
-        (org-mode))
-      (org-element-map (rmh-elfeed-org-import-trees
-                        rmh-elfeed-org-tree-id) 'headline
-        (lambda (h)
-          (let* ((current-level (org-element-property :level h))
-                 (tags (org-element-property :tags h))
-                 (heading (org-element-property :raw-value h))
-                 (link-and-title (and (string-match "^\\[\\[\\(http.+?\\)\\]\\[\\(.+?\\)\\]\\]" heading)
-                                      (list (match-string-no-properties 0 heading)
-                                            (match-string-no-properties 1 heading)
-                                            (match-string-no-properties 2 heading))))
-                 (hyperlink (and (string-match "^\\[\\[\\(http.+?\\)\\]\\(?:\\[.+?\\]\\)?\\]" heading)
-                                 (list (match-string-no-properties 0 heading)
-                                       (match-string-no-properties 1 heading))))
-                 url
-                 title
-                 opml-outline)
-            ;; fill missing end outlines
-            (while (and (car need-ends) (>= (car need-ends) current-level))
-              (let* ((level (pop need-ends)))
-                (setq opml-body (concat opml-body (format "  %s</outline>\n"
-                                                          (make-string (* 2 level) ? ))))))
-
-            (cond ((string-prefix-p "http" heading)
-                   (setq url heading)
-                   (setq title (or (elfeed-feed-title (elfeed-db-get-feed heading)) "Unknown")))
-                  (link-and-title (setq url (nth 1 link-and-title))
-                                  (setq title (nth 2 link-and-title)))
-                  (hyperlink (setq url (nth 1 hyperlink))
-                             (setq title (or (elfeed-feed-title (elfeed-db-get-feed (nth 1 hyperlink))) "Unknown")))
-                  (t (setq title heading)))
-            (if url
-                (setq opml-outline (format "  %s<outline title=\"%s\" xmlUrl=\"%s\"/>\n"
-                                           (make-string (* 2 current-level) ? )
-                                           (xml-escape-string title)
-                                           (xml-escape-string url)))
-              (unless (string-prefix-p "entry-title" heading)
-                (unless (member rmh-elfeed-org-tree-id tags)
-                  ;; insert category title only when it is neither the top
-                  ;; level elfeed node nor the entry-title node
-                  (progn
-                    (push current-level need-ends)
-                    (setq opml-outline (format "  %s<outline title=\"%s\">\n"
-                                               (make-string (* 2 current-level) ? )
-                                               (xml-escape-string title)))))))
-            (setq opml-body (concat opml-body opml-outline))))))
-
-    ;; fill missing end outlines at end
-    (while (car need-ends)
-      (let* ((level (pop need-ends)))
-        (setq opml-body (concat opml-body (format "  %s</outline>\n"
-                                                  (make-string (* 2 level) ? ))))))
-    opml-body))
-
-(defun elfeed-org-export-opml ()
-  "Export Org feeds under `rmh-elfeed-org-files' to a temporary OPML buffer.
-The first level elfeed node will be ignored. The user may need edit the output
-because most of Feed/RSS readers only support trees of 2 levels deep."
-  (interactive)
-  (let ((opml-body (cl-loop for org-file in rmh-elfeed-org-files
-                             concat
-                             (with-temp-buffer
-                               (insert-file-contents
-                                (expand-file-name org-file org-directory))
-                               (rmh-elfeed-org-convert-org-to-opml
-                                (current-buffer))))))
-    (with-current-buffer (get-buffer-create "*Exported OPML Feeds*")
-      (erase-buffer)
-      (insert "<?xml version=\"1.0\"?>\n")
-      (insert "<opml version=\"1.0\">\n")
-      (insert "  <head>\n")
-      (insert "    <title>Elfeed-Org Export</title>\n")
-      (insert "  </head>\n")
-      (insert "  <body>\n")
-      (insert opml-body)
-      (insert "  </body>\n")
-      (insert "</opml>\n")
-      (xml-mode)
-      (pop-to-buffer (current-buffer)))))
-
-(defun rmh-elfeed-org-process-advice ()
-  "Advice to add to `elfeed' to load the configuration before it is run."
-  (rmh-elfeed-org-process rmh-elfeed-org-files rmh-elfeed-org-tree-id))
-
-;;;###autoload
-(defun elfeed-org ()
-  "Hook up rmh-elfeed-org to read the `org-mode' configuration when elfeed is run."
-  (interactive)
-  (elfeed-log 'info "elfeed-org is set up to handle elfeed configuration")
-  (advice-add #'elfeed :before #'rmh-elfeed-org-process-advice)
-  (add-hook 'elfeed-new-entry-hook #'elfeed-org-run-new-entry-hook)
-  (advice-add 'elfeed-apply-autotags-now :after #'rmh-elfeed-apply-autotags-now-advice)
-  (add-hook 'elfeed-http-error-hooks
-            (lambda (url _status)
-              (when rmh-elfeed-org-auto-ignore-invalid-feeds
-                (rmh-elfeed-org-mark-feed-ignore url))))
-  (add-hook 'elfeed-parse-error-hooks
-            (lambda (url _error)
-              (when rmh-elfeed-org-auto-ignore-invalid-feeds
-                (rmh-elfeed-org-mark-feed-ignore url)))))
-
-
-(provide 'elfeed-org)
-;;; elfeed-org.el ends here
diff --git a/elpa/elfeed-org-20250219.950/elfeed-org-autoloads.el b/elpa/elfeed-org-20250219.950/elfeed-org-autoloads.el
new file mode 100644 (file)
index 0000000..ac9aaa0
--- /dev/null
@@ -0,0 +1,30 @@
+;;; elfeed-org-autoloads.el --- automatically extracted autoloads (do not edit)   -*- lexical-binding: t -*-
+;; Generated by the `loaddefs-generate' function.
+
+;; This file is part of GNU Emacs.
+
+;;; Code:
+
+(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
+
+
+\f
+;;; Generated autoloads from elfeed-org.el
+
+(autoload 'elfeed-org "elfeed-org" "\
+Hook up rmh-elfeed-org to read the `org-mode' configuration when elfeed is run." t)
+(register-definition-prefixes "elfeed-org" '("elfeed-org-" "rmh-elfeed-"))
+\f
+;;; End of scraped data
+
+(provide 'elfeed-org-autoloads)
+
+;; Local Variables:
+;; version-control: never
+;; no-byte-compile: t
+;; no-update-autoloads: t
+;; no-native-compile: t
+;; coding: utf-8-emacs-unix
+;; End:
+
+;;; elfeed-org-autoloads.el ends here
diff --git a/elpa/elfeed-org-20250219.950/elfeed-org-pkg.el b/elpa/elfeed-org-20250219.950/elfeed-org-pkg.el
new file mode 100644 (file)
index 0000000..d624759
--- /dev/null
@@ -0,0 +1,12 @@
+;; -*- no-byte-compile: t; lexical-binding: nil -*-
+(define-package "elfeed-org" "20250219.950"
+  "Configure elfeed with one or more org-mode files."
+  '((emacs  "28.1")
+    (elfeed "1.1.1")
+    (org    "8.2.7"))
+  :url "https://github.com/remyhonig/elfeed-org"
+  :commit "1197cf29f6604e572ec604874a8f50b58081176a"
+  :revdesc "1197cf29f660"
+  :keywords '("news")
+  :authors '(("Remy Honig" . "remyhonigNOSPAMgmail.com"))
+  :maintainers '(("Remy Honig" . "remyhonigNOSPAMgmail.com")))
diff --git a/elpa/elfeed-org-20250219.950/elfeed-org.el b/elpa/elfeed-org-20250219.950/elfeed-org.el
new file mode 100644 (file)
index 0000000..fead0ed
--- /dev/null
@@ -0,0 +1,429 @@
+;;; elfeed-org.el --- Configure elfeed with one or more org-mode files -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2014  Remy Honig
+
+;; Author           : Remy Honig <remyhonigNOSPAMgmail.com>
+;; Package-Requires : ((emacs "28.1") (elfeed "1.1.1") (org "8.2.7"))
+;; URL              : https://github.com/remyhonig/elfeed-org
+;; Version          : 20250104.0
+;; Keywords         : news
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;; Package-Version: 20250219.950
+;; Package-Revision: 1197cf29f660
+
+;;; Commentary:
+;; Maintaining tags for all rss feeds is cumbersome using the regular
+;; flat list where there is no hierarchy and tag names are duplicated
+;; a lot.  Org-mode makes the book keeping of tags and feeds much
+;; easier.  Tags get inherited from parent headlines.  Multiple files
+;; can be specified to separate your private from your work feeds for
+;; example.  You may also use tagging rules to tag feeds by entry-title
+;; keywords.  See https://github.com/remyhonig/elfeed-org for usage.
+
+;;; Code:
+
+(require 'elfeed)
+(require 'org)
+(require 'org-element)
+(require 'cl-lib)
+(require 'xml)
+
+
+(defgroup elfeed-org nil
+  "Configure the Elfeed RSS reader with an Orgmode file"
+  :group 'comm)
+
+
+(defcustom rmh-elfeed-org-tree-id "elfeed"
+  "The tag or ID property on the trees containing the RSS feeds."
+  :group 'elfeed-org
+  :type 'string)
+
+(defcustom rmh-elfeed-org-ignore-tag "ignore"
+  "The tag on the feed trees that will be ignored."
+  :group 'elfeed-org
+  :type 'string)
+
+(defcustom rmh-elfeed-org-auto-ignore-invalid-feeds nil
+  "Tag feeds to ignore them when a feed could not loaded."
+  :group 'elfeed-org
+  :type 'bool)
+
+(defcustom rmh-elfeed-org-files (list (locate-user-emacs-file "elfeed.org"))
+  "The files where we look to find trees with the `rmh-elfeed-org-tree-id'.
+In this file paths can be given relative to `org-directory'."
+  :group 'elfeed-org
+  :type '(repeat (file :tag "org-mode file")))
+
+(defvar elfeed-org-new-entry-hook nil
+  "List of new-entry tagger hooks created by elfeed-org.")
+
+(defun rmh-elfeed-org-check-configuration-file (file)
+  "Make sure FILE exists."
+  (when (not (file-exists-p (expand-file-name file org-directory)))
+    (error "Elfeed-org cannot open %s.  Make sure it exists or customize the variable \'rmh-elfeed-org-files\'"
+           (abbreviate-file-name file))))
+
+(defun rmh-elfeed-org-is-headline-contained-in-elfeed-tree ()
+  "Is any ancestor a headline with the elfeed tree id.
+Return t if it does or nil if it does not."
+  (let ((result nil))
+    (save-excursion
+      (while (and (not result) (org-up-heading-safe))
+        (setq result (member rmh-elfeed-org-tree-id (org-get-tags))))
+    result)))
+
+(defun rmh-elfeed-org-mark-feed-ignore (url)
+  "Set tag `rmh-elfeed-org-ignore-tag' to headlines containing the feed URL."
+  (let ((org-inhibit-startup t))
+    (dolist (org-file rmh-elfeed-org-files)
+      (with-current-buffer (find-file-noselect
+                            (expand-file-name org-file))
+        (org-mode)
+        (goto-char (point-min))
+        (while (and
+                (search-forward url nil t)
+                ;; Prefer outline-on-heading-p because org-on-heading-p
+                ;; is obsolete but org-at-heading-p was only introduced
+                ;; in org 9.0:
+                (outline-on-heading-p t)
+                (rmh-elfeed-org-is-headline-contained-in-elfeed-tree))
+          (org-toggle-tag rmh-elfeed-org-ignore-tag 'on))
+        (elfeed-log 'info "elfeed-org tagged '%s' in file '%s' with '%s' to be ignored" url org-file rmh-elfeed-org-ignore-tag)))))
+
+(defun rmh-elfeed-org-import-trees (tree-id)
+  "Get trees with \":ID:\" property or tag of value TREE-ID.
+Return trees with TREE-ID as the value of the id property or
+with a tag of the same value.  Setting an \":ID:\" property is not
+recommended but I support it for backward compatibility of
+current users."
+  (org-element-map
+      (org-element-parse-buffer)
+      'headline
+    (lambda (h)
+      (when (or (member tree-id (org-element-property :tags h))
+                (equal tree-id (org-element-property :ID h))) h))))
+
+
+(defun rmh-elfeed-org-convert-tree-to-headlines (parsed-org)
+  "Get the inherited tags from PARSED-ORG structure if MATCH-FUNC is t.
+The algorithm to gather inherited tags depends on the tree being
+visited depth first by `org-element-map'.  The reason I don't use
+`org-get-tags-at' for this is that I can reuse the parsed org
+structure and I am not dependent on the setting of
+`org-use-tag-inheritance' or an org buffer being present at
+all.  Which in my opinion makes the process more traceable."
+  (let* ((tags '())
+         (level 1))
+    (org-element-map parsed-org 'headline
+      (lambda (h)
+        (pcase-let*
+            ((current-level (org-element-property :level h))
+             (delta-level (- current-level level))
+             (delta-tags (mapcar (lambda (tag)
+                                   (intern (substring-no-properties tag)))
+                                 (org-element-property :tags h)))
+             (heading (org-element-property :raw-value h))
+             (`(,link ,description)
+              (org-element-map (org-element-property :title h) 'link
+                (lambda (link)
+                  (list
+                   (org-element-property :raw-link link)
+                   (when (and (org-element-property :contents-begin link)
+                              (org-element-property :contents-end link))
+                     (buffer-substring
+                      (org-element-property :contents-begin link)
+                      (org-element-property :contents-end link)))))
+                nil t)))
+          ;; update the tags stack when we visit a parent or sibling
+          (unless (> delta-level 0)
+            (let ((drop-num (+ 1 (- delta-level))))
+              (setq tags (nthcdr drop-num tags))))
+          ;; save current level to compare with next heading that will be visited
+          (setq level current-level)
+          ;; save the tags that might apply to potential children of the current heading
+          (push (append (car tags) delta-tags) tags)
+          ;; return the heading and inherited tags
+          (if (and link description)
+              (append (list link)
+                       (car tags)
+                       (list description))
+            (append (list (if link link heading))
+                     (car tags))))))))
+
+;; TODO: mark wrongly formatted feeds (PoC for unretrievable feeds)
+(defun rmh-elfeed-org-flag-headlines (parsed-org)
+  "Flag headlines in PARSED-ORG if they don't have a valid value."
+  (org-element-map parsed-org 'headline
+    (lambda (h)
+      (let ((tags (org-element-property :tags h)))
+        (org-element-put-property h :tags (push "_flag_" tags))))))
+
+
+(defun rmh-elfeed-org-filter-relevant (list)
+  "Filter relevant entries from the LIST."
+  (cl-remove-if-not
+   (lambda (entry)
+     (and
+      (string-match-p "\\(http\\|gopher\\|file\\|entry-title\\)" (car entry))
+      (not (member (intern rmh-elfeed-org-ignore-tag) entry))))
+   list))
+
+
+(defun rmh-elfeed-org-cleanup-headlines (headlines tree-id)
+  "In all HEADLINES given remove the TREE-ID."
+  (mapcar (lambda (e) (delete tree-id e)) headlines))
+
+
+(defun rmh-elfeed-org-import-headlines-from-files (files tree-id)
+  "Visit all FILES and return the headlines stored under tree tagged TREE-ID or with the \":ID:\" TREE-ID in one list."
+  (cl-remove-duplicates
+   (mapcan (lambda (file)
+             (with-temp-buffer
+               (insert-file-contents (expand-file-name file org-directory))
+               (let ((org-inhibit-startup t)
+                     (org-mode-hook nil))
+                 (org-mode))
+               (rmh-elfeed-org-cleanup-headlines
+                (rmh-elfeed-org-filter-relevant
+                 (rmh-elfeed-org-convert-tree-to-headlines
+                  (rmh-elfeed-org-import-trees tree-id)))
+                (intern tree-id))))
+           files)
+   :test #'equal))
+
+
+(defun rmh-elfeed-org-convert-headline-to-tagger-params (tagger-headline)
+  "Add new entry hooks for tagging configured with the found headline in TAGGER-HEADLINE."
+  (list
+   (string-clean-whitespace
+    (string-remove-prefix "entry-title:" (car tagger-headline)))
+   (cdr tagger-headline)))
+
+
+(defun rmh-elfeed-org-export-entry-hook (tagger-params)
+  "Export TAGGER-PARAMS to the proper `elfeed' structure."
+  (add-hook 'elfeed-org-new-entry-hook
+            (elfeed-make-tagger
+             :entry-title (nth 0 tagger-params)
+             :add (nth 1 tagger-params))))
+
+(defun rmh-elfeed-org-export-feed (headline)
+  "Export HEADLINE to the proper `elfeed' structure."
+  (if (and (stringp (car (last headline)))
+           (> (length headline) 1))
+      (progn
+        (add-to-list 'elfeed-feeds (butlast headline))
+        (let ((feed (elfeed-db-get-feed (car headline)))
+              (title (substring-no-properties (car (last headline)))))
+          (setf (elfeed-meta feed :title) title)
+          (elfeed-meta feed :title)))
+    (add-to-list 'elfeed-feeds headline)))
+
+(defun rmh-elfeed-org-process (files tree-id)
+  "Process headlines and taggers from FILES with org headlines with TREE-ID."
+
+  ;; Warn if configuration files are missing
+  (mapc #'rmh-elfeed-org-check-configuration-file files)
+
+  ;; Clear elfeed structures
+  (setq elfeed-feeds nil)
+  (setq elfeed-org-new-entry-hook nil)
+
+  ;; Convert org structure to elfeed structure and register taggers and subscriptions
+  (let* ((headlines (rmh-elfeed-org-import-headlines-from-files files tree-id))
+         (subscriptions (rmh-elfeed-org-filter-subscriptions headlines))
+         (taggers (rmh-elfeed-org-filter-taggers headlines))
+         (elfeed-taggers (mapcar #'rmh-elfeed-org-convert-headline-to-tagger-params taggers)))
+    (mapc #'rmh-elfeed-org-export-feed subscriptions)
+    (mapc #'rmh-elfeed-org-export-entry-hook elfeed-taggers))
+
+  ;; Tell user what we did
+  (elfeed-log 'info "elfeed-org loaded %i feeds, %i rules"
+           (length elfeed-feeds)
+           (length elfeed-org-new-entry-hook)))
+
+(defun elfeed-org-run-new-entry-hook (entry)
+  "Run ENTRY through elfeed-org taggers."
+  (dolist (hook elfeed-org-new-entry-hook)
+    (funcall hook entry)))
+
+(defun rmh-elfeed-apply-autotags-now-advice ()
+  "Make entry title matching rules works with `elfeed-apply-autotags-now'."
+  (interactive)
+  (let* ((headlines (rmh-elfeed-org-import-headlines-from-files
+                     rmh-elfeed-org-files rmh-elfeed-org-tree-id))
+         (taggers (rmh-elfeed-org-filter-taggers headlines))
+         (elfeed-taggers (mapcar #'rmh-elfeed-org-convert-headline-to-tagger-params taggers))
+         (entry-match-taggers (mapcar (lambda (tagger-params)
+                                        (elfeed-make-tagger
+                                         :entry-title (nth 0 tagger-params)
+                                         :add (nth 1 tagger-params)))
+                                      elfeed-taggers)))
+    (with-elfeed-db-visit (entry feed)
+                          (dolist (tagger entry-match-taggers)
+                            (funcall tagger entry)))))
+
+(defun rmh-elfeed-org-filter-taggers (headlines)
+  "Filter tagging rules from the HEADLINES in the tree."
+  (cl-remove-if-not (lambda (headline) (string-prefix-p "entry-title" (car headline)))
+                    headlines))
+
+(defun rmh-elfeed-org-filter-subscriptions (headlines)
+  "Filter subscriptions to rss feeds from the HEADLINES in the tree."
+  (cl-remove-if (lambda (headline) (string-prefix-p "entry-title" (car headline)))
+                headlines))
+
+(defun rmh-elfeed-org-convert-opml-to-org (xml level)
+  "Convert OPML content to Org format.
+Argument XML content of the OPML file.
+Argument LEVEL current level in the tree."
+  (cl-loop for (tag attr . content) in (cl-remove-if-not #'listp xml)
+           when (and (not (assoc 'xmlUrl attr)) (assoc 'title attr))
+           concat (format "%s %s\n" (make-string level ?*) (cdr it))
+           when (assoc 'xmlUrl attr)
+           concat (format "%s [[%s][%s]]\n" (make-string level ?*)
+                          (cdr it) (cdr (assoc 'title attr)))
+           concat (rmh-elfeed-org-convert-opml-to-org content (+ 1 level))))
+
+(defun elfeed-org-import-opml (opml-file)
+  "Import feeds from OPML file to a temporary Org buffer.
+Argument OPML-FILE filename of the OPML file."
+  (interactive "FInput OPML file: ")
+  (let* ((xml (xml-parse-file opml-file))
+         (content (rmh-elfeed-org-convert-opml-to-org xml 0)))
+    (with-current-buffer (get-buffer-create "*Imported Org Feeds*")
+      (erase-buffer)
+      (insert (format "* Imported Feeds            :%s:\n" rmh-elfeed-org-tree-id))
+      (insert content)
+      (let ((org-inhibit-startup t))
+        (org-mode))
+      (pop-to-buffer (current-buffer)))))
+
+
+(defun rmh-elfeed-org-convert-org-to-opml (org-buffer)
+  "Convert Org buffer content to OPML format.
+Argument ORG-BUFFER the buffer to write the OPML content to."
+  (let (need-ends
+        opml-body)
+    (with-current-buffer org-buffer
+      (let ((org-inhibit-startup t)
+            (org-mode-hook nil))
+        (org-mode))
+      (org-element-map (rmh-elfeed-org-import-trees
+                        rmh-elfeed-org-tree-id)
+          'headline
+        (lambda (h)
+          (let* ((current-level (org-element-property :level h))
+                 (tags (org-element-property :tags h))
+                 (heading (org-element-property :raw-value h))
+                 (link-and-title (and (string-match "^\\[\\[\\(http.+?\\)\\]\\[\\(.+?\\)\\]\\]" heading)
+                                      (list (match-string-no-properties 0 heading)
+                                            (match-string-no-properties 1 heading)
+                                            (match-string-no-properties 2 heading))))
+                 (hyperlink (and (string-match "^\\[\\[\\(http.+?\\)\\]\\(?:\\[.+?\\]\\)?\\]" heading)
+                                 (list (match-string-no-properties 0 heading)
+                                       (match-string-no-properties 1 heading))))
+                 url
+                 title
+                 opml-outline)
+            ;; fill missing end outlines
+            (while (and (car need-ends) (>= (car need-ends) current-level))
+              (let* ((level (pop need-ends)))
+                (setq opml-body (concat opml-body (format "  %s</outline>\n"
+                                                          (make-string (* 2 level) ? ))))))
+
+            (cond ((string-prefix-p "http" heading)
+                   (setq url heading)
+                   (setq title (or (elfeed-feed-title (elfeed-db-get-feed heading)) "Unknown")))
+                  (link-and-title (setq url (nth 1 link-and-title))
+                                  (setq title (nth 2 link-and-title)))
+                  (hyperlink (setq url (nth 1 hyperlink))
+                             (setq title (or (elfeed-feed-title (elfeed-db-get-feed (nth 1 hyperlink))) "Unknown")))
+                  (t (setq title heading)))
+            (if url
+                (setq opml-outline (format "  %s<outline title=\"%s\" xmlUrl=\"%s\"/>\n"
+                                           (make-string (* 2 current-level) ? )
+                                           (xml-escape-string title)
+                                           (xml-escape-string url)))
+              (unless (string-prefix-p "entry-title" heading)
+                (unless (member rmh-elfeed-org-tree-id tags)
+                  ;; insert category title only when it is neither the top
+                  ;; level elfeed node nor the entry-title node
+                  (progn
+                    (push current-level need-ends)
+                    (setq opml-outline (format "  %s<outline title=\"%s\">\n"
+                                               (make-string (* 2 current-level) ? )
+                                               (xml-escape-string title)))))))
+            (setq opml-body (concat opml-body opml-outline))))))
+
+    ;; fill missing end outlines at end
+    (while (car need-ends)
+      (let* ((level (pop need-ends)))
+        (setq opml-body (concat opml-body (format "  %s</outline>\n"
+                                                  (make-string (* 2 level) ? ))))))
+    opml-body))
+
+(defun elfeed-org-export-opml ()
+  "Export Org feeds under `rmh-elfeed-org-files' to a temporary OPML buffer.
+The first level elfeed node will be ignored. The user may need edit the output
+because most of Feed/RSS readers only support trees of 2 levels deep."
+  (interactive)
+  (let ((opml-body (cl-loop for org-file in rmh-elfeed-org-files
+                             concat
+                             (with-temp-buffer
+                               (insert-file-contents
+                                (expand-file-name org-file org-directory))
+                               (rmh-elfeed-org-convert-org-to-opml
+                                (current-buffer))))))
+    (with-current-buffer (get-buffer-create "*Exported OPML Feeds*")
+      (erase-buffer)
+      (insert "<?xml version=\"1.0\"?>\n")
+      (insert "<opml version=\"1.0\">\n")
+      (insert "  <head>\n")
+      (insert "    <title>Elfeed-Org Export</title>\n")
+      (insert "  </head>\n")
+      (insert "  <body>\n")
+      (insert opml-body)
+      (insert "  </body>\n")
+      (insert "</opml>\n")
+      (xml-mode)
+      (pop-to-buffer (current-buffer)))))
+
+(defun rmh-elfeed-org-process-advice ()
+  "Advice to add to `elfeed' to load the configuration before it is run."
+  (rmh-elfeed-org-process rmh-elfeed-org-files rmh-elfeed-org-tree-id))
+
+;;;###autoload
+(defun elfeed-org ()
+  "Hook up rmh-elfeed-org to read the `org-mode' configuration when elfeed is run."
+  (interactive)
+  (elfeed-log 'info "elfeed-org is set up to handle elfeed configuration")
+  (advice-add #'elfeed :before #'rmh-elfeed-org-process-advice)
+  (add-hook 'elfeed-new-entry-hook #'elfeed-org-run-new-entry-hook)
+  (advice-add 'elfeed-apply-autotags-now :after #'rmh-elfeed-apply-autotags-now-advice)
+  (add-hook 'elfeed-http-error-hooks
+            (lambda (url _status)
+              (when rmh-elfeed-org-auto-ignore-invalid-feeds
+                (rmh-elfeed-org-mark-feed-ignore url))))
+  (add-hook 'elfeed-parse-error-hooks
+            (lambda (url _error)
+              (when rmh-elfeed-org-auto-ignore-invalid-feeds
+                (rmh-elfeed-org-mark-feed-ignore url)))))
+
+
+(provide 'elfeed-org)
+;;; elfeed-org.el ends here