- Replace SQLite db module with file-based JSON storage in ~/.jigaido/
- Group bounties: ~/.jigaido/{group_id}/group.json
- User tracking: ~/.jigaido/{group_id}/user_{id}.json
- Personal bounties: ~/.jigaido/user_{id}/user.json
- Anyone can add bounties to groups; only creator can edit/delete
- Bounty IDs are sequential per group, not global
- Fix test mock compatibility issues in format_bounty function
5.4 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 bounty creator can edit/delete.
- DM mode: Personal bounty list. Creator can manage their own bounties.
- Tracking: Users can track group bounties 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.
- Created by: Every bounty stores the Telegram username of who created 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 directory (home of user running bot)
├── {group_id}/
│ ├── group.json # Group bounties
│ └── user_{user_id}.json # User tracking within this group
└── user_{user_id}/
└── user.json # User's personal bounties (DM mode)
Storage Design
File structure (~/.jigaido/{group_id}/group.json):
{
"group_id": -100123456789,
"bounties": [
{
"id": 1,
"text": "Fix login bug",
"link": "https://github.com/example/repo/issues/1",
"due_date_ts": 1735689600,
"created_by_user_id": 123456,
"created_by_username": "alice",
"created_at": 1735603200
}
]
}
File structure (~/.jigaido/{group_id}/user_{user_id}.json):
{
"user_id": 123456,
"tracked": [
{"bounty_id": 1, "tracked_at": 1735600000}
]
}
File structure (~/.jigaido/user_{user_id}/user.json):
{
"user_id": 123456,
"username": "alice",
"bounties": [
{
"id": 1,
"text": "Personal task",
"link": "https://example.com/task",
"due_date_ts": null,
"created_by_user_id": 123456,
"created_by_username": "alice",
"created_at": 1735603200
}
]
}
Key design decisions:
- Data at ~/.jigaido/ — Not inside the app directory, so data persists across app updates.
- Per-group bounties — All group bounties stored in
group.json, not scattered across creator files. - Per-user tracking — Each user's tracking within a group stored in their own
user_{id}.json. - Bounty IDs are sequential per group — Not global. Each
group.jsonhas its own ID counter. - Personal bounties isolated — Stored in
user_{id}/user.jsonseparate from group bounties. - 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 you're tracking |
/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 | Add a group bounty to your tracking |
/untrack <bounty_id> |
anyone | Remove a bounty from your tracking |
In DM (1:1 with bot)
| Command | Description |
|---|---|
/bounty |
List all your personal bounties |
/my |
List your personal bounties (same as /bounty in DM) |
/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 |
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
Created By
When a user triggers /add, the bot captures message.from_user.username and stores it in created_by_username. This is displayed on bounty listings so the group/DM knows who created the bounty.
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."
- Tracking in DM → "Tracking is only available in groups."
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