Files
baibot-withmcp/src/bot/messaging.rs

321 lines
9.5 KiB
Rust
Raw Normal View History

2024-09-12 13:44:06 +03:00
use mxlink::matrix_sdk::{
2025-02-27 07:46:16 +02:00
Room,
2024-09-12 13:44:06 +03:00
ruma::{
2025-02-27 07:46:16 +02:00
OwnedEventId, api::client::receipt::create_receipt::v3::ReceiptType,
events::room::message::OriginalSyncRoomMessageEvent,
2024-09-12 13:44:06 +03:00
},
};
use mxlink::{CallbackError, MessageResponseType};
use tracing::Instrument;
use crate::{
conversation::matrix::determine_interaction_context_for_room_event,
2024-09-12 13:44:06 +03:00
entity::{MessageContext, MessagePayload, RoomConfigContext, TriggerEventInfo},
};
#[derive(Clone)]
pub struct Messaging {
bot: super::Bot,
}
impl Messaging {
pub fn new(bot: super::Bot) -> Self {
Self { bot }
}
pub async fn send_text_markdown_no_fail(
&self,
room: &Room,
message: String,
response_type: MessageResponseType,
) -> Option<mxlink::matrix_sdk::ruma::api::client::message::send_message_event::v3::Response>
{
let result = self
.bot
.matrix_link()
.messaging()
.send_text_markdown(room, message, response_type)
.await;
match result {
Ok(result) => Some(result),
Err(err) => {
tracing::error!(
room_id = format!("{:?}", room.room_id()),
?err,
"Failed to send text message to room",
);
None
}
}
}
pub async fn send_notice_markdown_no_fail(
&self,
room: &Room,
message: String,
response_type: MessageResponseType,
) -> Option<mxlink::matrix_sdk::ruma::api::client::message::send_message_event::v3::Response>
{
let result = self
.bot
.matrix_link()
.messaging()
.send_notice_markdown(room, message, response_type)
.await;
match result {
Ok(result) => Some(result),
Err(err) => {
tracing::error!(
room_id = format!("{:?}", room.room_id()),
?err,
"Failed to send notice message to room",
);
None
}
}
}
pub async fn send_tooltip_markdown_no_fail(
&self,
room: &Room,
message: &str,
response_type: MessageResponseType,
) -> Option<mxlink::matrix_sdk::ruma::api::client::message::send_message_event::v3::Response>
{
self.send_notice_markdown_no_fail(
room,
crate::utils::status::create_tooltip_message_text(message),
response_type,
)
.await
}
pub async fn send_success_markdown_no_fail(
&self,
room: &Room,
message: &str,
response_type: MessageResponseType,
) -> Option<mxlink::matrix_sdk::ruma::api::client::message::send_message_event::v3::Response>
{
self.send_notice_markdown_no_fail(
room,
crate::utils::status::create_success_message_text(message),
response_type,
)
.await
}
pub async fn send_error_markdown_no_fail(
&self,
room: &Room,
err: &str,
response_type: MessageResponseType,
) -> Option<mxlink::matrix_sdk::ruma::api::client::message::send_message_event::v3::Response>
{
self.send_notice_markdown_no_fail(
room,
crate::utils::status::create_error_message_text(err),
response_type,
)
.await
}
pub async fn redact_event_no_fail(
&self,
room: &Room,
target_event_id: OwnedEventId,
reason: Option<String>,
) -> Option<mxlink::matrix_sdk::ruma::api::client::redact::redact_event::v3::Response> {
let result = self
.bot
.matrix_link()
.messaging()
.redact_event(room, target_event_id.clone(), reason)
.await;
match result {
Ok(result) => Some(result),
Err(err) => {
tracing::error!(
room_id = format!("{:?}", room.room_id()),
?target_event_id,
?err,
"Failed to send redaction to room",
);
None
}
}
}
pub(super) async fn attach_event_handlers(&self) {
let matrix_link_messaging = self.bot.matrix_link().messaging();
let this = self.clone();
matrix_link_messaging.on_actionable_room_message(|event, room| async move {
this.on_actionable_message(event, room).await
});
}
#[tracing::instrument(name = "bot_on_actionable_message", skip_all, fields(room_id = room.room_id().as_str(), event_id = event.event_id.as_str()))]
async fn on_actionable_message(
&self,
event: OriginalSyncRoomMessageEvent,
room: Room,
) -> Result<(), CallbackError> {
if self
.bot
.is_caught_up(event.origin_server_ts)
.await
.map_err(|e| {
CallbackError::Unknown(
format!("Failed to determine catch-up state: {:?}", e).into(),
)
})?
{
tracing::debug!(
event_origin_server_ts = format!("{:?}", event.origin_server_ts),
"Ignoring old message event",
);
return Ok(());
}
tracing::info!("Processing message");
let global_config = self
.bot
.global_config()
.await
.map_err(|err| CallbackError::Unknown(err.into()))?;
tracing::trace!(?global_config, "Global config");
let room_config = self
.bot
.room_config_manager()
.lock()
.await
.get_or_create_for_room(&room)
.await
.map_err(|err| CallbackError::Unknown(err.into()))?;
tracing::trace!(?room_config, "Room config");
let trigger_event_sender_is_admin = mxidwc::match_user_id(
event.sender.clone().as_str(),
self.bot.admin_pattern_regexes(),
);
let trigger_event_sender_is_allowed_user = match &global_config.access.user_patterns {
Some(user_patterns) => {
let allowed_user_regexes = mxidwc::parse_patterns_vector(user_patterns)
.map_err(|err| CallbackError::Unknown(err.into()))?;
mxidwc::match_user_id(event.sender.clone().as_str(), &allowed_user_regexes)
}
None => false,
};
if !trigger_event_sender_is_admin && !trigger_event_sender_is_allowed_user {
tracing::debug!("Ignoring message from non-admin/non-allowed user");
return Ok(());
}
let payload: Result<MessagePayload, String> = event.content.msgtype.clone().try_into();
let payload = match payload {
Ok(payload) => payload,
Err(err) => {
tracing::debug!(
msg_type = event.content.msgtype(),
?err,
"Ignoring message not supported by us",
);
return Ok(());
}
};
let bot_display_name = self.bot.user_display_name_in_room(&room).await;
let interaction_context = determine_interaction_context_for_room_event(
2024-09-12 13:44:06 +03:00
self.bot.user_id(),
&bot_display_name,
2024-09-12 13:44:06 +03:00
&room,
&event,
&payload,
&self.bot.room_event_fetcher(),
)
.await;
let interaction_context = match interaction_context {
2024-09-12 13:44:06 +03:00
Ok(value) => value,
Err(err) => {
tracing::error!(?err, "Failed to determine interaction context for event");
2024-09-12 13:44:06 +03:00
return Ok(());
}
};
let Some(interaction_context) = interaction_context else {
tracing::debug!(
"Ignoring message with unknown interaction context (likely not a message for us)"
);
2024-09-12 13:44:06 +03:00
return Ok(());
};
let room_config_context =
RoomConfigContext::new(global_config.clone(), room_config.clone());
let trigger_event_info = TriggerEventInfo::new(
event.event_id.clone(),
event.sender.clone(),
payload,
trigger_event_sender_is_admin,
);
let message_context = MessageContext::new(
room.clone(),
room_config_context,
self.bot.admin_pattern_regexes().clone(),
trigger_event_info,
interaction_context.thread_info.clone(),
)
.with_bot_display_name(bot_display_name);
2024-09-12 13:44:06 +03:00
let controller_type = crate::controller::determine_controller(
self.bot.command_prefix(),
&interaction_context.trigger,
2024-09-12 13:44:06 +03:00
&message_context,
);
tracing::info!(?controller_type, "Determined controller");
let _ = room
.send_single_receipt(
ReceiptType::Read,
interaction_context.thread_info.clone().into(),
2024-09-12 13:44:06 +03:00
event.event_id.clone(),
)
.await;
let start_time = std::time::Instant::now();
let event_span = tracing::error_span!("message_controller", ?controller_type);
crate::controller::dispatch_controller(&controller_type, &message_context, &self.bot)
.instrument(event_span)
.await;
let duration = std::time::Instant::now().duration_since(start_time);
tracing::debug!(?duration, "Controller finished");
self.bot.catch_up(event.origin_server_ts).await;
return Ok(());
}
}