Report
Date: 2026-02-22 Researcher: deep-research (6 parallel lines of enquiry)
Table of Contents¶
- Executive Summary
- Existing Framework Adapter Patterns
- Platform API Capabilities
- Adapter Architecture for Picobot
- Multi-Turn, Streaming, and Rich Responses
- Deployment Patterns
- Counter-Perspective and Alternatives
- Recommendations
1. Executive Summary¶
Channel integrations for AI agent frameworks follow a well-established adapter pattern: a thin layer that normalizes platform-specific messages into an internal format, routes them to the agent core, and translates responses back. Every major framework (nanobot, OpenClaw, Rasa, Botpress) uses this pattern with minor variations.
Picobot is uniquely positioned because it already has an NDJSON stdin/stdout protocol (JsonChannel) that cleanly separates the agent loop from transport. Telegram and Slack adapters can be built as external processes that spawn picobot agent --json and bridge between platform events and NDJSON messages, or as in-process channels that use the same AgentLoop.process_direct() interface the JsonChannel uses.
Both Telegram and Slack have added AI-specific streaming APIs in 2025 (Telegram's sendMessageDraft in API 9.3, Slack's chat.startStream/appendStream/stopStream in October 2025), making real-time token streaming feasible on both platforms.
The recommended approach: build a BaseChannel ABC with two concrete implementations (Telegram, Slack), integrated via a lightweight gateway process that uses the existing AgentLoop directly. This keeps adapters thin (300-500 lines each) while enabling the full feature set.
2. Existing Framework Adapter Patterns¶
2.1 Nanobot (HKUDS) — BaseChannel + MessageBus¶
Nanobot (picobot's upstream) uses an async message bus architecture:
- BaseChannel ABC defines
start(),stop(),send(), and_handle_message() - TelegramChannel extends BaseChannel using
python-telegram-botwith long polling - MessageBus provides asyncio.Queue-based decoupling between channels and agent
- Session keys use format
"channel:chat_id"(e.g.,"telegram:12345") - ChannelManager orchestrates all channel lifecycles and routes outbound messages
Inbound flow: Platform event -> TelegramChannel parses -> InboundMessage -> MessageBus -> SessionManager routes -> AgentLoop processes. Outbound flow reverses through the bus back to the originating channel.
Key design choices: connection pool of 16 to prevent exhaustion during long agent runs, typing indicator loop every 4 seconds during processing, message splitting at 4000 characters on line boundaries, markdown-to-HTML conversion for Telegram.
2.2 OpenClaw — ChannelPlugin Interface + Gateway¶
OpenClaw (formerly Clawdbot) takes a plugin-oriented approach:
- ChannelPlugin interface is the core contract; all channels (built-in and extension) implement it
- Channel monitors run as part of the Gateway process on port 18789
- InboundContext is the normalized message envelope with headers injected into message body
- Multi-account support per channel with account resolution fallback
- Access control with pairing system, allowlists, and per-group mention requirements
Built-in channels cover 15+ platforms (WhatsApp, Telegram, Slack, Discord, Signal, iMessage, Matrix, Teams, etc.). Extension channels register via package.json metadata and are loaded dynamically from node_modules.
The envelope header pattern is notable: [Telegram Ops id:42 2025-01-09 00:00:00 UTC] Sender: Ada Lovelace (@ada) gets prepended to message content, giving the agent context about message origin without requiring protocol changes.
2.3 Rasa — InputChannel/OutputChannel¶
Rasa uses a bidirectional connector pattern:
- InputChannel receives messages via webhook blueprint (Sanic routes), calls
on_new_message() - OutputChannel sends responses back, with methods like
send_text_message(),send_image_url(),send_text_with_buttons() - CollectingOutputChannel alternative gathers responses for REST-style return
- Streaming support via
send_response_chunk_start/chunk/chunk_endmethods - Built-in connectors for Facebook, Slack, Telegram, Mattermost, Twilio
The webhook URL pattern is /webhooks/{channel_name}/webhook, with channel name derived from the name() class method.
2.4 Botpress — IntegrationDefinition + SDK¶
Botpress uses a definition-driven approach:
- IntegrationDefinition class from the SDK declares channels, actions, events, and configurations
- Each channel has a
messagesproperty specifying supported message types - Telegram integration built on Telegraf (Node.js); Slack integration built on Bolt SDK
- Flows, policies, and AI agents operate identically across all channels
2.5 Common Patterns Across Frameworks¶
| Pattern | Nanobot | OpenClaw | Rasa | Botpress |
|---|---|---|---|---|
| Base class/interface | BaseChannel | ChannelPlugin | InputChannel | IntegrationDefinition |
| Message normalization | InboundMessage | InboundContext | UserMessage | SDK message types |
| Session key format | channel:chat_id | channel:chat_id | sender_id | conversation_id |
| Webhook support | No (polling) | Yes | Yes | Yes |
| Plugin/extension model | No | Yes (npm) | No | Yes (SDK) |
| Typing indicators | Yes (4s loop) | Yes | No | Platform-dependent |
| Message splitting | Yes (4000 char) | Yes | No | No |
| Access control | allowFrom list | Pairing + ACL | None built-in | None built-in |
3. Platform API Capabilities¶
3.1 Telegram Bot API¶
Connection modes:
- Long polling via getUpdates: Simple, no public URL needed, no SSL required. Offset-based cursor. Limit 1-100 updates per call.
- Webhook via setWebhook: Real-time, requires HTTPS on ports 443/80/88/8443. Max 100 concurrent connections (default 40). Exponential backoff retry on failure. allowed_updates parameter filters event types.
Streaming (API 9.3, late 2025):
- sendMessageDraft: Streams partial messages while generating. Restricted to bots with forum topic mode enabled.
- Known issue: Returns TEXTDRAFT_PEER_INVALID in regular private chats. Unreliable for general use.
- Practical alternative: Send initial message via sendMessage, then update via editMessageText as content generates. This is the approach nanobot and OpenClaw both use.
Message formatting:
- Parse modes: HTML (recommended), MarkdownV2, Markdown (legacy)
- 4096 UTF-8 character limit per message
- Rich types: photos, documents, voice, audio, video, location, contacts, polls, stickers
- Inline keyboards for interactive buttons
- Reply threading via ReplyParameters
Rate limits:
- Global: 30 messages/second per bot token (shared across all methods)
- Per chat: approximately 1 message/second
- Per group: 20 messages/minute
- API 8.0 added adaptive_retry in 429 responses (recommended sleep in milliseconds)
- Paid broadcasts: up to 1000 msg/sec at 0.1 Stars per message over free tier
Typing indicators:
- sendChatAction with action typing — displays for 5 seconds or until next message
- Must be re-sent periodically for long operations
3.2 Slack Bolt/Events API¶
Connection modes: - HTTP webhooks: Stateless, scales horizontally, recommended for production and Marketplace apps - Socket Mode: WebSocket-based, no public URL needed, max 10 concurrent connections, recommended for development and behind-firewall deployments - Legacy RTM API deprecated
Streaming (October 2025):
- chat.startStream: Initiates a text stream in a channel/thread
- chat.appendStream: Appends markdown text chunks to the stream
- chat.stopStream: Finalizes the stream, optionally adds Block Kit blocks (feedback buttons)
- Bolt Python/JS provide client.chat_stream() helper utility that wraps all three methods
- Purpose-built for LLM token streaming — first-class support
AI-specific features (Agents & AI Apps):
- Assistant class in Bolt for thread lifecycle management
- Side-panel split view for AI interactions
- Suggested prompts (dynamic, context-aware)
- Loading states during processing
- set_status() for "thinking..." indicators
- Feedback buttons (thumbs up/down) with Block Kit
- assistant_thread_started, assistant_thread_context_changed, message.im events
Message formatting:
- mrkdwn syntax (Slack's Markdown variant, not standard Markdown)
- Block Kit for structured layouts (sections, context, actions, dividers)
- 40,000 character total message limit
- 3,000 character limit per section block
- 12,000 character limit per markdown block
- msg_blocks_too_long error at approximately 13,297 characters aggregate
- Thread support via thread_ts parameter
Rate limits:
- Tier system: Tier 1 (1+/min), Tier 2 (20+/min), Tier 3 (50+/min), Tier 4 (100+/min), Special
- chat.postMessage: Special tier — 1 per second per channel, short bursts allowed
- Streaming methods: Not separately documented (likely Special tier)
- Non-Marketplace apps had limits tightened in May 2025
3.3 Platform Comparison for AI Agent Use¶
| Capability | Telegram | Slack |
|---|---|---|
| Native LLM streaming | Partial (sendMessageDraft, buggy) | Full (chat.startStream/append/stop) |
| Practical streaming | Edit-in-place pattern | Native streaming API |
| AI assistant framework | None | Assistant class, side panel |
| Message limit | 4096 chars | 40,000 chars (3000/section) |
| Markdown support | MarkdownV2 / HTML | mrkdwn (Slack variant) |
| Rich responses | Inline keyboards, media | Block Kit, actions, modals |
| Thread support | Reply threading | Native threads (thread_ts) |
| Typing indicators | sendChatAction (5s) | set_status() |
| Self-hosted ease | Very easy (polling) | Easy (Socket Mode) |
| Rate limits | 30 msg/sec global | 1 msg/sec/channel |
4. Adapter Architecture for Picobot¶
4.1 Design Principle: Picobot Already Has the Protocol¶
Picobot's JsonChannel is the key insight. It already defines:
Input: {"type": "message", "content": "...", "session": "...", "attachments": [...]}
Output: {"type": "response", "content": "...", "session": "..."}
Output: {"type": "progress", "event": "text_delta"|"tool_call_start"|"tool_call_done", ...}
Output: {"type": "error", "message": "...", "code": "..."}
The adapter's job is purely message format translation between platform events and NDJSON.
4.2 Architecture Options¶
Option A: External Process (Subprocess Bridge)
Telegram API <--> telegram_adapter.py <--stdin/stdout--> picobot agent --json
Pros: Maximum isolation, crash recovery, zero code changes to picobot core. Cons: Process management overhead, harder to share state, extra serialization layer.
Option B: In-Process Channel (Recommended)
Telegram API <--> TelegramChannel <--> AgentLoop.process_direct()
Pros: Direct access to streaming events, shared session state, simpler deployment. Cons: Channel crash can take down agent, tighter coupling.
Option C: Gateway Process (Nanobot Pattern)
Telegram API <--> TelegramChannel <--> MessageBus <--> AgentLoop
Slack API <--> SlackChannel <--> |
Pros: Multi-channel support, clean separation via message bus, proven pattern. Cons: More infrastructure code, overkill for 1-2 channels.
Recommendation: Option B for initial implementation, with the gateway pattern (Option C) as an evolution path if more channels are added. The process_direct() interface is clean enough that a thin channel adapter works perfectly.
4.3 Proposed Module Structure¶
picobot/channels/
base.py — BaseChannel ABC (start, stop, send, handle_message)
json_channel.py — existing NDJSON channel (unchanged)
telegram.py — TelegramChannel (aiogram or python-telegram-bot)
slack.py — SlackChannel (slack-bolt)
gateway.py — optional multi-channel orchestrator
4.4 BaseChannel Interface¶
class BaseChannel(ABC):
"""Base class for chat platform adapters."""
def __init__(self, agent_loop: AgentLoop, config: dict):
self.loop = agent_loop
self.config = config
@abstractmethod
async def start(self) -> None:
"""Start listening for platform events."""
@abstractmethod
async def stop(self) -> None:
"""Gracefully shut down."""
@abstractmethod
async def send(self, chat_id: str, content: str, **kwargs) -> None:
"""Send a message to the platform."""
def session_key(self, chat_id: str) -> str:
"""Generate session key for AgentLoop."""
return f"{self.name}:{chat_id}"
@property
@abstractmethod
def name(self) -> str:
"""Channel identifier (e.g., 'telegram', 'slack')."""
4.5 Message Format Translation¶
Telegram inbound -> picobot:
# Telegram Update
{"message": {"chat": {"id": 12345}, "from": {"id": 67890, "username": "alice"},
"text": "What files are in the workspace?"}}
# Translated to process_direct() call
await self.loop.process_direct(
"What files are in the workspace?",
session_key="telegram:12345",
on_event=self._stream_handler,
)
Slack inbound -> picobot:
# Slack event
{"type": "message", "channel": "C0123", "user": "U0456", "text": "List my cron jobs",
"thread_ts": "1234567890.123456"}
# Translated to process_direct() call
await self.loop.process_direct(
"List my cron jobs",
session_key="slack:C0123:1234567890.123456",
on_event=self._stream_handler,
)
4.6 Session Management¶
Session keys should encode enough context to maintain conversation isolation:
- Telegram:
telegram:{chat_id}— one session per chat (DM or group) - Slack:
slack:{channel_id}:{thread_ts}— one session per thread, orslack:{channel_id}for top-level
This maps directly to picobot's existing SessionManager which stores history as JSONL files keyed by session string.
4.7 User Identity and Access Control¶
@dataclass
class ChannelUser:
platform: str # "telegram" | "slack"
platform_id: str # telegram user_id or slack user_id
username: str | None # @handle if available
display_name: str | None
class AccessControl:
allow_from: list[str] # whitelist of platform_ids or usernames
# Empty list = allow all (for personal deployment)
Access control is essential for Telegram (public bot discovery) but less critical for Slack (workspace-scoped). The nanobot pattern of an allowFrom config list is simple and effective.
5. Multi-Turn, Streaming, and Rich Responses¶
5.1 Multi-Turn Conversations¶
Both platforms have native thread/reply support that maps cleanly to picobot sessions:
Telegram: Use ReplyParameters to link responses to the triggering message. Session continuity via chat_id — all messages in a DM share one session. Group chats need reply-threading to disambiguate conversations.
Slack: Use thread_ts to maintain thread context. The Assistant class handles this automatically with assistant_thread_started and assistant_thread_context_changed events. Each thread gets its own session key.
5.2 Streaming Responses¶
Telegram streaming strategy (edit-in-place):
async def _stream_handler(self, event: LoopEvent) -> None:
if isinstance(event, TextDelta):
self._buffer += event.text
if len(self._buffer) - len(self._last_sent) > 100: # throttle edits
await self.bot.edit_message_text(
chat_id=self._chat_id,
message_id=self._msg_id,
text=self._buffer + " ...",
parse_mode="HTML",
)
self._last_sent = self._buffer
Throttle edits to avoid rate limits. Send initial message on first TextDelta, then edit in place. Finalize with full content when response completes. The 100-character delta threshold prevents excessive API calls while keeping the display responsive.
Slack streaming strategy (native):
async def _stream_handler(self, event: LoopEvent) -> None:
if isinstance(event, TextDelta):
if not self._streamer:
self._streamer = self.client.chat_stream(
channel=self._channel,
thread_ts=self._thread_ts,
)
self._streamer.append(markdown_text=event.text)
elif isinstance(event, ToolCallDone):
# Tool calls complete — handled separately
async def _finalize(self):
if self._streamer:
self._streamer.stop(blocks=self._feedback_blocks())
Slack's native streaming is clean — start on first delta, append chunks, stop when done.
5.3 Tool Call Visibility¶
How to show tool execution on each platform:
Telegram:
- Send typing indicator during tool execution
- Optionally send a status message: "Running web_search..." then delete it on completion
- Or append tool status to the streaming buffer: [Searching web...]\n
- Final response replaces or follows the status
Slack:
- Use set_status("Running web_search...") during tool execution
- Thread context blocks can show tool call history
- Streaming naturally pauses during tool execution (no text deltas emitted)
- Feedback blocks on final message
5.4 Message Length Handling¶
Telegram (4096 chars): - Split long responses at paragraph boundaries - Maintain markdown/HTML validity across splits (close open tags before split, reopen after) - Send splits as sequential messages with short delays - Consider sending very long responses as a document attachment
Slack (40,000 chars total, 3000/section): - Use multiple section blocks for long responses - Code blocks in their own sections - For extremely long responses, use a thread reply chain - File upload for content exceeding practical limits
5.5 Markdown Translation¶
Picobot's agent produces standard Markdown. Both platforms need translation:
Telegram (MarkdownV2 or HTML):
- **bold** -> <b>bold</b> (HTML recommended over MarkdownV2 due to escaping complexity)
- `code` -> <code>code</code>
- ```lang\nblock\n``` -> <pre><code class="language-lang">block</code></pre>
- Links, lists need conversion; Telegram has no native list support in HTML mode
Slack (mrkdwn):
- **bold** -> *bold*
- *italic* -> _italic_
- `code` -> `code` (same)
- ```block``` -> ```block``` (same)
- [text](url) -> <url|text>
- Lists: roughly compatible but no nested list support
A MarkdownTranslator utility class with to_telegram_html() and to_slack_mrkdwn() methods keeps this contained.
6. Deployment Patterns¶
6.1 Self-Hosted (Recommended for Picobot)¶
Picobot is designed for personal/self-hosted use. The channel adapter runs alongside the agent:
Telegram (long polling — simplest):
- No public URL, no SSL, no domain required
- Just a bot token from @BotFather
- Run as: picobot gateway --telegram or picobot agent --telegram
- Can run behind NAT, firewall, VPN — no inbound ports needed
- Ideal for personal deployment on a home server, VPS, or laptop
Telegram (webhook — for always-on servers): - Requires HTTPS on ports 443/80/88/8443 - Self-signed certificate acceptable (upload to Telegram via setWebhook) - Better for high-volume or multi-instance deployments - Telegram retries with exponential backoff; must return 200 quickly
Slack (Socket Mode — simplest): - No public URL required - WebSocket connection initiated from bot to Slack - Max 10 concurrent connections - Ideal for self-hosted, behind-firewall deployments - Recommended by Slack for non-Marketplace apps
Slack (HTTP — for production): - Requires public HTTPS endpoint - Better reliability and horizontal scaling - Required for Slack Marketplace distribution
6.2 Daemon Integration¶
Picobot already has a daemon system (picobot daemon run). Channel adapters should integrate naturally:
picobot daemon run # existing: cron + heartbeat
picobot daemon run --telegram # add: telegram channel
picobot daemon run --slack # add: slack channel
picobot daemon run --telegram --slack # add: both channels
The daemon's existing asyncio.gather() pattern can include channel tasks alongside cron and heartbeat services. PID file locking ensures single instance.
6.3 Configuration¶
Extend ~/.picobot/config.json:
{
"channels": {
"telegram": {
"enabled": true,
"token": "123456:ABC-DEF...",
"allow_from": ["12345678"],
"mode": "polling",
"reply_to_message": true
},
"slack": {
"enabled": true,
"bot_token": "xoxb-...",
"app_token": "xapp-...",
"mode": "socket",
"allow_from": []
}
}
}
6.4 Rate Limiting Considerations¶
Telegram:
- Use python-telegram-bot's built-in AIORateLimiter or implement token-bucket rate limiting
- Respect 1 msg/sec per chat for edit-in-place streaming (throttle edits)
- Honor adaptive_retry field in 429 responses
- Connection pool size of 8-16 for concurrent operations
Slack: - Bolt SDK handles rate limiting internally with retry logic - chat.postMessage: 1/sec/channel — not usually an issue for single-user - Streaming methods not separately rate-limited in documentation - For bulk operations, use queue with backoff
6.5 Cost Analysis¶
Telegram: - Free for standard use (30 msg/sec included) - Paid broadcasts only if sending to 1000+ recipients/sec (0.1 Stars/msg) - No hosting costs if self-hosted
Slack: - Free tier: limited message history (90 days), limited integrations - Pro tier: $8.75/user/month for full history and integrations - Socket Mode: free, no infrastructure costs - HTTP mode: requires hosting for webhook endpoint
7. Counter-Perspective and Alternatives¶
7.1 Should Picobot Build Platform-Specific Adapters?¶
Arguments for: - Telegram and Slack cover the two largest use cases (personal mobile access and team workspace) - Both have mature Python libraries (aiogram/python-telegram-bot, slack-bolt) - AI-specific APIs (streaming, assistant framework) reward platform-specific integration - The nanobot upstream already has Telegram — picobot would be extending proven patterns - Personal AI agent is most useful when accessible from a phone (Telegram) or work tool (Slack)
Arguments against: - Platform-specific code creates maintenance burden as APIs evolve - Each adapter is 300-500 lines that must track breaking changes - Telegram's streaming API is still buggy (sendMessageDraft limitations) - Slack's rate limit changes (May 2025) show platform risk
7.2 Alternative Platforms¶
Discord: - Strong bot ecosystem, good for community/team use - discord.py library mature and async-native - Slash commands, threads, embeds for rich responses - No native AI streaming (would need edit-in-place pattern like Telegram) - Less relevant for personal/professional use than Telegram/Slack
Matrix: - Open protocol, self-hostable, end-to-end encrypted - Application Service API enables bot/bridge creation - mautrix Python library for bot development - Smaller user base but philosophically aligned with self-hosted picobot - Bridge to other platforms (Telegram, Slack, Discord) already exist
Signal: - Maximum privacy, end-to-end encrypted - Limited bot API (signal-cli, unofficial) - No rich formatting, no streaming - Niche but valuable for privacy-focused users
7.3 Generic Webhook Adapter¶
A generic webhook/HTTP adapter could handle arbitrary platforms:
class WebhookChannel(BaseChannel):
"""Generic HTTP webhook adapter.
POST /webhook/inbound -> receives messages
POST to configured URL -> sends responses
"""
Pros: Single implementation covers any platform with webhooks. Cons: Loses platform-specific features (streaming, rich formatting, typing indicators, threads). The lowest common denominator problem.
7.4 Universal Adapter Pattern¶
The ideal architecture separates concerns:
- BaseChannel ABC — defines the contract (already proposed in Section 4)
- Platform adapters — Telegram, Slack (300-500 lines each)
- Generic webhook — fallback for unsupported platforms
- Markdown translator — per-platform output formatting
This is exactly what OpenClaw does with its ChannelPlugin interface and what Rasa does with InputChannel/OutputChannel. The pattern is proven and scales to many platforms.
7.5 Recommendation: Start Specific, Abstract Later¶
Build Telegram and Slack first. They exercise different parts of the adapter interface (polling vs websocket, edit-in-place streaming vs native streaming, 4K vs 40K message limits). The BaseChannel ABC that emerges from these two implementations will be well-informed by real constraints. A generic webhook channel can follow as the third implementation.
Do not build a "universal adapter" first — the abstraction will be wrong without concrete implementations to guide it.
8. Recommendations¶
8.1 Implementation Plan¶
Phase 1: BaseChannel + Telegram (1-2 days)
- Define BaseChannel ABC in picobot/channels/base.py
- Implement TelegramChannel using python-telegram-bot with long polling
- Edit-in-place streaming via editMessageText
- Access control via allow_from config
- Typing indicator loop during agent processing
- Message splitting at paragraph boundaries for 4096 char limit
- Markdown-to-HTML translation
- CLI flag: picobot agent --telegram or picobot daemon run --telegram
- Config: channels.telegram section in config.json
Phase 2: Slack (1-2 days)
- Implement SlackChannel using slack-bolt with Socket Mode
- Native streaming via chat.startStream/appendStream/stopStream
- Assistant class integration for AI-native UX
- Thread-based session management
- Markdown-to-mrkdwn translation
- CLI flag: picobot agent --slack or picobot daemon run --slack
- Config: channels.slack section in config.json
Phase 3: Polish (1 day) - Gateway mode for running multiple channels simultaneously - Integration with daemon service (launchd/systemd) - Media handling (photos, documents, voice) - Error recovery and reconnection logic - Documentation
Phase 4: Optional Extensions - Discord adapter - Generic webhook adapter - Matrix adapter (via mautrix)
8.2 Library Choices¶
| Component | Library | Rationale |
|---|---|---|
| Telegram | python-telegram-bot v22+ |
Same as nanobot upstream, async, built-in rate limiter, mature |
| Slack | slack-bolt for Python |
Official SDK, Assistant class, chat_stream utility, Socket Mode |
| Markdown | Custom translator | Small utility (100 lines), no external dependency needed |
Alternative: aiogram for Telegram (fully async, modern) — but python-telegram-bot provides continuity with nanobot.
8.3 Key Design Decisions¶
-
In-process over subprocess: Use
AgentLoop.process_direct()directly, not picobot's NDJSON protocol. Avoids serialization overhead and enables direct streaming event callbacks. -
Polling over webhooks for Telegram: Simpler deployment, no SSL/domain needed, aligned with picobot's self-hosted philosophy. Webhook support as optional upgrade.
-
Socket Mode over HTTP for Slack: Same rationale — no public URL needed, simpler for self-hosted use.
-
Session key convention:
{channel}:{chat_id}for simple cases,{channel}:{chat_id}:{thread_ts}for threaded conversations. -
Edit-in-place for Telegram streaming: Despite sendMessageDraft existing, the edit pattern is proven reliable. Throttle edits to 1/sec to stay within rate limits.
-
Native streaming for Slack: First-class API support, no workarounds needed.
-
Optional dependencies: Both adapters guarded by try/import like the existing browser tool's
PLAYWRIGHT_AVAILABLEpattern.pip install picobot[telegram]orpip install picobot[slack].
8.4 Risk Mitigation¶
| Risk | Mitigation |
|---|---|
| Telegram API breaking changes | Pin python-telegram-bot version, test against Bot API changelog |
| Slack rate limit tightening | Bolt SDK handles retries; per-channel queuing for safety |
| sendMessageDraft remaining buggy | Edit-in-place is the primary strategy; draft streaming is optional |
| Token/credential leakage | Config file permissions (600), never log tokens, env var override option |
| Agent timeout on long tasks | Typing indicator loop, periodic status messages, configurable timeout |
| Platform downtime | Graceful reconnection with exponential backoff, session persistence across restarts |
8.5 Testing Strategy¶
- Unit tests: Mock platform APIs, verify message translation, session key generation, markdown conversion
- Integration tests: Use python-telegram-bot's test utilities and slack-bolt's MockApp
- Manual tests: Real bot token on a test Telegram group, test Slack workspace
- Existing test pattern: Follow picobot's 340-test suite conventions
Appendix A: NDJSON Protocol Reference¶
For reference, picobot's existing NDJSON protocol (from picobot/channels/json_channel.py):
Input messages (stdin):
{"type": "message", "content": "hello", "session": "optional-id", "attachments": [{"type": "text", "name": "file.csv", "content": "..."}]}
{"type": "ping"}
{"type": "shutdown"}
Output messages (stdout):
{"type": "ready"}
{"type": "response", "content": "...", "session": "..."}
{"type": "progress", "event": "tool_call_start", "tool": "web_search", "args": {...}}
{"type": "progress", "event": "tool_call_done", "tool": "web_search", "result_preview": "..."}
{"type": "progress", "event": "text_delta", "text": "Here is..."}
{"type": "error", "message": "...", "code": "..."}
{"type": "pong"}
The channel adapters do not use this protocol directly (they call process_direct() in-process), but it remains available for external tool integrations via picobot agent --json.
Appendix B: Estimated Line Counts¶
| File | Estimated Lines | Notes |
|---|---|---|
channels/base.py |
60-80 | ABC + ChannelUser + AccessControl |
channels/telegram.py |
350-450 | Polling, streaming, splitting, markdown |
channels/slack.py |
300-400 | Socket Mode, streaming, Assistant, mrkdwn |
channels/gateway.py |
80-120 | Multi-channel orchestrator |
channels/markdown.py |
80-120 | to_telegram_html, to_slack_mrkdwn |
config/schema.py changes |
30-40 | ChannelConfig models |
cli/commands.py changes |
20-30 | --telegram, --slack flags |
| Tests | 200-300 | Per existing test patterns |
| Total | 1120-1540 | Modest addition to codebase |