CCE

Tiny-Tiny RSS

Contents

NEXT What is TT-RSS?

TT-RSS on The Wobserver

with some extra plugins ...

The tt-rss plugin sources are pulled in as flake inputs pinned by flake.lock:

nix:tangle ~/nix/snippets/ttrss-wallabag-input.nix:mkdirp yes
ttrss_wallabag = {
  url = "github:joshp23/ttrss-to-wallabag-v2";
  flake = false;
};
nix:tangle ~/nix/snippets/large-apod-input.nix:mkdirp yes
large_apod = {
  url = "github:joshp23/TTRSS-APOD-Fix";
  flake = false;
};
nix:tangle ~/nix/snippets/ttrss-readability-input.nix:mkdirp yes
ttrss_readability = {
  url = "git+https://gitlab.tt-rss.org/tt-rss/plugins/ttrss-af-readability";
  flake = false;
};
nix:tangle ~/nix/snippets/elfeed-sync-input.nix:mkdirp yes
elfeed-sync = {
  url = "github:SqrtMinusOne/elfeed-sync";
  flake = false;
};
nix:tangle ~/nix/snippets/ttrss-adaptive_score-input.nix:mkdirp yes
ttrss_adaptive_score = {
  url = "git+https://code.rix.si/rrix/ttrss_adaptive_score";
  flake = false;
};
nix:tangle ~/nix/nixos/ttrss.nix
{ inputs, pkgs, ... }:

