Dozens B. McCuzzins 2024-08-10 22:30:31 -06:00
commit 28e9a51d90
9 changed files with 586 additions and 0 deletions

2
.gitignore vendored 100644
View File

@ -0,0 +1,2 @@
dist/*
!/dist/.gitkeep

17
LICENSE 100644
View File

@ -0,0 +1,17 @@
Copyright 2020
We're no strangers to love. You know the rules and so do I. A full
commitment's what I'm thinking of. You wouldn't get this from any other
guy. I just wanna tell you how I'm feeling. Gotta make you understand:
1. Never gonna give you up
2. Never gonna let you down
3. Never gonna run around and desert you
4. Never gonna make you cry
5. Never gonna say goodbye
6. Never gonna tell a lie and hurt you
WE'VE KNOWN EACH OTHER FOR SO LONG. YOUR HEART'S BEEN ACHING BUT YOU'RE
TOO SHY TO SAY IT. INSIDE WE BOTH KNOW WHAT'S BEEN GOING ON. WE KNOW THE
GAME AND WE'RE GONNA PLAY IT. AND IF YOU ASK ME HOW I'M FEELING. DON'T
TELL ME YOU'RE TOO BLIND TO SEE.

19
README.md 100644
View File

@ -0,0 +1,19 @@
# gemrec
This is a little recfile database that produces a gemlog
## REQUIREMENTS
- GNU recutils 1.9
- awk version 20240311
- and other stuff too
## INSTALL
1. copy `gemrec` somewhere in your path.
maybe `~/bin`?
2. copy `gemrec.1` to where your man pages are.
maybe `ls /usr/local/share/man/man1`?

20
db/db.rec 100644
View File

@ -0,0 +1,20 @@
%rec: gemlog
%doc: a gemlog entry
%key: id
%type: id int
%auto: id created updated
%sort: id
%type: created,updated date
%type: title,tags line
%type: flags enum draft published unlisted
%unique: title
%mandatory: id title created updated body
%allowed: id title created updated tags flags body
id: 1
title: First Impressions of Gemini
updated: 2024-08-10T18:18:40-06:00
created: 2020-06-14T00:00:00-06:00
tags: gemini
flags: published
body: Boy oh boy, it's hecking cool! I can't believe it.

0
dist/.gitkeep vendored 100644
View File

376
gemrec 100755
View File

@ -0,0 +1,376 @@
#!/bin/zsh
# GEMREC!
# It's a gemlog powered by recutils
# author: dozens <dozens@tilde.team>
# created: 2024-08-10
# updated: 2024-08-10
# version: Albatross.0
#############
## GLOBALS ##
#############
url="gemini://tilde.town/~dozens/gemlog" # where is your gemlog deployed
author="dozens <dozens@tilde.team>" # used basically just for metadata
db="db/db.rec" # the database file
outdir="dist" # build directory
feed="atom.xml" # name of your gemlog feed
remote="tilde:public_gemini" # where to upload your gemlog?
###############
## TEMPLATES ##
###############
# Templates are near the top of the file
# instead of near where they are used
# so you don't have to go searching so hard
# if/when you want to change them :)
### Header of the gemlog index page
TMPL_INDEXHEADER=$(
cat<<EOF
# Dozens Gemlog 💎
One of my goals here is to make new friends and interact with people on gemini. For this reason, I also keep a gem log here to record my thoughts and to reply to other geminauts.
EOF
)
### A list item on the gemlog index page
TMPL_INDEXITEM()(
updated=$1
cat<<EOF
=> {{id}}.gmi $updated - {{title}}
EOF
)
### the gemlog index page footer
TMPL_INDEXFOOTER=$(
cat<<EOF
---
=> atom.xml Atom Feed
=> gemini://tilde.town/~dozens/ Home
EOF
)
### A metadata block that goes on each gemlog page
TMPL_METADATA()(
cat<<EOF
\`\`\`metadata
title: {{title}}
author: $author
url: $url/{{id}}.gmi
created: {{created}}
updated: {{updated}}
tags: {{tags}}
\`\`\`
EOF
)
### A feed item
TMPL_FEEDITEM(){
Timestamp=$1
cat<<EOF
<entry>
<id>$url/{{id}}.gmi</id>
<title>{{title}}</title>
<updated>$Timestamp</updated>
<link href='$url/{{id}}.gmi' />
<content type="text/plain">
<![CDATA[
{{body}}
]]>
</content>
</entry>
EOF
}
### Header for the feed
TMPL_FEEDHEADER=$(
# feed updated always equals the updated field
# of the most recently updated log page
UPDATED=$(recsel $db -P updated -C | sort | tail -n 1)
cat<<EOF
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom">
<id>$url</id>
<title>dozens gemlog</title>
<updated>$UPDATED</updated>
<author>
<name>dozens</name>
<email>dozens@tilde.team</email>
</author>
<link href="$url/$feed" rel="self"/>
EOF
)
################
## BUILD FUNS ##
################
### Build gemlogs
Build_gemlogs()(
Do_build()(
echo "Building $id.."
exec > $outdir/$id.gmi
echo "#" $(recsel $db -e id=$id -P title) "\n"
recsel $db -e id=$id -P body
echo "\n:wq\n"
echo "Thoughts? Comments? Let me know at dozens@tilde.team"
echo "\nTags:"
recsel $db -P tags -e id=$id \
| awk '{while(i++<NF){print "=> tags/" $i ".gmi " $i}}'
echo "=> tags/index.gmi All tags"
recsel $db -e id=$id | recfmt "$(TMPL_METADATA)"
)
for id in $(recsel $db -e 'flags="published"' -P id)
do
if [ ! -f $outdir/$id.gmi ]
then
touch $outdir/$id.gmi && Do_build
else
file_updated=$(stat -f "%m" $outdir/$id.gmi)
rec_updated=$(recsel $db -P updated -e id=$id)
rec_epoch=$(gdate -d"$rec_updated" +'%s')
if [ $file_updated -lt $rec_epoch ]
then
Do_build
else
echo "Skipping $id"
fi
fi
done
)
### build gemlog index
Build_gemindex()(
exec > $outdir/index.gmi
printf "%s\n\n" $TMPL_INDEXHEADER
for id in $(recsel $db -e 'flags="published"' -P id | sort -nr)
do
_date=$(gdate -d"$(recsel $db -e id=$id -P updated)" +'%Y-%m-%d')
recsel $db -e id=$id | recfmt "$(TMPL_INDEXITEM $_date)"
echo
done
echo $TMPL_INDEXFOOTER
)
### Build feed
Build_feed()(
exec > $outdir/$feed
echo $TMPL_FEEDHEADER
ids=$(recsel db/db.rec -e 'flags="published"' -P id -C)
for id in $ids
do
dateorig=$(recsel $db -e id=$id -P updated)
datenew=$(gdate -Iseconds -d"$dateorig")
recsel $db -e id=$id | recfmt "$(TMPL_FEEDITEM $datenew)"
done
echo "</feed>"
)
### Build tag index
Build_tagindex()(
if [ ! -d $outdir/tags ]
then
mkdir -r $outdir/tags
fi
exec > $outdir/tags/index.gmi
echo "# Tags"
recsel $db -p id,title,tags | recfmt "{{id}} {{tags}}
" | awk '
{
for(i=2;i<=NF;i++){
tags[$i]=tags[$i] " " $1;
}
}
END {
for (t in tags) {
print t tags[t]
}
}'| sort | awk -v url="$url" -v db="$db" '
{
printf "\n## %s\n",$1
for(i=2;i<=NF;i++){
cmd="recsel " db " -P title -e id=" $i
cmd | getline title
close(cmd)
printf "=> %s/%s.gmi %s\n",
url,$i,title
}
print ""
}'
)
### build tag pages
Build_tagpages()(
# be warned this is terribly hacky
recsel $db -p id,title,tags \
| recfmt '{{tags}}@@{{id}}@{{title}}
' | awk -v outdir="$outdir" '{
split($0,a,"@@")
taglist=a[1]
id_and_title=a[2]
split(id_and_title,b,"@")
id=b[1]
title=b[2]
split(taglist,tags," ")
for (t in tags) {
tagdict[tags[t]] = (length(tagdict[tags[t]])==0) ? tagdict[tags[t]]=id_and_title : tagdict[tags[t]] "@@" id_and_title
}
}
END {
for (t in tagdict) {
cmd="echo \"# " t "\\n\" > " outdir "/tags/" t ".gmi"
system(cmd)
split(tagdict[t],parts,"@@")
for (part in parts) {
split(parts[part],a,"@")
id=a[1];title=a[2]
link="echo \"=> ../" id ".gmi " title "\" >> " outdir "/tags/" t ".gmi"
system(link)
}
link="echo \"\n=> index.gmi All tags\" >> " outdir "/tags/" t ".gmi"
system(link)
}
}
'
)
##################
## EDITING FUNS ##
##################
### create new gemlog
Edit_newlog()(
read -p "Title?> " Title
tmp=$(mktemp)
$EDITOR "$tmp"
Body=$(< "$tmp")
rm $tmp
read -p "[p]ublish or [d]raft?> " Flags
read -p "tags?> " Tags
case "$Flags" in
"d") Flags="draft";;
"p") Flags="published";;
*) echo "Unknown type!"; exit 1;;
esac
recins --verbose db/db.rec -t gemlog \
-f title -v "$Title" \
-f flags -v "$Flags" \
-f tags -v "$Tags" \
-f body -v "$Body"
created="recsel db/db.rec -e 'title=\"$Title\"' -P id"
echo "Created $(eval $created)"
)
### update existing gemlog
Edit_updatelog()(
recsel $db -p id,flags,title | recfmt "{{id}}. [{{flags}}] {{title}}
"
printf "Enter a number to edit: "
read myid
grep -w -q $myid <(recsel $db -P id -C)
if [ ! "$?" = 0 ]
then
echo "\nERROR: id does not exist, try again\n"
Edit_updatelog
else
tmpmeta=$(mktemp)
tmpbody=$(mktemp)
recsel $db -e id=$myid -p title,tags,flags > $tmpmeta
recsel $db -e id=$myid -P body > $tmpbody
$EDITOR "$tmpmeta"
$EDITOR "$tmpbody"
Meta=$(< "$tmpmeta")
Body=$(< "$tmpbody")
recset --verbose $db -e id=$myid -f body -s $Body
awk -v db="$db" -v id="$myid" '
BEGIN {FS=": "}
{
for(i=2;i<=NF;i++){
if(length(rec[$1]) == 0){
rec[$1] = $i
} else {
rec[$1] = " " $i
}
}
}
END {
for(r in rec){
cmd = "recset " db " -e id=" id " -f " r " -s \"" rec[r] "\""
print time
system(cmd)
}
"gdate -Iseconds" | getline time
cmd = "recset " db " -e id=" id " -f updated -s \"" time "\""
system(cmd)
}
' $tmpmeta
rm $tmpmeta
rm $tmpbody
fi
)
###########
## TASKS ##
###########
### copy static assets to out dir
Task_copyassets()(
rsync -vurp static/* $outdir
)
### upload log to remote server
Task_upload()(
rsync -azP --exclude=.git dist/* $remote
)
##########
## MAIN ##
##########
### Show help
Show_help()(
echo "Usage: gemrec <options>"
echo "Options:"
echo " b|build: [f|feed t|tags g|gem|gemlog] defaults to all"
echo " n|new: create a new post"
echo " u|update: update an existing post"
echo " s|static: copy static files to output"
echo " u|up|upload: upload to remote server"
)
case "$1" in
"b"|"build")
case "$2" in
"f"|"feed")
echo "Build feed"
Build_feed
;;
"t"|"tags"|"tag")
echo "Build tags"
Build_tagindex
Build_tagpages
;;
"g"|"gem"|"gemlog")
echo "Build gemlog"
Build_gemindex
Build_gemlogs
;;
*)
echo "Build all"
Build_feed
Build_tagindex
Build_tagpages
Build_gemindex
Build_gemlogs
;;
esac
;;
"n"|"new") Edit_newlog;;
"e"|"edit") Edit_updatelog;;
"s"|"static") Task_copyassets;;
"u"|"up"|"upload") Task_upload;;
*) Show_help;;
esac

83
gemrec.1 100644
View File

@ -0,0 +1,83 @@
.\" Generated by scdoc 1.11.3
.\" Complete documentation for this program is not available as a GNU info page
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.nh
.ad l
.\" Begin generated content:
.TH "GEMREC" "1" "2024-08-11"
.PP
.SH NAME
.PP
gemrec - a gemlog powered by recutils
.PP
.SH SYNOPSIS
.PP
gemrec [options]
.PP
.SH DESCRIPTION
.PP
gemrec
is a wrapper for GNU recutils
that turns a recfile database full of posts
into a gemlog.\&
.PP
Features include:
.PP
.PD 0
.IP \(bu 4
All posts and metadata centrally located
in a single rec database
.IP \(bu 4
Gemlog posts, each with a metadata block and tags
.IP \(bu 4
A gemsub compatible index of posts
.IP \(bu 4
A page for each tag,
linking back to each tagged page,
and an index of tags
.IP \(bu 4
An atom feed
.PD
.PP
.SH OPTIONS
.PP
.SS gemrec build|b
Buid all: feed, gemlog (and index), and tags (and index)
.PP
.SS gemrec b|build f|feed
Build feed
.PP
.SS gemrec b|build g|gemlog
Build gemlog pages that are older than the record'\&s \fIupdated\fR value.\& And the index.\&
.PP
.SS gemrec b|build t|tag|tags
Build tag pages, and the index.\&
.PP
.SS gemrec e|edit
Edit an existing post.\&
The post will be opened in $EDITOR.\&
First the editable metadata.\&
(Title, flags, tags.\&)
And then in a second editor instance,
the post body.\&
.PP
.SS gemrec n|new
Create a new post
.PP
.SS gemrec s|static
Copy static files
(images, static pages, etc)
from \fI/static\fR
to the build dir.\&
.PP
.SS gemrec u|up|upload
Upload to a remote server.\&
.PP
.SH SEE ALSO
.PP
\fIgemrec(5)\fR \fIrecsel\fR(1) \fIrecset\fR(1)
.PP
.SH AUTHORS
.PP
dozens <dozens@tilde.\&team>

69
gemrec.1.scd 100644
View File

@ -0,0 +1,69 @@
GEMREC(1)
# NAME
gemrec - a gemlog powered by recutils
# SYNOPSIS
gemrec [options]
# DESCRIPTION
gemrec
is a wrapper for GNU recutils
that turns a recfile database full of posts
into a gemlog.
Features include:
- All posts and metadata centrally located
in a single rec database
- Gemlog posts, each with a metadata block and tags
- A gemsub compatible index of posts
- A page for each tag,
linking back to each tagged page,
and an index of tags
- An atom feed
# OPTIONS
## gemrec build|b
Buid all: feed, gemlog (and index), and tags (and index)
## gemrec b|build f|feed
Build feed
## gemrec b|build g|gemlog
Build gemlog pages that are older than the record's _updated_ value. And the index.
## gemrec b|build t|tag|tags
Build tag pages, and the index.
## gemrec e|edit
Edit an existing post.
The post will be opened in $EDITOR.
First the editable metadata.
(Title, flags, tags.)
And then in a second editor instance,
the post body.
## gemrec n|new
Create a new post
## gemrec s|static
Copy static files
(images, static pages, etc)
from _/static_
to the build dir.
## gemrec u|up|upload
Upload to a remote server.
# SEE ALSO
_gemrec(5)_ _recsel_(1) _recset_(1)
# AUTHORS
dozens <dozens@tilde.team>

BIN
static/img/utp.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB