storkit: merge 383_refactor_reorganize_chat_system_into_chat_module_with_transport_submodules

This commit is contained in:
dave
2026-03-24 17:54:51 +00:00
parent 92c53704f0
commit 79edc28334
31 changed files with 89 additions and 87 deletions

View File

@@ -4,6 +4,8 @@
//! sending and editing messages, allowing the bot logic (commands, htop,
//! notifications) to work against any chat platform — Matrix, WhatsApp, etc.
pub mod transport;
use async_trait::async_trait;
/// A platform-agnostic identifier for a sent message.
@@ -65,11 +67,11 @@ mod tests {
#[test]
fn whatsapp_transport_satisfies_trait() {
fn assert_transport<T: ChatTransport>() {}
assert_transport::<crate::whatsapp::WhatsAppTransport>();
assert_transport::<crate::chat::transport::whatsapp::WhatsAppTransport>();
// Verify it can be wrapped in Arc<dyn ChatTransport>.
let _: Arc<dyn ChatTransport> =
Arc::new(crate::whatsapp::WhatsAppTransport::new(
Arc::new(crate::chat::transport::whatsapp::WhatsAppTransport::new(
"test-phone".to_string(),
"test-token".to_string(),
"pipeline_notification".to_string(),
@@ -81,7 +83,7 @@ mod tests {
#[test]
fn matrix_transport_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<crate::matrix::transport_impl::MatrixTransport>();
assert_send_sync::<crate::chat::transport::matrix::transport_impl::MatrixTransport>();
}
/// Verify that SlackTransport satisfies the ChatTransport trait and
@@ -89,10 +91,10 @@ mod tests {
#[test]
fn slack_transport_satisfies_trait() {
fn assert_transport<T: ChatTransport>() {}
assert_transport::<crate::slack::SlackTransport>();
assert_transport::<crate::chat::transport::slack::SlackTransport>();
let _: Arc<dyn ChatTransport> =
Arc::new(crate::slack::SlackTransport::new("xoxb-test".to_string()));
Arc::new(crate::chat::transport::slack::SlackTransport::new("xoxb-test".to_string()));
}
/// Verify that TwilioWhatsAppTransport satisfies the ChatTransport trait
@@ -100,10 +102,10 @@ mod tests {
#[test]
fn twilio_transport_satisfies_trait() {
fn assert_transport<T: ChatTransport>() {}
assert_transport::<crate::whatsapp::TwilioWhatsAppTransport>();
assert_transport::<crate::chat::transport::whatsapp::TwilioWhatsAppTransport>();
let _: Arc<dyn ChatTransport> =
Arc::new(crate::whatsapp::TwilioWhatsAppTransport::new(
Arc::new(crate::chat::transport::whatsapp::TwilioWhatsAppTransport::new(
"ACtest".to_string(),
"authtoken".to_string(),
"+14155551234".to_string(),

View File

@@ -2,7 +2,7 @@ use crate::agents::AgentPool;
use crate::http::context::{PermissionDecision, PermissionForward};
use crate::llm::providers::claude_code::{ClaudeCodeProvider, ClaudeCodeResult};
use crate::slog;
use crate::transport::ChatTransport;
use crate::chat::ChatTransport;
use matrix_sdk::{
Client,
config::SyncSettings,
@@ -371,14 +371,14 @@ pub async fn run_bot(
"whatsapp" => {
if config.whatsapp_provider == "twilio" {
slog!("[matrix-bot] Using WhatsApp/Twilio transport");
Arc::new(crate::whatsapp::TwilioWhatsAppTransport::new(
Arc::new(crate::chat::transport::whatsapp::TwilioWhatsAppTransport::new(
config.twilio_account_sid.clone().unwrap_or_default(),
config.twilio_auth_token.clone().unwrap_or_default(),
config.twilio_whatsapp_number.clone().unwrap_or_default(),
))
} else {
slog!("[matrix-bot] Using WhatsApp/Meta transport");
Arc::new(crate::whatsapp::WhatsAppTransport::new(
Arc::new(crate::chat::transport::whatsapp::WhatsAppTransport::new(
config.whatsapp_phone_number_id.clone().unwrap_or_default(),
config.whatsapp_access_token.clone().unwrap_or_default(),
config
@@ -1612,7 +1612,7 @@ mod tests {
ambient_rooms: Arc::new(std::sync::Mutex::new(HashSet::new())),
agents: Arc::new(AgentPool::new_test(3000)),
htop_sessions: Arc::new(TokioMutex::new(HashMap::new())),
transport: Arc::new(crate::whatsapp::WhatsAppTransport::new("test-phone".to_string(), "test-token".to_string(), "pipeline_notification".to_string())),
transport: Arc::new(crate::chat::transport::whatsapp::WhatsAppTransport::new("test-phone".to_string(), "test-token".to_string(), "pipeline_notification".to_string())),
};
// Clone must work (required by Matrix SDK event handler injection).
let _cloned = ctx.clone();

View File

@@ -1,7 +1,7 @@
//! Handler for the `ambient` command.
use super::CommandContext;
use crate::matrix::config::save_ambient_rooms;
use crate::chat::transport::matrix::config::save_ambient_rooms;
/// Toggle ambient mode for this room.
///

View File

@@ -1,6 +1,6 @@
//! Handler stub for the `assign` command.
//!
//! The real implementation lives in `crate::matrix::assign` (async). This
//! The real implementation lives in `crate::chat::transport::matrix::assign` (async). This
//! stub exists only so that `assign` appears in the help registry — the
//! handler always returns `None` so the bot's message loop falls through to
//! the async handler in `bot.rs`.
@@ -8,7 +8,7 @@
use super::CommandContext;
pub(super) fn handle_assign(_ctx: &CommandContext) -> Option<String> {
// Handled asynchronously in bot.rs / crate::matrix::assign.
// Handled asynchronously in bot.rs / crate::chat::transport::matrix::assign.
None
}

View File

@@ -40,7 +40,7 @@ pub struct BotCommand {
/// message body.
///
/// All identifiers are platform-agnostic strings so this struct works with
/// any [`ChatTransport`](crate::transport::ChatTransport) implementation.
/// any [`ChatTransport`](crate::chat::ChatTransport) implementation.
pub struct CommandDispatch<'a> {
/// The bot's display name (e.g., "Timmy").
pub bot_name: &'a str,

View File

@@ -14,7 +14,7 @@ use tokio::sync::{Mutex as TokioMutex, watch};
use crate::agents::{AgentPool, AgentStatus};
use crate::slog;
use crate::transport::ChatTransport;
use crate::chat::ChatTransport;
use super::bot::markdown_to_html;

View File

@@ -6,7 +6,7 @@
use crate::io::story_metadata::parse_front_matter;
use crate::io::watcher::WatcherEvent;
use crate::slog;
use crate::transport::ChatTransport;
use crate::chat::ChatTransport;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;
@@ -265,7 +265,7 @@ pub fn spawn_notification_listener(
mod tests {
use super::*;
use async_trait::async_trait;
use crate::transport::MessageId;
use crate::chat::MessageId;
// ── MockTransport ───────────────────────────────────────────────────────
@@ -284,7 +284,7 @@ mod tests {
}
#[async_trait]
impl crate::transport::ChatTransport for MockTransport {
impl crate::chat::ChatTransport for MockTransport {
async fn send_message(&self, room_id: &str, plain: &str, html: &str) -> Result<MessageId, String> {
self.calls.lock().unwrap().push((room_id.to_string(), plain.to_string(), html.to_string()));
Ok("mock-msg-id".to_string())

View File

@@ -5,7 +5,7 @@
//! with clean context. File-system memories (auto-memory directory) are not
//! affected — only the in-memory/persisted conversation state is cleared.
use crate::matrix::bot::{ConversationHistory, RoomConversation};
use crate::chat::transport::matrix::bot::{ConversationHistory, RoomConversation};
use matrix_sdk::ruma::OwnedRoomId;
use std::path::Path;
@@ -52,7 +52,7 @@ pub async fn handle_reset(
let conv = guard.entry(room_id.clone()).or_insert_with(RoomConversation::default);
conv.session_id = None;
conv.entries.clear();
crate::matrix::bot::save_history(project_root, &guard);
crate::chat::transport::matrix::bot::save_history(project_root, &guard);
}
crate::slog!("[matrix-bot] reset command: cleared session for room {room_id} (bot={bot_name})");
"Session reset. Starting fresh — previous context has been cleared.".to_string()
@@ -138,7 +138,7 @@ mod tests {
#[tokio::test]
async fn handle_reset_clears_session_and_entries() {
use crate::matrix::bot::{ConversationEntry, ConversationRole};
use crate::chat::transport::matrix::bot::{ConversationEntry, ConversationRole};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex as TokioMutex;

View File

@@ -356,14 +356,14 @@ mod tests {
#[test]
fn start_command_is_registered() {
use crate::matrix::commands::commands;
use crate::chat::transport::matrix::commands::commands;
let found = commands().iter().any(|c| c.name == "start");
assert!(found, "start command must be in the registry");
}
#[test]
fn start_command_appears_in_help() {
let result = crate::matrix::commands::tests::try_cmd_addressed(
let result = crate::chat::transport::matrix::commands::tests::try_cmd_addressed(
"Timmy",
"@timmy:homeserver.local",
"@timmy help",
@@ -378,7 +378,7 @@ mod tests {
#[test]
fn start_command_falls_through_to_none_in_registry() {
// The start handler in the registry returns None (handled async in bot.rs).
let result = crate::matrix::commands::tests::try_cmd_addressed(
let result = crate::chat::transport::matrix::commands::tests::try_cmd_addressed(
"Timmy",
"@timmy:homeserver.local",
"@timmy start 42",

View File

@@ -10,7 +10,7 @@ use matrix_sdk::ruma::events::room::message::{
ReplacementMetadata, RoomMessageEventContent, RoomMessageEventContentWithoutRelation,
};
use crate::transport::{ChatTransport, MessageId};
use crate::chat::{ChatTransport, MessageId};
/// Matrix-backed [`ChatTransport`] implementation.
///

View File

@@ -0,0 +1,3 @@
pub mod matrix;
pub mod slack;
pub mod whatsapp;

View File

@@ -14,9 +14,9 @@ use std::sync::Arc;
use tokio::sync::Mutex as TokioMutex;
use crate::agents::AgentPool;
use crate::matrix::{ConversationEntry, ConversationRole, RoomConversation};
use crate::chat::transport::matrix::{ConversationEntry, ConversationRole, RoomConversation};
use crate::slog;
use crate::transport::{ChatTransport, MessageId};
use crate::chat::{ChatTransport, MessageId};
// ── Slack API base URL (overridable for tests) ──────────────────────────
@@ -669,7 +669,7 @@ pub async fn slash_command_receive(
format!("{} {keyword} {}", ctx.bot_name, payload.text)
};
use crate::matrix::commands::{CommandDispatch, try_handle_command};
use crate::chat::transport::matrix::commands::{CommandDispatch, try_handle_command};
let dispatch = CommandDispatch {
bot_name: &ctx.bot_name,
@@ -701,7 +701,7 @@ async fn handle_incoming_message(
user: &str,
message: &str,
) {
use crate::matrix::commands::{CommandDispatch, try_handle_command};
use crate::chat::transport::matrix::commands::{CommandDispatch, try_handle_command};
let dispatch = CommandDispatch {
bot_name: &ctx.bot_name,
@@ -721,12 +721,12 @@ async fn handle_incoming_message(
}
// Check for async commands (htop, delete).
if let Some(htop_cmd) = crate::matrix::htop::extract_htop_command(
if let Some(htop_cmd) = crate::chat::transport::matrix::htop::extract_htop_command(
message,
&ctx.bot_name,
&ctx.bot_user_id,
) {
use crate::matrix::htop::HtopCommand;
use crate::chat::transport::matrix::htop::HtopCommand;
slog!("[slack] Handling htop command from {user} in {channel}");
match htop_cmd {
HtopCommand::Stop => {
@@ -738,7 +738,7 @@ async fn handle_incoming_message(
HtopCommand::Start { duration_secs } => {
// On Slack, htop uses native message editing for live updates.
let snapshot =
crate::matrix::htop::build_htop_message(&ctx.agents, 0, duration_secs);
crate::chat::transport::matrix::htop::build_htop_message(&ctx.agents, 0, duration_secs);
let msg_id = match ctx.transport.send_message(channel, &snapshot, "").await {
Ok(id) => id,
Err(e) => {
@@ -755,7 +755,7 @@ async fn handle_incoming_message(
let total_ticks = (duration_secs as usize) / 2;
for tick in 1..=total_ticks {
tokio::time::sleep(interval).await;
let updated = crate::matrix::htop::build_htop_message(
let updated = crate::chat::transport::matrix::htop::build_htop_message(
&agents,
(tick * 2) as u32,
duration_secs,
@@ -773,15 +773,15 @@ async fn handle_incoming_message(
return;
}
if let Some(del_cmd) = crate::matrix::delete::extract_delete_command(
if let Some(del_cmd) = crate::chat::transport::matrix::delete::extract_delete_command(
message,
&ctx.bot_name,
&ctx.bot_user_id,
) {
let response = match del_cmd {
crate::matrix::delete::DeleteCommand::Delete { story_number } => {
crate::chat::transport::matrix::delete::DeleteCommand::Delete { story_number } => {
slog!("[slack] Handling delete command from {user}: story {story_number}");
crate::matrix::delete::handle_delete(
crate::chat::transport::matrix::delete::handle_delete(
&ctx.bot_name,
&story_number,
&ctx.project_root,
@@ -789,7 +789,7 @@ async fn handle_incoming_message(
)
.await
}
crate::matrix::delete::DeleteCommand::BadArgs => {
crate::chat::transport::matrix::delete::DeleteCommand::BadArgs => {
format!("Usage: `{} delete <number>`", ctx.bot_name)
}
};
@@ -810,7 +810,7 @@ async fn handle_llm_message(
user_message: &str,
) {
use crate::llm::providers::claude_code::{ClaudeCodeProvider, ClaudeCodeResult};
use crate::matrix::drain_complete_paragraphs;
use crate::chat::transport::matrix::drain_complete_paragraphs;
use std::sync::atomic::{AtomicBool, Ordering};
use tokio::sync::watch;
@@ -1408,7 +1408,7 @@ mod tests {
fn slash_command_dispatches_through_command_registry() {
// Verify that the synthetic message built by the slash handler
// correctly dispatches through try_handle_command.
use crate::matrix::commands::{CommandDispatch, try_handle_command};
use crate::chat::transport::matrix::commands::{CommandDispatch, try_handle_command};
let agents = test_agents();
let ambient_rooms = test_ambient_rooms();
@@ -1435,7 +1435,7 @@ mod tests {
#[test]
fn slash_command_show_passes_args_through_registry() {
use crate::matrix::commands::{CommandDispatch, try_handle_command};
use crate::chat::transport::matrix::commands::{CommandDispatch, try_handle_command};
let agents = test_agents();
let ambient_rooms = test_ambient_rooms();

View File

@@ -14,9 +14,9 @@ use std::sync::Arc;
use tokio::sync::Mutex as TokioMutex;
use crate::agents::AgentPool;
use crate::matrix::{ConversationEntry, ConversationRole, RoomConversation};
use crate::chat::transport::matrix::{ConversationEntry, ConversationRole, RoomConversation};
use crate::slog;
use crate::transport::{ChatTransport, MessageId};
use crate::chat::{ChatTransport, MessageId};
// ── API base URLs (overridable for tests) ────────────────────────────────
@@ -908,7 +908,7 @@ async fn handle_incoming_message(
sender: &str,
message: &str,
) {
use crate::matrix::commands::{CommandDispatch, try_handle_command};
use crate::chat::transport::matrix::commands::{CommandDispatch, try_handle_command};
// Record this inbound message to keep the 24-hour window open.
ctx.window_tracker.record_message(sender);
@@ -931,12 +931,12 @@ async fn handle_incoming_message(
}
// Check for async commands (htop, delete).
if let Some(htop_cmd) = crate::matrix::htop::extract_htop_command(
if let Some(htop_cmd) = crate::chat::transport::matrix::htop::extract_htop_command(
message,
&ctx.bot_name,
&ctx.bot_user_id,
) {
use crate::matrix::htop::HtopCommand;
use crate::chat::transport::matrix::htop::HtopCommand;
slog!("[whatsapp] Handling htop command from {sender}");
match htop_cmd {
HtopCommand::Stop => {
@@ -951,7 +951,7 @@ async fn handle_incoming_message(
// On WhatsApp, send a single snapshot instead of a live-updating
// dashboard since we can't edit messages.
let snapshot =
crate::matrix::htop::build_htop_message(&ctx.agents, 0, duration_secs);
crate::chat::transport::matrix::htop::build_htop_message(&ctx.agents, 0, duration_secs);
let _ = ctx
.transport
.send_message(sender, &snapshot, "")
@@ -961,15 +961,15 @@ async fn handle_incoming_message(
return;
}
if let Some(del_cmd) = crate::matrix::delete::extract_delete_command(
if let Some(del_cmd) = crate::chat::transport::matrix::delete::extract_delete_command(
message,
&ctx.bot_name,
&ctx.bot_user_id,
) {
let response = match del_cmd {
crate::matrix::delete::DeleteCommand::Delete { story_number } => {
crate::chat::transport::matrix::delete::DeleteCommand::Delete { story_number } => {
slog!("[whatsapp] Handling delete command from {sender}: story {story_number}");
crate::matrix::delete::handle_delete(
crate::chat::transport::matrix::delete::handle_delete(
&ctx.bot_name,
&story_number,
&ctx.project_root,
@@ -977,7 +977,7 @@ async fn handle_incoming_message(
)
.await
}
crate::matrix::delete::DeleteCommand::BadArgs => {
crate::chat::transport::matrix::delete::DeleteCommand::BadArgs => {
format!("Usage: `{} delete <number>`", ctx.bot_name)
}
};
@@ -997,7 +997,7 @@ async fn handle_llm_message(
user_message: &str,
) {
use crate::llm::providers::claude_code::{ClaudeCodeProvider, ClaudeCodeResult};
use crate::matrix::drain_complete_paragraphs;
use crate::chat::transport::matrix::drain_complete_paragraphs;
use std::sync::atomic::{AtomicBool, Ordering};
use tokio::sync::watch;

View File

@@ -10,7 +10,7 @@
//! (it clears local session state and message history) and is not routed here.
use crate::http::context::{AppContext, OpenApiResult};
use crate::matrix::commands::CommandDispatch;
use crate::chat::transport::matrix::commands::CommandDispatch;
use poem::http::StatusCode;
use poem_openapi::{Object, OpenApi, Tags, payload::Json};
use serde::{Deserialize, Serialize};
@@ -112,7 +112,7 @@ fn dispatch_sync(
format!("{bot_name} {cmd} {args}")
};
match crate::matrix::commands::try_handle_command(&dispatch, &synthetic) {
match crate::chat::transport::matrix::commands::try_handle_command(&dispatch, &synthetic) {
Some(response) => response,
None => {
// Command exists in the registry but its fallback handler returns None
@@ -138,7 +138,7 @@ async fn dispatch_assign(
return "Usage: `/assign <number> <model>` (e.g. `/assign 42 opus`)".to_string();
}
crate::matrix::assign::handle_assign("web-ui", number_str, model_str, project_root, agents)
crate::chat::transport::matrix::assign::handle_assign("web-ui", number_str, model_str, project_root, agents)
.await
}
@@ -163,7 +163,7 @@ async fn dispatch_start(
Some(hint_str)
};
crate::matrix::start::handle_start("web-ui", number_str, agent_hint, project_root, agents)
crate::chat::transport::matrix::start::handle_start("web-ui", number_str, agent_hint, project_root, agents)
.await
}
@@ -176,14 +176,14 @@ async fn dispatch_delete(
if number_str.is_empty() || !number_str.chars().all(|c| c.is_ascii_digit()) {
return "Usage: `/delete <number>` (e.g. `/delete 42`)".to_string();
}
crate::matrix::delete::handle_delete("web-ui", number_str, project_root, agents).await
crate::chat::transport::matrix::delete::handle_delete("web-ui", number_str, project_root, agents).await
}
async fn dispatch_rebuild(
project_root: &std::path::Path,
agents: &Arc<crate::agents::AgentPool>,
) -> String {
crate::matrix::rebuild::handle_rebuild("web-ui", project_root, agents).await
crate::chat::transport::matrix::rebuild::handle_rebuild("web-ui", project_root, agents).await
}
// ---------------------------------------------------------------------------

View File

@@ -31,8 +31,8 @@ use settings::SettingsApi;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use crate::slack::SlackWebhookContext;
use crate::whatsapp::WhatsAppWebhookContext;
use crate::chat::transport::slack::SlackWebhookContext;
use crate::chat::transport::whatsapp::WhatsAppWebhookContext;
const DEFAULT_PORT: u16 = 3001;
@@ -85,8 +85,8 @@ pub fn build_routes(
if let Some(wa_ctx) = whatsapp_ctx {
route = route.at(
"/webhook/whatsapp",
get(crate::whatsapp::webhook_verify)
.post(crate::whatsapp::webhook_receive)
get(crate::chat::transport::whatsapp::webhook_verify)
.post(crate::chat::transport::whatsapp::webhook_receive)
.data(wa_ctx),
);
}
@@ -95,11 +95,11 @@ pub fn build_routes(
route = route
.at(
"/webhook/slack",
post(crate::slack::webhook_receive).data(sl_ctx.clone()),
post(crate::chat::transport::slack::webhook_receive).data(sl_ctx.clone()),
)
.at(
"/webhook/slack/command",
post(crate::slack::slash_command_receive).data(sl_ctx),
post(crate::chat::transport::slack::slash_command_receive).data(sl_ctx),
);
}

View File

@@ -9,13 +9,10 @@ mod http;
mod io;
mod llm;
pub mod log_buffer;
mod matrix;
mod chat;
pub mod rebuild;
pub mod slack;
mod state;
mod store;
pub mod transport;
pub mod whatsapp;
mod workflow;
mod worktree;
@@ -267,15 +264,15 @@ async fn main() -> Result<(), std::io::Error> {
let agents_for_shutdown = Arc::clone(&agents);
// Build WhatsApp webhook context if bot.toml configures transport = "whatsapp".
let whatsapp_ctx: Option<Arc<whatsapp::WhatsAppWebhookContext>> = startup_root
let whatsapp_ctx: Option<Arc<chat::transport::whatsapp::WhatsAppWebhookContext>> = startup_root
.as_ref()
.and_then(|root| matrix::BotConfig::load(root))
.and_then(|root| chat::transport::matrix::BotConfig::load(root))
.filter(|cfg| cfg.transport == "whatsapp")
.map(|cfg| {
let provider = cfg.whatsapp_provider.clone();
let transport: Arc<dyn crate::transport::ChatTransport> =
let transport: Arc<dyn crate::chat::ChatTransport> =
if provider == "twilio" {
Arc::new(whatsapp::TwilioWhatsAppTransport::new(
Arc::new(chat::transport::whatsapp::TwilioWhatsAppTransport::new(
cfg.twilio_account_sid.clone().unwrap_or_default(),
cfg.twilio_auth_token.clone().unwrap_or_default(),
cfg.twilio_whatsapp_number.clone().unwrap_or_default(),
@@ -285,7 +282,7 @@ async fn main() -> Result<(), std::io::Error> {
.whatsapp_notification_template
.clone()
.unwrap_or_else(|| "pipeline_notification".to_string());
Arc::new(whatsapp::WhatsAppTransport::new(
Arc::new(chat::transport::whatsapp::WhatsAppTransport::new(
cfg.whatsapp_phone_number_id.clone().unwrap_or_default(),
cfg.whatsapp_access_token.clone().unwrap_or_default(),
template_name,
@@ -296,8 +293,8 @@ async fn main() -> Result<(), std::io::Error> {
.clone()
.unwrap_or_else(|| "Assistant".to_string());
let root = startup_root.clone().unwrap();
let history = whatsapp::load_whatsapp_history(&root);
Arc::new(whatsapp::WhatsAppWebhookContext {
let history = chat::transport::whatsapp::load_whatsapp_history(&root);
Arc::new(chat::transport::whatsapp::WhatsAppWebhookContext {
verify_token: cfg.whatsapp_verify_token.clone().unwrap_or_default(),
provider,
transport,
@@ -308,17 +305,17 @@ async fn main() -> Result<(), std::io::Error> {
ambient_rooms: Arc::new(std::sync::Mutex::new(std::collections::HashSet::new())),
history: std::sync::Arc::new(tokio::sync::Mutex::new(history)),
history_size: cfg.history_size,
window_tracker: Arc::new(whatsapp::MessagingWindowTracker::new()),
window_tracker: Arc::new(chat::transport::whatsapp::MessagingWindowTracker::new()),
})
});
// Build Slack webhook context if bot.toml configures transport = "slack".
let slack_ctx: Option<Arc<slack::SlackWebhookContext>> = startup_root
let slack_ctx: Option<Arc<chat::transport::slack::SlackWebhookContext>> = startup_root
.as_ref()
.and_then(|root| matrix::BotConfig::load(root))
.and_then(|root| chat::transport::matrix::BotConfig::load(root))
.filter(|cfg| cfg.transport == "slack")
.map(|cfg| {
let transport = Arc::new(slack::SlackTransport::new(
let transport = Arc::new(chat::transport::slack::SlackTransport::new(
cfg.slack_bot_token.clone().unwrap_or_default(),
));
let bot_name = cfg
@@ -326,10 +323,10 @@ async fn main() -> Result<(), std::io::Error> {
.clone()
.unwrap_or_else(|| "Assistant".to_string());
let root = startup_root.clone().unwrap();
let history = slack::load_slack_history(&root);
let history = chat::transport::slack::load_slack_history(&root);
let channel_ids: std::collections::HashSet<String> =
cfg.slack_channel_ids.iter().cloned().collect();
Arc::new(slack::SlackWebhookContext {
Arc::new(chat::transport::slack::SlackWebhookContext {
signing_secret: cfg.slack_signing_secret.clone().unwrap_or_default(),
transport,
project_root: root,
@@ -353,7 +350,7 @@ async fn main() -> Result<(), std::io::Error> {
if let Some(ref ctx) = slack_ctx {
let channels: Vec<String> = ctx.channel_ids.iter().cloned().collect();
Some(Arc::new(BotShutdownNotifier::new(
Arc::clone(&ctx.transport) as Arc<dyn crate::transport::ChatTransport>,
Arc::clone(&ctx.transport) as Arc<dyn crate::chat::ChatTransport>,
channels,
ctx.bot_name.clone(),
)))
@@ -362,7 +359,7 @@ async fn main() -> Result<(), std::io::Error> {
};
// Retain a reference to the WhatsApp context for shutdown notifications.
// At shutdown time we read ambient_rooms to get the current set of active senders.
let whatsapp_ctx_for_shutdown: Option<Arc<whatsapp::WhatsAppWebhookContext>> =
let whatsapp_ctx_for_shutdown: Option<Arc<chat::transport::whatsapp::WhatsAppWebhookContext>> =
whatsapp_ctx.clone();
// Watch channel: signals the Matrix bot task to send a shutdown announcement.
@@ -391,7 +388,7 @@ async fn main() -> Result<(), std::io::Error> {
// Optional Matrix bot: connect to the homeserver and start listening for
// messages if `.storkit/bot.toml` is present and enabled.
if let Some(ref root) = startup_root {
matrix::spawn_bot(
chat::transport::matrix::spawn_bot(
root,
watcher_tx_for_bot,
perm_rx_for_bot,
@@ -446,7 +443,7 @@ async fn main() -> Result<(), std::io::Error> {
let rooms: Vec<String> = ctx.ambient_rooms.lock().unwrap().iter().cloned().collect();
if !rooms.is_empty() {
let wa_notifier = BotShutdownNotifier::new(
Arc::clone(&ctx.transport) as Arc<dyn crate::transport::ChatTransport>,
Arc::clone(&ctx.transport) as Arc<dyn crate::chat::ChatTransport>,
rooms,
ctx.bot_name.clone(),
);

View File

@@ -2,7 +2,7 @@
use crate::agents::AgentPool;
use crate::slog;
use crate::transport::ChatTransport;
use crate::chat::ChatTransport;
use std::path::Path;
use std::sync::Arc;
@@ -186,7 +186,7 @@ pub async fn rebuild_and_restart(
mod tests {
use super::*;
use async_trait::async_trait;
use crate::transport::MessageId;
use crate::chat::MessageId;
use std::sync::Mutex;
/// In-memory transport that records sent messages.