Compare commits

...

1 Commits

Author SHA1 Message Date
projectmoon 395753e8a9 Remove room state mgmt; let matrix SDK do it on-demand instead.
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
Fixes #71.

Fixes #20.
2021-05-24 21:45:51 +00:00
5 changed files with 32 additions and 156 deletions

View File

@ -7,15 +7,14 @@ use super::DiceBot;
use crate::db::sqlite::Database; use crate::db::sqlite::Database;
use crate::db::Rooms; use crate::db::Rooms;
use crate::error::BotError; use crate::error::BotError;
use crate::logic::record_room_information;
use async_trait::async_trait; use async_trait::async_trait;
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use matrix_sdk::{ use matrix_sdk::{
self, self,
events::{ events::{
room::member::{MemberEventContent, MembershipChange}, room::member::MemberEventContent,
room::message::{MessageEventContent, MessageType, TextMessageEventContent}, room::message::{MessageEventContent, MessageType, TextMessageEventContent},
StrippedStateEvent, SyncMessageEvent, SyncStateEvent, StrippedStateEvent, SyncMessageEvent,
}, },
room::Room, room::Room,
EventHandler, EventHandler,
@ -111,73 +110,6 @@ async fn should_process_event(db: &Database, room_id: &str, event_id: &str) -> b
/// Originally adapted from the matrix-rust-sdk examples. /// Originally adapted from the matrix-rust-sdk examples.
#[async_trait] #[async_trait]
impl EventHandler for DiceBot { impl EventHandler for DiceBot {
async fn on_room_member(&self, room: Room, event: &SyncStateEvent<MemberEventContent>) {
//let room = Common::new(self.client.clone(), room);
let (room_id, room_display_name) = match room.display_name().await {
Ok(display_name) => (room.room_id(), display_name),
_ => return,
};
let room_id_str = room_id.as_str();
let username = &event.state_key;
if !should_process_event(&self.db, room_id_str, event.event_id.as_str()).await {
return;
}
let event_affects_us = if let Some(our_user_id) = self.client.user_id().await {
event.state_key == our_user_id
} else {
false
};
// user_joing is true if a user is joining this room, and
// false if they have left for some reason. This user may be
// us, or another user in the room.
use MembershipChange::*;
let user_joining = match event.membership_change() {
Joined => true,
Banned | Left | Kicked | KickedAndBanned => false,
_ => return,
};
let result = if event_affects_us && !user_joining {
info!("Clearing all information for room ID {}", room_id);
self.db.clear_info(room_id_str).await.map_err(|e| e.into())
} else if event_affects_us && user_joining {
info!("Joined room {}; recording room information", room_id);
record_room_information(
&self.client,
&self.db,
&room_id,
&room_display_name,
&event.state_key,
)
.await
} else if !event_affects_us && user_joining {
info!("Adding user {} to room ID {}", username, room_id);
self.db
.add_user_to_room(username, room_id_str)
.await
.map_err(|e| e.into())
} else if !event_affects_us && !user_joining {
info!("Removing user {} from room ID {}", username, room_id);
self.db
.remove_user_from_room(username, room_id_str)
.await
.map_err(|e| e.into())
} else {
debug!("Ignoring a room member event: {:#?}", event);
Ok(())
};
if let Err(e) = result {
error!("Could not update room information: {}", e.to_string());
} else {
debug!("Successfully processed room member update.");
}
}
async fn on_stripped_state_member( async fn on_stripped_state_member(
&self, &self,
room: Room, room: Room,

View File

@ -2,40 +2,9 @@ use super::{Command, Execution, ExecutionResult};
use crate::context::Context; use crate::context::Context;
use crate::db::Users; use crate::db::Users;
use crate::error::BotError::{AccountDoesNotExist, AuthenticationError, PasswordCreationError}; use crate::error::BotError::{AccountDoesNotExist, AuthenticationError, PasswordCreationError};
use crate::logic::{hash_password, record_room_information}; use crate::logic::hash_password;
use crate::models::User; use crate::models::User;
use async_trait::async_trait; use async_trait::async_trait;
use matrix_sdk::identifiers::UserId;
pub struct ResyncCommand;
#[async_trait]
impl Command for ResyncCommand {
fn name(&self) -> &'static str {
"resync room information"
}
fn is_secure(&self) -> bool {
false
}
async fn execute(&self, ctx: &Context<'_>) -> ExecutionResult {
let our_username: Option<UserId> = ctx.matrix_client.user_id().await;
let our_username: &str = our_username.as_ref().map_or("", UserId::as_str);
record_room_information(
ctx.matrix_client,
&ctx.db,
ctx.room_id(),
&ctx.room.display_name,
our_username,
)
.await?;
let message = "Room information resynced.".to_string();
Execution::success(message)
}
}
pub struct RegisterCommand(pub String); pub struct RegisterCommand(pub String);

View File

@ -9,7 +9,7 @@ use crate::commands::{
basic_rolling::RollCommand, basic_rolling::RollCommand,
cofd::PoolRollCommand, cofd::PoolRollCommand,
cthulhu::{CthAdvanceRoll, CthRoll}, cthulhu::{CthAdvanceRoll, CthRoll},
management::{CheckCommand, RegisterCommand, ResyncCommand, UnregisterCommand}, management::{CheckCommand, RegisterCommand, UnregisterCommand},
misc::HelpCommand, misc::HelpCommand,
variables::{ variables::{
DeleteVariableCommand, GetAllVariablesCommand, GetVariableCommand, SetVariableCommand, DeleteVariableCommand, GetAllVariablesCommand, GetVariableCommand, SetVariableCommand,
@ -96,10 +96,6 @@ fn get_all_variables() -> Result<Box<dyn Command>, BotError> {
Ok(Box::new(GetAllVariablesCommand)) Ok(Box::new(GetAllVariablesCommand))
} }
fn parse_resync() -> Result<Box<dyn Command>, BotError> {
Ok(Box::new(ResyncCommand))
}
fn help(topic: &str) -> Result<Box<dyn Command>, BotError> { fn help(topic: &str) -> Result<Box<dyn Command>, BotError> {
let topic = parse_help_topic(topic); let topic = parse_help_topic(topic);
Ok(Box::new(HelpCommand(topic))) Ok(Box::new(HelpCommand(topic)))
@ -146,7 +142,6 @@ pub fn parse_command(input: &str) -> Result<Box<dyn Command>, BotError> {
"get" => parse_get_variable_command(&cmd_input), "get" => parse_get_variable_command(&cmd_input),
"set" => parse_set_variable_command(&cmd_input), "set" => parse_set_variable_command(&cmd_input),
"del" => parse_delete_variable_command(&cmd_input), "del" => parse_delete_variable_command(&cmd_input),
"resync" => parse_resync(),
"r" | "roll" => parse_roll(&cmd_input), "r" | "roll" => parse_roll(&cmd_input),
"rp" | "pool" => parse_pool_roll(&cmd_input), "rp" | "pool" => parse_pool_roll(&cmd_input),
"cthroll" => parse_cth_roll(&cmd_input), "cthroll" => parse_cth_roll(&cmd_input),

View File

@ -1,57 +1,12 @@
use crate::context::Context; use crate::context::Context;
use crate::db::{Rooms, Variables}; use crate::db::Variables;
use crate::error::{BotError, DiceRollingError}; use crate::error::{BotError, DiceRollingError};
use crate::matrix;
use crate::models::RoomInfo;
use crate::parser::dice::{Amount, Element}; use crate::parser::dice::{Amount, Element};
use argon2::{self, Config, Error as ArgonError}; use argon2::{self, Config, Error as ArgonError};
use futures::stream::{self, StreamExt, TryStreamExt}; use futures::stream::{self, StreamExt, TryStreamExt};
use matrix_sdk::{self, identifiers::RoomId, Client};
use rand::Rng; use rand::Rng;
use std::slice; use std::slice;
/// Record the information about a room, including users in it.
pub async fn record_room_information(
client: &Client,
db: &crate::db::sqlite::Database,
room_id: &RoomId,
room_display_name: &str,
our_username: &str,
) -> Result<(), BotError> {
//Clear out any old room info first.
db.clear_info(room_id.as_str()).await?;
let room_id_str = room_id.as_str();
let usernames = matrix::get_users_in_room(&client, &room_id).await?;
let info = RoomInfo {
room_id: room_id_str.to_owned(),
room_name: room_display_name.to_owned(),
};
// TODO this and the username adding should be one whole
// transaction in the db.
db.insert_room_info(&info).await?;
let filtered_usernames = usernames
.into_iter()
.filter(|username| username != our_username);
// Async collect into vec of results, then use into_iter of result
// to go to from Result<Vec<()>> to just Result<()>. Easier than
// attempting to async-collect our way to a single Result<()>.
stream::iter(filtered_usernames)
.then(|username| async move {
db.add_user_to_room(&username, &room_id_str)
.await
.map_err(|e| e.into())
})
.collect::<Vec<Result<(), BotError>>>()
.await
.into_iter()
.collect()
}
/// Calculate the amount of dice to roll by consulting the database /// Calculate the amount of dice to roll by consulting the database
/// and replacing variables with corresponding the amount. Errors out /// and replacing variables with corresponding the amount. Errors out
/// if it cannot find a variable defined, or if the database errors. /// if it cannot find a variable defined, or if the database errors.

View File

@ -1,5 +1,6 @@
use futures::stream::{self, StreamExt, TryStreamExt};
use log::error; use log::error;
use matrix_sdk::events::room::message::NoticeMessageEventContent; use matrix_sdk::{events::room::message::NoticeMessageEventContent, room::Joined, StoreError};
use matrix_sdk::{ use matrix_sdk::{
events::room::message::{InReplyTo, Relation}, events::room::message::{InReplyTo, Relation},
events::room::message::{MessageEventContent, MessageType}, events::room::message::{MessageEventContent, MessageType},
@ -7,7 +8,7 @@ use matrix_sdk::{
identifiers::EventId, identifiers::EventId,
Error as MatrixError, Error as MatrixError,
}; };
use matrix_sdk::{identifiers::RoomId, Client}; use matrix_sdk::{identifiers::RoomId, identifiers::UserId, Client};
/// Extracts more detailed error messages out of a matrix SDK error. /// Extracts more detailed error messages out of a matrix SDK error.
fn extract_error_message(error: MatrixError) -> String { fn extract_error_message(error: MatrixError) -> String {
@ -36,6 +37,30 @@ pub async fn get_users_in_room(
} }
} }
pub async fn get_rooms_for_user(
client: &Client,
user: &UserId,
) -> Result<Vec<Joined>, MatrixError> {
// Carries errors through, in case we cannot load joined user IDs
// from the room for some reason.
let user_is_in_room = |room: Joined| async move {
match room.joined_user_ids().await {
Ok(users) => match users.contains(user) {
true => Some(Ok(room)),
false => None,
},
Err(e) => Some(Err(e)),
}
};
let rooms_for_user: Vec<Joined> = stream::iter(client.joined_rooms())
.filter_map(user_is_in_room)
.try_collect()
.await?;
Ok(rooms_for_user)
}
pub async fn send_message( pub async fn send_message(
client: &Client, client: &Client,
room_id: &RoomId, room_id: &RoomId,