summary refs log tree commit diff stats
path: root/twerk
diff options
context:
space:
mode:
Diffstat (limited to 'twerk')
-rwxr-xr-xtwerk237
1 files changed, 237 insertions, 0 deletions
diff --git a/twerk b/twerk new file mode 100755 index 0000000..2b0dfd7 --- /dev/null +++ b/twerk
@@ -0,0 +1,237 @@
1#!/bin/sh
2## twerk: a twtxt client
3# by Case Duckworth
4
5### Entry point
6
7usage() {
8 cat >&2 <<EOF
9TWERK: a twtxt client
10usage: twerk [options] [file]
11
12twerk can be fed a list of URLs from standard input or from a FILE.
13Each line is expected to contain a URL of a twtxt feed and (optionally) the
14display name to use for that feed.
15
16options (default):
17 -h Show this help and exit.
18 -t Include the time in each post date.
19 -f Force redownload of twt data.
20 -n LIMIT Only show LIMIT posts (25). 0 means no limit.
21 -w WIDTH Limit the output's width to WIDTH characters (72).
22 -W WIDTH Limit the username's width to WIDTH characters (12).
23 -i INDENT Use INDENT spaces as a hanging indent for too-long lines
24 ([calculated]).
25 -p COMMAND Use COMMAND to post twts ([not set]).
26 This command will be fed a twtxt-formatted string via stdin.
27 Recommended: \`ssh <host> tee -a <file>\`.
28
29parameters:
30 FILE List of twtxt feeds to fetch, one feed per line.
31 Optionally, a string after the feed URL will be its
32 display name.
33EOF
34 exit "${1:-0}"
35}
36
37configure() {
38 # defaults
39 TWERK_WIDTH="${TWERK_WIDTH:-72}"
40 TWERK_LIMIT="${TWERK_LIMIT:-25}"
41 TWERK_INCLUDE_TIME="${TWERK_INCLUDE_TIME:-0}"
42 TWERK_USER_WIDTH="${TWERK_USER_WIDTH:-12}"
43 TWERK_HANG="${TWERK_HANG:-}" # empty to calculate off other variables
44 TWERK_FORCE="${TWERK_FORCE:-false}"
45 TWERK_POST_COMMAND="${TWERK_POST_COMMAND:-:}" # no-op
46
47 while getopts htfn:w:W:i:p:c: opt
48 do
49 case "$opt" in
50 h) usage ;;
51 t) TWERK_INCLUDE_TIME=1 ;;
52 f) TWERK_FORCE=true ;;
53 n) TWERK_LIMIT="$OPTARG" ;;
54 w) TWERK_WIDTH="$OPTARG" ;;
55 W) TWERK_USER_WIDTH="$OPTARG" ;;
56 i) TWERK_HANG="$OPTARG" ;;
57 p) TWERK_POST_COMMAND="$OPTARG" ;;
58 c) TWERK_FEEDS="$OPTARG" ;;
59 *) usage 1 ;;
60 esac
61 done
62
63 if test -z "$TWERK_HANG"
64 then
65 TWERK_HANG=$((TWERK_USER_WIDTH+(TWERK_INCLUDE_TIME*6)+10+3))
66 echo >&2 "$TWERK_HANG"
67 fi
68}
69
70main() {
71 TWERK_CACHE="${XDG_CACHE_HOME:-$HOME/.cache}/twerk"
72 mkdir -p "$TWERK_CACHE"
73 TWERK_FILE="$TWERK_CACHE/:file:"
74
75 TWERK_LESSKEY="$TWERK_CACHE/:lesskey:"
76 lesskey_setup
77
78 if "$_TWERK_INITIAL"
79 then
80 configure "$@"
81 shift "$((OPTIND - 1))"
82 TWERK_FEEDS="$1"
83 fi
84
85 cat "$TWERK_FEEDS" |
86 fetch_posts |
87 sort_posts |
88 limit_posts "$TWERK_LIMIT" |
89 format_posts > "$TWERK_FILE"
90
91 read_posts "$TWERK_FILE"
92}
93
94### Library
95
96fetch_posts() {
97 while read -r url name
98 do
99 if test -z "$name"
100 then
101 file="$TWERK_CACHE/$(url_normalize "$name")"
102 else
103 file="$TWERK_CACHE/$name"
104 fi
105
106 if "$TWERK_FORCE" || test -z "$(find "$file" -prune -mtime -1 2>/dev/null)"
107 then
108 printf >&2 'Downloading %s...\n' "$url"
109 curl -sf "$url" |
110 filter_lines |
111 while read -r date post
112 do
113 printf '%s\t%s\t%s\t%s\n' \
114 "$date" "$name" "$url" "$post"
115 done |
116 tee "$file"
117 else
118 cat "$file"
119 fi
120 done
121}
122
123url_normalize() {
124 printf '%s\n' "$1" | tr -d ':/~'
125}
126
127filter_lines() {
128 while read -r line
129 do
130 case "$line" in
131 \#*) ;;
132 '') ;;
133 *) printf '%s\n' "$line" ;;
134 esac
135 done
136}
137
138limit_posts() {
139 ln=0
140 while read -r line
141 do
142 if test "$1" -ne 0 && test "$ln" -gt "$1"
143 then
144 break
145 fi
146 printf '%s\n' "$line"
147 ln=$((ln+1))
148 done
149}
150
151sort_posts() {
152 sort -r
153}
154
155format_posts() {
156 while IFS=' ' read -r date name url post
157 do
158 if test 0 -eq "$TWERK_INCLUDE_TIME"
159 then
160 TWERK_DATE_WIDTH=10
161 date="${date%T*}"
162 else
163 TWERK_DATE_WIDTH=16
164 fi
165
166 printf "%${TWERK_DATE_WIDTH}s | %${TWERK_USER_WIDTH}s | " \
167 "$(echo "$date" | head -c$TWERK_DATE_WIDTH)" \
168 "$(echo "$name" | head -c$TWERK_USER_WIDTH)"
169
170 export url name date post TWERK_WIDTH TWERK_HANG
171 export linewidth="$TWERK_HANG"
172
173 printf '%s\n' "$post" |
174 tr ' ' '\n' |
175 while IFS= read word
176 do
177 if test $(( linewidth + ${#word} )) -ge "$TWERK_WIDTH"
178 then
179 echo
180 linewidth=0
181 printf "%${TWERK_HANG}s \` " ""
182 linewidth=$((linewidth+TWERK_HANG+2))
183 fi
184 printf '%s ' "$word"
185 linewidth=$((linewidth + ${#word}))
186 done
187 echo
188 done
189}
190
191
192lesskey_setup() {
193 # t will exit with code 48
194 if ! test -r "$TWERK_LESSKEY"
195 then
196 lesskey -o "$TWERK_LESSKEY" - <<EOF
197#command
198t quit 0
199g quit 1
200#line-edit
201#env
202EOF
203 fi
204}
205
206read_posts() {
207 less \
208 -k "$TWERK_LESSKEY" \
209 -Pm'twerk! t\: twt, g\: refresh' -m \
210 "$1"
211
212 case "$?" in
213 0) exit ;;
214 48) post ;;
215 49) refresh ;;
216 esac
217}
218
219post() {
220 printf 'Twt: ' >&2
221 read -r post
222 echo >&2
223
224 post="$(printf '%s\t%s\n' "$(date +'%FT%T%z')" "$post")"
225
226 eval "printf '%s\n' \"\$post\" | $TWERK_POST_COMMAND"
227 _TWERK_INITIAL=false main "$TWERK_FEEDS"
228}
229
230refresh() {
231 TWERK_FORCE=true main "$TWERK_FEEDS"
232}
233
234###################################
235_TWERK_INITIAL=true
236test -z "$DEBUG" || set -x
237test -z "$SOURCE" && main "$@"