Skip to content

Comments

feat(telegram): implement sendPayload for channelData support#1917

Merged
obviyus merged 5 commits intoopenclaw:mainfrom
JoshuaLelon:feat/telegram-sendpayload-support
Jan 26, 2026
Merged

feat(telegram): implement sendPayload for channelData support#1917
obviyus merged 5 commits intoopenclaw:mainfrom
JoshuaLelon:feat/telegram-sendpayload-support

Conversation

@JoshuaLelon
Copy link
Contributor

@JoshuaLelon JoshuaLelon commented Jan 25, 2026

Summary

Implements plugin support for Telegram-specific features:

  1. sendPayload handler - Enables channel-specific data via the channelData pattern (inline buttons, etc.)
  2. Plugin command menu sync - Merges plugin commands into Telegram's / autocomplete menu
  3. Plugin command handlers - Registers bot.command() handlers so plugin commands execute deterministically
  4. Button extraction in deliverReplies - Extracts and sends buttons from plugin responses
  5. Gateway method types - Exports gateway request handler types for plugin authors

Motivation

Plugins need access to Telegram-specific features and gateway RPC methods:

  • Inline buttons: Plugins want button-driven workflows without custom ReplyPayload types
  • Command discovery: Plugin commands registered internally don't appear in Telegram's menu
  • Command execution: Plugin commands must execute deterministically, not fall through to LLM
  • Button delivery: Plugin command responses with buttons need proper rendering
  • Gateway methods: Plugins want to expose RPC-style methods for programmatic access

The infrastructure exists in core but wasn't fully wired up for plugins.

Changes

1. Telegram sendPayload Handler

  • Add sendPayload handler to telegramOutbound adapter
  • Extract telegram.buttons from payload.channelData
  • Pass buttons to existing sendMessageTelegram function (already supports this)

2. Plugin Command Menu Sync

  • Import getPluginCommandSpecs() in bot-native-commands.ts
  • Merge plugin commands into allCommands array
  • Commands appear in Telegram's / menu alongside native commands

3. Plugin Command Handler Registration

  • Register bot.command() handlers for each plugin command
  • Match and execute plugin commands via executePluginCommand()
  • Apply full authorization checks (same as native commands)
  • Deliver results through existing Telegram delivery pipeline

Critical Fix: Without this, plugin commands show in the menu but fall through to the general message handler, where the LLM processes them conversationally instead of executing them deterministically.

4. Button Extraction in deliverReplies

  • Extract buttons from reply.channelData?.telegram?.buttons in deliverReplies()
  • Build inline keyboard markup using buildInlineKeyboard()
  • Pass reply_markup to sendMessage() API
  • Buttons are attached to the first chunk when text is chunked

Critical Fix: Plugin commands can return buttons in channelData, but deliverReplies() was ignoring them. Now buttons render properly.

5. Gateway Method Types Export

  • Export GatewayRequestHandler, GatewayRequestHandlerOptions, RespondFn in plugin-sdk
  • Enables plugins to register gateway methods with proper types

Example Usage

Inline Buttons

return {
  text: "Choose an option",
  channelData: {
    telegram: {
      buttons: [
        [{ text: "Option A", callback_data: "/command option-a" }],
        [{ text: "Option B", callback_data: "/command option-b" }]
      ]
    }
  }
};

Gateway Methods

api.registerGatewayMethod("my-plugin.action", async ({ params, respond }) => {
  try {
    const result = await doSomething(params);
    respond(true, { result });
  } catch (error) {
    respond(false, { error: error.message });
  }
});

Testing

  • Builds successfully
  • Follows existing patterns (sendText/sendMedia, voice-call plugin)
  • skill-flow plugin: 46/46 tests pass, 0 lint errors
  • Buttons render correctly in Telegram

Use Case

Enables the skill-flow plugin to:

  • Send button-driven workflows in Telegram
  • Show commands in Telegram's autocomplete menu
  • Execute commands deterministically (not via LLM interpretation)
  • Render inline keyboard buttons in responses
  • Expose gateway methods for programmatic flow creation

🤖 AI-Assisted: Generated with Claude Sonnet 4.5

@thewilloftheshadow thewilloftheshadow added the channel: telegram Channel integration: telegram label Jan 26, 2026
@obviyus obviyus self-assigned this Jan 26, 2026
Joshua Mitchell and others added 5 commits January 26, 2026 22:13
Add sendPayload handler to Telegram outbound adapter to support
channel-specific data via the channelData pattern. This enables
features like inline keyboard buttons without custom ReplyPayload fields.

Implementation:
- Extract telegram.buttons from payload.channelData
- Pass buttons to sendMessageTelegram (already supports this)
- Follows existing sendText/sendMedia patterns
- Completes optional ChannelOutboundAdapter.sendPayload interface

This enables plugins to send Telegram-specific features (buttons, etc.)
using the standard channelData envelope pattern instead of custom fields.

Related: delivery system in src/infra/outbound/deliver.ts:324 already
checks for sendPayload handler and routes accordingly.

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
…ay types

- Add plugin command specs to Telegram setMyCommands for autocomplete
- Export GatewayRequestHandler types in plugin-sdk for plugin authors
- Enables plugins to register gateway methods and appear in command menus
Plugin commands were added to setMyCommands menu but didn't have
bot.command() handlers registered. This meant /flow-start and other
plugin commands would fall through to the general message handler
instead of being dispatched to the plugin command executor.

Now we register bot.command() handlers for each plugin command,
with full authorization checks and proper result delivery.
Plugin commands can return buttons in channelData.telegram.buttons,
but deliverReplies() was ignoring them. Now we:

1. Extract buttons from reply.channelData?.telegram?.buttons
2. Build inline keyboard using buildInlineKeyboard()
3. Pass reply_markup to sendMessage()

Buttons are attached to the first text chunk when text is chunked.
@obviyus obviyus force-pushed the feat/telegram-sendpayload-support branch from 1a8f367 to 96f1c6f Compare January 26, 2026 16:58
@obviyus obviyus merged commit 94ead83 into openclaw:main Jan 26, 2026
20 of 23 checks passed
@obviyus
Copy link
Contributor

obviyus commented Jan 26, 2026

Landed via temp rebase onto main.

Changes:

  • Telegram plugin sendPayload supports media URLs + channelData buttons (buttons on first send).

  • Telegram delivery attaches inline buttons for first chunk/media and carries them to follow-up text/voice fallback.

  • Telegram plugin command registration validates names/desc/conflicts; auth gate honors requireAuth=false.

  • Tests added for sendPayload + requireAuth=false handling.

  • Gate: pnpm lint && pnpm build && pnpm test

  • Land commit: 96f1c6f

  • Merge commit: 94ead83

Thanks @JoshuaLelon!