2023-10-15 4chan client shell script I wrote this shell script a while back because I wanted to browse the Lisp and Emacs general thread on 4chans /g/ board[1] in Emacs. I thought about writing it in Elisp but handling Json data is a real pain. So instead I use curl[2] to request a thread via the 4chan API[3], then pipe it to jq[4] to process the result, then AWK[5] to work its black magic to construct a very simplified html version of the thread which I finally can view with Eww[6] in Emacs. Looking at this code a few months later it kinda looks like the work of a madman, though. Features ~~~~~~~~ - produces a simple html file ideal for text browsers - shows replies for every post (see screenshot) - images are linked Drawbacks ~~~~~~~~~ - if no thread number is provided it shows a list of threads from the catalogue but ONLY those with a proper title - the /g/ board is hard-coded (which can be easily changed of course) Screenshot ~~~~~~~~~~ (lol) - Lisp General (M-x eshell Edition): file:///tmp/thread.html [IMG] 96623294 Do 12. Okt 17:29:30 CEST 2023 How can I mentally think of C-x? It seems to do too much. Replies: >>96623403 >>96623705 [IMG] 96623403 Do 12. Okt 17:34:58 CEST 2023 >>96623294 It's a sequence of keys, the control key and the x key Replies: >>96623624 [IMG] 96623624 Do 12. Okt 17:45:21 CEST 2023 >>96623403 I know, but what do I call it? Replies: >>96623807 -UUU: @%*- F2 *eww* 9% (165,0) (eww) ----------- Code ~~~~ #!/bin/sh # if no thread number is provided if [ $# -lt 1 ]; then # print catalog, ONLY THREADS WITH TITLE (because I only need /lol/ # and printing the OP would be too much clutter, html and all) # https://github.com/4chan/4chan-API/blob/master/pages/Catalog.md curl -s https://a.4cdn.org/g/catalog.json | \ jq -r '.[].threads | map([.no, .sub] | join(" ")) | join("\n")' | \ awk '$2' exit 1 fi # pipeline: curl (thread.json) -> jq (thread.csv) -> awk (thread.html) # https://github.com/4chan/4chan-API/blob/master/pages/Threads.md curl -s https://a.4cdn.org/g/thread/$1.json > /tmp/thread.json # extract title jq -r '.[][0].sub' /tmp/thread.json > /tmp/thread.csv # extract thread-number, unix time stamp, comment, # unix timestamp + microtime (=filename), file extension jq -r '.posts | map([.no, .time, .com, .tim, .ext] | join("\t")) | join("\n")' \ /tmp/thread.json >> /tmp/thread.csv # black awk magic awk ' BEGIN { FS="\t"; } # first loop over file to get references NR==FNR && match ($0, />>[0-9]*/) { # e.g. match = >>91962953, (> is html code for >) split(substr($0, RSTART, RLENGTH), to_arr, ";"); # e.g. x = 91962953 (= referenced post), no capture groups in mawk x=to_arr[3]; # generate new html link new_link = sprintf("<a href=\"#p%s\" class=\"quotelink\">>>%s</a>", $1 ,$1); # replies[referenced_post] += referring_post (string concat) replies[x] = sprintf("%s %s", replies[x] , new_link); } # second loop, write html NR!=FNR && FNR==1 { printf "<html><head><title>%s</title></head><body>", $0; } NR!=FNR && FNR >1 { print "<table>"; # insert image link or placeholder print "<tr><td>"; if ($4) printf "<a href=\"https://i.4cdn.org/g/%s%s\">[IMG]</a>", $4, $5; else print "[IMG]"; print "</td>"; # insert post id, creation date of post in local time and comment sprintf("date -d @%s", $2) | getline local_time printf "<td>"; printf "<i id=\"p%s\">%s</i> %s <br><br> %s", $1, $1, local_time, $3; print "</td></tr>"; # insert replies to this post if (replies[$1]) { print "<tr><td></td><td>Replies: "; print replies[$1]; print "</td><tr>"; } print "</table>"; } END{ print "</body></html>"; }' /tmp/thread.csv /tmp/thread.csv > /tmp/thread.html # open in *eww* emacsclient --eval '(eww-open-file "/tmp/thread.html")' Footnotes ~~~~~~~~~ [1] https://boards.4channel.org/g/ [2] https://curl.se/ [3] https://github.com/4chan/4chan-API/blob/master/pages/Threads.md [4] https://github.com/jqlang/jq [5] https://en.wikipedia.org/wiki/AWK [6] https://www.gnu.org/software/emacs/manual/html_mono/eww.html