Files
cg_api_secure-webshare/agent2_telegram_bot.md

7.4 KiB

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:

    // Admin commands in groups
    if msg.chat.is_group() || msg.chat.is_supergroup() {

newText:

    // 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:

                        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:

                        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:

                "/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:

                "/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:

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:

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:

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:

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.
  • Continue with remaining plan tasks (if any) or run cargo test -p cgcx-bot if tests exist.