Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
801a752
feat: add bio-bait spam detection with profile bio scanning
rezhajulio May 1, 2026
b764bed
chore: sanitize spam example references
rezhajulio May 1, 2026
7097a1e
fix: narrow promo hints and word-boundary matching for bio link detec…
rezhajulio May 14, 2026
811c4ce
feat: add bio-bait monitor-only mode with owner alerts and metrics
rezhajulio May 14, 2026
f36a3a0
fix: bio-bait review - trusted bypass, monitor-only alert semantics, …
rezhajulio May 18, 2026
6d67a41
Fix bio-bait routing: remove TEXT|CAPTION filter restriction
rezhajulio May 18, 2026
8e3bb01
fix: bio-bait review - cache eviction, f-string logging, shared white…
rezhajulio May 18, 2026
2260aed
docs: add built-in plugin loader design spec
rezhajulio May 22, 2026
60dcd58
chore: ignore local worktrees directory
rezhajulio May 22, 2026
2a6ab58
feat(config): add strict plugin toggle validation
rezhajulio May 22, 2026
77ba0e6
fix(config): apply plugins_default in single-group fallback
rezhajulio May 22, 2026
707857f
feat(plugins): add plugin contracts and toggle resolver
rezhajulio May 22, 2026
a633ac9
fix(plugins): align manifest types and export definitions
rezhajulio May 22, 2026
31eb3e3
feat(plugins): add built-in wrappers with fixed registration order
rezhajulio May 22, 2026
f0d2b99
feat(main): register handlers and jobs via plugin manager
rezhajulio May 22, 2026
90cfb1b
feat(plugins): enforce per-group plugin enable map at runtime
rezhajulio May 22, 2026
9124755
fix(plugins): apply runtime gating to group-scoped handlers
rezhajulio May 22, 2026
58f704e
docs(config): add plugin toggle examples for env and groups
rezhajulio May 22, 2026
84b003f
refactor(plugins): unify plugin name registry to definitions.py
rezhajulio May 22, 2026
b57ba2d
fix: resolve DeepSeek V4 Pro review issues for plugin system
rezhajulio May 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,29 @@ DUPLICATE_SPAM_MIN_LENGTH=20
# 0.95 catches minor edits, 0.97 only near-exact copies, 0.90 is more aggressive
DUPLICATE_SPAM_SIMILARITY=0.95

# Enable/disable bio bait detection (true/false)
BIO_BAIT_ENABLED=true

# Monitor-only mode for bio bait detection (true/false)
# When true: no delete/restrict/warning-topic notification, only metrics + owner alert
BIO_BAIT_MONITOR_ONLY=false

# Owner/admin chat ID to receive bio bait monitor alerts (optional)
# Example: 57747812
# BIO_BAIT_ALERT_CHAT_ID=57747812

# Path to groups.json for multi-group support (optional)
# If this file exists, per-group settings are loaded from it instead of the
# GROUP_ID/WARNING_TOPIC_ID/etc. fields above. See groups.json.example.
# GROUPS_CONFIG_PATH=groups.json

# Default plugin enable/disable map for all groups (optional, single-group mode)
# JSON object mapping built-in plugin names to booleans.
# Plugins not listed inherit their built-in default (enabled).
# Keys must match known plugin names (e.g. "captcha", "dm", "verify").
# Example: PLUGINS_DEFAULT={"captcha":true,"dm":false}
# PLUGINS_DEFAULT={"captcha":true,"dm":false}

# Logfire Configuration (optional - for production logging)
# Get your token from https://logfire.pydantic.dev
LOGFIRE_TOKEN=your_logfire_token_here
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ __pycache__/
data/
.vscode
# AGENTS.md
.worktrees/
219 changes: 219 additions & 0 deletions docs/superpowers/specs/2026-05-22-plugin-system-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
# Built-in Plugin Loader Design (Zero-Behavior Migration)

## Context

Current bot wiring in `src/bot/main.py` contains long, tightly-coupled registration flow for commands, callbacks, message handlers, and jobs. This makes feature growth harder and boundaries unclear.

Goal for this iteration is architectural cleanup without runtime behavior changes.

## Goals

- Introduce built-in plugin loader architecture.
- Keep zero behavior change by default when all plugins enabled.
- Support per-group and single-group plugin toggles using existing config sources (`groups.json` + `.env` fallback).
- Validate plugin config strictly (unknown plugin key fails startup).
- Keep handler order, handler groups, callback patterns, job intervals, and allowed updates unchanged.

## Non-Goals

- No third-party/external plugin packages.
- No runtime plugin discovery from filesystem/packages.
- No behavior refactor inside existing handlers.
- No profile monitor splitting (`require_photo` and `require_username`) in this iteration.

## Plugin Granularity

Fine-grained plugin units (one plugin per existing feature unit), including:

- `topic_guard`
- `verify`
- `unverify`
- `check`
- `trust`
- `untrust`
- `trusted_list`
- `check_forwarded_message`
- `verify_callback`
- `unverify_callback`
- `warn_callback`
- `trust_callback`
- `untrust_callback`
- `captcha`
- `dm`
- `inline_keyboard_spam`
- `bio_bait_spam`
- `contact_spam`
- `new_user_spam`
- `duplicate_spam`
- `profile_monitor`
- `auto_restrict_job`
- `refresh_admin_ids_job`

This preserves selective disable use cases such as disabling `profile_monitor` in specific groups while keeping anti-spam protections active.

## Proposed Architecture

### New modules

