Fix Telegram poll action wiring#18122
Conversation
| const replyToMessageId = readNumberParam(params, "replyTo", { integer: true }); | ||
| const messageThreadId = readNumberParam(params, "threadId", { integer: true }); | ||
| const silent = typeof params.silent === "boolean" ? params.silent : undefined; | ||
| const isAnonymous = typeof params.isAnonymous === "boolean" ? params.isAnonymous : undefined; |
There was a problem hiding this comment.
pollAnonymous and pollPublic schema params not wired. Should read params.pollAnonymous and params.pollPublic, then derive isAnonymous like message-action-runner.ts:574-579:
const pollAnonymous = typeof params.pollAnonymous === "boolean" ? params.pollAnonymous : undefined;
const pollPublic = typeof params.pollPublic === "boolean" ? params.pollPublic : undefined;
if (pollAnonymous && pollPublic) {
throw new Error("pollAnonymous and pollPublic are mutually exclusive");
}
const isAnonymous = pollAnonymous ? true : pollPublic ? false : undefined;
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/channels/plugins/actions/telegram.ts
Line: 140:140
Comment:
`pollAnonymous` and `pollPublic` schema params not wired. Should read `params.pollAnonymous` and `params.pollPublic`, then derive `isAnonymous` like `message-action-runner.ts:574-579`:
```
const pollAnonymous = typeof params.pollAnonymous === "boolean" ? params.pollAnonymous : undefined;
const pollPublic = typeof params.pollPublic === "boolean" ? params.pollPublic : undefined;
if (pollAnonymous && pollPublic) {
throw new Error("pollAnonymous and pollPublic are mutually exclusive");
}
const isAnonymous = pollAnonymous ? true : pollPublic ? false : undefined;
```
How can I resolve this? If you propose a fix, please make it concise.
src/agents/tools/telegram-actions.ts
Outdated
| const maxSelections = | ||
| typeof params.allowMultiselect === "boolean" && params.allowMultiselect === true ? 2 : 1; |
There was a problem hiding this comment.
hardcoding maxSelections to 2 limits multi-select polls to only 2 choices. Should match message-action-runner.ts:586:
| const maxSelections = | |
| typeof params.allowMultiselect === "boolean" && params.allowMultiselect === true ? 2 : 1; | |
| const maxSelections = | |
| typeof params.allowMultiselect === "boolean" && params.allowMultiselect === true ? Math.max(2, pollOptions.length) : 1; |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/tools/telegram-actions.ts
Line: 229:230
Comment:
hardcoding `maxSelections` to 2 limits multi-select polls to only 2 choices. Should match `message-action-runner.ts:586`:
```suggestion
const maxSelections =
typeof params.allowMultiselect === "boolean" && params.allowMultiselect === true ? Math.max(2, pollOptions.length) : 1;
```
How can I resolve this? If you propose a fix, please make it concise.|
All checks passed ready to be merged!! |
|
solved issue #18157 close |
|
EVERYTHING DONE!!!!!!!!!!!!! close #18157 |
| bot.on("poll_answer", async (ctx) => { | ||
| try { | ||
| if (shouldSkipUpdate(ctx)) { | ||
| return; | ||
| } | ||
| const pollAnswer = (ctx.update as { poll_answer?: unknown })?.poll_answer as | ||
| | { | ||
| poll_id?: string; | ||
| user?: { id?: number; username?: string; first_name?: string }; | ||
| option_ids?: number[]; | ||
| } | ||
| | undefined; | ||
| if (!pollAnswer) { | ||
| return; | ||
| } | ||
| const pollId = pollAnswer?.poll_id?.trim(); | ||
| if (!pollId) { | ||
| return; | ||
| } | ||
| const pollMeta = getSentPoll(pollId); | ||
| if (!pollMeta) { | ||
| return; | ||
| } | ||
| if (pollMeta.accountId && pollMeta.accountId !== accountId) { | ||
| return; | ||
| } | ||
| const userId = pollAnswer.user?.id; | ||
| if (typeof userId !== "number") { | ||
| return; | ||
| } | ||
| const optionIds = Array.isArray(pollAnswer.option_ids) ? pollAnswer.option_ids : []; | ||
| const selected = optionIds.map((id) => pollMeta.options[id] ?? `option#${id + 1}`); | ||
| const selectedText = selected.length > 0 ? selected.join(", ") : "(cleared vote)"; | ||
| const syntheticText = `Poll vote update: "${pollMeta.question}" -> ${selectedText}`; | ||
| const syntheticMessage = { | ||
| message_id: Date.now(), | ||
| date: Math.floor(Date.now() / 1000), | ||
| chat: { | ||
| id: Number(pollMeta.chatId), | ||
| type: String(pollMeta.chatId).startsWith("-") ? "supergroup" : "private", | ||
| }, | ||
| from: { | ||
| id: userId, | ||
| is_bot: false, | ||
| first_name: pollAnswer.user?.first_name ?? "User", | ||
| username: pollAnswer.user?.username, | ||
| }, | ||
| text: syntheticText, | ||
| } as unknown as Message; | ||
| const storeAllowFrom = await loadStoreAllowFrom(); | ||
| await processMessage(buildSyntheticContext(ctx, syntheticMessage), [], storeAllowFrom, { | ||
| forceWasMentioned: true, | ||
| messageIdOverride: `poll:${pollId}:${userId}:${Date.now()}`, | ||
| }); |
There was a problem hiding this comment.
Missing access control on poll_answer handler
The poll_answer handler processes votes and injects them into processMessage with forceWasMentioned: true, but unlike the message handler (line 864+) and the callback_query handler (line 462+), it does not perform any allowlist or group policy checks (shouldSkipGroupMessage, isAllowlistAuthorized).
This means any Telegram user who votes on a bot-created poll will have their vote processed as a synthetic message that triggers the agent — bypassing DM policy, group policy, and allowlist gates. In deployments with restrictive dmPolicy / groupPolicy / allowFrom configurations, this is a meaningful access control gap.
Consider adding the same allowlist/group-policy checks that callback_query performs before calling processMessage.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/telegram/bot-handlers.ts
Line: 753:806
Comment:
**Missing access control on poll_answer handler**
The `poll_answer` handler processes votes and injects them into `processMessage` with `forceWasMentioned: true`, but unlike the `message` handler (line 864+) and the `callback_query` handler (line 462+), it does not perform any allowlist or group policy checks (`shouldSkipGroupMessage`, `isAllowlistAuthorized`).
This means any Telegram user who votes on a bot-created poll will have their vote processed as a synthetic message that triggers the agent — bypassing DM policy, group policy, and allowlist gates. In deployments with restrictive `dmPolicy` / `groupPolicy` / `allowFrom` configurations, this is a meaningful access control gap.
Consider adding the same allowlist/group-policy checks that `callback_query` performs before calling `processMessage`.
How can I resolve this? If you propose a fix, please make it concise.
Technical Roadmap for OpenClaw: Cron System Optimization and User-Driven Feature Evolution
As OpenClaw transitions from a viral repository to stable infrastructure, the development focus has shifted toward hardening core subsystems and fulfilling power-user demands for enterprise-grade security and sophisticated automation. This report details the current "state of the union" for the Cron automation engine and provides a blueprint of the most requested features identified from community feedback and technical issue tracking.
The "Cron One" Report: Current Stability and Required Fixes
The OpenClaw Cron engine is the backbone of its "proactive" intelligence, allowing agents to execute tasks without user prompts. However, the system has recently faced significant regressions.
Users on the stable/beta branch (v2026.2.3-1) have reported a complete breakdown in scheduling logic, characterized by jobs failing to trigger and a "48-hour gap" in execution history.
The recomputeNextRuns Bug: A flaw in the scheduling calculation prevents the system from correctly determining the next execution window after a gateway restart.
Windows Resource Locking (EBUSY): On Windows deployments, the gateway often crashes during atomic renames of the cron/jobs.json file because the resource is flagged as busy.
Timer Spin Loops: A critical performance bug exists where timers re-arm every 18ms when delayMs is set to 0, leading to massive log bloat and CPU spikes.
Beyond bug fixes, users are demanding more granular control over how automated tasks behave:
Multi-Schedule Support: Current jobs support only simple intervals. Users are requesting the ability to define complex schedules (e.g., "Run at 9 AM on weekdays and 11 AM on weekends") within a single job definition.
Configurable Per-Job Timeouts: Automated tasks currently have a hardcoded 120-second timeout. Complex research or coding tasks often exceed this limit, causing the job to fail prematurely.
Isolated Announce Reliability: A regression in version 2026.2.13 has broken "announce" delivery for isolated jobs, where the agent completes the work but fails to send the output to the specified messaging channel.
The User Demand Blueprint: Top Requested Features
The community has prioritized several "game-changing" features that would elevate OpenClaw from a personal tool to a professional-grade assistant.
As agents are granted higher system permissions, the "plaintext" nature of configuration files has become a primary concern.
Secrets Manager Integration: There is a heavy demand for native 1Password or Vault integration. This would allow the gateway to fetch API keys at runtime rather than storing them in .env or openclaw.json files.
Membrane-Based Security: Using the Calyx Protocol, users want to establish strict "membranes" around agent actions. This would enforce trust verification at the boundary of every tool call, preventing "excessive agency" or unauthorized shell execution.
The high cost of frontier models like Claude 4.5 (Opus) has led to requests for more efficient model management.
Smart Model Routing (Opus-as-Orchestrator): A highly-voted proposal where a high-end model acts as the "brain" to plan tasks, but delegates the actual execution of simple sub-tasks to cheaper models like GPT-4o-mini or Haiku.
Fact-Checking Middleware: To combat hallucinations in persistent memory, users have proposed a hybrid indexing system that uses a middleware layer to verify "memories" against a grounded knowledge base before the agent acts on them.
Custom Bootstrap Files (agents.defaults.bootstrapFiles): This feature would allow users to inject specific environment variables or initialization scripts into every agent session automatically, facilitating complex dev environments.
Control UI Audio Input: A requested "microphone record" button in the browser-based dashboard would bring the voice-first experience of the mobile nodes to the desktop web interface.
Internationalization (i18n): With a massive surge in global adoption, specifically in the Chinese market, there is an urgent need for localized UIs and prompts.
Strategic Recommendation: "Stabilisation Mode"
To manage the influx of over 3,000 open issues and a PR arriving "roughly every two minutes," maintainers are moving toward a Stabilisation Mode.
Core Lockdown: The core gateway will be locked to ensure it remains "rock solid."
Skill-First Development: New features (like the proposed LinkedIn Scraper or specialized CRM tools) should be developed as external Skills rather than core PRs.
Diagnostic Context: Enhancing error messages with "diagnostic tags" (identifying exactly which service or resource failed) is considered a "good first issue" that would immediately help the community troubleshoot their own deployments.
Conclusion
The path forward for OpenClaw requires a dual approach: first, resolving the "Cron One" stability crisis to ensure reliability for professional users; and second, implementing high-impact security features like Vault integration and Membrane protocols. By shifting feature development to the modular Skills ecosystem, the project can continue to innovate rapidly without compromising the stability of the core gateway.
Greptile Summary
This PR wires Telegram poll actions end-to-end: adding the
pollaction to the Telegram action handler and adapter, registeringpoll_answeras an allowed update type, caching sent polls in-memory, and processing incoming vote updates as synthetic messages routed throughprocessMessage. It also changes the default poll anonymity from anonymous (is_anonymous: true) to public (is_anonymous: false) so that vote tracking can work (Telegram only sendspoll_answerupdates for non-anonymous polls). A Mattermost docs improvement forchatmodebehavior is bundled in.sendPollTelegramintegration intelegram-actions.tsand the adapter inactions/telegram.tswith proper param normalization from the shared schema (pollQuestion→question,pollOption→options, etc.)poll-vote-cache.tsprovides an in-memory store for sent poll metadata with 24h TTL, used by bothsend.ts(recording) andbot-handlers.ts(lookup on vote)poll_answerhandler inbot-handlers.tscreates synthetic messages from vote updates and routes them throughprocessMessage— but does not enforce allowlist or group-policy access control, unlike themessageandcallback_queryhandlers. This is a security gap worth addressing.pollAnonymous/pollPublicparams andmaxSelectionsbehavior inconsistencies were noted in prior review threads and appear unresolved in this iterationConfidence Score: 2/5
src/telegram/bot-handlers.ts(missing access control in poll_answer handler) andsrc/agents/tools/telegram-actions.ts(previously flagged issues unresolved).Last reviewed commit: 5bbafec