;;; my-ttrss.el -- ttrss utilities -*- lexical-binding: t -*- ;; Copyright (C) 2025 Free Software Foundation, Inc. ;; Author: Yuchen Pei ;; Package-Requires: ((emacs "30.1")) ;; This file is part of dotted. ;; dotted is free software: you can redistribute it and/or modify it under ;; the terms of the GNU Affero General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; dotted 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 Affero General ;; Public License for more details. ;; You should have received a copy of the GNU Affero General Public ;; License along with dotted. If not, see . ;;; Commentary: ;; ttrss utilities. ;;; Code: (require 'ttrss) ;;; TODO: my-ttrss-save-recent (defun my-ttrss-hello () (let ((sid (ttrss-login ttrss-address ttrss-user ttrss-password))) (message "Server running version %s and API level %d\n" (ttrss-get-version ttrss-address sid) (ttrss-get-api-level ttrss-address sid)) (message "There are %s unread articles in %d feeds" (ttrss-get-unread ttrss-address sid) (length (ttrss-get-feeds ttrss-address sid :unread_only t))))) (defun my-ttrss-fetch-feeds () (let ((sid (ttrss-login ttrss-address ttrss-user ttrss-password))) (ttrss-get-feeds ttrss-address sid :cat_id -3))) (defun my-ttrss-feed-dir (feed-title feed-id) (file-name-concat my-ttrss-dir (my-make-doc-file-name (format "%s [ttrss%s]" feed-title feed-id)))) (defun my-ttrss-feed-last-id-file (feed-info) (let* ((title (plist-get feed-info :title)) (id (plist-get feed-info :id))) (expand-file-name (file-name-concat (my-ttrss-feed-dir title id) "last-id")))) (defun my-ttrss-feed-get-last-id (feed-info) (let* ((file (my-ttrss-feed-last-id-file feed-info))) (if (file-exists-p file) (with-temp-buffer (insert-file-contents file) (string-to-number (buffer-string))) 0))) (defun my-ttrss-feed-write-last-id (feed-info last-id) (let* ((file (my-ttrss-feed-last-id-file feed-info)) (inhibit-message t)) (with-temp-buffer (insert (format "%d" last-id)) (write-file file)))) (defun my-ttrss-fetch () "Fetch and save latest articles from all feeds." (interactive) (let* ((sid (ttrss-login ttrss-address ttrss-user ttrss-password)) (feeds (ttrss-get-feeds ttrss-address sid :cat_id -3)) (n (length feeds))) (seq-do-indexed (lambda (feed i) (my-ttrss-fetch-feed-articles sid feed (format "[my-ttrss] (%d/%d) Fetching articles from %s..." (1+ i) n (plist-get feed :title)))) feeds))) (defun my-ttrss-fetch-feed-articles (sid feed &optional message-head) (unless message-head (setq message-head (format "[my-ttrss] Fetching articles from %s..." (plist-get feed :title)))) (message "%s" message-head) (let ((articles (ttrss-get-headlines ttrss-address sid :feed_id (plist-get feed :id) :show_content t :include_attachments t :since_id (my-ttrss-feed-last-id feed)))) (seq-do 'my-ttrss-save-article articles) (unless (seq-empty-p articles) (my-ttrss-feed-write-last-id feed (plist-get (elt articles 0) :id))) (message "%s Done - total %d articles" message-head (length articles)))) (defun my-ttrss-fetch-latest-articles (n &optional since-id) "Fetch N latest articles." (let ((sid (ttrss-login ttrss-address ttrss-user ttrss-password))) (unless since-id (setq since-id 0)) (if (ttrss-logged-in-p ttrss-address sid) (ttrss-get-headlines ttrss-address sid :feed_id -4 :limit n :show_content t :include_attachments t :since_id since-id) (message "Login failed")))) (defun my-ttrss-save-article (info) (with-temp-buffer (insert "\n") (insert "

" "" (plist-get info :title) "" "

") (insert "

" "" (plist-get info :feed_title) "") (when-let ((author (plist-get info :author))) (unless (or (string-empty-p author) (equal author (plist-get info :feed_title))) (insert " (" author ")"))) (insert " " (format-time-string "%Y-%m-%d %a %H:%M:%S" (encode-time (decode-time (plist-get info :updated))))) (insert "

") (let ((tags (plist-get info :tags))) (unless (seq-empty-p tags) (insert "

tags: " (string-join tags ";") "

"))) (insert (plist-get info :content)) (let ((attached (plist-get info :attachments))) (unless (seq-empty-p attached) (insert "

Article attachments:

\n"))) (let* ((change-major-mode-with-file-name nil) (coding-system-for-write 'utf-8) (inhibit-message t) (file-name (my-ttrss-format-file-name info)) (dir (file-name-directory file-name))) (unless (file-exists-p dir) (make-directory dir t)) (write-file file-name)))) (defvar my-ttrss-dir "~/Downloads") (defun my-ttrss-format-file-name (info) "Format: $author - $title ($year) [ttrss$id].html" (let* ((author (plist-get info :author)) (feed-title (plist-get info :feed_title)) (feed-id (plist-get info :feed_id)) (name (expand-file-name (file-name-concat (my-ttrss-feed-dir feed-title feed-id) (my-make-doc-file-name (format "%s - %s (%s) [ttrss%s].html" (if (string-empty-p author) feed-title author) (plist-get info :title) (format-time-string "%Y" (encode-time (decode-time (plist-get info :updated)))) (plist-get info :id))))))) (if (length> name 250) (expand-file-name (file-name-concat (my-ttrss-feed-dir feed-title feed-id) (my-make-doc-file-name (format "_ - _ (%s) [ttrss%s].html" (format-time-string "%Y" (encode-time (decode-time (plist-get info :updated)))) (plist-get info :id))))) name))) (provide 'my-ttrss) ;;; my-ttrss.el ends here