- `src/bot/plugins/base.py`
- Defines plugin interface/protocol with stable registration contract.

- `src/bot/plugins/definitions.py`
- Static manifest of built-in plugins in deterministic order.
- Order must mirror existing `main.py` registration behavior.

- `src/bot/plugins/config.py`
- Plugin config parsing + validation.
- Resolves effective toggle state per group.

- `src/bot/plugins/manager.py`
- Loads manifest.
- Validates uniqueness and plugin keys.
- Registers plugins into application.

- `src/bot/plugins/builtin/*.py`
- Thin wrappers around current registration logic.
- Each wrapper owns only registration binding and optional enabled-gating.

### Existing modules updated

- `src/bot/main.py`
- Replace direct handler/job registration wall with plugin manager call.
- Keep startup/init/logging/error-handling/post-init behavior intact.

- `src/bot/group_config.py`
- Add per-group plugin override field.
- Validate override key/value types.

- `src/bot/config.py`
- Add single-group plugin default override support via env.

## Configuration Model

Use existing source model (Option 2):

- Multi-group from `groups.json`
- Single-group fallback from `.env`

Add default-plus-override semantics similar to dedicated `plugins.json` pattern:

1. Start with default enabled `true` for all plugins.
2. Apply `.env` global defaults (single-group fallback path).
3. Apply per-group `groups.json` overrides.
4. Validate all keys against manifest; unknown key fails startup.

### `groups.json` extension

Each group object can include:

```json
{
"plugins": {
"profile_monitor": false,
"captcha": true,
"duplicate_spam": true
}
}
```

### `.env` extension (single-group fallback)

Use JSON object string:

```env
PLUGINS_DEFAULT={"profile_monitor": false, "captcha": true}
```

If not present, all plugins remain enabled by default.

## Registration and Runtime Flow

1. `main.py` initializes settings, group registry, database (unchanged).
2. `PluginManager` loads static manifest and validates duplicate names.
3. `PluginConfigResolver` computes effective per-group enabled matrix.
4. Plugins register in fixed order.
5. Runtime checks apply group-specific enablement where applicable.

### Zero-behavior guarantees

- Handler order preserved exactly.
- Handler group numbers preserved exactly.
- Callback regex patterns preserved exactly.
- Job intervals and first-run delays preserved exactly.
- `allowed_updates` list preserved exactly.
- Existing feature logic untouched except for plugin-enabled guard checks.

## Error Handling

Fail-fast startup errors:

- Unknown plugin key in `.env`/`groups.json`.
- Non-boolean plugin toggle values.
- Duplicate plugin names in manifest.

Safe defaults:

- Missing key means enabled (`true`).

Runtime behavior:

- Disabled plugin returns immediately for affected group context.
- Existing non-monitored group checks remain unchanged.

## Testing Strategy (TDD)

### New test files

- `tests/test_plugin_config.py`
- unknown plugin key -> startup failure
- non-bool value -> startup failure
- missing keys -> default true
- merge semantics (`.env` defaults + `groups.json` override)

- `tests/test_plugin_manager.py`
- deterministic manifest order
- duplicate plugin name failure
- plugin registration behavior with toggles

- `tests/test_main_plugins_bootstrap.py`
- `main.py` delegates registration to plugin manager
- all-enabled baseline registration parity checks

### Existing tests impact

- Minimal updates where wrapper-level gating changes execution path.
- No expected user-visible behavior change.

### Verification commands

```bash
uv run pytest tests/test_plugin_config.py tests/test_plugin_manager.py tests/test_main_plugins_bootstrap.py
uv run pytest
uv run ruff check .
```

## Rollout Plan

1. Add plugin interface + manifest + manager.
2. Move current registration wiring into plugin wrappers preserving order.
3. Add config parsing/validation for toggles.
4. Integrate manager in `main.py`.
5. Add/adjust tests and verify parity.

## Risks and Mitigations

- **Risk:** accidental registration order drift.
- **Mitigation:** explicit static manifest order tests.

- **Risk:** command/callback plugins difficult to scope per group.
- **Mitigation:** keep existing command semantics; only apply group toggles where group context is resolvable and meaningful.

- **Risk:** config fragility from JSON string in `.env`.
- **Mitigation:** strict validation + clear startup error messages.

## Success Criteria

- Plugin system exists with fine-grained built-ins.
- Per-group/plugin toggles functional via `groups.json` and `.env` fallback model.
- Unknown plugin keys fail startup.
- All tests pass and bot behavior remains unchanged when all toggles enabled.
22 changes: 19 additions & 3 deletions groups.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,15 @@
"duplicate_spam_window_seconds": 120,
"duplicate_spam_threshold": 2,
"duplicate_spam_min_length": 20,
"duplicate_spam_similarity": 0.95
"duplicate_spam_similarity": 0.95,
"bio_bait_enabled": true,
"bio_bait_monitor_only": false,
"bio_bait_alert_chat_id": null,
"plugins": {
"captcha": false,
"dm": true,
"verify": true
}
},
{
"group_id": -1009876543210,
Expand All @@ -33,6 +41,14 @@
"duplicate_spam_window_seconds": 60,
"duplicate_spam_threshold": 2,
"duplicate_spam_min_length": 20,
"duplicate_spam_similarity": 0.90
"duplicate_spam_similarity": 0.90,
"bio_bait_enabled": true,
"bio_bait_monitor_only": false,
"bio_bait_alert_chat_id": null,
"plugins": {
"contact_spam": false,
"duplicate_spam": false,
"profile_monitor": true
}
}
]
]
Loading
Loading