iens

Manager of links to read
git clone https://git.instinctive.eu/iens.git
Log | Files | Refs | README | LICENSE

commit 502c7cff529add72228d0adedc7f04057ba9e787
parent a2ea632db771cf5df47c3bc0235f3c43a41e3f8e
Author: Natasha Kerensikova <natgh@instinctive.eu>
Date:   Mon,  1 Jan 2024 12:46:46 +0000

Add Atom feed generation
Diffstat:
Msrc/iens.scm | 142++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 138 insertions(+), 4 deletions(-)

diff --git a/src/iens.scm b/src/iens.scm @@ -1,14 +1,17 @@ (import (chicken condition) + (chicken file) + (chicken file posix) + (chicken io) (chicken process signal) (chicken process-context) - (chicken io) (chicken sort) (chicken string) (chicken time) (chicken time posix) breadline sql-de-lite - srfi-1) + srfi-1 + sxml-serializer) (define (starts-with? maybe-prefix s) (substring=? s maybe-prefix 0 0 (string-length maybe-prefix))) @@ -105,13 +108,25 @@ "ON UPDATE CASCADE ON DELETE CASCADE, " "tag_id REFERENCES tag(id) " "ON UPDATE CASCADE ON DELETE CASCADE);") + (conc "CREATE TABLE feed (" + "id INTEGER PRIMARY KEY, filename TEXT NOT NULL, " + "url TEXT NOT NULL, selector TEXT NOT NULL, " + "title TEXT NOT NULL, active INTEGER NOT NULL DEFAULT 1);") + "CREATE INDEX i_mtime ON entry(mtime);" + "CREATE INDEX i_pmtime ON entry(protected,mtime);" "CREATE UNIQUE INDEX i_url ON entry(url);" "CREATE UNIQUE INDEX i_tag ON tag(name);" - "CREATE UNIQUE INDEX i_rel ON tagrel(url_id,tag_id);"))) + "CREATE UNIQUE INDEX i_rel0 ON tagrel(url_id,tag_id);" + "CREATE INDEX i_rel1 ON tagrel(url_id);" + "CREATE INDEX i_rel2 ON tagrel(tag_id);"))) ;;;;;;;;;;;;;;;;;; ;; Configuration +(define config-author-name #f) +(define config-author-email #f) +(define config-author-uri #f) +(define config-entry-id-prefix "") (define config-verbose #f) (define (get-config key) @@ -131,7 +146,12 @@ (else data))) (define (read-config!) - (set! display-trace (= 0 (get-config/default "display-trace" 0))) + (set! display-trace (not (= 0 (get-config/default "display-trace" 0)))) + (set! config-verbose (not (= 0 (get-config/default "verbose" 0)))) + (set! config-author-name (get-config "author-name")) + (set! config-author-email (get-config "author-email")) + (set! config-author-uri (get-config "author-uri")) + (set! config-entry-id-prefix (get-config/default "entry-id-prefix" "")) (let ((trace-filename (get-config "trace"))) (when trace-port (close-output-port trace-port)) (set! trace-port @@ -534,6 +554,120 @@ "Disssociates tags from an entry" (apply untag* (time-id-strings args))) +;;;;;;;;;;;;;;;;;;;; +;; Feed Generation + +(define activate-feed-stmt + (sql db "UPDATE feed SET active=? WHERE id=?;")) +(define add-feed-stmt + (sql db "INSERT INTO feed(filename,url,selector,title) VALUES (?,?,?,?);")) +(define list-active-feed-stmt + (sql db "SELECT * FROM feed WHERE active=1;")) +(define list-feed-stmt + (sql db "SELECT * FROM feed;")) +(define remove-feed-stmt + (sql db "DELETE FROM feed WHERE id=?;")) +(define select-feed-stmt + (sql db "SELECT * FROM feed WHERE id=?;")) + +(defcmd (activate-feed feed-id) + "feed-id" "Activate the given feed" + (trace `(activate-feed ,feed-id)) + (exec activate-feed-stmt 1 feed-id)) + +(defcmd (add-feed filename url selector title) + "filename url selector title" "Add a new feed" + (trace `(add-feed ,filename ,url ,selector ,title)) + (exec add-feed-stmt filename url selector title) + (write-line (conc "Added feed " (last-insert-rowid db)))) + +(defcmd (disable-feed feed-id) + "feed-id" "Disable the given feed" + (trace `(disable-feed ,feed-id)) + (exec activate-feed-stmt 0 feed-id)) + +(define (feed->sxml id url type descr notes ptime ctime mtime) + `(atom:entry + (atom:id ,(string-append config-entry-id-prefix (number->string id))) + (atom:title ,url) + (atom:updated ,(rfc-3339 mtime)) + (atom:published ,(rfc-3339 (if (null? ptime) ctime ptime))) + (atom:link (@ (rel "related") (href ,url))) + (atom:content + ,@(cond ((null? descr) `(,notes)) + ((null? type) `(,descr)) + (else `((@ (type ,type)) ,descr)))) + ,@(query (map-rows (lambda (x) `(atom:category (@ (term ,(car x)))))) + select-tags-stmt id))) + +(define (write-feed stmt mtime title self) + (write-string + (serialize-sxml + `(*TOP* (@ (*NAMESPACES* (atom "http://www.w3.org/2005/Atom"))) + (*PI* xml "version='1.0' encoding='utf-8'") + (atom:feed + (atom:title ,title) + (atom:author + (atom:name ,(if config-author-name + config-author-name + "Unknown Author")) + ,@(if config-author-email `((atom:email ,config-author-email)) '()) + ,@(if config-author-uri `((atom:uri ,config-author-uri)) '())) + (atom:id ,self) + (atom:link (@ (rel "self") (href ,self))) + (atom:updated ,(rfc-3339 mtime)) + ,@(map (lambda (row) (apply feed->sxml row)) + (query fetch-rows stmt)))) + ns-prefixes: '((*default* . "http://www.w3.org/2005/Atom"))))) + +(define (generate-feed feed-id filename url selector title active-int) + (let* ((stmt (sql db (string-append "SELECT id,url,type,description,notes,ptime,ctime,mtime FROM entry " selector))) + (first-row (query fetch-row stmt)) + (mtime (if (null? first-row) #f (list-ref first-row 6)))) + (cond ((not mtime) + (when config-verbose + (write-line (conc "Feed " feed-id " is empty")))) + ((and (file-exists? filename) + (< mtime (file-modification-time filename))) + (when config-verbose + (write-line (conc "Feed " feed-id " is already up to date")))) + (else + (when config-verbose + (write-line (conc "Generating feed " feed-id))) + (with-output-to-file filename + (lambda () (write-feed stmt mtime title url))))))) + +(defcmd (generate . args) + "[feed-id ...]" "Generate the given feeds, or all active feeds" + (let loop ((todo (if (null? args) + (query fetch-all list-active-feed-stmt) + (map (lambda (id) (query fetch select-feed-stmt id)) + args)))) + (unless (null? todo) + (apply generate-feed (car todo)) + (loop (cdr todo))))) + +(defcmd (print-feeds) + "" "Display all feeds" + (query + (map-rows* + (lambda (id filename url selector title active-int) + (write-line (conc (if (= 0 active-int) + (conc "(" id ")") + (conc "#" id)) + " " + filename + " - " + title)) + (write-line (conc " " url)) + (write-line (conc " " selector)))) + list-feed-stmt)) + +(defcmd (remove-feed feed-id) + "feed-id" "Remove the given feed" + (trace `(remove-feed ,feed-id)) + (exec remove-feed-stmt feed-id)) + ;;;;;;;;;;;;; ;; Auto Add