This commit is contained in:
Dozens B. McCuzzins 2025-05-19 22:46:50 -06:00
commit 8ee02ab018
7 changed files with 60665 additions and 0 deletions

41
README.md Normal file
View File

@ -0,0 +1,41 @@
# serial
> a book serializer
this is the worst thingie i can imagine that sends you an email at a scheduled
interval containing a snippet of a book, length of your choosing.
## requirements
- jq
- recutils
- awk
- postfix: configuring postfix (or some other mail transfer agent) is up to you!
i successfully did it after a lot of trial and error. see: `/doc/postfix.txt`
- just (optional)
## usage
1. update the recepient's email address in `justfile`
2. put plain text ebooks in `/src`.
(check out https://www.gutenberg.org/)
3. create a new entry for each new book in `config.rec`
(see `config.rec` for details)
4. either run `just process` every day, or set up a cron job to run it
(https://crontab.guru/#12_*_*_*_*)
## details
All variables and values mentioned below exist in each record of `config.rec`
1. Iterate over the records in config.rec. If today is not present in `delivery`, continue.
2. Iterate over paragraphs (sep = '\n\n') starting with paragraph number
`progress` and ending when the running word count exceeds `WPM * minutes`.
3. Pipe those paragraphs to postfix mail
4. Increment `iter` and update `progress` to be the final paragraph number (plus one)

41
config.rec Normal file
View File

@ -0,0 +1,41 @@
%rec: doc
%doc: a document
%key: id
%type: id,iter,progress,minutes int
%type: title,author,file,delivery line
%type: created,updated date
%auto: id created updated
# Further details:
# id : just an id
# title, author : you know what to do
# file : the path and filename
# iter (iteration) : how many times the file has been processed; how many emails have been sent. default = 1
# progress : the "current index" or paragraph number; where to start the next serial. default = 0
# delivery : must be a json array of days of the week, capitalized (so it matched `date +'%A'`)
# minutes : how many minutes you want each serial to be (multiplied by
# 240 which I read on the internet is roughly how many words
# per minute a person reads)
# created, updated : timestamps
id: 1
title: Bleak House
author: Charles Dickens
file: src/bleak.txt
iter: 2
progress: 10
delivery: [ "Monday", "Wednesdsay", "Thursday" ]
minutes: 5
created: 2025-05-14
updated: 2025-05-19
id: 2
title: Moby Dick
author: Herman Melville
file: src/mobydick.txt
iter: 1
progress: 0
delivery: [ "Friday" ]
minutes: 15
created: 2025-05-16
updated: 2025-05-16

6
doc/postfix.txt Normal file
View File

@ -0,0 +1,6 @@
whew this thing is hard to understand and configure
- https://www.postfix.org/documentation.html
- https://blog.konoson.com/set-up-macos-to-use-postfix-for-sending-automated-emails-from-cron
- https://knazarov.com/posts/setting_up_postfix_on_os_x/
- https://altoplace.com/macos/create-a-send-only-smtp-server-on-macos/

49
justfile Normal file
View File

@ -0,0 +1,49 @@
set quiet
conf:="config.rec"
email:="pooper@bigpoop.com"
# list all recipes
default:
just --list --unsorted
# add new book
new:
#!/usr/bin/env sh
read -p "Title: " title
read -p "File: " file
recins {{conf}} -t doc -f title -v "$title" -f file -v "$file"
alias n:= new
# process books
process:
#!/usr/bin/env sh
WPM=240
recs=$(recsel {{conf}} -c)
for rec in $(seq 0 $(($recs - 1)))
do
deliver=$(recsel {{conf}} -P delivery -n "$rec" | jq --arg today $(gdate +'%A') 'any(. == $today))
if ! $deliver; then continue; fi
idx=$(recsel {{conf}} -P progress -n "$rec")
minutes=$(recsel {{conf}} -P minutes -n "$rec")
words=$((minutes * WPM))
file=$(recsel {{conf}} -P file -n "$rec")
title=$(recsel {{conf}} -P title -n "$rec")
author=$(recsel {{conf}} -P author -n "$rec")
iter=$(recsel {{conf}} -P iter -n "$rec")
awk -v start="$idx" -v rec="$rec" -v title="$title" -v author="$author" -v iter="$iter" 'BEGIN {
RS="\n\n"; ORS="\n\n"; Limit=1200
}
NR >= start && total < Limit {
out = out "\n\n" $0;
total = total + NF; par = NR;
}
END {
cmd = "recset {{conf}} -n " rec " -f progress -s " par + 1;
cmd | getline
print out
}' "$file" \
| mail -s "$title, by $author Part $iter" {{email}}
recset {{conf}} -n "$rec" -f iter -s $((iter + 1))
recset {{conf}} -n "$rec" -f updated -s $(gdate -I)
done

39378
src/bleak.txt Normal file

File diff suppressed because it is too large Load Diff

21116
src/mobydick.txt Normal file

File diff suppressed because it is too large Load Diff

34
support/quickreads.drawio Normal file
View File

@ -0,0 +1,34 @@
<mxfile host="Electron" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/27.0.5 Chrome/134.0.6998.205 Electron/35.3.0 Safari/537.36" version="27.0.5">
<diagram name="Page-1" id="1J1rS7NrqHWSoNJc5LON">
<mxGraphModel dx="823" dy="612" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="lY4qc6qsTG00_l9DgUZb-1" value="User Service" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="360" y="40" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="lY4qc6qsTG00_l9DgUZb-2" value="User DB" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="520" y="40" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="lY4qc6qsTG00_l9DgUZb-3" value="Client" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="40" y="40" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="lY4qc6qsTG00_l9DgUZb-4" value="API Gateway" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="200" y="40" width="120" height="140" as="geometry" />
</mxCell>
<mxCell id="lY4qc6qsTG00_l9DgUZb-5" value="Document Service" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="360" y="120" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="lY4qc6qsTG00_l9DgUZb-6" value="Document DB" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="520" y="120" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="lY4qc6qsTG00_l9DgUZb-7" value="Delivery Service" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="360" y="200" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="lY4qc6qsTG00_l9DgUZb-8" value="Delivery Queue" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="520" y="200" width="120" height="60" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>