Add Elfeed configuration

This commit is contained in:
Jens Luedicke
2025-09-05 14:42:17 +02:00
parent 4bddd84c6d
commit ec3929a1eb
3 changed files with 507 additions and 1 deletions

371
elfeed-config.el Normal file
View File

@@ -0,0 +1,371 @@
;;; elfeed-config.el --- Elfeed RSS reader configuration
;;; Code:
;; Install required packages if not already installed
(use-package elfeed
:ensure t
:bind (("C-x w" . elfeed))
:config
;; Set default search filter to show entries from last 2 weeks
(setq elfeed-search-filter "@2-weeks-ago +unread")
;; Store database in .emacs.d
(setq elfeed-db-directory (expand-file-name "elfeed" user-emacs-directory))
;; Refresh feeds on startup
(setq elfeed-search-remain-on-entry t)
;; Set update interval
(setq elfeed-search-title-max-width 100)
(setq elfeed-search-title-min-width 30)
;; Sorting configuration
(setq elfeed-sort-order 'descending)
(setq elfeed-search-clipboard-type 'CLIPBOARD)
(setq elfeed-search-date-format '("%F %R" 16 :left))
;; Face customization for different tags
(defface elfeed-face-tag-news
'((t :foreground "#8B4513"))
"Face for news-tagged entries")
(defface elfeed-face-tag-tech
'((t :foreground "#4682B4"))
"Face for tech-tagged entries")
(defface elfeed-face-tag-security
'((t :foreground "#DC143C"))
"Face for security-tagged entries")
(defface elfeed-face-tag-programming
'((t :foreground "#228B22"))
"Face for programming-tagged entries")
(defface elfeed-face-tag-opensource
'((t :foreground "#FF8C00"))
"Face for opensource-tagged entries")
;; Apply faces to tags
(setq elfeed-search-face-alist
'((news elfeed-face-tag-news)
(tech elfeed-face-tag-tech)
(security elfeed-face-tag-security)
(programming elfeed-face-tag-programming)
(opensource elfeed-face-tag-opensource))))
(use-package elfeed-org
:ensure t
:after elfeed
:config
;; Load feeds from org file
(setq rmh-elfeed-org-files (list (expand-file-name "elfeed.org" user-emacs-directory)))
(elfeed-org))
;; Optional: Web browser for opening links
(setq browse-url-browser-function 'browse-url-firefox)
;; Keybindings for elfeed
(with-eval-after-load 'elfeed
(define-key elfeed-search-mode-map (kbd "j") 'next-line)
(define-key elfeed-search-mode-map (kbd "k") 'previous-line)
(define-key elfeed-search-mode-map (kbd "m") 'elfeed-search-toggle-all-star)
(define-key elfeed-search-mode-map (kbd "u") 'elfeed-search-toggle-all-unread)
(define-key elfeed-search-mode-map (kbd "U") 'elfeed-update)
(define-key elfeed-search-mode-map (kbd "f") 'elfeed-search-live-filter))
;; Update feeds every hour
(run-at-time 0 (* 60 60) 'elfeed-update)
;; Sorting functions
(defun elfeed-sort-by-date-ascending ()
"Sort elfeed entries by date ascending."
(interactive)
(setq elfeed-sort-order 'ascending)
(elfeed-search-update :force))
(defun elfeed-sort-by-date-descending ()
"Sort elfeed entries by date descending."
(interactive)
(setq elfeed-sort-order 'descending)
(elfeed-search-update :force))
(defun elfeed-sort-by-title ()
"Sort elfeed entries by title."
(interactive)
(setf elfeed-search-sort-function
(lambda (a b)
(string< (elfeed-entry-title a)
(elfeed-entry-title b))))
(elfeed-search-update :force))
(defun elfeed-sort-by-feed ()
"Sort elfeed entries by feed source."
(interactive)
(setf elfeed-search-sort-function
(lambda (a b)
(string< (elfeed-feed-title (elfeed-entry-feed a))
(elfeed-feed-title (elfeed-entry-feed b)))))
(elfeed-search-update :force))
;; Helper function to show only specific categories
(defun elfeed-show-news ()
"Show only news entries."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +news"))
(defun elfeed-show-tech ()
"Show only tech entries."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +tech"))
(defun elfeed-show-security ()
"Show only security entries."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +security"))
(defun elfeed-show-programming ()
"Show only programming entries."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +programming"))
(defun elfeed-show-opensource ()
"Show only open source entries."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +opensource"))
;; Location-based filters
(defun elfeed-show-munich ()
"Show only Munich/Bavaria news."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +munich"))
(defun elfeed-show-bavaria ()
"Show only Bavaria news."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +bavaria"))
(defun elfeed-show-germany ()
"Show only German news."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +germany"))
(defun elfeed-show-europe ()
"Show only European/EU news."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +eu"))
(defun elfeed-show-us ()
"Show only US news."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +us"))
(defun elfeed-show-world ()
"Show only world news."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +world"))
;; Language filters
(defun elfeed-show-german-language ()
"Show only German language feeds."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +de"))
;; Programming language filters
(defun elfeed-show-cpp ()
"Show only C++ entries."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +cpp"))
(defun elfeed-show-python ()
"Show only Python entries."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +python"))
(defun elfeed-show-qt ()
"Show only Qt entries."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread +qt"))
;; Combined filters
(defun elfeed-show-today ()
"Show entries from today only."
(interactive)
(elfeed-search-set-filter "@1-day-ago +unread"))
(defun elfeed-show-starred ()
"Show starred entries."
(interactive)
(elfeed-search-set-filter "+star"))
(defun elfeed-show-all ()
"Show all unread entries."
(interactive)
(elfeed-search-set-filter "@2-weeks-ago +unread"))
(defun elfeed-mark-all-read ()
"Mark all entries as read."
(interactive)
(mark-whole-buffer)
(elfeed-search-untag-all-unread))
;; Advanced filter prompt
(defun elfeed-filter-by-source ()
"Filter by specific feed source."
(interactive)
(let* ((feeds (elfeed-feed-list))
(titles (mapcar (lambda (url)
(or (plist-get (elfeed-db-get-feed url) :title)
url))
feeds))
(selected (completing-read "Select feed: " titles)))
(elfeed-search-set-filter
(format "@2-weeks-ago +unread =%s" selected))))
;; Custom search function
(defun elfeed-search-custom ()
"Prompt for a custom search filter."
(interactive)
(let ((filter (read-string "Enter filter: " elfeed-search-filter)))
(elfeed-search-set-filter filter)))
;; Article reading functions
(defun elfeed-show-entry-in-eww ()
"Open the current elfeed entry in eww browser."
(interactive)
(let ((entry (elfeed-search-selected :single)))
(when entry
(eww (elfeed-entry-link entry))
(add-hook 'eww-after-render-hook 'eww-readable nil t))))
(defun elfeed-search-eww-open (&optional use-generic-p)
"Open the current elfeed entry in eww.
If USE-GENERIC-P is non-nil, use eww-readable after loading."
(interactive "P")
(let ((entries (elfeed-search-selected)))
(cl-loop for entry in entries
do (elfeed-untag entry 'unread)
when (elfeed-entry-link entry)
do (eww (elfeed-entry-link entry)))
(when use-generic-p
(add-hook 'eww-after-render-hook 'eww-readable nil t))
(unless (use-region-p) (forward-line))
(elfeed-search-update-entry)))
(defun elfeed-show-eww-open (&optional use-generic-p)
"Open the current elfeed show entry in eww.
If USE-GENERIC-P is non-nil, use eww-readable after loading."
(interactive "P")
(let ((link (elfeed-entry-link elfeed-show-entry)))
(when link
(eww link)
(when use-generic-p
(add-hook 'eww-after-render-hook 'eww-readable nil t)))))
;; Fetch and display article content in elfeed-show buffer
(defun elfeed-show-refresh-mail-style ()
"Refresh the current elfeed entry, fetching full content and displaying it."
(interactive)
(let ((link (elfeed-entry-link elfeed-show-entry)))
(when link
(message "Fetching full article content...")
(url-retrieve
link
(lambda (status)
(if (plist-get status :error)
(message "Error fetching article: %s" (plist-get status :error))
(let ((html (buffer-string))
(inhibit-read-only t))
(with-current-buffer (get-buffer "*elfeed-entry*")
(erase-buffer)
(insert (format "Title: %s\n" (elfeed-entry-title elfeed-show-entry)))
(insert (format "Feed: %s\n" (elfeed-feed-title (elfeed-entry-feed elfeed-show-entry))))
(insert (format "Date: %s\n" (format-time-string "%Y-%m-%d %H:%M" (elfeed-entry-date elfeed-show-entry))))
(insert (format "Link: %s\n\n" link))
(insert "--- Full Article ---\n\n")
(let ((shr-width (- (window-width) 5))
(shr-max-image-proportion 0.7))
(shr-render-region (point) (point-max)))
(goto-char (point-min))))))))))
;; Enhanced readable mode for elfeed
(defun elfeed-show-readable ()
"Make the current elfeed entry more readable by extracting main content."
(interactive)
(let ((inhibit-read-only t))
(save-excursion
(goto-char (point-min))
(when (search-forward "\n\n" nil t)
(let ((shr-width (min 80 (- (window-width) 5)))
(shr-max-image-proportion 0.6)
(shr-use-fonts nil))
(shr-render-region (point) (point-max)))))
(text-scale-increase 1)
(olivetti-mode 1)))
;; Toggle between summary and full article
(defvar-local elfeed-show-full-article-p nil
"Whether the full article is currently displayed.")
(defun elfeed-show-toggle-full-article ()
"Toggle between entry summary and full article content."
(interactive)
(if elfeed-show-full-article-p
(progn
(elfeed-show-refresh)
(setq-local elfeed-show-full-article-p nil)
(message "Showing summary"))
(let ((link (elfeed-entry-link elfeed-show-entry)))
(when link
(message "Fetching full article...")
(url-retrieve
link
(lambda (status)
(if (plist-get status :error)
(message "Error fetching article: %s" (plist-get status :error))
(let ((html (buffer-substring (point) (point-max)))
(inhibit-read-only t))
(with-current-buffer (get-buffer "*elfeed-entry*")
(let ((pos (point)))
(erase-buffer)
(elfeed-show-refresh)
(goto-char (point-max))
(insert "\n\n--- Full Article ---\n\n")
(let ((start (point)))
(insert html)
(shr-render-region start (point-max))
(goto-char pos))
(setq-local elfeed-show-full-article-p t)
(message "Showing full article")))))))))))
;; Open in external browser as fallback
(defun elfeed-open-in-browser ()
"Open current entry in external browser."
(interactive)
(let ((entry (if (eq major-mode 'elfeed-show-mode)
elfeed-show-entry
(elfeed-search-selected :single))))
(when entry
(browse-url (elfeed-entry-link entry)))))
;; Bind keys for article viewing
(with-eval-after-load 'elfeed
;; In search mode
(define-key elfeed-search-mode-map (kbd "E") 'elfeed-search-eww-open)
(define-key elfeed-search-mode-map (kbd "B") 'elfeed-open-in-browser)
;; In show mode
(define-key elfeed-show-mode-map (kbd "E") 'elfeed-show-eww-open)
(define-key elfeed-show-mode-map (kbd "R") 'elfeed-show-readable)
(define-key elfeed-show-mode-map (kbd "F") 'elfeed-show-toggle-full-article)
(define-key elfeed-show-mode-map (kbd "B") 'elfeed-open-in-browser))
;; Disable line numbers in elfeed buffers
(add-hook 'elfeed-show-mode-hook (lambda () (display-line-numbers-mode 0)))
(add-hook 'elfeed-search-mode-hook (lambda () (display-line-numbers-mode 0)))
;; Disable line numbers in EWW
(add-hook 'eww-mode-hook (lambda () (display-line-numbers-mode 0)))
(provide 'elfeed-config)
;;; elfeed-config.el ends here

116
elfeed.org Normal file
View File

@@ -0,0 +1,116 @@
#+TITLE: Elfeed Feed Configuration
#+STARTUP: overview
* Feeds :elfeed:
** News :news:
*** München/Bavaria :munich:bavaria:de:
**** [[https://www.sueddeutsche.de/muenchen/rss][Süddeutsche Zeitung - München]]
**** [[https://www.merkur.de/lokales/muenchen/rssfeed.rdf][Merkur - München]]
**** [[https://www.tz.de/muenchen/rssfeed.rdf][tz - München]]
**** [[https://www.br.de/nachrichten/bayern/rss.xml][BR24 - Bayern]]
**** [[https://www.abendzeitung-muenchen.de/storage/rss/rss/muenchen.xml][Abendzeitung München]]
*** Germany :germany:de:
**** [[https://www.tagesschau.de/index~rss2.xml][Tagesschau]]
**** [[https://www.spiegel.de/schlagzeilen/index.rss][Der Spiegel]]
**** [[https://www.zeit.de/index/feed][Die Zeit]]
**** [[https://www.faz.net/rss/aktuell/][FAZ - Aktuell]]
**** [[https://www.sueddeutsche.de/rss][Süddeutsche Zeitung]]
**** [[https://www.heise.de/rss/heise.rdf][Heise News]]
*** Europe/EU :eu:europe:
**** [[https://www.politico.eu/feed/][Politico EU]]
**** [[https://www.euronews.com/rss][Euronews]]
**** [[https://www.dw.com/rss/en/european-union/rss-17498][Deutsche Welle - EU]]
**** [[https://www.theguardian.com/world/europe-news/rss][The Guardian - Europe]]
**** [[https://feeds.bbci.co.uk/news/world/europe/rss.xml][BBC News - Europe]]
*** United States :us:
**** [[https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml][New York Times]]
**** [[https://feeds.washingtonpost.com/rss/national][Washington Post - National]]
**** [[https://feeds.npr.org/1001/rss.xml][NPR News]]
**** [[https://feeds.bbci.co.uk/news/world/us_and_canada/rss.xml][BBC News - US & Canada]]
**** [[https://www.theguardian.com/us-news/rss][The Guardian - US]]
*** World :world:
**** [[https://feeds.bbci.co.uk/news/world/rss.xml][BBC World News]]
**** [[https://www.theguardian.com/world/rss][The Guardian - World]]
**** [[https://rss.nytimes.com/services/xml/rss/nyt/World.xml][New York Times - World]]
**** [[https://feeds.washingtonpost.com/rss/world][Washington Post - World]]
**** [[https://www.aljazeera.com/xml/rss/all.xml][Al Jazeera English]]
**** [[https://www.dw.com/rss/en/top-stories/rss-691][Deutsche Welle - World]]
** Technology :tech:
*** General Tech News :technews:
**** [[https://feeds.arstechnica.com/arstechnica/index][Ars Technica]]
**** [[https://www.theverge.com/rss/index.xml][The Verge]]
**** [[https://techcrunch.com/feed/][TechCrunch]]
**** [[https://www.wired.com/feed/rss][Wired]]
**** [[https://www.anandtech.com/rss][AnandTech]]
**** [[https://www.heise.de/developer/rss/news-atom.xml][Heise Developer]]
**** [[https://www.golem.de/rss.php?feed=RSS2.0][Golem.de]]
**** [[https://news.ycombinator.com/rss][Hacker News]]
**** [[https://lobste.rs/rss][Lobsters]]
*** Open Source :opensource:
**** [[https://opensource.com/feed][Opensource.com]]
**** [[https://lwn.net/headlines/rss][LWN.net]]
**** [[https://www.linux.com/feed/][Linux.com]]
**** [[https://www.linuxtoday.com/feed][Linux Today]]
**** [[https://itsfoss.com/feed/][It's FOSS]]
**** [[https://www.phoronix.com/rss.php][Phoronix]]
**** [[https://www.omgubuntu.co.uk/feed][OMG! Ubuntu!]]
**** [[https://planet.kde.org/global/atom.xml][Planet KDE]]
**** [[https://planet.gnome.org/atom.xml][Planet GNOME]]
** Programming :programming:
*** C++ :cpp:
**** [[https://isocpp.org/blog/rss][ISO C++ Blog]]
**** [[https://www.fluentcpp.com/feed/][Fluent C++]]
**** [[https://www.cppstories.com/index.xml][C++ Stories]]
**** [[https://herbsutter.com/feed/][Herb Sutter]]
**** [[https://blog.tartanllama.xyz/feed.xml][Tartanllama]]
**** [[https://devblogs.microsoft.com/cppblog/feed/][Microsoft C++ Team Blog]]
**** [[https://www.reddit.com/r/cpp/.rss][Reddit - r/cpp]]
*** Python :python:
**** [[https://realpython.com/atom.xml][Real Python]]
**** [[https://planet.python.org/rss20.xml][Planet Python]]
**** [[https://www.python.org/jobs/feed/rss/][Python.org News]]
**** [[https://pycoders.com/feed][PyCoder's Weekly]]
**** [[https://www.pythonweekly.com/feed][Python Weekly]]
**** [[https://www.reddit.com/r/Python/.rss][Reddit - r/Python]]
**** [[https://talkpython.fm/episodes/rss][Talk Python To Me]]
*** Qt :qt:
**** [[https://www.qt.io/blog/rss.xml][Qt Blog]]
**** [[https://planet.qt.io/rss20.xml][Planet Qt]]
**** [[https://woboq.com/blog/feed][Woboq Blog]]
**** [[https://www.kdab.com/category/blogs/feed/][KDAB Blogs]]
**** [[https://www.ics.com/blog/feed][ICS Qt Blog]]
*** General Programming :general:
**** [[https://stackoverflow.blog/feed/][Stack Overflow Blog]]
**** [[https://dev.to/feed][DEV Community]]
**** [[https://news.ycombinator.com/rss][Hacker News]]
**** [[https://www.infoq.com/feed][InfoQ]]
**** [[https://martinfowler.com/feed.atom][Martin Fowler]]
**** [[https://www.joelonsoftware.com/feed/][Joel on Software]]
** IT Security :security:
**** [[https://krebsonsecurity.com/feed/][Krebs on Security]]
**** [[https://www.schneier.com/feed/atom/][Schneier on Security]]
**** [[https://threatpost.com/feed/][Threatpost]]
**** [[https://www.darkreading.com/rss.xml][Dark Reading]]
**** [[https://feeds.feedburner.com/TheHackersNews][The Hacker News]]
**** [[https://www.bleepingcomputer.com/feed/][BleepingComputer]]
**** [[https://www.csoonline.com/index.rss][CSO Online]]
**** [[https://nakedsecurity.sophos.com/feed/][Naked Security]]
**** [[https://www.heise.de/security/rss/news-atom.xml][Heise Security]]
**** [[https://www.golem.de/rss.php?feed=RSS2.0&ms=security][Golem Security]]
**** [[https://www.bsi.bund.de/SiteGlobals/Functions/RSSFeed/RSSNewsfeed/RSSNewsfeed.xml][BSI News]]
**** [[https://www.reddit.com/r/netsec/.rss][Reddit - r/netsec]]

21
init.el
View File

@@ -39,7 +39,13 @@
ibuffer-sidebar ibuffer-projectile
;; Required for some functionality
org dash s f ht spinner lv hydra avy))
org dash s f ht spinner lv hydra avy
;; RSS/News reader
elfeed elfeed-org
;; Email (mu4e installed separately)
))
;; Auto-install missing packages
(when (cl-find-if-not #'package-installed-p package-selected-packages)
@@ -697,12 +703,25 @@
(when (file-exists-p dev-config)
(load-file dev-config)))
;; Reload elfeed config if it exists
(let ((elfeed-config (expand-file-name "elfeed-config.el" user-emacs-directory)))
(when (file-exists-p elfeed-config)
(load-file elfeed-config)))
(message "Emacs configuration fully reloaded!"))
;;; Final Keybindings
(global-set-key (kbd "C-x k") 'kill-current-buffer-no-confirm)
(global-set-key (kbd "C-c C-r") 'reload-emacs-config)
(global-set-key (kbd "C-x C-b") 'helm-buffers-list)
;;; RSS Reader Configuration (Elfeed)
(let ((elfeed-config (expand-file-name "elfeed-config.el" user-emacs-directory)))
(when (file-exists-p elfeed-config)
(load-file elfeed-config)
(message "Elfeed RSS reader configuration loaded.")))
;;; Development Mode Information
(defun show-dev-mode-info ()
"Show information about development mode."