Channels let ZeroClaw communicate through any messaging platform. Implement theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/zeroclaw-labs/zeroclaw/llms.txt
Use this file to discover all available pages before exploring further.
Channel trait, register it, and your agent works everywhere.
Overview
Channels implement theChannel trait, which defines how messages are sent, received, and health-checked:
Step-by-Step Guide
use anyhow::Result;
use async_trait::async_trait;
use tokio::sync::mpsc;
use crate::channels::traits::{Channel, ChannelMessage};
/// Telegram Bot API channel
pub struct TelegramChannel {
bot_token: String,
allowed_users: Vec<String>,
client: reqwest::Client,
}
impl TelegramChannel {
pub fn new(bot_token: &str, allowed_users: Vec<String>) -> Self {
Self {
bot_token: bot_token.to_string(),
allowed_users,
client: reqwest::Client::new(),
}
}
fn api_url(&self, method: &str) -> String {
format!("https://api.telegram.org/bot{}/{method}", self.bot_token)
}
}
async fn send(&self, message: &str, chat_id: &str) -> Result<()> {
self.client
.post(self.api_url("sendMessage"))
.json(&serde_json::json!({
"chat_id": chat_id,
"text": message,
"parse_mode": "Markdown",
}))
.send()
.await?;
Ok(())
}
async fn listen(&self, tx: mpsc::Sender<ChannelMessage>) -> Result<()> {
let mut offset: i64 = 0;
loop {
let resp = self
.client
.get(self.api_url("getUpdates"))
.query(&[("offset", offset.to_string()), ("timeout", "30".into())])
.send()
.await?
.json::<serde_json::Value>()
.await?;
if let Some(updates) = resp["result"].as_array() {
for update in updates {
if let Some(msg) = update.get("message") {
let sender = msg["from"]["username"]
.as_str()
.unwrap_or("unknown")
.to_string();
// Check allowlist
if !self.allowed_users.is_empty()
&& !self.allowed_users.contains(&sender) {
continue;
}
let chat_id = msg["chat"]["id"].to_string();
let channel_msg = ChannelMessage {
id: msg["message_id"].to_string(),
sender,
reply_target: chat_id,
content: msg["text"].as_str().unwrap_or("").to_string(),
channel: "telegram".into(),
timestamp: msg["date"].as_u64().unwrap_or(0),
};
if tx.send(channel_msg).await.is_err() {
return Ok(()); // Channel closed
}
}
offset = update["update_id"].as_i64().unwrap_or(offset) + 1;
}
}
}
}
async fn health_check(&self) -> bool {
self.client
.get(self.api_url("getMe"))
.send()
.await
.map(|r| r.status().is_success())
.unwrap_or(false)
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TelegramConfig {
pub bot_token: String,
pub allowed_users: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChannelsConfig {
pub telegram: Option<TelegramConfig>,
// ... other channels
}
pub fn create_channels(config: &ChannelsConfig) -> Vec<Box<dyn Channel>> {
let mut channels: Vec<Box<dyn Channel>> = Vec::new();
if let Some(ref telegram) = config.telegram {
channels.push(Box::new(TelegramChannel::new(
&telegram.bot_token,
telegram.allowed_users.clone(),
)));
}
// ... other channels
channels
}
[channels.telegram]
bot_token = "123456789:ABCdefGHIjklMNOpqrsTUVwxyz"
allowed_users = ["your_username"]
Complete Example
Here’s the full implementation fromexamples/custom_channel.rs:
Advanced Features
Webhook-Based Channels
For platforms that support webhooks, implement webhook handlers instead of long-polling:Message Threading
Support threaded conversations:Rich Content
Handle images, files, and other media:Best Practices
Implement robust error handling
Implement robust error handling
Handle network failures gracefully:
Use allowlists for security
Use allowlists for security
Always validate senders:
Handle rate limits
Handle rate limits
Respect API rate limits:
Test with mock APIs
Test with mock APIs
Use wiremock or similar for testing:
Next Steps
Gateway Setup
Configure webhook endpoints for your channel
Creating Tools
Give your agent new capabilities