Files
jigaido/SPEC.md
shokollm 8e589fbc47 Implement new storage design per issue #2
- 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
2026-04-02 13:48:52 +00:00

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 as NULL.
  • 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:

  1. Data at ~/.jigaido/ — Not inside the app directory, so data persists across app updates.
  2. Per-group bounties — All group bounties stored in group.json, not scattered across creator files.
  3. Per-user tracking — Each user's tracking within a group stored in their own user_{id}.json.
  4. Bounty IDs are sequential per group — Not global. Each group.json has its own ID counter.
  5. Personal bounties isolated — Stored in user_{id}/user.json separate from group bounties.
  6. Atomic writes — Uses tempfile + rename for safe writes.
  7. 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
  • link is optional
  • due_date is 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//delete by non-creator → " Only the creator can edit/delete this bounty."
  • /track already tracked → "Already tracking" (idempotent)
  • /untrack not 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