Skip to content

feat(telegram): add topic-edit action for editForumTopic API#46407

Closed
Lukavyi wants to merge 2 commits intoopenclaw:mainfrom
Lukavyi:feat-topic-edit
Closed

feat(telegram): add topic-edit action for editForumTopic API#46407
Lukavyi wants to merge 2 commits intoopenclaw:mainfrom
Lukavyi:feat-topic-edit

Conversation

@Lukavyi
Copy link
Copy Markdown
Contributor

@Lukavyi Lukavyi commented Mar 14, 2026

Summary

Add topic-edit action to the Telegram message plugin, enabling agents to rename forum topics and change their icons using the Telegram Bot API's editForumTopic method.

CleanShot 2026-03-14 at 18 12 01

Changes

  • Add editForumTopicTelegram function in extensions/telegram/src/send.ts
  • Add topic-edit action name to message action names
  • Add editForumTopic gate to Telegram action config
  • Wire up action handling in channel-actions and telegram-actions
  • Add tests

Motivation

Closes #7746

Currently OpenClaw supports creating forum topics (topic-create) but not editing them. The Telegram Bot API's editForumTopic method allows changing a topic's name and icon, which is useful for agents managing conversations in forum-enabled chats (including 1:1 DM topics with bots).

Testing

  • Unit tests for the new function and action handler
  • Manual testing with Telegram bot in forum-enabled chat

lobster-biscuit

Sign-Off

Built with Claude (claude-opus-4-6). AI-assisted development.

@openclaw-barnacle openclaw-barnacle bot added channel: telegram Channel integration: telegram agents Agent runtime and tooling size: M labels Mar 14, 2026
@Lukavyi Lukavyi marked this pull request as ready for review March 14, 2026 17:13
@obviyus obviyus self-assigned this Mar 14, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0d9d1ce805

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

/** Enable forum topic creation. */
createForumTopic?: boolean;
/** Enable forum topic editing (rename / change icon). */
editForumTopic?: boolean;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Register editForumTopic in the strict config schema

editForumTopic was added to TelegramActionConfig, but channels.telegram.actions is validated by a strict Zod object that still only includes createForumTopic (src/config/zod-schema.providers-core.ts:250-258). If a user sets channels.telegram.actions.editForumTopic (the gate introduced here), config loading fails on an unknown key, which blocks startup and makes the new gate unusable.

Useful? React with 👍 / 👎.

