storkit: merge 383_refactor_reorganize_chat_system_into_chat_module_with_transport_submodules
This commit is contained in:
@@ -4,6 +4,8 @@
|
|||||||
//! sending and editing messages, allowing the bot logic (commands, htop,
|
//! sending and editing messages, allowing the bot logic (commands, htop,
|
||||||
//! notifications) to work against any chat platform — Matrix, WhatsApp, etc.
|
//! notifications) to work against any chat platform — Matrix, WhatsApp, etc.
|
||||||
|
|
||||||
|
pub mod transport;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
/// A platform-agnostic identifier for a sent message.
|
/// A platform-agnostic identifier for a sent message.
|
||||||
@@ -65,11 +67,11 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn whatsapp_transport_satisfies_trait() {
|
fn whatsapp_transport_satisfies_trait() {
|
||||||
fn assert_transport<T: ChatTransport>() {}
|
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>.
|
// Verify it can be wrapped in Arc<dyn ChatTransport>.
|
||||||
let _: 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-phone".to_string(),
|
||||||
"test-token".to_string(),
|
"test-token".to_string(),
|
||||||
"pipeline_notification".to_string(),
|
"pipeline_notification".to_string(),
|
||||||
@@ -81,7 +83,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn matrix_transport_is_send_sync() {
|
fn matrix_transport_is_send_sync() {
|
||||||
fn assert_send_sync<T: 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
|
/// Verify that SlackTransport satisfies the ChatTransport trait and
|
||||||
@@ -89,10 +91,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn slack_transport_satisfies_trait() {
|
fn slack_transport_satisfies_trait() {
|
||||||
fn assert_transport<T: ChatTransport>() {}
|
fn assert_transport<T: ChatTransport>() {}
|
||||||
assert_transport::<crate::slack::SlackTransport>();
|
assert_transport::<crate::chat::transport::slack::SlackTransport>();
|
||||||
|
|
||||||
let _: Arc<dyn ChatTransport> =
|
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
|
/// Verify that TwilioWhatsAppTransport satisfies the ChatTransport trait
|
||||||
@@ -100,10 +102,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn twilio_transport_satisfies_trait() {
|
fn twilio_transport_satisfies_trait() {
|
||||||
fn assert_transport<T: ChatTransport>() {}
|
fn assert_transport<T: ChatTransport>() {}
|
||||||
assert_transport::<crate::whatsapp::TwilioWhatsAppTransport>();
|
assert_transport::<crate::chat::transport::whatsapp::TwilioWhatsAppTransport>();
|
||||||
|
|
||||||
let _: Arc<dyn ChatTransport> =
|
let _: Arc<dyn ChatTransport> =
|
||||||
Arc::new(crate::whatsapp::TwilioWhatsAppTransport::new(
|
Arc::new(crate::chat::transport::whatsapp::TwilioWhatsAppTransport::new(
|
||||||
"ACtest".to_string(),
|
"ACtest".to_string(),
|
||||||
"authtoken".to_string(),
|
"authtoken".to_string(),
|
||||||
"+14155551234".to_string(),
|
"+14155551234".to_string(),
|
||||||
@@ -2,7 +2,7 @@ use crate::agents::AgentPool;
|
|||||||
use crate::http::context::{PermissionDecision, PermissionForward};
|
use crate::http::context::{PermissionDecision, PermissionForward};
|
||||||
use crate::llm::providers::claude_code::{ClaudeCodeProvider, ClaudeCodeResult};
|
use crate::llm::providers::claude_code::{ClaudeCodeProvider, ClaudeCodeResult};
|
||||||
use crate::slog;
|
use crate::slog;
|
||||||
use crate::transport::ChatTransport;
|
use crate::chat::ChatTransport;
|
||||||
use matrix_sdk::{
|
use matrix_sdk::{
|
||||||
Client,
|
Client,
|
||||||
config::SyncSettings,
|
config::SyncSettings,
|
||||||
@@ -371,14 +371,14 @@ pub async fn run_bot(
|
|||||||
"whatsapp" => {
|
"whatsapp" => {
|
||||||
if config.whatsapp_provider == "twilio" {
|
if config.whatsapp_provider == "twilio" {
|
||||||
slog!("[matrix-bot] Using WhatsApp/Twilio transport");
|
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_account_sid.clone().unwrap_or_default(),
|
||||||
config.twilio_auth_token.clone().unwrap_or_default(),
|
config.twilio_auth_token.clone().unwrap_or_default(),
|
||||||
config.twilio_whatsapp_number.clone().unwrap_or_default(),
|
config.twilio_whatsapp_number.clone().unwrap_or_default(),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
slog!("[matrix-bot] Using WhatsApp/Meta transport");
|
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_phone_number_id.clone().unwrap_or_default(),
|
||||||
config.whatsapp_access_token.clone().unwrap_or_default(),
|
config.whatsapp_access_token.clone().unwrap_or_default(),
|
||||||
config
|
config
|
||||||
@@ -1612,7 +1612,7 @@ mod tests {
|
|||||||
ambient_rooms: Arc::new(std::sync::Mutex::new(HashSet::new())),
|
ambient_rooms: Arc::new(std::sync::Mutex::new(HashSet::new())),
|
||||||
agents: Arc::new(AgentPool::new_test(3000)),
|
agents: Arc::new(AgentPool::new_test(3000)),
|
||||||
htop_sessions: Arc::new(TokioMutex::new(HashMap::new())),
|
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).
|
// Clone must work (required by Matrix SDK event handler injection).
|
||||||
let _cloned = ctx.clone();
|
let _cloned = ctx.clone();
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Handler for the `ambient` command.
|
//! Handler for the `ambient` command.
|
||||||
|
|
||||||
use super::CommandContext;
|
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.
|
/// Toggle ambient mode for this room.
|
||||||
///
|
///
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Handler stub for the `assign` command.
|
//! 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
|
//! 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
|
//! handler always returns `None` so the bot's message loop falls through to
|
||||||
//! the async handler in `bot.rs`.
|
//! the async handler in `bot.rs`.
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
use super::CommandContext;
|
use super::CommandContext;
|
||||||
|
|
||||||
pub(super) fn handle_assign(_ctx: &CommandContext) -> Option<String> {
|
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
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ pub struct BotCommand {
|
|||||||
/// message body.
|
/// message body.
|
||||||
///
|
///
|
||||||
/// All identifiers are platform-agnostic strings so this struct works with
|
/// 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> {
|
pub struct CommandDispatch<'a> {
|
||||||
/// The bot's display name (e.g., "Timmy").
|
/// The bot's display name (e.g., "Timmy").
|
||||||
pub bot_name: &'a str,
|
pub bot_name: &'a str,
|
||||||
@@ -14,7 +14,7 @@ use tokio::sync::{Mutex as TokioMutex, watch};
|
|||||||
|
|
||||||
use crate::agents::{AgentPool, AgentStatus};
|
use crate::agents::{AgentPool, AgentStatus};
|
||||||
use crate::slog;
|
use crate::slog;
|
||||||
use crate::transport::ChatTransport;
|
use crate::chat::ChatTransport;
|
||||||
|
|
||||||
use super::bot::markdown_to_html;
|
use super::bot::markdown_to_html;
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
use crate::io::story_metadata::parse_front_matter;
|
use crate::io::story_metadata::parse_front_matter;
|
||||||
use crate::io::watcher::WatcherEvent;
|
use crate::io::watcher::WatcherEvent;
|
||||||
use crate::slog;
|
use crate::slog;
|
||||||
use crate::transport::ChatTransport;
|
use crate::chat::ChatTransport;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -265,7 +265,7 @@ pub fn spawn_notification_listener(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use crate::transport::MessageId;
|
use crate::chat::MessageId;
|
||||||
|
|
||||||
// ── MockTransport ───────────────────────────────────────────────────────
|
// ── MockTransport ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -284,7 +284,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[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> {
|
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()));
|
self.calls.lock().unwrap().push((room_id.to_string(), plain.to_string(), html.to_string()));
|
||||||
Ok("mock-msg-id".to_string())
|
Ok("mock-msg-id".to_string())
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
//! with clean context. File-system memories (auto-memory directory) are not
|
//! with clean context. File-system memories (auto-memory directory) are not
|
||||||
//! affected — only the in-memory/persisted conversation state is cleared.
|
//! 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 matrix_sdk::ruma::OwnedRoomId;
|
||||||
use std::path::Path;
|
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);
|
let conv = guard.entry(room_id.clone()).or_insert_with(RoomConversation::default);
|
||||||
conv.session_id = None;
|
conv.session_id = None;
|
||||||
conv.entries.clear();
|
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})");
|
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()
|
"Session reset. Starting fresh — previous context has been cleared.".to_string()
|
||||||
@@ -138,7 +138,7 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn handle_reset_clears_session_and_entries() {
|
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::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::Mutex as TokioMutex;
|
use tokio::sync::Mutex as TokioMutex;
|
||||||
@@ -356,14 +356,14 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn start_command_is_registered() {
|
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");
|
let found = commands().iter().any(|c| c.name == "start");
|
||||||
assert!(found, "start command must be in the registry");
|
assert!(found, "start command must be in the registry");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn start_command_appears_in_help() {
|
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",
|
||||||
"@timmy:homeserver.local",
|
"@timmy:homeserver.local",
|
||||||
"@timmy help",
|
"@timmy help",
|
||||||
@@ -378,7 +378,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn start_command_falls_through_to_none_in_registry() {
|
fn start_command_falls_through_to_none_in_registry() {
|
||||||
// The start handler in the registry returns None (handled async in bot.rs).
|
// 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",
|
||||||
"@timmy:homeserver.local",
|
"@timmy:homeserver.local",
|
||||||
"@timmy start 42",
|
"@timmy start 42",
|
||||||
@@ -10,7 +10,7 @@ use matrix_sdk::ruma::events::room::message::{
|
|||||||
ReplacementMetadata, RoomMessageEventContent, RoomMessageEventContentWithoutRelation,
|
ReplacementMetadata, RoomMessageEventContent, RoomMessageEventContentWithoutRelation,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::transport::{ChatTransport, MessageId};
|
use crate::chat::{ChatTransport, MessageId};
|
||||||
|
|
||||||
/// Matrix-backed [`ChatTransport`] implementation.
|
/// Matrix-backed [`ChatTransport`] implementation.
|
||||||
///
|
///
|
||||||
3
server/src/chat/transport/mod.rs
Normal file
3
server/src/chat/transport/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pub mod matrix;
|
||||||
|
pub mod slack;
|
||||||
|
pub mod whatsapp;
|
||||||
@@ -14,9 +14,9 @@ use std::sync::Arc;
|
|||||||
use tokio::sync::Mutex as TokioMutex;
|
use tokio::sync::Mutex as TokioMutex;
|
||||||
|
|
||||||
use crate::agents::AgentPool;
|
use crate::agents::AgentPool;
|
||||||
use crate::matrix::{ConversationEntry, ConversationRole, RoomConversation};
|
use crate::chat::transport::matrix::{ConversationEntry, ConversationRole, RoomConversation};
|
||||||
use crate::slog;
|
use crate::slog;
|
||||||
use crate::transport::{ChatTransport, MessageId};
|
use crate::chat::{ChatTransport, MessageId};
|
||||||
|
|
||||||
// ── Slack API base URL (overridable for tests) ──────────────────────────
|
// ── Slack API base URL (overridable for tests) ──────────────────────────
|
||||||
|
|
||||||
@@ -669,7 +669,7 @@ pub async fn slash_command_receive(
|
|||||||
format!("{} {keyword} {}", ctx.bot_name, payload.text)
|
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 {
|
let dispatch = CommandDispatch {
|
||||||
bot_name: &ctx.bot_name,
|
bot_name: &ctx.bot_name,
|
||||||
@@ -701,7 +701,7 @@ async fn handle_incoming_message(
|
|||||||
user: &str,
|
user: &str,
|
||||||
message: &str,
|
message: &str,
|
||||||
) {
|
) {
|
||||||
use crate::matrix::commands::{CommandDispatch, try_handle_command};
|
use crate::chat::transport::matrix::commands::{CommandDispatch, try_handle_command};
|
||||||
|
|
||||||
let dispatch = CommandDispatch {
|
let dispatch = CommandDispatch {
|
||||||
bot_name: &ctx.bot_name,
|
bot_name: &ctx.bot_name,
|
||||||
@@ -721,12 +721,12 @@ async fn handle_incoming_message(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for async commands (htop, delete).
|
// 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,
|
message,
|
||||||
&ctx.bot_name,
|
&ctx.bot_name,
|
||||||
&ctx.bot_user_id,
|
&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}");
|
slog!("[slack] Handling htop command from {user} in {channel}");
|
||||||
match htop_cmd {
|
match htop_cmd {
|
||||||
HtopCommand::Stop => {
|
HtopCommand::Stop => {
|
||||||
@@ -738,7 +738,7 @@ async fn handle_incoming_message(
|
|||||||
HtopCommand::Start { duration_secs } => {
|
HtopCommand::Start { duration_secs } => {
|
||||||
// On Slack, htop uses native message editing for live updates.
|
// On Slack, htop uses native message editing for live updates.
|
||||||
let snapshot =
|
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 {
|
let msg_id = match ctx.transport.send_message(channel, &snapshot, "").await {
|
||||||
Ok(id) => id,
|
Ok(id) => id,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -755,7 +755,7 @@ async fn handle_incoming_message(
|
|||||||
let total_ticks = (duration_secs as usize) / 2;
|
let total_ticks = (duration_secs as usize) / 2;
|
||||||
for tick in 1..=total_ticks {
|
for tick in 1..=total_ticks {
|
||||||
tokio::time::sleep(interval).await;
|
tokio::time::sleep(interval).await;
|
||||||
let updated = crate::matrix::htop::build_htop_message(
|
let updated = crate::chat::transport::matrix::htop::build_htop_message(
|
||||||
&agents,
|
&agents,
|
||||||
(tick * 2) as u32,
|
(tick * 2) as u32,
|
||||||
duration_secs,
|
duration_secs,
|
||||||
@@ -773,15 +773,15 @@ async fn handle_incoming_message(
|
|||||||
return;
|
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,
|
message,
|
||||||
&ctx.bot_name,
|
&ctx.bot_name,
|
||||||
&ctx.bot_user_id,
|
&ctx.bot_user_id,
|
||||||
) {
|
) {
|
||||||
let response = match del_cmd {
|
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}");
|
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,
|
&ctx.bot_name,
|
||||||
&story_number,
|
&story_number,
|
||||||
&ctx.project_root,
|
&ctx.project_root,
|
||||||
@@ -789,7 +789,7 @@ async fn handle_incoming_message(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
crate::matrix::delete::DeleteCommand::BadArgs => {
|
crate::chat::transport::matrix::delete::DeleteCommand::BadArgs => {
|
||||||
format!("Usage: `{} delete <number>`", ctx.bot_name)
|
format!("Usage: `{} delete <number>`", ctx.bot_name)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -810,7 +810,7 @@ async fn handle_llm_message(
|
|||||||
user_message: &str,
|
user_message: &str,
|
||||||
) {
|
) {
|
||||||
use crate::llm::providers::claude_code::{ClaudeCodeProvider, ClaudeCodeResult};
|
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 std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use tokio::sync::watch;
|
use tokio::sync::watch;
|
||||||
|
|
||||||
@@ -1408,7 +1408,7 @@ mod tests {
|
|||||||
fn slash_command_dispatches_through_command_registry() {
|
fn slash_command_dispatches_through_command_registry() {
|
||||||
// Verify that the synthetic message built by the slash handler
|
// Verify that the synthetic message built by the slash handler
|
||||||
// correctly dispatches through try_handle_command.
|
// 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 agents = test_agents();
|
||||||
let ambient_rooms = test_ambient_rooms();
|
let ambient_rooms = test_ambient_rooms();
|
||||||
@@ -1435,7 +1435,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn slash_command_show_passes_args_through_registry() {
|
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 agents = test_agents();
|
||||||
let ambient_rooms = test_ambient_rooms();
|
let ambient_rooms = test_ambient_rooms();
|
||||||
@@ -14,9 +14,9 @@ use std::sync::Arc;
|
|||||||
use tokio::sync::Mutex as TokioMutex;
|
use tokio::sync::Mutex as TokioMutex;
|
||||||
|
|
||||||
use crate::agents::AgentPool;
|
use crate::agents::AgentPool;
|
||||||
use crate::matrix::{ConversationEntry, ConversationRole, RoomConversation};
|
use crate::chat::transport::matrix::{ConversationEntry, ConversationRole, RoomConversation};
|
||||||
use crate::slog;
|
use crate::slog;
|
||||||
use crate::transport::{ChatTransport, MessageId};
|
use crate::chat::{ChatTransport, MessageId};
|
||||||
|
|
||||||
// ── API base URLs (overridable for tests) ────────────────────────────────
|
// ── API base URLs (overridable for tests) ────────────────────────────────
|
||||||
|
|
||||||
@@ -908,7 +908,7 @@ async fn handle_incoming_message(
|
|||||||
sender: &str,
|
sender: &str,
|
||||||
message: &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.
|
// Record this inbound message to keep the 24-hour window open.
|
||||||
ctx.window_tracker.record_message(sender);
|
ctx.window_tracker.record_message(sender);
|
||||||
@@ -931,12 +931,12 @@ async fn handle_incoming_message(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for async commands (htop, delete).
|
// 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,
|
message,
|
||||||
&ctx.bot_name,
|
&ctx.bot_name,
|
||||||
&ctx.bot_user_id,
|
&ctx.bot_user_id,
|
||||||
) {
|
) {
|
||||||
use crate::matrix::htop::HtopCommand;
|
use crate::chat::transport::matrix::htop::HtopCommand;
|
||||||
slog!("[whatsapp] Handling htop command from {sender}");
|
slog!("[whatsapp] Handling htop command from {sender}");
|
||||||
match htop_cmd {
|
match htop_cmd {
|
||||||
HtopCommand::Stop => {
|
HtopCommand::Stop => {
|
||||||
@@ -951,7 +951,7 @@ async fn handle_incoming_message(
|
|||||||
// On WhatsApp, send a single snapshot instead of a live-updating
|
// On WhatsApp, send a single snapshot instead of a live-updating
|
||||||
// dashboard since we can't edit messages.
|
// dashboard since we can't edit messages.
|
||||||
let snapshot =
|
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
|
let _ = ctx
|
||||||
.transport
|
.transport
|
||||||
.send_message(sender, &snapshot, "")
|
.send_message(sender, &snapshot, "")
|
||||||
@@ -961,15 +961,15 @@ async fn handle_incoming_message(
|
|||||||
return;
|
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,
|
message,
|
||||||
&ctx.bot_name,
|
&ctx.bot_name,
|
||||||
&ctx.bot_user_id,
|
&ctx.bot_user_id,
|
||||||
) {
|
) {
|
||||||
let response = match del_cmd {
|
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}");
|
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,
|
&ctx.bot_name,
|
||||||
&story_number,
|
&story_number,
|
||||||
&ctx.project_root,
|
&ctx.project_root,
|
||||||
@@ -977,7 +977,7 @@ async fn handle_incoming_message(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
crate::matrix::delete::DeleteCommand::BadArgs => {
|
crate::chat::transport::matrix::delete::DeleteCommand::BadArgs => {
|
||||||
format!("Usage: `{} delete <number>`", ctx.bot_name)
|
format!("Usage: `{} delete <number>`", ctx.bot_name)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -997,7 +997,7 @@ async fn handle_llm_message(
|
|||||||
user_message: &str,
|
user_message: &str,
|
||||||
) {
|
) {
|
||||||
use crate::llm::providers::claude_code::{ClaudeCodeProvider, ClaudeCodeResult};
|
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 std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use tokio::sync::watch;
|
use tokio::sync::watch;
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
//! (it clears local session state and message history) and is not routed here.
|
//! (it clears local session state and message history) and is not routed here.
|
||||||
|
|
||||||
use crate::http::context::{AppContext, OpenApiResult};
|
use crate::http::context::{AppContext, OpenApiResult};
|
||||||
use crate::matrix::commands::CommandDispatch;
|
use crate::chat::transport::matrix::commands::CommandDispatch;
|
||||||
use poem::http::StatusCode;
|
use poem::http::StatusCode;
|
||||||
use poem_openapi::{Object, OpenApi, Tags, payload::Json};
|
use poem_openapi::{Object, OpenApi, Tags, payload::Json};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -112,7 +112,7 @@ fn dispatch_sync(
|
|||||||
format!("{bot_name} {cmd} {args}")
|
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,
|
Some(response) => response,
|
||||||
None => {
|
None => {
|
||||||
// Command exists in the registry but its fallback handler returns 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();
|
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
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ async fn dispatch_start(
|
|||||||
Some(hint_str)
|
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
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,14 +176,14 @@ async fn dispatch_delete(
|
|||||||
if number_str.is_empty() || !number_str.chars().all(|c| c.is_ascii_digit()) {
|
if number_str.is_empty() || !number_str.chars().all(|c| c.is_ascii_digit()) {
|
||||||
return "Usage: `/delete <number>` (e.g. `/delete 42`)".to_string();
|
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(
|
async fn dispatch_rebuild(
|
||||||
project_root: &std::path::Path,
|
project_root: &std::path::Path,
|
||||||
agents: &Arc<crate::agents::AgentPool>,
|
agents: &Arc<crate::agents::AgentPool>,
|
||||||
) -> String {
|
) -> 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ use settings::SettingsApi;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::slack::SlackWebhookContext;
|
use crate::chat::transport::slack::SlackWebhookContext;
|
||||||
use crate::whatsapp::WhatsAppWebhookContext;
|
use crate::chat::transport::whatsapp::WhatsAppWebhookContext;
|
||||||
|
|
||||||
const DEFAULT_PORT: u16 = 3001;
|
const DEFAULT_PORT: u16 = 3001;
|
||||||
|
|
||||||
@@ -85,8 +85,8 @@ pub fn build_routes(
|
|||||||
if let Some(wa_ctx) = whatsapp_ctx {
|
if let Some(wa_ctx) = whatsapp_ctx {
|
||||||
route = route.at(
|
route = route.at(
|
||||||
"/webhook/whatsapp",
|
"/webhook/whatsapp",
|
||||||
get(crate::whatsapp::webhook_verify)
|
get(crate::chat::transport::whatsapp::webhook_verify)
|
||||||
.post(crate::whatsapp::webhook_receive)
|
.post(crate::chat::transport::whatsapp::webhook_receive)
|
||||||
.data(wa_ctx),
|
.data(wa_ctx),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -95,11 +95,11 @@ pub fn build_routes(
|
|||||||
route = route
|
route = route
|
||||||
.at(
|
.at(
|
||||||
"/webhook/slack",
|
"/webhook/slack",
|
||||||
post(crate::slack::webhook_receive).data(sl_ctx.clone()),
|
post(crate::chat::transport::slack::webhook_receive).data(sl_ctx.clone()),
|
||||||
)
|
)
|
||||||
.at(
|
.at(
|
||||||
"/webhook/slack/command",
|
"/webhook/slack/command",
|
||||||
post(crate::slack::slash_command_receive).data(sl_ctx),
|
post(crate::chat::transport::slack::slash_command_receive).data(sl_ctx),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,10 @@ mod http;
|
|||||||
mod io;
|
mod io;
|
||||||
mod llm;
|
mod llm;
|
||||||
pub mod log_buffer;
|
pub mod log_buffer;
|
||||||
mod matrix;
|
mod chat;
|
||||||
pub mod rebuild;
|
pub mod rebuild;
|
||||||
pub mod slack;
|
|
||||||
mod state;
|
mod state;
|
||||||
mod store;
|
mod store;
|
||||||
pub mod transport;
|
|
||||||
pub mod whatsapp;
|
|
||||||
mod workflow;
|
mod workflow;
|
||||||
mod worktree;
|
mod worktree;
|
||||||
|
|
||||||
@@ -267,15 +264,15 @@ async fn main() -> Result<(), std::io::Error> {
|
|||||||
let agents_for_shutdown = Arc::clone(&agents);
|
let agents_for_shutdown = Arc::clone(&agents);
|
||||||
|
|
||||||
// Build WhatsApp webhook context if bot.toml configures transport = "whatsapp".
|
// 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()
|
.as_ref()
|
||||||
.and_then(|root| matrix::BotConfig::load(root))
|
.and_then(|root| chat::transport::matrix::BotConfig::load(root))
|
||||||
.filter(|cfg| cfg.transport == "whatsapp")
|
.filter(|cfg| cfg.transport == "whatsapp")
|
||||||
.map(|cfg| {
|
.map(|cfg| {
|
||||||
let provider = cfg.whatsapp_provider.clone();
|
let provider = cfg.whatsapp_provider.clone();
|
||||||
let transport: Arc<dyn crate::transport::ChatTransport> =
|
let transport: Arc<dyn crate::chat::ChatTransport> =
|
||||||
if provider == "twilio" {
|
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_account_sid.clone().unwrap_or_default(),
|
||||||
cfg.twilio_auth_token.clone().unwrap_or_default(),
|
cfg.twilio_auth_token.clone().unwrap_or_default(),
|
||||||
cfg.twilio_whatsapp_number.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
|
.whatsapp_notification_template
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| "pipeline_notification".to_string());
|
.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_phone_number_id.clone().unwrap_or_default(),
|
||||||
cfg.whatsapp_access_token.clone().unwrap_or_default(),
|
cfg.whatsapp_access_token.clone().unwrap_or_default(),
|
||||||
template_name,
|
template_name,
|
||||||
@@ -296,8 +293,8 @@ async fn main() -> Result<(), std::io::Error> {
|
|||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| "Assistant".to_string());
|
.unwrap_or_else(|| "Assistant".to_string());
|
||||||
let root = startup_root.clone().unwrap();
|
let root = startup_root.clone().unwrap();
|
||||||
let history = whatsapp::load_whatsapp_history(&root);
|
let history = chat::transport::whatsapp::load_whatsapp_history(&root);
|
||||||
Arc::new(whatsapp::WhatsAppWebhookContext {
|
Arc::new(chat::transport::whatsapp::WhatsAppWebhookContext {
|
||||||
verify_token: cfg.whatsapp_verify_token.clone().unwrap_or_default(),
|
verify_token: cfg.whatsapp_verify_token.clone().unwrap_or_default(),
|
||||||
provider,
|
provider,
|
||||||
transport,
|
transport,
|
||||||
@@ -308,17 +305,17 @@ async fn main() -> Result<(), std::io::Error> {
|
|||||||
ambient_rooms: Arc::new(std::sync::Mutex::new(std::collections::HashSet::new())),
|
ambient_rooms: Arc::new(std::sync::Mutex::new(std::collections::HashSet::new())),
|
||||||
history: std::sync::Arc::new(tokio::sync::Mutex::new(history)),
|
history: std::sync::Arc::new(tokio::sync::Mutex::new(history)),
|
||||||
history_size: cfg.history_size,
|
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".
|
// 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()
|
.as_ref()
|
||||||
.and_then(|root| matrix::BotConfig::load(root))
|
.and_then(|root| chat::transport::matrix::BotConfig::load(root))
|
||||||
.filter(|cfg| cfg.transport == "slack")
|
.filter(|cfg| cfg.transport == "slack")
|
||||||
.map(|cfg| {
|
.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(),
|
cfg.slack_bot_token.clone().unwrap_or_default(),
|
||||||
));
|
));
|
||||||
let bot_name = cfg
|
let bot_name = cfg
|
||||||
@@ -326,10 +323,10 @@ async fn main() -> Result<(), std::io::Error> {
|
|||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| "Assistant".to_string());
|
.unwrap_or_else(|| "Assistant".to_string());
|
||||||
let root = startup_root.clone().unwrap();
|
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> =
|
let channel_ids: std::collections::HashSet<String> =
|
||||||
cfg.slack_channel_ids.iter().cloned().collect();
|
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(),
|
signing_secret: cfg.slack_signing_secret.clone().unwrap_or_default(),
|
||||||
transport,
|
transport,
|
||||||
project_root: root,
|
project_root: root,
|
||||||
@@ -353,7 +350,7 @@ async fn main() -> Result<(), std::io::Error> {
|
|||||||
if let Some(ref ctx) = slack_ctx {
|
if let Some(ref ctx) = slack_ctx {
|
||||||
let channels: Vec<String> = ctx.channel_ids.iter().cloned().collect();
|
let channels: Vec<String> = ctx.channel_ids.iter().cloned().collect();
|
||||||
Some(Arc::new(BotShutdownNotifier::new(
|
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,
|
channels,
|
||||||
ctx.bot_name.clone(),
|
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.
|
// 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.
|
// 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();
|
whatsapp_ctx.clone();
|
||||||
|
|
||||||
// Watch channel: signals the Matrix bot task to send a shutdown announcement.
|
// 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
|
// Optional Matrix bot: connect to the homeserver and start listening for
|
||||||
// messages if `.storkit/bot.toml` is present and enabled.
|
// messages if `.storkit/bot.toml` is present and enabled.
|
||||||
if let Some(ref root) = startup_root {
|
if let Some(ref root) = startup_root {
|
||||||
matrix::spawn_bot(
|
chat::transport::matrix::spawn_bot(
|
||||||
root,
|
root,
|
||||||
watcher_tx_for_bot,
|
watcher_tx_for_bot,
|
||||||
perm_rx_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();
|
let rooms: Vec<String> = ctx.ambient_rooms.lock().unwrap().iter().cloned().collect();
|
||||||
if !rooms.is_empty() {
|
if !rooms.is_empty() {
|
||||||
let wa_notifier = BotShutdownNotifier::new(
|
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,
|
rooms,
|
||||||
ctx.bot_name.clone(),
|
ctx.bot_name.clone(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::agents::AgentPool;
|
use crate::agents::AgentPool;
|
||||||
use crate::slog;
|
use crate::slog;
|
||||||
use crate::transport::ChatTransport;
|
use crate::chat::ChatTransport;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -186,7 +186,7 @@ pub async fn rebuild_and_restart(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use crate::transport::MessageId;
|
use crate::chat::MessageId;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
/// In-memory transport that records sent messages.
|
/// In-memory transport that records sent messages.
|
||||||
|
|||||||
Reference in New Issue
Block a user