//! Matrix implementation of [`ChatTransport`]. //! //! Wraps a [`matrix_sdk::Client`] and delegates message sending / editing //! to the Matrix SDK. use async_trait::async_trait; use matrix_sdk::Client; use matrix_sdk::ruma::OwnedRoomId; use matrix_sdk::ruma::events::room::message::{ ReplacementMetadata, RoomMessageEventContent, RoomMessageEventContentWithoutRelation, }; use crate::chat::{ChatTransport, MessageId}; /// Matrix-backed [`ChatTransport`] implementation. /// /// Holds a [`Client`] and resolves room IDs at send time. pub struct MatrixTransport { client: Client, } impl MatrixTransport { /// Creates a new `MatrixTransport` wrapping the given Matrix SDK `Client`. pub fn new(client: Client) -> Self { Self { client } } } #[async_trait] impl ChatTransport for MatrixTransport { async fn send_message( &self, room_id: &str, plain: &str, html: &str, ) -> Result { let room_id: OwnedRoomId = room_id .parse() .map_err(|e| format!("Invalid room ID '{room_id}': {e}"))?; let room = self .client .get_room(&room_id) .ok_or_else(|| format!("Room {room_id} not found in client state"))?; let content = RoomMessageEventContent::text_html(plain.to_string(), html.to_string()); let resp = room .send(content) .await .map_err(|e| format!("Matrix send error: {e}"))?; Ok(resp.event_id.to_string()) } async fn edit_message( &self, room_id: &str, original_message_id: &str, plain: &str, html: &str, ) -> Result<(), String> { let room_id: OwnedRoomId = room_id .parse() .map_err(|e| format!("Invalid room ID '{room_id}': {e}"))?; let room = self .client .get_room(&room_id) .ok_or_else(|| format!("Room {room_id} not found in client state"))?; let original_event_id = original_message_id .parse() .map_err(|e| format!("Invalid event ID '{original_message_id}': {e}"))?; let new_content = RoomMessageEventContentWithoutRelation::text_html(plain.to_string(), html.to_string()); let metadata = ReplacementMetadata::new(original_event_id, None); let content = new_content.make_replacement(metadata); room.send(content) .await .map(|_| ()) .map_err(|e| format!("Matrix edit error: {e}")) } async fn send_typing(&self, room_id: &str, typing: bool) -> Result<(), String> { let room_id: OwnedRoomId = room_id .parse() .map_err(|e| format!("Invalid room ID '{room_id}': {e}"))?; let room = self .client .get_room(&room_id) .ok_or_else(|| format!("Room {room_id} not found in client state"))?; room.typing_notice(typing) .await .map_err(|e| format!("Matrix typing indicator error: {e}")) } }