commit 74a0533784c66a32e4ee3c26a4e075a52475246a
Author: lou woell <lou@repetitions.de>
Date:   Sat, 21 Mar 2026 20:04:21 +0100

initial commit

Diffstat:
Alinkhut-guile.scm | 367+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 367 insertions(+), 0 deletions(-)

diff --git a/linkhut-guile.scm b/linkhut-guile.scm @@ -0,0 +1,367 @@ +#!/bin/guile -s +!# + +;;; linkhut interface for guile scheme + +;; Copyright (C) 2026 lou woell <lou@repetitions.de> +;; +;; This library is free software; you can redistribute it and/or modify it under +;; the terms of the GNU Lesser General Public License as published by the Free +;; Software Foundation; either version 3 of the License, or (at your option) any +;; later version. +;; +;; This library 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 Lesser General Public License for more +;; details. +;; +;; You should have received a copy of the GNU Lesser General Public License +;; along with this library; if not, write to the Free Software Foundation, Inc., +;; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +;;; Commentary: + +;; API documentation: https://docs.linkhut.org/overview.html + +;;; Code: + +(use-modules + ;; let-values + (srfi srfi-11) + ;; date-time + (srfi srfi-19) + + ;; readline + (ice-9 rdelim) + + (web uri) + (web response) + (web client) + + (sxml simple) + (sxml match) + (sxml xpath)) + +;; TODO: make this a bit more user friendly +(define token (call-with-input-file "./token" read-line)) +(define host "api.ln.ht") +(define api-version "v1") +(define post-url "posts") +(define tags-url "tags") + +(define (encode-query-val val) + (cond ((string? val) (uri-encode val)) + ((list? val) (string-join (map uri-encode val) "+")))) + +(define (query-part parts) + (let ((key (symbol->string (car parts))) + (val (cdr parts))) + (if val + (string-join (list (uri-encode key) + (encode-query-val val)) + "=") + ""))) + +(define (build-query parts) + (string-join (cons "?" (map query-part parts)) "&")) + +(define* (call-linkhut path #:optional (query #f)) + (let-values + (((resp body) + (http-get (build-uri 'https + #:host host + #:path (encode-and-join-uri-path (cons "" (cons api-version path))) + #:query (if query (build-query query) query)) + #:headers `((authorization . (basic . ,token)))))) + (when (= 200 (response-code resp)) + (sxml->lisp (xml->sxml body #:trim-whitespace? #t))))) + +(define (timestamp string) + (string->date string "~Y~m~d~H~M~S~z")) + +(define (sxml->lisp x) + (sxml-match x + ((*TOP* ,pi ,(data)) data) + + ((posts (@ (user ,user)) ,(post) ...) + `(,post ...)) + + ((tags ,(tag) ...) tag) + + ((tag (@ (tag ,name) (count ,n))) + (list name (string->number n))) + + ((post (@ (tag ,tags) + (description ,desc) + (extended ,ext) + (hash ,hash) + (href ,link) + (others ,others) + (time ,time) + (meta ,meta))) + (list (string->number hash 16) + (cons 'title desc) + (cons 'note ext) + (cons 'link link) + (cons 'time (timestamp time)) + (cons 'count (string->number others)) + (cons 'meta (string->number meta 16)) + (cons 'tags (string-split tags char-whitespace?)))) + + ((result (@ (code ,c))) + (string= "done" c)) + + ((dates (@ (tag ,tag) + (user ,user)) + ,(date) ...) + (list (cons 'tag tag) date)) + + ((date (@ (count ,count) + (date ,date))) + (list date (string->number count))) + + ((update (@ (time ,t) + (code ,c) + (inboxnew ,i))) + + (list 'update (timestamp t) (string= "done" c) i)))) + +;; posts update +;; +;; Returns the most recent time a bookmark was added, updated or deleted. +;; +;; Use this before calling posts/all to see if the data has changed since the +;; last fetch. + +(define (post-update) + (call-linkhut (list post-url "update"))) + +;; add post +;; +;; Add a new bookmark. +;; +;; Arguments: +;; +;; &url={URL} (required) +;; The url of the item. +;; +;; &description={...} (required) +;; The description of the item. +;; +;; &extended={...} (optional) +;; Notes for the item. +;; +;; &tags={...} (optional) +;; Tags for the item (comma delimited). +;; +;; &dt={CCYY-MM-DDThh:mm:ssZ} (optional) +;; Datestamp of the item (format “CCYY-MM-DDThh:mm:ssZ”). Requires a LITERAL +;; “T” and “Z” like in ISO8601 at http://www.cl.cam.ac.uk/~mgk25/iso-time.html +;; for Example: 1984-09-01T14:21:31Z. +;; +;; &replace=no (optional) +;; Don’t replace post if given url has already been posted. +;; +;; &shared=no (optional) +;; Make the item private. +;; +;; &toread=yes (optional) +;; Mark the item as unread. + +(define* (post-add url description + #:key extended tags dt replace shared toread) + + (call-linkhut (list post-url "add") + `((url . ,url) + (description . ,description) + (extended . ,extended) + (tags . ,tags) + (dt . ,dt) + (replace . ,(if replace "yes" "no")) + (shared . ,(if shared "yes" "no")) + (toread . ,(if toread "yes" "no"))))) + +;; delete post +;; +;; Delete a bookmark +;; +;; Arguments: +;; +;; &url={URL} (required) +;; The URL of the item. + +(define (post-delete url) + (call-linkhut (list post-url "delete") + `((url . ,url)))) + +;; get posts +;; +;; Returns one or more posts on a single day matching the arguments. If no +;; date or url is given, date of most recent bookmark will be used. +;; +;; Arguments: +;; +;; &tag={TAG}+{TAG}+...+{TAG} (optional) — +;; Filter by this tag. +;; +;; &dt={CCYY-MM-DDThh:mm:ssZ} (optional) +;; Filter by this date, defaults to the most recent date on which bookmarks +;; were saved. &url={URL} (optional) — Fetch a bookmark for this URL, +;; regardless of date. Note: Be sure to URL-encode the argument value. +;; +;; &hashes={MD5}+{MD5}+...+{MD5} (optional) +;; Fetch multiple bookmarks by one or more URL MD5s regardless of date, +;; separated by URL-encoded spaces (i.e. ‘+’). +;; +;; &meta=yes (optional) +;; Include change detection signatures on each item in a ‘meta’ attribute. +;; Clients wishing to maintain a synchronized local store of bookmarks should +;; retain the value of this attribute — its value will change when any +;; significant field of the bookmark changes. + +(define* (post-get #:key tag date url hashes) + (call-linkhut (list post-url "get") + `((tag . ,tag) + (date . ,date) + (url . ,url) + (hashes . ,hashes) + (meta . "yes")))) + +;; recent posts +;; +;; Returns a list of the user's most recent posts, filtered by tag. +;; +;; Arguments: +;; +;; &tag={TAG} (optional) +;; Filter by this tag. +;; +;; &count={1..100} (optional) +;; Number of items to retrieve (Default:15, Maximum:100). + +(define* (post-recent #:key tag count) + (call-linkhut (list post-url "recent") + `((tag . ,tag) + (count . ,count)))) + +;; post dates +;; +;; Returns a list of dates with the number of posts at each date. +;; +;; Arguments: +;; +;; &tag={TAG} (optional) +;; Filter by this tag. + +(define* (post-dates #:key tag) + (call-linkhut (list post-url "dates") + `((tag . ,tag)))) + +;; all posts +;; +;; Returns a list of dates with the number of posts at each date. +;; +;; Arguments: +;; +;; &tag={TAG} (optional) +;; Filter by this tag. + +;; &start={xx} (optional) +;; Start returning posts this many results into the set. +;; +;; &results={xx} (optional) +;; Return up to this many results. By default, up to 1000 bookmarks are +;; returned, and a maximum of 100000 bookmarks is supported via this API. +;; +;; &fromdt={CCYY-MM-DDThh:mm:ssZ} (optional) +;; Filter for posts on this date or later. +;; +;; &todt={CCYY-MM-DDThh:mm:ssZ} (optional) +;; Filter for posts on this date or earlier. +;; +;; &meta=yes (optional) +;; Include change detection signatures on each item in a ‘meta’ attribute. +;; Clients wishing to maintain a synchronized local store of bookmarks should +;; retain the value of this attribute - its value will change when any +;; significant field of the bookmark changes. + +(define* (post-all #:key tag start results fromdt todt meta) + (call-linkhut (list post-url "all") + `((tag . ,tag) + (start . ,start) + (results . ,results) + (fromdt . ,fromdt) + (todt . ,todt) + (meta . ,(if meta "yes" "no"))))) + +;; post all?hashes +;; +;; Returns a change manifest of all posts. Call the update function to see if +;; you need to fetch this at all. +;; +;; This method is intended to provide information on changed bookmarks, without +;; the overhead of a complete download of all post data. +;; +;; Each post element returned offers a url attribute containing an URL MD5, with +;; an associated meta attribute containing the current change detection +;; signature for that bookmark. + +(define (post-all-hashes) + (call-linkhut (list post-url "all") + `((hashes . #t)))) + +;; suggest post +;; +;; Returns a list of popular tags and recommended tags for a given URL. +;; Popular tags are tags used site-wide for the url; recommended tags are +;; drawn from the user's own tags. +;; +;; This method is intended to provide suggestions for tagging a particular +;; url. +;; +;; Arguments +;; +;; &url={URL} (required) +;; URL for which you’d like suggestions. + +(define (post-suggest url) + (call-linkhut (list post-url "suggest") + `((url . ,url)))) + +;; get tags +;; +;; Returns a list of tags and number of times used by a user. + +(define (tags-get) + (call-linkhut (list tags-url "get"))) + +;; get tags +;; delete tag +;; +;; Delete an existing tag from all posts +;; +;; Arguments +;; +;; &tag={TAG} (required) +;; Tag to delete. + +(define (tag-delete tag) + (call-linkhut (list tags-url "delete") + `((tag . ,tag)))) + +;; rename tag +;; +;; Rename an existing tag with a new tag name. +;; +;; Arguments +;; +;; &old={TAG} (required) +;; Tag to rename. +;; +;; &new={TAG} (required) +;; New tag name. + +(define (tag-rename old new) + (call-linkhut (list tags-url "delete") + `((old . ,old) + (new . ,new))))