diff --git a/server/src/matrix/bot.rs b/server/src/matrix/bot.rs index f335b0f..f87e005 100644 --- a/server/src/matrix/bot.rs +++ b/server/src/matrix/bot.rs @@ -167,6 +167,18 @@ pub struct BotContext { pub bot_name: String, } +// --------------------------------------------------------------------------- +// Startup announcement +// --------------------------------------------------------------------------- + +/// Format the startup greeting the bot sends to each room when it comes online. +/// +/// Uses the bot's configured display name so the message reads naturally +/// (e.g. "Timmy is online."). +pub fn format_startup_announcement(bot_name: &str) -> String { + format!("{bot_name} is online.") +} + // --------------------------------------------------------------------------- // Bot entry point // --------------------------------------------------------------------------- @@ -305,10 +317,11 @@ pub async fn run_bot( target_room_ids ); - // Clone values needed by the notification listener before they are moved - // into BotContext. + // Clone values needed by the notification listener and startup announcement + // before they are moved into BotContext. let notif_room_ids = target_room_ids.clone(); let notif_project_root = project_root.clone(); + let announce_room_ids = target_room_ids.clone(); let persisted = load_history(&project_root); slog!( @@ -320,6 +333,7 @@ pub async fn run_bot( .display_name .clone() .unwrap_or_else(|| "Assistant".to_string()); + let announce_bot_name = bot_name.clone(); let ctx = BotContext { bot_user_id, @@ -351,6 +365,23 @@ pub async fn run_bot( notif_project_root, ); + // Send a startup announcement to each configured room so users know the + // bot is online. This runs once per process start — the sync loop handles + // reconnects internally so this code is never reached again on a network + // blip or sync resumption. + let announce_msg = format_startup_announcement(&announce_bot_name); + slog!("[matrix-bot] Sending startup announcement: {announce_msg}"); + for room_id in &announce_room_ids { + if let Some(room) = client.get_room(room_id) { + let content = RoomMessageEventContent::text_plain(announce_msg.clone()); + if let Err(e) = room.send(content).await { + slog!("[matrix-bot] Failed to send startup announcement to {room_id}: {e}"); + } + } else { + slog!("[matrix-bot] Room {room_id} not found in client state, skipping announcement"); + } + } + slog!("[matrix-bot] Starting Matrix sync loop"); // This blocks until the connection is terminated or an error occurs. @@ -1663,6 +1694,19 @@ mod tests { assert!(is_permission_approval("\tyes\n")); } + // -- format_startup_announcement ---------------------------------------- + + #[test] + fn startup_announcement_uses_bot_name() { + assert_eq!(format_startup_announcement("Timmy"), "Timmy is online."); + } + + #[test] + fn startup_announcement_uses_configured_display_name_not_hardcoded() { + assert_eq!(format_startup_announcement("HAL"), "HAL is online."); + assert_eq!(format_startup_announcement("Assistant"), "Assistant is online."); + } + // -- bot_name / system prompt ------------------------------------------- #[test]