# 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. Admins can add/edit/delete bounties. Anyone can track. - **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 as `NULL`. - **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 via `adapters/storage/json_file.py` - **Date parsing**: `dateparser` - **Runtime**: Python 3.10+ - **Deployment**: Any $5 VPS with Python 3.10+ ### Directory Structure ``` jigaido/ ├── core/ # Domain layer │ ├── models.py # Domain dataclasses (Bounty, Tracking) │ ├── ports.py # Port interfaces (abstract base classes) │ └── services.py # Domain services (BountyService, TrackingService) ├── adapters/ # Infrastructure adapters │ └── storage/ │ └── json_file.py # JSON file storage implementation ├── apps/ │ └── telegram-bot/ # Telegram bot CLI application │ ├── bot.py # Bot entry point │ └── commands.py # Command handlers ├── config.py # Configuration management └── tests/ # Unit tests ``` ### Hexagonal Architecture - **Core** (`core/`): Pure domain logic, no external dependencies - `models.py`: Domain dataclasses - `ports.py`: Abstract interfaces for storage - `services.py`: Business logic - **Adapters** (`adapters/`): Implementations of ports - `storage/json_file.py`: JSON file-based storage - **Apps** (`apps/`): CLI applications - `telegram-bot/`: Telegram bot interface ### Data Storage Data is stored at `~/.jigaido/` (home directory), NOT inside the repository. **File: `~/.jigaido/{group_id}/group.json`** ```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: `~/.jigaido/{group_id}/{user_id}.json`** ```json { "tracked": [ {"bounty_id": 1, "created_at": 1735600000} ] } ``` **File: `~/.jigaido/{user_id}/user.json`** ```json { "bounties": [ { "id": 1, "text": "Personal task", "link": null, "due_date_ts": 1735689600, "created_at": 1735603200 } ], "next_id": 2 } ``` **Key design decisions:** 1. **Hexagonal architecture** — Core domain is isolated from infrastructure 2. **Group-isolated storage** — Each group has its own directory. No cross-group access. 3. **Bounty IDs are sequential per group.json** — Not global. Each group's file has its own ID counter. 4. **Atomic writes** — Uses `tempfile` + `rename` for safe writes. --- ## 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 [link] [due date]` | admin only | Add a new bounty to the group | | `/edit [text] [link] [due_date]` | admin only | Edit an existing bounty | | `/delete ` | admin only | Delete a bounty | | `/track ` | anyone | Track a group bounty | | `/untrack ` | 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 [link] [due date]` | Add a personal bounty | | `/edit [text] [link] [due_date]` | Edit a personal bounty | | `/delete ` | Delete a personal bounty | | `/track ` | 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 ``` - `link` is optional - `due_date` is 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 - `/add`/`/edit`/`/delete` by non-admin → "⛔ Only admins can add/edit/delete bounties." - `/track` already tracked → "Already tracking" (idempotent) - `/untrack` not 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