mx-sanebot: split out a cleaner API between the Matrix events and the bot's event-handling logic
This commit is contained in:
parent
7b141f6f58
commit
afc916c9f8
|
@ -9,7 +9,8 @@ use matrix_sdk::{
|
|||
deserialized_responses::SyncResponse,
|
||||
};
|
||||
use ruma::{
|
||||
RoomId,
|
||||
OwnedRoomId,
|
||||
OwnedUserId,
|
||||
events::{
|
||||
AnySyncMessageLikeEvent,
|
||||
AnySyncTimelineEvent,
|
||||
|
@ -34,6 +35,22 @@ struct Runner {
|
|||
client: Client,
|
||||
}
|
||||
|
||||
/// this encodes the specific set of Matrix events i might ever care about.
|
||||
#[derive(Debug)]
|
||||
enum Event {
|
||||
/// i was invited to the given room
|
||||
Invitation(OwnedRoomId),
|
||||
/// i see a message in the given room, from the given user
|
||||
Message(OwnedRoomId, OwnedUserId, String),
|
||||
}
|
||||
|
||||
/// some action i might do, usually in response to an Event.
|
||||
#[derive(Debug)]
|
||||
enum Action {
|
||||
AcceptInvite(OwnedRoomId),
|
||||
SendMessage(OwnedRoomId, String),
|
||||
}
|
||||
|
||||
impl Runner {
|
||||
async fn login(
|
||||
homeserver: &str,
|
||||
|
@ -91,37 +108,90 @@ impl Runner {
|
|||
// doing so do not need to worry about double responses.
|
||||
let settings = build_sync_settings(None);
|
||||
let response = self.client.sync_once(settings).await.unwrap();
|
||||
self.handle_sync_response(&response).await;
|
||||
let next_token = response.next_batch.clone();
|
||||
self.act_on_sync_response(response).await;
|
||||
println!("sync'd");
|
||||
|
||||
let settings = build_sync_settings(Some(response.next_batch));
|
||||
let settings = build_sync_settings(Some(next_token));
|
||||
let mut sync_stream = Box::pin(
|
||||
self.client.sync_stream(settings)
|
||||
.await
|
||||
);
|
||||
while let Some(Ok(response)) = sync_stream.next().await {
|
||||
// println!("handling sync responses");
|
||||
self.handle_sync_response(&response).await;
|
||||
self.act_on_sync_response(response).await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_sync_response(&self, response: &SyncResponse) {
|
||||
for (room_id, _room) in &response.rooms.invite {
|
||||
self.handle_room_invite(room_id).await;
|
||||
fn parse_sync_response<'a>(&'a self, response: SyncResponse) -> impl Iterator<Item=Event> + 'a {
|
||||
let events_from_invited_rooms = response.rooms.invite
|
||||
.into_iter()
|
||||
.map(|(room_id, _room)| {
|
||||
self.parse_room_invite(room_id)
|
||||
});
|
||||
let events_from_joined_rooms = response.rooms.join
|
||||
.into_iter()
|
||||
.flat_map(move |(room_id, room)| {
|
||||
room.timeline.events.into_iter().flat_map(move |e| match e.event.deserialize() {
|
||||
Ok(event) => self.parse_timeline_event(room_id.clone(), event),
|
||||
Err(_) => None,
|
||||
})
|
||||
});
|
||||
events_from_invited_rooms.chain(events_from_joined_rooms)
|
||||
}
|
||||
for (room_id, room) in &response.rooms.join {
|
||||
for e in &room.timeline.events {
|
||||
if let Ok(event) = e.event.deserialize() {
|
||||
self.handle_event(room_id, event).await;
|
||||
|
||||
async fn act_on_sync_response(&self, response: SyncResponse) {
|
||||
for event in self.parse_sync_response(response) {
|
||||
self.act_on_event(event).await;
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_room_invite(&self, room_id: OwnedRoomId) -> Event {
|
||||
println!("Received invite {:?}", room_id);
|
||||
Event::Invitation(room_id)
|
||||
}
|
||||
|
||||
fn parse_timeline_event(&self, room_id: OwnedRoomId, event: AnySyncTimelineEvent) -> Option<Event> {
|
||||
println!("Considering timeline event {:?}", event);
|
||||
let sender = event.sender();
|
||||
// protect against a bad sync filter that would cause me to see my own events
|
||||
assert_ne!(Some(sender), self.client.user_id());
|
||||
|
||||
match event {
|
||||
AnySyncTimelineEvent::MessageLike(ref msg_like) => match msg_like {
|
||||
AnySyncMessageLikeEvent::RoomMessage(SyncMessageLikeEvent::Original(room_msg)) => match room_msg.content.msgtype {
|
||||
MessageType::Text(ref text_msg) => Some(
|
||||
Event::Message(room_id, sender.to_owned(), text_msg.body.clone())
|
||||
),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
async fn act_on_event(&self, event: Event) {
|
||||
self.perform_action(self.map_event_to_action(event)).await;
|
||||
}
|
||||
|
||||
fn map_event_to_action(&self, event: Event) -> Action {
|
||||
println!("processing event {event:?}");
|
||||
match event {
|
||||
Event::Invitation(room_id) => Action::AcceptInvite(room_id),
|
||||
Event::Message(room_id, _sender_id, body) => {
|
||||
let resp = MessageHandler.on_msg(&body);
|
||||
Action::SendMessage(room_id, resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_room_invite(&self, room_id: &RoomId) {
|
||||
println!("Received invite {:?}", room_id);
|
||||
async fn perform_action(&self, action: Action) {
|
||||
println!("performing action: {action:?}");
|
||||
match action {
|
||||
Action::AcceptInvite(room_id) => {
|
||||
// matrix example claims:
|
||||
// """
|
||||
// The event handlers are called before the next sync begins, but
|
||||
|
@ -129,7 +199,7 @@ impl Runner {
|
|||
// wait for the sync to return the new room state so we need to spawn
|
||||
// a new task for them.
|
||||
// """
|
||||
let room = self.client.get_invited_room(room_id).unwrap();
|
||||
let room = self.client.get_invited_room(&room_id).unwrap();
|
||||
tokio::spawn(async move {
|
||||
let mut delay = 2;
|
||||
|
||||
|
@ -150,37 +220,12 @@ impl Runner {
|
|||
println!("Successfully joined room {}", room.room_id());
|
||||
});
|
||||
}
|
||||
Action::SendMessage(room_id, msg) => {
|
||||
|
||||
async fn handle_event(&self, room_id: &RoomId, event: AnySyncTimelineEvent) {
|
||||
println!("Considering event {:?}", event);
|
||||
let sender = event.sender();
|
||||
// protect against a bad sync filter that would cause me to see my own events
|
||||
assert_ne!(Some(sender), self.client.user_id());
|
||||
|
||||
match event {
|
||||
AnySyncTimelineEvent::MessageLike(ref msg_like) => match msg_like {
|
||||
AnySyncMessageLikeEvent::RoomMessage(SyncMessageLikeEvent::Original(room_msg)) => match room_msg.content.msgtype {
|
||||
MessageType::Text(ref text_msg) => {
|
||||
let body = &text_msg.body;
|
||||
println!("message from {sender}: {body}\n");
|
||||
let resp = MessageHandler.on_msg(&body);
|
||||
println!("response: {resp}");
|
||||
|
||||
let room = self.client.get_joined_room(room_id).unwrap();
|
||||
let resp_content = RoomMessageEventContent::text_plain(&resp);
|
||||
let room = self.client.get_joined_room(&room_id).unwrap();
|
||||
let resp_content = RoomMessageEventContent::text_plain(&msg);
|
||||
room.send(resp_content, None).await.unwrap();
|
||||
},
|
||||
ref other => {
|
||||
println!("dropping RoomMessage event {other:?}");
|
||||
},
|
||||
},
|
||||
other => {
|
||||
println!("dropping MessageLike event {other:?}");
|
||||
},
|
||||
},
|
||||
AnySyncTimelineEvent::State(state) => {
|
||||
println!("dropping State event {state:?}");
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user