- Storage: Change from per-user to per-group JSON files
- Data location: ~/.jigaido/ instead of apps/telegram-bot/data/
- Group bounties: data/{group_id}/group.json
- User tracking: data/{group_id}/{user_id}.json
- Personal bounties: data/{user_id}/user.json
- Update commands.py for new storage model
- Update bot.py to remove admin handlers
- Update tests to reflect created_by_user_id field
- Update SPEC.md with new design
Addresses user feedback from issue #2
4.7 KiB
JIGAIDO — Specification
Named after Nanami Kento's Cursed Technique restriction. Suppresses power during normal hours, exerts it during overtime. A bounty tracker that keeps you honest when the clock runs long.
Overview
JIGAIDO is a Telegram bot that lets groups and individuals track bounties — tasks, obligations, and deadlines — with optional due dates and personal tracking.
- Group mode: Each Telegram group has its own bounty list. Anyone can add bounties. Only creator can edit/delete.
- DM mode: Personal bounty list. Anyone can manage their own bounties.
- Tracking: Users can track any bounty (group or personal) to their tracking list.
- Due dates: Free-form text (
"april 15","in 3 days","tomorrow") parsed at add time, stored as Unix timestamp. If unparseable, stored asNULL. - Links: Optional. If provided, stored with the bounty.
- Informed by: Every bounty stores the user ID of who posted/added it.
Architecture
Stack
- Bot:
python-telegram-bot(pure Python, no C extensions) - Storage: Per-group JSON files (zero-setup, no DB server)
- Date parsing:
dateparser - Runtime: Python 3.10+
- Deployment: Any $5 VPS with Python 3.10+
Directory Structure
~/.jigaido/ # Data root (~/.jigaido/)
├── {group_id}/
│ ├── group.json # Group bounties
│ └── {user_id}.json # User tracking within this group
└── {user_id}/
└── user.json # User's personal bounties (DM mode)
Note: Data directory is at ~/.jigaido/ (home directory), NOT inside the repository or app directory.
Storage Design
File: data/{group_id}/group.json
{
"bounties": [
{
"id": 1,
"created_by_user_id": 123456,
"text": "Fix login bug",
"link": "https://github.com/example/repo/issues/1",
"due_date_ts": 1735689600,
"created_at": 1735603200
}
],
"next_id": 2
}
File: data/{group_id}/{user_id}.json
{
"tracked": [
{"bounty_id": 1, "created_at": 1735600000}
]
}
File: data/{user_id}/user.json
{
"bounties": [
{
"id": 1,
"text": "Personal task",
"link": null,
"due_date_ts": 1735689600,
"created_at": 1735603200
}
],
"next_id": 2
}
Key design decisions:
- Group-isolated storage — Each group has its own directory. No cross-group access.
- Bounty IDs are sequential per group.json — Not global. Each group's file has its own ID counter.
- Atomic writes — Uses
tempfile+renamefor safe writes. - No reminders in v1 — Dropped for simplicity.
Commands
In Group
| Command | Who | Description |
|---|---|---|
/bounty |
anyone | List all bounties in this group |
/my |
anyone | List bounties tracked by you in this group |
/add <text> [link] [due date] |
anyone | Add a new bounty to the group |
/update <bounty_id> [text] [link] [due_date] |
creator only | Update an existing bounty |
/delete <bounty_id> |
creator only | Delete a bounty |
/track <bounty_id> |
anyone | Track a group bounty |
/untrack <bounty_id> |
anyone | Stop tracking a bounty |
In DM (1:1 with bot)
| Command | Description |
|---|---|
/bounty |
List all your personal bounties |
/my |
List all your personal bounties |
/add <text> [link] [due date] |
Add a personal bounty |
/update <bounty_id> [text] [link] [due_date] |
Update a personal bounty |
/delete <bounty_id> |
Delete a personal bounty |
/track <bounty_id> |
Track a personal bounty |
Add/Update Syntax
/add Some task description https://example.com in 3 days
/add Just a text reminder
/add https://link-only.example.com tomorrow
linkis optionaldue_dateis optional, free-form
Due Date Parsing
Uses dateparser library. Examples:
"april 15","April 15, 2026""in 3 days""tomorrow""2026-04-15""next friday"
If parsing fails → due_date_ts = NULL. No error is shown to user.
Stored as Unix timestamp. User-facing display can be localized/converted to any timezone at render time.
Error Handling
- Unknown command → help text with available commands
/update//deleteby non-creator → "⛔ Only the creator can edit/delete this bounty."/trackalready tracked → "Already tracking" (idempotent)/untracknot tracked → "Not tracking" (idempotent)- Bounty not found → "Bounty not found"
When to Revert to SQLite
- Multiple concurrent users with write conflicts
- Complex queries across users
- Reminder system with proper dedup
- Scale > 1,000 users
- Need ACID guarantees on concurrent writes