commit 74a0533784c66a32e4ee3c26a4e075a52475246a
Author: lou woell <lou@repetitions.de>
Date: Sat, 21 Mar 2026 20:04:21 +0100
initial commit
Diffstat:
| A | linkhut-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))))