feat(gateway): full Telegram+Discord integration with agent loop

Complete multi-platform gateway wiring:

config:
- [platform.telegram] section with bot_token
- [platform.discord] section with bot_token + guild_id
- Threaded through GyandootConfig → AgentConfig via typed structs

gateway:
- GatewayRunner::new(platforms) takes Vec<Box<dyn Platform>>
- start() spawns all platform pollers, merges receivers
- serve(agent) runs full message loop: receive → access control →
  typing indicator → session_turn() → platform.send()
- Ctrl+C graceful shutdown via tokio::select!

cmd_serve now:
1. Loads config, creates agent
2. Builds Telegram + Discord adapters from config
3. Starts gateway pollers
4. Enters serve loop until Ctrl+C
This commit is contained in:
2026-06-12 16:52:28 +05:30
parent 07a14e2d7b
commit a694757b6f
6 changed files with 293 additions and 44 deletions

View File

@@ -131,9 +131,12 @@ pub struct PlatformSection {
/// Default platform (e.g. "cli", "telegram", "discord").
#[serde(default = "default_platform_default")]
pub default: String,
/// Platform-specific config (flattened for extensibility).
#[serde(default, flatten)]
pub platforms: HashMap<String, toml::Value>,
/// Telegram bot configuration.
#[serde(default)]
pub telegram: Option<TelegramPlatformCfg>,
/// Discord bot configuration.
#[serde(default)]
pub discord: Option<DiscordPlatformCfg>,
}
fn default_platform_default() -> String {
@@ -144,11 +147,31 @@ impl Default for PlatformSection {
fn default() -> Self {
Self {
default: default_platform_default(),
platforms: HashMap::new(),
telegram: None,
discord: None,
}
}
}
/// Telegram bot config as written in [platform.telegram] section.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TelegramPlatformCfg {
/// Bot token from @BotFather.
#[serde(default)]
pub bot_token: String,
}
/// Discord bot config as written in [platform.discord] section.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiscordPlatformCfg {
/// Bot token from Discord Developer Portal.
#[serde(default)]
pub bot_token: String,
/// Optional guild ID for channel discovery.
#[serde(default)]
pub guild_id: Option<u64>,
}
/// `[memory]` section.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemorySection {
@@ -343,8 +366,14 @@ impl From<GyandootConfig> for AgentConfig {
},
platform: PlatformRootConfig {
default: cfg.platform.default,
telegram: None,
discord: None,
telegram: cfg.platform.telegram.map(|t| TelegramConfig {
bot_token: t.bot_token,
allowed_users: Vec::new(),
}),
discord: cfg.platform.discord.map(|d| DiscordConfig {
bot_token: d.bot_token,
guild_id: d.guild_id,
}),
webhook: None,
},
memory: MemoryRootConfig {

View File

@@ -11,11 +11,11 @@ pub mod service;
pub use config::{
AgentConfig, AgentSection, AnthropicConfig, BrowserSection, ConfigLoader,
GatewaySection, GyandootConfig, IdentitySection,
DiscordPlatformCfg, GatewaySection, GyandootConfig, IdentitySection,
KanbanRootConfig, MemoryRootConfig, MemorySection, OllamaConfig,
OpenAIConfig, PlatformRootConfig, PlatformSection, ProviderRootConfig,
ProvidersSection, SecurityRootConfig, SkillsRootConfig, TelegramConfig,
WebhookConfig, DiscordConfig, load_config_from_file,
TelegramPlatformCfg, WebhookConfig, DiscordConfig, load_config_from_file,
};
pub use error::*;
pub use types::*;