simfish:

Emacs/Feeds

Elfeed is an RSS and Atom client for Emacs. Combined with org-feeds it can be used to setup feed client.

cover image

Introduction

Elfeed package is an Emacs RSS and Atom client. In combination with Org-Feeds it can be used to setup RSS client per a git project or system-wide.

Customization

Template project feed file

(defconst feeds:template-feed-file
  (expand-file-name
   "share/templates/feeds.org"
   (substitute-in-file-name "${LITDOC_CACHEDIR}/feeds"))
  "Template project feed file")

Template project dashboard file path

(defconst feeds:template-dashboard-file
  (expand-file-name
   "share/templates/dashboard.org"
   (substitute-in-file-name "${LITDOC_CACHEDIR}"))

  "Template project dashboard file path")

Default feed file

(defconst feeds:default-file
  (expand-file-name
   "news.feed"
   (substitute-in-file-name "${HOME}/Documents/planner"))

  "system wide default feed file")

Key Bindings

Elfeed's elfeed-show-mode-map keybindings are useful for controlling the elfeed buffer once it is loaded.

(kb::def :states 'normal
  "o"     '(:ignore t       :which-key  "Org")
  "oe"    '(:ignore t       :which-key  "News Feed(Elfeed)")
  "oee"   '(feeds::open   :which-key  "Open Elfeed")
  "oed"   '(feeds::close  :which-key  "Exit Elfeed")
  "oeE"   '(feeds::edit-feed-file  :which-key "Edit Feed File"))

Templates

Template Feeds dashboard:

*Basics
 [U] Update Elfeed
 [s] Search
 [E] Edit
 [g] Refresh counts
 [q] Kill dashboard

*Bookmarks
 [u] Unread      ([[elfeed:+unread][   688]]/[[elfeed:][  7882]])
 [e] Emacs       ([[elfeed:+unread +emacs][     1]]/[[elfeed:+emacs][  3634]])
 [b] Blogs       ([[elfeed:+unread +blog][   506]]/[[elfeed:+blog][------]])
 [m] Monitoring  ([[elfeed:+unread +monitoring][     0]]/[[elfeed:+monitoring][------]])
 [n] News        ([[elfeed:+unread +news][    70]]/[[elfeed:+news][------]])
 [v] Video       ([[elfeed:+unread +video][    10]]/[[elfeed:+video][------]])

  :PROPERTIES:
  :VISIBILITY: hideall
  :END:

  Press "E" to edit and M-x elfeed-dashboard-mode to go back

  #+STARTUP: showall showstars indent
  #+KEYMAP: u | elfeed-dashboard-query "+unread"
  #+KEYMAP: e | elfeed-dashboard-query "+unread +emacs"
  #+KEYMAP: b | elfeed-dashboard-query "+unread +blogs"
  #+KEYMAP: m | elfeed-dashboard-query "+unread +monitoring"
  #+KEYMAP: v | elfeed-dashboard-query "+unread +video"
  #+KEYMAP: n | elfeed-dashboard-query "+unread +news"
  #+KEYMAP: s | elfeed
  #+KEYMAP: g | elfeed-dashboard-update-links
  #+KEYMAP: U | elfeed-dashboard-update
  #+KEYMAP: E | elfeed-dashboard-edit
  #+KEYMAP: q | kill-current-buffer

Template Feeds Page

* Blogs                                                               :elfeed:
** Title: \(linux\|linus\|ubuntu\|kde\|gnome\)                        :linux:
** exampl.com/index.rss                                               :mustread:

Functions

Find feed files?

Issue: How to dynamically find feed? Feed files are configured per project. Each feed file is stored under the current project's org directory, [project-root]/.config/feeds/feeds.org.

The default global feed file's location can be configured using feeds::default-file.

(cl-defun feeds::find-project-feed-file (  )
  "Retruns the current project's feed file.

  If current project is not available it returns nil.
  Otherwise, it returns feed file path wether it exists or not."

  (require 'pm)
  (require 'log)
  (let ((project-dir    (pm::root-dir) )
        (feed-file nil))

    ;; working in project?
    (unless (file-directory-p project-dir)
      (cl-return-from feeds::find-project-feed-file))

    ;; return feed file path
    (setq feed-file  (expand-file-name "news.feed" project-dir))
    (unless (file-exists-p feed-file)
      (setq feed-file feeds:default-file))

    feed-file))

Edit feed files

Problem it's easy to forget the location and format of feed files.

Commands in this section can be used to quickly edit existing feed file or create a new one from a template feed file.

(defun feeds::edit-feed-file ( )
  "Edit the default `elfeed-org' feed file."
  (interactive)
  (require 'fs)
  (require 'og)
  (let ((feed-file   (feeds::find-project-feed-file))
        (feed-buffer  nil))
    (cond
     (;; if not in a project, then there's nothing to do
      (null feed-file)
      (log::info "edit feed file got canceled")
      (log::sub-info "unable to locate current project")
      (log::sub-info "unable to locate project feed file"))

     (;; feed file exists?
      (file-exists-p feed-file)
      (setq feed-buffer (find-file-noselect feed-file))
      (with-current-buffer feed-buffer
        (org-mode))
      (switch-to-buffer feed-buffer))
     (t ;; file is configured, but does not exist yet.
      ;; In this case, just open a template feed file
      ;; for the user to edit
      (feeds:fs/dir-ensure (file-name-directory feed-file))
      (setq feed-buffer  (find-file-noselect feed-file))
      (with-current-buffer feed-buffer
        (org-mode)
        (insert-file-contents feeds:template-feed-file))
      (switch-to-buffer feed-buffer)))))

Open Feeds

Functions to support syncing feeds by forcing elfeed reads index from disk before launching

(defun feeds::open ()
  (interactive)
  (feeds::open-internal))


(cl-defun feeds::open-internal ( )
  "Start elfeed after syncing it with database(if necessary)"
  (require 'elfeed)
  (require 'elfeed-db)
  (require 'elfeed-search)
  (require 'elfeed-org)
  (require 'f)
  (require 'log)
  (require 'fs)

  (let ((project-root    (pm::root-dir))
        (feed-file       (feeds::find-project-feed-file)))
    (when (null feed-file)
      (log::info "feed is only available inside a project.")
      (cl-return-from feeds::open-internal))

    (log::info "checking for feed file ..." )
    (unless (file-exists-p feed-file)
      (log::info "feed file not found: <%s>" feed-file)
      (log::info "Please, use `feeds::edit-feed-file' to edit feed files first")
      (cl-return-from feeds::open-internal))

    (log::sub-info "found feed file ... [%s]"  feed-file)

    ;; project specific dashboard
    (let ((rmh-elfeed-org-files    `( ,feed-file)))

      ;; prepare el-feed database
      (log::info "preparing elfeed database index ...")
      (log::sub-info "database path: %s"   elfeed-db-directory)
      (fs::dir-ensure elfeed-db-directory)
      (elfeed-db-load)
      (elfeed)
      (elfeed-search-update--force))))

Saving Feeds

(defun feeds::close( )
    "Save the elfeed db to disk before deleting buffer"
    (interactive)
    (require 'elfeed-db)
    (elfeed-db-save)
    (quit-window))

Mark All Feeds in Inbox as Read

(defun feeds::inbox-mark-all-as-read ( )
   "Marks all feeds as read"
   (interactive)
   (require 'elfeed-search)
   (mark-whole-buffer)
   (elfeed-search-untag-all-unread))

Packages

elfeed-feed

This main elfeed package can be installed directly from guix default channel.

(defface elfeed-feed-face
  '((default :weight bold :height 1.2)
    (((class color) (background dark))  :foreground "white")
    (((class color) (background light)) :foreground "black"))
  "Face for news feeds."
  :group 'elfeed-face)


(use-package elfeed
    :guix (:name emacs-elfeed))

elfeed-org

Elfeed-org enables managing Elfeed subscriptions using using org-mode file. The package is available in GNU/Guixdefault default channel. The channel method is the prefered method of installation.

(use-package elfeed-org
  :guix (:name emacs-elfeed-org)
  :config
  (require 'fs)
  (setq elfeed-db-directory (fs::dir-ensure "${HOME}/.config/litdoc/elfeed/elfeed-db"))
  (elfeed-org))

elfeed-show

  (use-package elfeed-show
      :init
      (defun elfeed-show::setup-font ( )
        (set-face-attribute
         'variable-pitch
         (selected-frame)
         :font
         (font-spec :family "Vollkorn" :size 20)))
     ;; :hook
     ;; (elfeed-show-mode  .  elfeed-show::setup-font)
      :config
      (setq fill-column 90))

References