From 78ec0debf68bf175985f570678d49c6329ac924f Mon Sep 17 00:00:00 2001 From: Rodger Castle Date: Thu, 4 Jun 2026 19:38:33 -0400 Subject: [PATCH] Updated README.md with project description and deployment instructions --- README.md | 273 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 271 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8452661..4de1a96 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,272 @@ -# mnemosyne +# Mnemosyne -Task manager to help remember periodic or upcoming tasks with Telegram front-end. \ No newline at end of file +A self-hosted, single-user task manager built around long-timeframe recurring tasks. Its job is to keep things **on the radar** — recurring obligations, periodic maintenance, and someday/maybe items — without the bloat of a full project management system. + +The primary interface is a Telegram bot. Every morning it delivers a **Day at a Glance** digest: what's overdue, what's due today, what's coming up in the next week, and a rotation of floating reminders. Everything else is query-on-demand. No mid-day push notifications, ever. + +The name is Mnemosyne — the Greek personification of memory. The app remembers so you don't have to. + +--- + +## Task classes + +Five scheduling models cover most recurring-task patterns: + +| Class | Description | Example | +|---|---|---| +| `monthly_date` | Fixed day of the month | Pay rent on the 1st | +| `monthly_weekday` | Nth weekday of the month | First Thursday, last Friday | +| `every_n_period` | Every N days/weeks/months from an anchor date | Every 2 weeks from 2026-01-06 | +| `interval` | Every N days, clock resets on completion | Change oil every 90 days | +| `floating` | No fixed date; surfaces by priority rotation | Call parents, learn Spanish | + +Missed dated tasks roll into an **Overdue** section rather than disappearing. Slippage is a feature, not an error. + +--- + +## Telegram commands + +| Command | What it does | +|---|---| +| `/today` or `/glance` | Day at a Glance digest on demand | +| `/list [filter]` | Browse tasks; filter by class, priority, or `inactive` | +| `/add` | Create a task via guided wizard | +| `/done ` | Mark a task complete | +| `/edit ` | Update a single field | +| `/disable ` | Hide a task without deleting it | +| `/delete ` | Permanently delete (with confirmation) | +| `/settime HH:MM` | Change the morning digest delivery time | +| `/help` | List commands | + +Actionable tasks in digests and list results get inline **Mark Done** buttons. Tapping one completes the task immediately and replaces the button with **Undo** in case of misclicks. + +--- + +## Stack + +- **Perl** — application logic +- **SQLite** — single-file database via `DBD::SQLite`; WAL mode +- **Mojolicious** — webhook receiver and outbound Telegram API calls (`Mojo::UserAgent`) +- **nginx** — TLS termination and reverse proxy; bot binds to localhost only +- **systemd** — long-running webhook service with `Restart=always` +- **cron** — morning digest (runs every 5 minutes; the script checks the configured time) + +--- + +## Prerequisites + +- A Linux server with a public IP (AlmaLinux 10 / RHEL family recommended; Debian/Ubuntu also supported) +- A domain or subdomain with an A record pointing at the server +- A Telegram bot token from [@BotFather](https://t.me/BotFather) +- Your Telegram chat ID ([@userinfobot](https://t.me/userinfobot) will tell you) +- Perl 5.26+, nginx, certbot, sqlite3 + +--- + +## Deployment + +### 1. Clone the repo + +```bash +git clone https://git.castlehollow.com/rodger/mnemosyne.git +cd mnemosyne +``` + +### 2. Run the install script + +Must be run as root. Safe to re-run — it will not overwrite an existing database or config file. + +```bash +sudo bash scripts/install.sh +``` + +This will: +- Install system packages (Perl, cpanminus, sqlite3, gcc) via `dnf` or `apt-get` +- Install CPAN dependencies from `cpanfile` via `cpanm` +- Create a `mnemosyne` system user and group +- Deploy application files to `/opt/mnemosyne` +- Install the systemd service (enabled but **not started** until you configure it) +- Install a cron job at `/etc/cron.d/mnemosyne` for the digest and daily backup + +Default paths (override with flags if needed): + +| Flag | Default | +|---|---| +| `--app-dir` | `/opt/mnemosyne` | +| `--config-dir` | `/etc/mnemosyne` | +| `--data-dir` | `/var/lib/mnemosyne` | + +### 3. Edit the config file + +```bash +sudo nano /etc/mnemosyne/mnemosyne.conf +``` + +Fields to fill in: + +```ini +[bot] +token = YOUR_BOT_TOKEN_HERE # from @BotFather +webhook_secret = ... # openssl rand -hex 32 +webhook_url = https://bot.example.com/hook/YOUR_RANDOM_PATH +listen_port = 8765 +allowed_chat_ids = 123456789 # your Telegram chat ID + +[db] +path = /var/lib/mnemosyne/mnemosyne.db + +[app] +timezone = America/New_York # any IANA timezone name +digest_time = 06:30 +``` + +The `webhook_url` path segment (`/hook/YOUR_RANDOM_PATH`) should be a hard-to-guess string — it acts as a second layer of security alongside the `webhook_secret` header. Generate one with `openssl rand -hex 16`. + +### 4. Configure nginx + +Copy the example config and adjust the server name and port: + +```bash +sudo cp /opt/mnemosyne/nginx/mnemosyne.conf.example \ + /etc/nginx/conf.d/mnemosyne.conf +sudo nano /etc/nginx/conf.d/mnemosyne.conf +# Replace mnemosyne.example.com with your subdomain +# Replace 8765 with your listen_port if you changed it +sudo nginx -t && sudo systemctl reload nginx +``` + +### 5. Obtain a TLS certificate + +Telegram requires HTTPS. Use certbot: + +```bash +sudo certbot --nginx -d bot.example.com +``` + +Verify the nginx config still works after certbot modifies it: + +```bash +sudo nginx -t && sudo systemctl reload nginx +``` + +### 6. Start the bot + +```bash +sudo systemctl start mnemosyne-bot +sudo systemctl status mnemosyne-bot +sudo journalctl -u mnemosyne-bot -f +``` + +### 7. Register the Telegram webhook + +This tells Telegram where to send updates. Run it once, and again any time the `webhook_url` changes: + +```bash +sudo -u mnemosyne /opt/mnemosyne/bin/mnemosyne-webhook \ + --config /etc/mnemosyne/mnemosyne.conf +``` + +To remove the webhook (e.g. before switching servers): + +```bash +sudo -u mnemosyne /opt/mnemosyne/bin/mnemosyne-webhook \ + --config /etc/mnemosyne/mnemosyne.conf --delete +``` + +### 8. Verify + +Send `/today` to your bot. You should receive a digest (or an "all clear" message if no tasks are due). + +--- + +## AlmaLinux / RHEL-specific notes + +**SELinux** — if nginx cannot connect to the local bot port, allow it: + +```bash +sudo setsebool -P httpd_can_network_connect 1 +``` + +**Firewall** — the bot port (`8765` by default) does not need to be open to the internet; nginx proxies to it locally. Only ports 80 and 443 need to be reachable. + +**Perl modules** — some CPAN modules require `perl-devel` and `gcc` for XS compilation. The install script handles this, but if you install manually: + +```bash +sudo dnf install -y perl-devel gcc make +sudo cpanm --notest --installdeps . +``` + +--- + +## Backup and restore + +The install script sets up a daily backup cron at 02:15. You can also run it manually: + +```bash +sudo -u mnemosyne /opt/mnemosyne/scripts/backup.sh \ + --config /etc/mnemosyne/mnemosyne.conf +``` + +Backups land in `/var/backups/mnemosyne/` and are retained for 30 days by default. Override with `--keep-days N`. + +The backup uses SQLite's Online Backup API, so it's safe to run while the bot is live — no downtime required. + +**Restore:** + +```bash +sudo systemctl stop mnemosyne-bot +sudo cp /var/backups/mnemosyne/mnemosyne-YYYYMMDD-HHMMSS.db \ + /var/lib/mnemosyne/mnemosyne.db +sudo chown mnemosyne:mnemosyne /var/lib/mnemosyne/mnemosyne.db +sudo systemctl start mnemosyne-bot +``` + +--- + +## Updating + +```bash +cd /path/to/mnemosyne-repo +git pull +sudo bash scripts/install.sh # re-runs safely; does not touch config or database +sudo systemctl restart mnemosyne-bot +``` + +--- + +## Repository layout + +``` +mnemosyne/ +├── bin/ +│ ├── mnemosyne-bot # webhook receiver (Mojolicious app, run by systemd) +│ ├── mnemosyne-digest # morning digest sender (run by cron) +│ └── mnemosyne-webhook # one-shot webhook registration helper +├── lib/Mnemosyne/ +│ ├── Config.pm # INI config loader +│ ├── DB.pm # SQLite connection, schema, migration +│ ├── Digest.pm # Day at a Glance builder and renderer +│ ├── Schedule.pm # per-class due/overdue/upcoming resolver +│ ├── Task.pm # task CRUD + completions +│ ├── Telegram.pm # Bot API client and keyboard factories +│ └── Webhook.pm # update routing, command and callback handlers +├── config/ +│ └── mnemosyne.conf.example +├── nginx/ +│ └── mnemosyne.conf.example +├── scripts/ +│ ├── backup.sh +│ ├── install.sh +│ └── print-digest # debug: render a digest to the terminal +├── share/ +│ └── schema.sql +├── systemd/ +│ └── mnemosyne-bot.service +└── t/ # test suite +``` + +--- + +## License + +MIT