remailback

send email reminders after a time period
git clone https://a3nm.net/git/remailback/
Log | Files | Refs

commit 87aafff0df4fff512e8d376919d42c5a57faac53
Author: Antoine Amarilli <a3nm@a3nm.net>
Date:   Sat, 23 Nov 2019 17:13:41 +0100

first commit

Diffstat:
.gitignore | 8++++++++
README.md | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
add.sh | 49+++++++++++++++++++++++++++++++++++++++++++++++++
delete.sh | 42++++++++++++++++++++++++++++++++++++++++++
init.sh | 15+++++++++++++++
poll.sh | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
show.sh | 28++++++++++++++++++++++++++++
7 files changed, 279 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,8 @@ +deleted/ +scheduled/ +sent/ +scheduled_reminders/ +sent_reminders/ +remailback.sqlite +lock.flock +email diff --git a/README.md b/README.md @@ -0,0 +1,81 @@ +This is a set of shell scripts to make it easy to schedule reminders to be sent +to yourself about a specific email at a specific time. + +## Requirements + +You should have in your $PATH the commands get_email_header and +prepare_email_forward from https://a3nm.net/git/mybin. + +Reminder emails will be sent with "sendmail -t" -- you should ensure that this +will actually send emails correctly, e.g., using msmtp-mta. + +## Setup + +To get started, write in the "email" file the email address that should receive +the reminders (it will also be used as a from address for the reminders): + + echo "your_email_address@example.com" > email + +Then initialize the DB: + + ./init.sh + +Schedule the following in your crontab, e.g., daily, to catch any missing +reminders: + + /path/to/remailback/poll.sh + +## Usage + +To configure a reminder about a message, do: + + cat ~/Mailstore/cur/messagefile | ./add.sh 2 weeks, check that order arrived + +This invocation will schedule a reminder about the message to be sent in 2 weeks +(give or take 5 minutes). Details about the reminder are stored in the +remailback folder and in its DB. An at job with atd is scheduled to send the +message (but it will be sent by the cron job above even if at fails). + +When the time is elapsed, remailback will send a reminder about the email: this +is an email which is In-Reply-To the original message, includes the original +message as an attachment, has a similar subject to the original message, and +includes your note (e.g., "check that order arrived" above). + +If you want to check which reminders exist about a message, issue: + + cat ~/Mailstore/cur/messagefile | ./show.sh + +If you want to delete all reminders about a message, issue: + + cat ~/Mailstore/cur/messagefile | ./delete.sh + +## Using within mutt + +TODO + +## Reference + +### Commands + +- init.sh: initialize the DB +- add.sh: add a reminder for a message +- delete.sh: delete all reminders for a message +- show.sh: show all reminders for a message +- poll.sh: poll for due reminders and send them + +### Working files and folders + +- remailback.sqlite: sqlite3 database +- lock.flock: lock (to prevent concurrent writes on the DB) + +- scheduled/: copy of email with scheduled reminders +- sent/: copy of email for which a reminder was sent +- deleted/: copy of email for which a reminder was deleted + +At any time, the contents of these three directories should exactly reflect what +is in the sqlite database. + +- scheduled_reminders/: copy of reminders which were scheduled (just before they + were sent) +- sent_reminders/: copy of reminders which were sent + diff --git a/add.sh b/add.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# Add a reminder for a message +# Usage: cat message | ./add.sh 2 weeks, check that order arrived + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +cd "$DIR" + +TARGET=$(echo "$@" | cut -d, -f1) +REASON=$(echo "$@" | cut -d, -f2- | sed 's/^\s*//;s/\s*$//') + +CDATE=$(date +%s) +TDATE=$(date +%s -d "$TARGET") + +if [ $? -ne 0 ] +then + echo "Could not parse date $TARGET" + exit 1 +fi + +TEMPFILE=$(tempfile) +cat > $TEMPFILE + +# get_email_header command from https://a3nm.net/git/mybin + +SUBJECT=$(get_email_header subject < "$TEMPFILE") +MESSAGEID=$(get_email_header message-id < "$TEMPFILE") + +UUID=$(uuidgen) + +FILENAME=$(echo "${MESSAGEID}_${SUBJECT}_${TDATE}_${REASON}_${UUID}" | tr -dc 'a-zA-Z@._0-9-') + +mkdir -p scheduled + +# adapted from https://stackoverflow.com/a/169969 +( + # Wait for lock on /var/lock/.myscript.exclusivelock (fd 200) for 10 seconds + flock -x -w 10 200 || exit 1 + + mv "$TEMPFILE" "scheduled/$FILENAME" + + echo "INSERT INTO reminders (sendtime, createtime, messageid, filename, explanation, status) VALUES('$TDATE', '$CDATE', '$MESSAGEID', '$FILENAME', '$REASON', 'scheduled');" | sqlite3 remailback.sqlite + + # from https://unix.stackexchange.com/a/116451/ + TDATEDATE=$(date -d "@$TDATE" +'%H:%M %D') + echo "$DIR/poll.sh" | at "$TDATEDATE" + +) 200>lock.flock + diff --git a/delete.sh b/delete.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +# Mark all reminders about a message as deleted. +# Usage: cat message | ./delet.sh + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +cd "$DIR" + +TEMPFILE=$(tempfile) +cat > $TEMPFILE + +# get_email_header command from https://a3nm.net/git/mybin + +MESSAGEID=$(get_email_header message-id < "$TEMPFILE") + +# adapted from https://stackoverflow.com/a/169969 +( + # Wait for lock on /var/lock/.myscript.exclusivelock (fd 200) for 10 seconds + flock -x -w 10 200 || exit 1 + + echo "Currently scheduled reminders:" + ./show.sh < $TEMPFILE + + mkdir -p deleted + + echo "SELECT * FROM reminders WHERE messageid='$MESSAGEID' AND status='scheduled'" | + sqlite3 remailback.sqlite | while read l; do + ID=$(echo "$l" | cut -d\| -f1) + FILE=$(echo "$l" | cut -d\| -f5) + mv "scheduled/$FILE" "deleted/$FILE" + echo "UPDATE reminders SET status='deleted' WHERE id='$ID'" | + sqlite3 remailback.sqlite + echo "Deleted reminder $ID" + done + + echo "Scheduled reminders are now:" + ./show.sh < $TEMPFILE +) 200>lock.flock + +rm -f "$TEMPFILE" + + diff --git a/init.sh b/init.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +cd "$DIR" + +if [ -f remailback.sqlite ] +then + echo "Database already exists -- not initializing" + exit 1 +fi + +echo "CREATE TABLE reminders(id INTEGER PRIMARY KEY AUTOINCREMENT, sendtime INTEGER, createtime INTEGER, messageid TEXT, filename TEXT, explanation TEXT, status TEXT);" | sqlite3 remailback.sqlite + +mkdir -p sent sent_reminders scheduled scheduled_reminders deleted + diff --git a/poll.sh b/poll.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +# Poll all scheduled reminders and send the ones that are due. +# This script is scheduled with "at" when a reminder is added. +# To be safe, it can also be scheduled to run regularly with cron. +# Usage: ./poll.sh + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +cd "$DIR" + +if [ ! -f email ] +then + echo "Missing from email! Aborting!" + exit 1 +fi + +EMAIL=$(cat email) + +CDATE=$(date +%s) +# adjust to time offsets -- potentially send reminders 5min early +# this is in case the clock drifts +CDATEA=$(($CDATE + 300)) + +# adapted from https://stackoverflow.com/a/169969 +( + # Wait for lock on /var/lock/.myscript.exclusivelock (fd 200) for 10 seconds + flock -x -w 10 200 || exit 1 + + echo "SELECT * FROM reminders WHERE sendtime < '$CDATEA' AND status='scheduled'" | + sqlite3 remailback.sqlite | while read l; do + + ID=$(echo "$l" | cut -d\| -f1) + TTIME=$(echo "$l" | cut -d\| -f2) + FILENAME=$(echo "$l" | cut -d\| -f5) + EXPL=$(echo "$l" | cut -d\| -f6) + FILE="scheduled/$FILENAME" + + # get_email_header and prepare_email_forward commands from https://a3nm.net/git/mybin + SUBJECT=$(get_email_header subject < $FILE) + MESSAGEID=$(get_email_header message-id < $FILE) + + mkdir -p sent sent_reminders scheduled_reminders + + prepare_email_forward "$EMAIL" "$EMAIL" "PING: $SUBJECT: $EXPL" "$MESSAGEID" "$FILE" "$EXPL" \ + > "scheduled_reminders/$FILENAME" + sendmail -t < "scheduled_reminders/$FILENAME" + + mv "scheduled_reminders/$FILENAME" "sent_reminders/$FILENAME" + mv "scheduled/$FILENAME" "sent/$FILENAME" + + echo "UPDATE reminders SET status='sent' WHERE id='$ID'" | + sqlite3 remailback.sqlite + done + +) 200>lock.flock + diff --git a/show.sh b/show.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# Show all reminders about a message. +# Usage: cat message | ./delet.sh + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +cd "$DIR" + +TEMPFILE=$(tempfile) +cat > $TEMPFILE + +# get_email_header command from https://a3nm.net/git/mybin + +MESSAGEID=$(get_email_header message-id < "$TEMPFILE") + +rm -f "$TEMPFILE" + +echo "SELECT * FROM reminders WHERE messageid='$MESSAGEID'" | + sqlite3 remailback.sqlite | while read l; do + ID=$(echo "$l" | cut -d\| -f1) + TTIME=$(echo "$l" | cut -d\| -f2) + TDATE=$(date -d "@$TTIME") + EXPL=$(echo "$l" | cut -d\| -f6) + STATUS=$(echo "$l" | cut -d\| -f7) + echo "Reminder $ID on $TDATE (status '$STATUS'): $EXPL" +done + +