Skip to content

feat: core UX and safety package - #24

Merged
rezhajulio merged 5 commits into
mainfrom
feat/core-ux-safety-package
Jul 31, 2026
Merged

feat: core UX and safety package#24
rezhajulio merged 5 commits into
mainfrom
feat/core-ux-safety-package

Conversation

@rezhajulio

Copy link
Copy Markdown
Owner

Core UX and Safety Package

PTB 22.8 upgrade & admin cache

  • Upgrade python-telegram-bot to >=22.8
  • Exclude bot accounts from admin ID caches (client-side is_bot filter)

Group-scoped admin authorization

  • /check finds only groups where the caller is an admin, shows a group selector for multi-group admins
  • All action callbacks encode group_id (e.g. warn:{group_id}:{user_id}:{missing_code}) and revalidate the caller per-group
  • New helpers: is_user_admin_in_group(), get_admin_groups()
  • Plugin manifest expanded from 23 to 25 entries (check_group_callback, unrestrict_callback)

Trust / photo exemption / bot-owned unrestriction separation

  • Trust = anti-spam bypass + probation cleanup only (no auto-unrestrict)
  • Photo verification whitelist is a distinct exemption
  • Separate "Buka pembatasan bot" action only lifts bot-applied restrictions, never manual admin restrictions

Recoverable/idempotent captcha completion

  • Captcha callback validates callback message/chat IDs match the persisted challenge
  • Telegram unrestrict_user runs before DB finalization — transient failures preserve the pending captcha so the user can retry
  • remove_pending_captcha returning False provides idempotent duplicate-callback protection

Contextual DM recovery with group deep links

  • Restriction/captcha timeout links use https://t.me/<bot>?start=verify_<group_id>
  • DM handler parses /start verify_<group_id> and uses the target group's rules_link

Stale enforcement safety

  • get_active_user_warning() non-creating lookup clears stale warnings when profile becomes compliant
  • Scheduler rechecks profile immediately before auto-restriction; skips if now complete
  • Warning-only copy no longer falsely claims restriction

Group-scoped /status

  • Shows only groups the caller administers
  • Per-group enforcement mode, probation/captcha counts, disabled plugins
  • Indonesian labels throughout

Audit fixes

  • Removed redundant api_kwargs={"return_bots": False} (Telegram already excludes other bots by default)
  • Fixed scheduler test RuntimeWarning from unawaited AsyncMock coroutine
  • 11 new tests: spoofed message/chat ID rejection, retry after transient failure, deep-link payload edge cases

Validation

  • 992 tests passing
  • ruff: clean
  • mypy: clean (40 source files)

- Bump python-telegram-bot dependency from >=22.5 to >=22.8
- Pass api_kwargs={'return_bots': False} to get_chat_administrators
- Also filter admin.user.is_bot client-side as a safety net
- Update tests to set is_bot=False on mock admins and verify bot exclusion
- Add test_fetch_admins_excludes_bots test case
Major changes:

1. Group-scoped admin authorization:
  - Add is_user_admin_in_group() and get_admin_groups() helpers
  - /check now shows group selector when admin is admin in multiple groups
  - All callback data encodes group_id: action:{group_id}:{user_id}
  - Callbacks verify caller is admin of the specific group, not global union
  - Add handle_check_group_callback for group selection flow
  - Warn callback sends to one group only, not all groups

2. Separate Trust, photo exemption, and unrestriction:
  - Trust no longer auto-unrestricts (removes bot param from trust_user)
  - Trust only clears probation, does not lift restrictions
  - Add separate 'Buka pembatasan bot' (unrestrict) action
  - unrestrict_user_in_group only lifts bot-applied restrictions
  - Verify (photo exemption) is group-scoped, clears warnings in one group
  - Rename buttons to Indonesian: 'Izinkan foto tersembunyi',
    'Kecualikan dari anti-spam', 'Buka pembatasan bot'

3. New plugins: check_group_callback, unrestrict_callback
4. Updated all tests for group-scoped callback data and authorization
1. Captcha callback validation (Item 4):
  - Validate callback message/chat IDs against persisted challenge
  - Prevents stale or spoofed callbacks from acting on wrong challenge

2. Contextual DM recovery with deep links (Item 5):
  - Add /start verify_<group_id> deep link parsing in DM handler
  - Captcha timeout and restriction messages now include group-specific
    deep links instead of generic bot URL
  - DM handler uses target group's rules_link instead of global settings
  - Pending captcha DM message lists specific groups

3. Stale enforcement fix + accurate copy (Item 6):
  - Clear active profile warnings when compliant profile is observed
  - Add get_active_user_warning() DB method (non-creating lookup)
  - Recheck profile before scheduler restriction, skip if now complete
  - Remove false 'akan dibatasi' threat from warning-only mode
  - Warning-only mode no longer references threshold_display

Updated all affected tests for new behavior.
- /status now shows only groups where the caller is admin
- Per-group enforcement mode (Restriksi/Peringatan)
- Per-group probation and captcha counts
- Per-group disabled plugins list
- All labels translated to Indonesian (Grup, Jadwal terakhir, etc.)
- Updated tests for new scoped behavior and Indonesian text
… test warning

- Swap captcha callback ordering: Telegram unrestrict now runs BEFORE DB
  finalization, so a transient unrestrict failure preserves the pending
  captcha and the user can retry by pressing the button again
- Remove redundant api_kwargs={'return_bots': False} from
  get_chat_administrators — Telegram already excludes other bots by
  default; client-side is_bot filter remains as safety net
- Fix RuntimeWarning in scheduler test by adding proper bot.get_chat_member
  mock (was leaving an AsyncMock coroutine unawaited)
- Update AGENTS.md Code Map with current line counts and roles
- Update AGENTS.md captcha DB ordering documentation to match new flow
- Add tests: spoofed message/chat ID rejection, successful retry after
  transient failure, deep-link payload parsing edge cases
@rezhajulio
rezhajulio merged commit 6545438 into main Jul 31, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant