Files
jigaido/SPEC.md
shokollm 7c2bd09ada feat: implement new storage design per issue #2
- 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
2026-04-02 14:56:42 +00:00

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 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 (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:

  1. Group-isolated storage — Each group has its own directory. No cross-group access.
  2. Bounty IDs are sequential per group.json — Not global. Each group's file has its own ID counter.
  3. Atomic writes — Uses tempfile + rename for safe writes.
  4. 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
  • 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
  • /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"

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