- Add same sort_key logic as /bounty (due date first, then created_at)
- Add default limit of 5 with "Showing X of Y" message
- Add "all" flag to show expired bounties
- Add delete button for consistency with /bounty
Fixes#94
Models & Storage layer for category feature (Issue #85):
- Add Category dataclass to core/models.py
- id (slug): lowercase alphabetic only
- name: display name
- created_at: Unix timestamp
- deleted_at: soft delete timestamp (None if active)
- Add category_ids field to Bounty dataclass
- list[str] for multiple categories per bounty
- Default empty list for backward compatibility
- Add categories field to RoomData dataclass
- list[Category] for room-level categories
- Default empty list
- Update JsonFileRoomStorage to serialize/deserialize:
- Category fields (id, name, created_at, deleted_at)
- Bounty.category_ids
- RoomData.categories
Backward compatible: existing data without categories works fine.
- Add normalize_url() helper function in commands.py
- Automatically prefix URLs without scheme (e.g. github.com → https://github.com)
- Applies to both -link flag and auto-detected URLs
- Add 5 new tests for URL normalization
- Fix existing tests to handle 5-value return from parse_args()
Examples:
/add Fix bug github.com/user/repo
→ stored as: https://github.com/user/repo
- Replace admin_user_ids (list[int]) with admin_usernames (list[str])
- Update all service methods to use username for permission checks
- Add delete button to bot responses for message cleanup
- Update tests to match new implementation
Note: Breaking change - existing data files need fresh start
_find_user_id_by_username now uses ctx.bot.get_chat() to lookup
any user by username, not just bounty creators. This allows
/admin add to work for any user in the group.
The parse_args function now accepts timezone_str parameter and uses it
to localize parsed dates. This ensures dates are interpreted in the
group's timezone, not the server's local timezone.
1. format_bounty now accepts timezone_str parameter
2. Calculate hours/days remaining using group's timezone
3. Format dates using group's timezone (not server local time)
4. Updated cmd_bounty, cmd_my, cmd_show to pass timezone
1. format_bounty now shows time (HH:MM) if time is set
2. Bounty list shows hours remaining if < 48 hours:
- < 1 hour: shows minutes (e.g., 45m)
- < 48 hours: shows hours (e.g., 6h)
- >= 48 hours: shows days (e.g., 3d)
3. Update message now shows time in date display
The parse_args function now:
1. Recognizes time format (HH:MM) after parsing a date
2. Combines date + time into a single timestamp
3. Only text comes before link or date flags
Now /update 6 -date 2026-04-05 12:00 properly sets date+time
- /bounty now shows only ONE delete button (not per-bounty)
- Callback uses query.message.delete() instead of delete_message by ID
- This is more reliable and simpler
Previously, /update 2 -link s would clear the link and then parse
's' as a date. Now, any value after -link is used as the link,
regardless of whether it looks like a URL.
The is_url() function now accepts any string with a dot and no spaces,
not just URLs with http:// or containing /. This allows /update 2 -link google.com
to properly set the link instead of treating google.com as text.
1. Accept any URL-like string as link (not just http/https)
- Now detects URLs by pattern: contains "." and "/" (e.g., github.com/foo)
2. Show old -> new changes in update response
- Now shows exactly what changed for verification
- Helps user catch mistakes immediately
- Add inline keyboard with delete button on bounty list messages
- Only the user who triggered the command can delete the message
- Message is actually removed from the chat
- Uses callback query handler for button clicks
When loading room data with admin_user_ids=null in JSON,
the code now properly initializes admin_user_ids to []
instead of incorrectly creating a new RoomData.
Also removed debug logging added during troubleshooting.
- config.py: Added _resolve_bot_token() to read from config file
- bot.py: Uses config.config.bot_token instead of env var directly
- test_config.py: Added test for config file token reading
- Add /recover command for listing and recovering soft-deleted bounties
- /recover - list recoverable bounties (admin only)
- /recover <id> [<id>...] - recover specific bounties (admin only)
- Fix /admin list to show @username instead of admin_id
- Add recover_bounty and recover_bounties methods to BountyService
- Add get_deleted_bounty method to BountyService
- Clean up duplicate cmd_admin functions
- Add /recover to bot command menu
- Fixes#49 and #50