273 lines
8.4 KiB
Markdown
273 lines
8.4 KiB
Markdown
# Mnemosyne
|
|
|
|
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 <id\|title>` | Mark a task complete |
|
|
| `/edit <id> <field> <value>` | Update a single field |
|
|
| `/disable <id\|title>` | Hide a task without deleting it |
|
|
| `/delete <id\|title>` | 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
|