Comment on lines +298 to +300
const messageThreadId = readNumberParam(params, "messageThreadId", {
required: true,
integer: true,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Accept threadId alias when handling topic-edit

topic-edit currently requires messageThreadId, but the shared message tool schema exposes threadId for threading (src/agents/tools/message-tool.ts:196) and plugin actions do not remap that field automatically. This means normal tool calls using threadId fail with messageThreadId is required, so the new action is effectively broken unless callers discover an undocumented parameter name.

Useful? React with 👍 / 👎.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 14, 2026

Greptile Summary

This PR adds the topic-edit action to the Telegram message plugin, enabling agents to rename forum topics and change their icons via the Telegram Bot API's editForumTopic method. The change is symmetrically applied across all relevant layers (config types, Zod schema, action name registry, target-mode spec, channel-actions, telegram-actions, and the low-level send.ts helper), following the same pattern established by the existing topic-create / createForumTopic feature.

Key points:

  • editForumTopicTelegram validates input thoroughly: whitespace-only names and emoji IDs surface precise errors before the "at least one field required" guard, and the 128-character name limit is enforced.
  • All previously raised review concerns (ordering of validation guards, silent data-loss for whitespace-only iconCustomEmojiId, and missing whitespace-only test cases) appear to have been addressed in this revision.
  • The topic-edit entry in message-action-spec.ts is correctly set to "to" mode, consistent with topic-create.
  • Tests cover the happy paths (name-only, icon-only, both, target stripping) and all validation error paths, including the newly added whitespace guards.

Confidence Score: 5/5

  • This PR is safe to merge — the implementation is correct, well-tested, and follows established codebase patterns.
  • All changed files follow existing patterns faithfully. Input validation in editForumTopicTelegram is correct and complete (empty-after-trim, at-least-one, max-length guards are properly ordered). Previous review issues around whitespace validation and missing test cases have been addressed. No logic, type, or security issues were found across all nine changed files.
  • No files require special attention.

Last reviewed commit: 3822d89

import { resolveTelegramReactionLevel } from "../../../extensions/telegram/src/reaction-level.js";
import {
createForumTopicTelegram,
editForumTopicTelegram,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import out of alphabetical order

editForumTopicTelegram (starts with e) is inserted before deleteMessageTelegram (starts with d), breaking the alphabetical ordering of the other named imports in this block. It should come after deleteMessageTelegram and before the existing editMessageTelegram.

Suggested change
editForumTopicTelegram,
deleteMessageTelegram,
editForumTopicTelegram,
editMessageTelegram,
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/tools/telegram-actions.ts
Line: 17

Comment:
**Import out of alphabetical order**

`editForumTopicTelegram` (starts with `e`) is inserted before `deleteMessageTelegram` (starts with `d`), breaking the alphabetical ordering of the other named imports in this block. It should come after `deleteMessageTelegram` and before the existing `editMessageTelegram`.

```suggestion
  deleteMessageTelegram,
  editForumTopicTelegram,
  editMessageTelegram,
```

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@aisle-research-bot
Copy link
Copy Markdown

aisle-research-bot bot commented Mar 14, 2026

🔒 Aisle Security Analysis

We found 1 potential security issue(s) in this PR:

# Severity Title
1 🔵 Low Telegram forum topic edit action enabled by default (insecure feature-flag default)

1. 🔵 Telegram forum topic edit action enabled by default (insecure feature-flag default)

Property Value
Severity Low
CWE CWE-284
Location extensions/telegram/src/channel-actions.ts:89-120

Description

The newly added Telegram forum-topic edit capability is enabled by default when channels.telegram.actions (or per-account actions) omits editForumTopic.

Why this is a problem:

  • editForumTopic is a new privileged capability (rename/change icon) that can alter group/forum state.
  • The action gate defaults to true when an action key is absent (createAccountActionGate(...)(key, defaultValue=true)), so omitting configuration implicitly enables the new action.
  • This causes unintended capability expansion for existing installations after upgrade (no explicit opt-in required).

Vulnerable code (newly added enablement uses default-true gating):

const isEnabled = (key: keyof TelegramActionConfig, defaultValue = true) =>
  gate(key, defaultValue);
...
if (isEnabled("editForumTopic")) {
  actions.add("topic-edit");
}

A similar default-true gate is used when executing the tool action:

  • handleTelegramAction checks isActionEnabled("editForumTopic") without passing a safer default.

Impact:

  • Any caller who can invoke message actions/tools and has a configured Telegram token can now attempt topic-edit / editForumTopic without configuration changes.
  • If the bot has Telegram can_manage_topics, this can be abused to rename topics/change icons (defacement / disruption).

Recommendation

Make editForumTopic opt-in by default.

  1. When advertising support:
if (isEnabled("editForumTopic", false)) {
  actions.add("topic-edit");
}
  1. When executing the action (defense in depth):
if (!isActionEnabled("editForumTopic", false)) {
  throw new Error("Telegram editForumTopic is disabled.");
}
  1. Document the new default and require explicit config to enable:
channels:
  telegram:
    actions:
      editForumTopic: true

This prevents silent privilege expansion on upgrade.


Analyzed PR: #46407 at commit d391259

Last updated on: 2026-03-14T17:55:45Z

@Lukavyi
Copy link
Copy Markdown
Contributor Author

Lukavyi commented Mar 14, 2026

@greptile review

@Lukavyi
Copy link
Copy Markdown
Contributor Author

Lukavyi commented Mar 14, 2026

@codex review

Comment on lines +1566 to +1571
if (!name && !iconCustomEmojiId) {
throw new Error("At least one of name or iconCustomEmojiId must be provided");
}
if (name != null && name.length === 0) {
throw new Error("Forum topic name cannot be empty");
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Misleading error when name is whitespace-only

When opts.name is a whitespace-only string (e.g. " ") and no iconCustomEmojiId is provided, name becomes "" after trimming. The first guard (!name && !iconCustomEmojiId) fires with the message "At least one of name or iconCustomEmojiId must be provided" — even though the caller explicitly passed a name. The more accurate error ("Forum topic name cannot be empty") is unreachable in this path because "" is falsy.

Swapping the two checks fixes the ordering:

Suggested change
if (!name && !iconCustomEmojiId) {
throw new Error("At least one of name or iconCustomEmojiId must be provided");
}
if (name != null && name.length === 0) {
throw new Error("Forum topic name cannot be empty");
}
if (name != null && name.length === 0) {
throw new Error("Forum topic name cannot be empty");
}
if (!name && !iconCustomEmojiId) {
throw new Error("At least one of name or iconCustomEmojiId must be provided");
}

With this ordering, a whitespace-only name surfaces the correct diagnostic immediately, and the "at least one of" guard still catches the case where neither field is provided at all.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/telegram/src/send.ts
Line: 1566-1571

Comment:
**Misleading error when name is whitespace-only**

When `opts.name` is a whitespace-only string (e.g. `"   "`) and no `iconCustomEmojiId` is provided, `name` becomes `""` after trimming. The first guard (`!name && !iconCustomEmojiId`) fires with the message *"At least one of name or iconCustomEmojiId must be provided"* — even though the caller explicitly passed a `name`. The more accurate error ("Forum topic name cannot be empty") is unreachable in this path because `""` is falsy.

Swapping the two checks fixes the ordering:

```suggestion
  if (name != null && name.length === 0) {
    throw new Error("Forum topic name cannot be empty");
  }
  if (!name && !iconCustomEmojiId) {
    throw new Error("At least one of name or iconCustomEmojiId must be provided");
  }
```

With this ordering, a whitespace-only name surfaces the correct diagnostic immediately, and the "at least one of" guard still catches the case where neither field is provided at all.

How can I resolve this? If you propose a fix, please make it concise.

@Lukavyi
Copy link
Copy Markdown
Contributor Author

Lukavyi commented Mar 14, 2026

@greptile review

@Lukavyi
Copy link
Copy Markdown
Contributor Author

Lukavyi commented Mar 14, 2026

@codex review

Comment on lines +1563 to +1574
const name = opts.name?.trim();
const iconCustomEmojiId = opts.iconCustomEmojiId?.trim();

if (name != null && name.length === 0) {
throw new Error("Forum topic name cannot be empty");
}
if (!name && !iconCustomEmojiId) {
throw new Error("At least one of name or iconCustomEmojiId must be provided");
}
if (name != null && name.length > 128) {
throw new Error("Forum topic name must be 128 characters or fewer");
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silent data loss when iconCustomEmojiId is whitespace-only

When iconCustomEmojiId is a whitespace-only string (e.g. " ") it trims to "", which is falsy. If a valid name is also supplied, the code passes the "at least one" guard, proceeds normally, and only name ends up in extra — the caller's intended icon change is silently dropped.

The symmetric guard that already protects name (the name != null && name.length === 0 block on line 1566) should be added for iconCustomEmojiId as well:

Suggested change
const name = opts.name?.trim();
const iconCustomEmojiId = opts.iconCustomEmojiId?.trim();
if (name != null && name.length === 0) {
throw new Error("Forum topic name cannot be empty");
}
if (!name && !iconCustomEmojiId) {
throw new Error("At least one of name or iconCustomEmojiId must be provided");
}
if (name != null && name.length > 128) {
throw new Error("Forum topic name must be 128 characters or fewer");
}
const name = opts.name?.trim();
const iconCustomEmojiId = opts.iconCustomEmojiId?.trim();
if (name != null && name.length === 0) {
throw new Error("Forum topic name cannot be empty");
}
if (iconCustomEmojiId != null && iconCustomEmojiId.length === 0) {
throw new Error("Forum topic icon custom emoji ID cannot be empty");
}
if (!name && !iconCustomEmojiId) {
throw new Error("At least one of name or iconCustomEmojiId must be provided");
}
if (name != null && name.length > 128) {
throw new Error("Forum topic name must be 128 characters or fewer");
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/telegram/src/send.ts
Line: 1563-1574

Comment:
**Silent data loss when `iconCustomEmojiId` is whitespace-only**

When `iconCustomEmojiId` is a whitespace-only string (e.g. `"   "`) it trims to `""`, which is falsy. If a valid `name` is also supplied, the code passes the "at least one" guard, proceeds normally, and only `name` ends up in `extra` — the caller's intended icon change is silently dropped.

The symmetric guard that already protects `name` (the `name != null && name.length === 0` block on line 1566) should be added for `iconCustomEmojiId` as well:

```suggestion
  const name = opts.name?.trim();
  const iconCustomEmojiId = opts.iconCustomEmojiId?.trim();

  if (name != null && name.length === 0) {
    throw new Error("Forum topic name cannot be empty");
  }
  if (iconCustomEmojiId != null && iconCustomEmojiId.length === 0) {
    throw new Error("Forum topic icon custom emoji ID cannot be empty");
  }
  if (!name && !iconCustomEmojiId) {
    throw new Error("At least one of name or iconCustomEmojiId must be provided");
  }
  if (name != null && name.length > 128) {
    throw new Error("Forum topic name must be 128 characters or fewer");
  }
```

How can I resolve this? If you propose a fix, please make it concise.

@Lukavyi
Copy link
Copy Markdown
Contributor Author

Lukavyi commented Mar 14, 2026

@greptile review

@Lukavyi
Copy link
Copy Markdown
Contributor Author

Lukavyi commented Mar 14, 2026

@codex review

Comment on lines +2072 to +2089
it("throws when neither name nor iconCustomEmojiId is provided", async () => {
const api = {} as unknown as Bot["api"];
await expect(
editForumTopicTelegram("-1001234567890", 42, { token: "tok", api }),
).rejects.toThrow("At least one of name or iconCustomEmojiId must be provided");
});

it("throws when name exceeds 128 characters", async () => {
const api = {} as unknown as Bot["api"];
await expect(
editForumTopicTelegram("-1001234567890", 42, {
token: "tok",
api,
name: "x".repeat(129),
}),
).rejects.toThrow("Forum topic name must be 128 characters or fewer");
});
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing tests for whitespace-only validation paths

The two validation checks added to address previous review feedback — empty name after trim and empty iconCustomEmojiId after trim — are exercised by the implementation but have no corresponding test cases. The test suite currently covers:

  • opts = {} (neither field provided) → "At least one…" ✓
  • name: "x".repeat(129) → "must be 128 characters…" ✓

But it does not cover:

  • name: " " (whitespace-only) → should throw "Forum topic name cannot be empty"
  • iconCustomEmojiId: " " (whitespace-only) → should throw "Forum topic icon custom emoji ID cannot be empty"
  • name: " " + valid iconCustomEmojiId → should still throw "Forum topic name cannot be empty", not "At least one of…"

These paths exist specifically because of the previously raised bugs, so adding tests ensures the fix is verifiable and won't regress.

it("throws when name is whitespace-only", async () => {
  const api = {} as unknown as Bot["api"];
  await expect(
    editForumTopicTelegram("-1001234567890", 42, { token: "tok", api, name: "   " }),
  ).rejects.toThrow("Forum topic name cannot be empty");
});

it("throws when iconCustomEmojiId is whitespace-only", async () => {
  const api = {} as unknown as Bot["api"];
  await expect(
    editForumTopicTelegram("-1001234567890", 42, {
      token: "tok",
      api,
      iconCustomEmojiId: "   ",
    }),
  ).rejects.toThrow("Forum topic icon custom emoji ID cannot be empty");
});
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/telegram/src/send.test.ts
Line: 2072-2089

Comment:
**Missing tests for whitespace-only validation paths**

The two validation checks added to address previous review feedback — empty `name` after trim and empty `iconCustomEmojiId` after trim — are exercised by the implementation but have no corresponding test cases. The test suite currently covers:

- `opts = {}` (neither field provided) → "At least one…" ✓  
- `name: "x".repeat(129)` → "must be 128 characters…" ✓  

But it does not cover:
- `name: "   "` (whitespace-only) → should throw `"Forum topic name cannot be empty"`  
- `iconCustomEmojiId: "   "` (whitespace-only) → should throw `"Forum topic icon custom emoji ID cannot be empty"`  
- `name: "   "` + valid `iconCustomEmojiId` → should still throw `"Forum topic name cannot be empty"`, not `"At least one of…"`

These paths exist specifically because of the previously raised bugs, so adding tests ensures the fix is verifiable and won't regress.

```ts
it("throws when name is whitespace-only", async () => {
  const api = {} as unknown as Bot["api"];
  await expect(
    editForumTopicTelegram("-1001234567890", 42, { token: "tok", api, name: "   " }),
  ).rejects.toThrow("Forum topic name cannot be empty");
});

it("throws when iconCustomEmojiId is whitespace-only", async () => {
  const api = {} as unknown as Bot["api"];
  await expect(
    editForumTopicTelegram("-1001234567890", 42, {
      token: "tok",
      api,
      iconCustomEmojiId: "   ",
    }),
  ).rejects.toThrow("Forum topic icon custom emoji ID cannot be empty");
});
```

How can I resolve this? If you propose a fix, please make it concise.

@Lukavyi
Copy link
Copy Markdown
Contributor Author

Lukavyi commented Mar 14, 2026

@greptile review

@Lukavyi
Copy link
Copy Markdown
Contributor Author

Lukavyi commented Mar 14, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3822d8929b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +1569 to +1570
if (iconCustomEmojiId != null && iconCustomEmojiId.length === 0) {
throw new Error("Forum topic icon custom emoji ID cannot be empty");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Permit clearing forum topic icons in topic-edit

editForumTopicTelegram rejects an empty iconCustomEmojiId after trimming, which prevents callers from sending icon_custom_emoji_id: "" through editForumTopic. In Telegram’s Bot API, that empty value is the way to remove an existing topic icon, so the new topic-edit action cannot perform icon removal (either standalone or alongside a rename) and only supports replacing icons.

Useful? React with 👍 / 👎.

@obviyus
Copy link
Copy Markdown
Contributor

obviyus commented Mar 16, 2026

Superseded by #47798.

That PR landed the Telegram topic-edit work on top of the newer post-#45318 plugin/runtime seam, including the follow-up fix for topic-qualified targets.

@obviyus obviyus closed this Mar 16, 2026
@obviyus
Copy link
Copy Markdown
Contributor

obviyus commented Mar 29, 2026

Closing as duplicate; this was superseded by #47798.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling channel: telegram Channel integration: telegram size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Add editForumTopic action to Telegram message tool

2 participants