"""Domain dataclasses for JIGAIDO bounty tracker.""" from dataclasses import dataclass, field @dataclass class Category: """A category for organizing bounties in a room. Categories are per-room and support soft delete. The id (slug) must be lowercase alphabetic only (e.g., "bug", "feature"). """ id: str # slug: lowercase alphabetic only, e.g., "bug", "feature" name: str # display name: e.g., "Bug", "Feature" created_at: int deleted_at: int | None = None # soft delete @dataclass class Bounty: """A bounty created by a user. The created_by_user_id field always refers to the user who created the bounty. It does NOT indicate whether the bounty is a group or personal bounty. The deleted_at field indicates soft-delete: None means not deleted, a value means deleted at that Unix timestamp. The category_ids field lists category slugs associated with this bounty. """ id: int text: str | None link: str | None due_date_ts: int | None created_at: int created_by_user_id: int deleted_at: int | None = None created_by_username: str | None = None category_ids: list[str] = field(default_factory=list) @dataclass class TrackedBounty: """A bounty that a user is tracking. Lightweight relation/pointer - the actual tracking context (including room) lives in TrackingData, not here. """ bounty_id: int created_at: int @dataclass class RoomData: """All data for a room (group or DM). The room_id can be negative for Telegram groups or positive for DMs. The next_id field is used to generate unique bounty IDs within this room. The timezone field stores the room's timezone (e.g., "Asia/Jakarta"), default UTC+0. The admin_usernames field lists usernames who have admin privileges in this room. The categories field contains all categories for organizing bounties in this room. """ room_id: int bounties: list[Bounty] next_id: int timezone: str | None = None admin_usernames: list[str] | None = None categories: list[Category] = field(default_factory=list) def __post_init__(self): if self.admin_usernames is None: self.admin_usernames = [] if self.categories is None: self.categories = [] @dataclass class TrackingData: """User tracking state within a room (group or DM). TrackingData vs TrackedBounty: - Use TrackingData to store ALL tracked bounties for a user in a specific room. It contains the room_id, user_id, and a list of TrackedBounty entries. - Use TrackedBounty to represent a single tracked bounty entry within that list. TrackingData is the container, TrackedBounty is the item. """ room_id: int user_id: int tracked: list[TrackedBounty]