let
  wallabag = pkgs.stdenv.mkDerivation rec {
    pname = "tt-rss-plugin-wallabag";
    version = "2.0.0";

    src = inputs.ttrss_wallabag;
    installPhase = ''
      mkdir -p $out/wallabag_v2
      cp -r wallabag_v2/* $out/wallabag_v2/
    '';
  };

  large_apod = pkgs.stdenv.mkDerivation rec {
    pname = "tt-rss-plugin-large-apod";
    version = "0.0.1";
    src = inputs.large_apod;

    installPhase = ''
      mkdir -p $out/large_apod
      cp -r large_apod/* $out/large_apod
    '';
  };

  readability = pkgs.stdenv.mkDerivation rec {
    pname = "tt-rss-plugin-af_readability";
    version = "0.0.1";
    src = inputs.ttrss_readability;

    installPhase = ''
      mkdir -p $out/af_readability
      cp -r ./* $out/af_readability
    '';
  };

  elfeed_sync = pkgs.stdenv.mkDerivation rec {
    pname = "tt-rss-plugin-elfeed_sync";
    version = "0.1.1";
    src = inputs.elfeed-sync;

    installPhase = ''
      mkdir -p $out/elfeed_sync
      cp -r ./* $out/elfeed_sync
    '';
  };
  
  adaptive_score = pkgs.stdenv.mkDerivation rec {
    pname = "tt-rss-plugin-adaptive_score";
    version = "1.0.0";
    src = inputs.ttrss_adaptive_score;
  
    installPhase = ''
      mkdir -p $out/adaptive_score
      cp -r ./* $out/adaptive_score
    '';
  };
in {
  services.nginx.virtualHosts."feeds.whatthefuck.computer" = {
    extraConfig = ''
      client_max_body_size 10M;
      fastcgi_read_timeout 600;
    '';
    addSSL = true;
    useACMEHost = "fontkeming.fail";
  };
  services.tt-rss = {
    enable = true;
    database = {
      type = "pgsql";
    };
    logDestination = "syslog";
    root = "/srv/tt-rss";
    selfUrlPath = "https://feeds.whatthefuck.computer";
    virtualHost = "feeds.whatthefuck.computer";
    pluginPackages = [
      wallabag
      large_apod
      readability
      elfeed_sync
      adaptive_score
    ];
    plugins = [
      "auth_internal"
      "note"
      "wallabag_v2"
      "large_apod"
      "af_readability"
      "elfeed_sync"
      "adaptive_score"
    ];
  };
}

Syncing tt-rss in Emacs with Elfeed

Base elfeed configuration

emacs-lisp:tangle ~/nix/lisp/ttrss.el
(use-package elfeed
  :config
  ;; add feeds' embedded categories as elfeed tags like tt-rss does
  (defun cce/elfeed-category-tags (entry)
    (let* ((m (elfeed-entry-meta entry))
           (cats (plist-get m :categories))
           (sym-cats (-map (lambda (cat) (make-symbol cat)) cats)))
      (when cats
        (apply (apply-partially #'elfeed-tag entry)
               sym-cats))))
  (add-to-list 'elfeed-new-entry-hook #'cce/elfeed-category-tags))

Elfeed Summary and Evil Mode bindings for it

emacs-lisp:tangle ~/nix/lisp/ttrss.el
(use-package elfeed-summary
  :config
  (add-to-list 'display-buffer-alist '("\\`\\*elfeed-summary" (display-buffer-full-frame)))
  (evil-collection-set-readonly-bindings 'elfeed-search-mode-map)
  (evil-set-initial-state 'elfeed-search-mode 'normal)
  (evil-collection-define-key 'normal 'elfeed-summary-mode-map
    (kbd "<tab>") #'magit-section-toggle
    (kbd "RET") #'elfeed-summary--action
    (kbd "M-RET") #'elfeed-summary--action-show-read

    "R" #'elfeed-summary-update
    "U" #'elfeed-summary--action-mark-read
    "h" #'magit-section-up
    "j" #'evil-next-line
    "J" #'magit-section-forward
    "k" #'evil-previous-line
    "K" #'magit-section-backward
    "p" #'elfeed-summary-update-at-point
    "r" #'elfeed-summary--refresh
    "u" #'elfeed-summary-toggle-only-unread

    ;; quit
    "q" 'elfeed-summary-quit-window
    "ZQ" 'elfeed-summary-quit-window
    "ZZ" 'elfeed-summary-quit-window)

  (evil-collection-define-key 'normal 'elfeed-show-mode
    "n" #'elfeed-show-next
    "p" #'elfeed-show-prev))

Summary groups:

emacs-lisp:tangle ~/nix/lisp/ttrss.el
(setq elfeed-summary-settings
      '((search (:filter . "@1-day-ago +unread") (:title . "Fresh"))
        (search (:filter . "@1970-01-01--@4-day-ago +unread") (:title . "Stale"))
        (search (:filter . "+Tech") (:title . "Tech"))
        (search (:filter . "+Tea") (:title . "Tea"))
        (search (:filter . "+Art") (:title . "Art"))
        (search (:filter . "+Motorsports") (:title . "Motorsports"))
        (search (:filter . "+Regional") (:title . "Regional"))
        (search (:filter . "+NatWorld") (:title . "Nation and World"))
        (search (:filter . "+Blog") (:title . "Blogs"))
        (search (:filter . "+Media") (:title . "Media"))
        (search (:filter . "+Brain") (:title . "Brain"))
        (search (:filter . "+Videos") (:title . "Videos"))
        (auto-tags (:original-order t :hide t))
        (group (:title . "Miscellaneous")
               (:elements
                (group
                 (:title . "Searches")
                 (:elements
                  (search (:filter . "@6-months-ago rrix") (:title . "About me"))
                  (search (:filter . "+later") (:title . "Check later"))))))))

My Feeds and the blogroll

elfeed-sync "only" goes one way but elfeed-org will import the OPML that tt-rss spits out to set up the org doc for the first time. It took some small effort to get the elfeed-sync-feeds function to run but once it is all lined up things are nice. To subscribe to something the RSS can go in either My RSS Feeds or My Blog Roll.

emacs-lisp:tangle ~/nix/lisp/ttrss.el
(use-package elfeed-org
  :config
  (elfeed-org)
  (setq rmh-elfeed-org-files
        '("~/org/elfeed_feeds.org"
          "~/org/my_blogroll.org")))

Server Sync Setup

emacs-lisp:tangle ~/nix/lisp/ttrss.el
(use-package elfeed-sync
  :after elfeed
  :config
  (elfeed-sync-mode))

Then set up the following variables:

  • elfeed-sync-tt-rss-instance - point that to your tt-rss instance, e.g. (No trailing slash)

  • elfeed-sync-tt-rss-login

  • elfeed-sync-tt-rss-password

  • elfeed-sync-unread-tag - elfeed tag to map to read status in tt-rss. unread by default.

  • elfeed-sync-marked-tag - elfeed tag to map to marked status in tt-rss. later by default.

This isn't in melpa, and I'll end up doing some more work on it to add some functionality but for now it pulls its the package from GitHub via a flake input pinned by flake.lock:

nix:tangle ~/nix/overrides/elfeed-sync.nix
elfeed-sync = epkgs.melpaBuild {
  pname = "elfeed-sync";
  version = "20231226.1546";
  commit = "unknown";

  src = inputs.elfeed-sync;

  recipe = pkgs.writeText "recipe" ''
    (elfeed-sync
     :repo "SqrtMinusOne/elfeed-sync"
     :fetcher github)
  '';

  packageRequires = [ epkgs.elfeed epkgs.request ];

  meta = {
    homepage = "https://github.com/SqrtMinusOne/elfeed-sync/";
    license = lib.licenses.gpl3Plus;
  };
};

NEXT add score filter fetching from the API

emacs-lisp:tangle ~/nix/lisp/ttrss.el
(use-package elfeed-score)