init
This commit is contained in:
commit
8ee02ab018
41
README.md
Normal file
41
README.md
Normal 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
41
config.rec
Normal 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
6
doc/postfix.txt
Normal 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
49
justfile
Normal 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
39378
src/bleak.txt
Normal file
File diff suppressed because it is too large
Load Diff
21116
src/mobydick.txt
Normal file
21116
src/mobydick.txt
Normal file
File diff suppressed because it is too large
Load Diff
34
support/quickreads.drawio
Normal file
34
support/quickreads.drawio
Normal 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>
|
Loading…
x
Reference in New Issue
Block a user