Files
cg_api_secure-webshare/agent2_telegram_bot.md

216 lines
7.4 KiB
Markdown

# Agent 2: Telegram Bot Fixes (Tasks B, C, E)
## Summary
Fixed three issues in `crates/cgcx-bot/src/main.rs`:
- **Task B**: Enabled admin commands (including `/get_id`) in channels.
- **Task C**: Escaped angle-bracket placeholders in `/help` text to prevent Telegram HTML parse errors.
- **Task E**: Restricted `/blacklist_uid` and `/whitelist_uid` to admin groups at the outer dispatch level and removed redundant inner checks.
---
## Task B — /get_id in channels
**Problem:** Admin command dispatch was gated behind `msg.chat.is_group() || msg.chat.is_supergroup()`, so channels were excluded.
**Change:** Added `|| msg.chat.is_channel()` to the dispatch guard.
**oldText:**
```rust
// Admin commands in groups
if msg.chat.is_group() || msg.chat.is_supergroup() {
```
**newText:**
```rust
// Admin commands in groups
if msg.chat.is_group() || msg.chat.is_supergroup() || msg.chat.is_channel() {
```
---
## Task C — /help HTML parse errors
**Problem:** The `help_text` raw string contained unescaped placeholders like `<ID>`, `<@username>`, `<displayname>`, `<user_id>`, `<dur>`, `<unit>`. Telegram HTML parse mode rejects unsupported tags.
**Change:** Replaced every `<arg>` placeholder with `[arg]` (e.g., `<ID>``[ID]`). Existing `&lt;dest&gt;` / `&lt;review&gt;` were left untouched because they were already properly escaped.
**oldText:**
```rust
let help_text = r#"<b>Admin Commands</b>
/reload — Reload moderation lists.
/blacklist_uid <ID> — Blacklist a user ID.
/whitelist_uid <ID> — Remove a user from blacklist.
/help — Show this message.
/get_id — Get current chat ID.
/get_id <@username> — Search administrators by username.
/get_id <displayname> — Search members in this chat by display name.
/create_submit_forward &lt;dest&gt; &lt;review&gt; [msg] — Create a submission forward.
/show_c_forward [page] — List forward links.
/add_blacklist <user_id> — Blacklist a user in all active forwards.
/rm_blacklist <user_id> — Remove a user from blacklist in all active forwards.
/sban @user <dur> <unit> [reason] — Ban for duration
/smute @user <dur> <unit> [reason] — Mute for duration
/mute @user [reason] — Mute indefinitely
/pban @user [reason] — Permanent ban
/kick @user [reason] — Kick from group
/rmute @user — Revoke mute
/rban @user — Revoke ban"#;
```
**newText:**
```rust
let help_text = r#"<b>Admin Commands</b>
/reload — Reload moderation lists.
/blacklist_uid [ID] — Blacklist a user ID.
/whitelist_uid [ID] — Remove a user from blacklist.
/help — Show this message.
/get_id — Get current chat ID.
/get_id [@username] — Search administrators by username.
/get_id [displayname] — Search members in this chat by display name.
/create_submit_forward &lt;dest&gt; &lt;review&gt; [msg] — Create a submission forward.
/show_c_forward [page] — List forward links.
/add_blacklist [user_id] — Blacklist a user in all active forwards.
/rm_blacklist [user_id] — Remove a user from blacklist in all active forwards.
/sban @user [dur] [unit] [reason] — Ban for duration
/smute @user [dur] [unit] [reason] — Mute for duration
/mute @user [reason] — Mute indefinitely
/pban @user [reason] — Permanent ban
/kick @user [reason] — Kick from group
/rmute @user — Revoke mute
/rban @user — Revoke ban"#;
```
---
## Task E — /blacklist_uid and /whitelist_uid behavior
**Problem:** The outer dispatch only checked `is_admin()`, then the inner handler checked `admin_group_ids`. This leaked the command's existence to non-admin groups.
**Changes:**
1. At the outer command dispatch, both commands now require:
- `ctx.config.groups.admin_group_ids.contains(&chat_id.0)`
- `is_admin(&bot, msg.chat.id, user.id).await`
2. Removed the redundant `admin_group_ids` checks from inside `handle_admin_blacklist_uid` and `handle_admin_whitelist_uid`.
3. Missing-parameter usage replies remain intact in the handlers.
### Outer dispatch
**oldText:**
```rust
"/blacklist_uid" => {
tracing::info!("admin command /blacklist_uid chat={} user={}", chat_id, user_id);
if is_admin(&bot, msg.chat.id, user.id).await {
handle_admin_blacklist_uid(&bot, chat_id, text, &ctx).await?;
}
return Ok(());
}
"/whitelist_uid" => {
tracing::info!("admin command /whitelist_uid chat={} user={}", chat_id, user_id);
if is_admin(&bot, msg.chat.id, user.id).await {
handle_admin_whitelist_uid(&bot, chat_id, text, &ctx).await?;
}
return Ok(());
}
```
**newText:**
```rust
"/blacklist_uid" => {
tracing::info!("admin command /blacklist_uid chat={} user={}", chat_id, user_id);
if ctx.config.groups.admin_group_ids.contains(&chat_id.0) && is_admin(&bot, msg.chat.id, user.id).await {
handle_admin_blacklist_uid(&bot, chat_id, text, &ctx).await?;
}
return Ok(());
}
"/whitelist_uid" => {
tracing::info!("admin command /whitelist_uid chat={} user={}", chat_id, user_id);
if ctx.config.groups.admin_group_ids.contains(&chat_id.0) && is_admin(&bot, msg.chat.id, user.id).await {
handle_admin_whitelist_uid(&bot, chat_id, text, &ctx).await?;
}
return Ok(());
}
```
### Inner handler `handle_admin_blacklist_uid`
**oldText:**
```rust
async fn handle_admin_blacklist_uid(
bot: &Bot,
chat_id: ChatId,
text: &str,
ctx: &BotContext,
) -> HandlerResult {
if !ctx.config.groups.admin_group_ids.contains(&chat_id.0) {
bot.send_message(chat_id, "This command is only available in the admin group.")
.await?;
return Ok(());
}
let uid = text.split_whitespace().nth(1).and_then(|s| s.parse::<i64>().ok());
```
**newText:**
```rust
async fn handle_admin_blacklist_uid(
bot: &Bot,
chat_id: ChatId,
text: &str,
ctx: &BotContext,
) -> HandlerResult {
let uid = text.split_whitespace().nth(1).and_then(|s| s.parse::<i64>().ok());
```
### Inner handler `handle_admin_whitelist_uid`
**oldText:**
```rust
async fn handle_admin_whitelist_uid(
bot: &Bot,
chat_id: ChatId,
text: &str,
ctx: &BotContext,
) -> HandlerResult {
if !ctx.config.groups.admin_group_ids.contains(&chat_id.0) {
bot.send_message(chat_id, "This command is only available in the admin group.")
.await?;
return Ok(());
}
let uid = text.split_whitespace().nth(1).and_then(|s| s.parse::<i64>().ok());
```
**newText:**
```rust
async fn handle_admin_whitelist_uid(
bot: &Bot,
chat_id: ChatId,
text: &str,
ctx: &BotContext,
) -> HandlerResult {
let uid = text.split_whitespace().nth(1).and_then(|s| s.parse::<i64>().ok());
```
---
## Validation
```
$ cargo check -p cgcx-bot
Finished `dev` profile [unoptimized + debuginfo] target(s) in 45.10s
```
**Result:** `cargo check -p cgcx-bot` passed with no errors or warnings introduced by these changes.
---
## Files Changed
- `crates/cgcx-bot/src/main.rs`
- `progress.md` (updated task list)
## Open risks/questions
- None identified for these three tasks.
## Recommended next step
- Continue with remaining plan tasks (if any) or run `cargo test -p cgcx-bot` if tests exist.