feat(telegram): add topic-edit action for editForumTopic API#46407
feat(telegram): add topic-edit action for editForumTopic API#46407Lukavyi wants to merge 2 commits intoopenclaw:mainfrom
Conversation
There was a problem hiding this comment.
💡 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; |
There was a problem hiding this comment.
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 👍 / 👎.
| const messageThreadId = readNumberParam(params, "messageThreadId", { | ||
| required: true, | ||
| integer: true, |
There was a problem hiding this comment.
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 SummaryThis PR adds the Key points:
Confidence Score: 5/5
Last reviewed commit: 3822d89 |
src/agents/tools/telegram-actions.ts
Outdated
| import { resolveTelegramReactionLevel } from "../../../extensions/telegram/src/reaction-level.js"; | ||
| import { | ||
| createForumTopicTelegram, | ||
| editForumTopicTelegram, |
There was a problem hiding this 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.
| 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 Security AnalysisWe found 1 potential security issue(s) in this PR:
1. 🔵 Telegram forum topic edit action enabled by default (insecure feature-flag default)
DescriptionThe newly added Telegram forum-topic edit capability is enabled by default when Why this is a problem:
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:
Impact:
RecommendationMake
if (isEnabled("editForumTopic", false)) {
actions.add("topic-edit");
}
if (!isActionEnabled("editForumTopic", false)) {
throw new Error("Telegram editForumTopic is disabled.");
}
channels:
telegram:
actions:
editForumTopic: trueThis prevents silent privilege expansion on upgrade. Analyzed PR: #46407 at commit Last updated on: 2026-03-14T17:55:45Z |
|
@greptile review |
|
@codex review |
extensions/telegram/src/send.ts
Outdated
| 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"); | ||
| } |
There was a problem hiding this 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:
| 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.d391259 to
beeecf0
Compare
|
@greptile review |
|
@codex review |
| 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"); | ||
| } |
There was a problem hiding this 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:
| 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.beeecf0 to
e5d125a
Compare
|
@greptile review |
|
@codex review |
| 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"); | ||
| }); | ||
| }); |
There was a problem hiding this 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: " "+ validiconCustomEmojiId→ 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.e5d125a to
3822d89
Compare
|
@greptile review |
|
@codex review |
There was a problem hiding this comment.
💡 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".
| if (iconCustomEmojiId != null && iconCustomEmojiId.length === 0) { | ||
| throw new Error("Forum topic icon custom emoji ID cannot be empty"); |
There was a problem hiding this comment.
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 👍 / 👎.
|
Closing as duplicate; this was superseded by #47798. |
Summary
Add
topic-editaction to the Telegram message plugin, enabling agents to rename forum topics and change their icons using the Telegram Bot API'seditForumTopicmethod.Changes
editForumTopicTelegramfunction inextensions/telegram/src/send.tstopic-editaction name to message action nameseditForumTopicgate to Telegram action configMotivation
Closes #7746
Currently OpenClaw supports creating forum topics (
topic-create) but not editing them. The Telegram Bot API'seditForumTopicmethod 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
lobster-biscuit
Sign-Off
Built with Claude (claude-opus-4-6). AI-assisted development.