Add proper constraints to db tables. Report errors when listing users.

This commit is contained in:
projectmoon 2021-05-16 21:39:19 +00:00
parent 9798821b7b
commit bfc5609ab6
8 changed files with 97 additions and 33 deletions

View File

@ -161,19 +161,25 @@ impl DiceBot {
let client = self.client.clone(); let client = self.client.clone();
self.login(&client).await?; self.login(&client).await?;
// Initial sync without event handler prevents responding to
// messages received while bot was offline. TODO: selectively
// respond to old messages? e.g. comands missed while offline.
//info!("Performing intial sync (no commands will be responded to)");
// self.client.sync_once(SyncSettings::default()).await?;
client.set_event_handler(Box::new(self)).await; client.set_event_handler(Box::new(self)).await;
info!("Listening for commands"); info!("Listening for commands");
let token = client // let token = client
.sync_token() // .sync_token()
.await // .await
.ok_or(BotError::SyncTokenRequired)?; // .ok_or(BotError::SyncTokenRequired)?;
let settings = SyncSettings::default().token(token); // let settings = SyncSettings::default().token(token);
// TODO replace with sync_with_callback for cleaner shutdown // TODO replace with sync_with_callback for cleaner shutdown
// process. // process.
client.sync(settings).await; client.sync(SyncSettings::default()).await;
Ok(()) Ok(())
} }

View File

@ -136,7 +136,7 @@ impl EventHandler for DiceBot {
let result = if event_affects_us && !adding_user { let result = if event_affects_us && !adding_user {
info!("Clearing all information for room ID {}", room_id); info!("Clearing all information for room ID {}", room_id);
self.db.clear_info(room_id_str).await self.db.clear_info(room_id_str).await.map_err(|e| e.into())
} else if event_affects_us && adding_user { } else if event_affects_us && adding_user {
info!("Joined room {}; recording room information", room_id); info!("Joined room {}; recording room information", room_id);
record_room_information( record_room_information(
@ -149,10 +149,16 @@ impl EventHandler for DiceBot {
.await .await
} else if !event_affects_us && adding_user { } else if !event_affects_us && adding_user {
info!("Adding user {} to room ID {}", username, room_id); info!("Adding user {} to room ID {}", username, room_id);
self.db.add_user_to_room(username, room_id_str).await self.db
.add_user_to_room(username, room_id_str)
.await
.map_err(|e| e.into())
} else if !event_affects_us && !adding_user { } else if !event_affects_us && !adding_user {
info!("Removing user {} from room ID {}", username, room_id); info!("Removing user {} from room ID {}", username, room_id);
self.db.remove_user_from_room(username, room_id_str).await self.db
.remove_user_from_room(username, room_id_str)
.await
.map_err(|e| e.into())
} else { } else {
debug!("Ignoring a room member event: {:#?}", event); debug!("Ignoring a room member event: {:#?}", event);
Ok(()) Ok(())

View File

@ -36,10 +36,10 @@ pub enum BotError {
#[error("error in matrix state store: {0}")] #[error("error in matrix state store: {0}")]
MatrixStateStoreError(#[from] matrix_sdk::StoreError), MatrixStateStoreError(#[from] matrix_sdk::StoreError),
#[error("uncategorized matrix SDK error")] #[error("uncategorized matrix SDK error: {0}")]
MatrixError(#[from] matrix_sdk::Error), MatrixError(#[from] matrix_sdk::Error),
#[error("uncategorized matrix SDK base error")] #[error("uncategorized matrix SDK base error: {0}")]
MatrixBaseError(#[from] matrix_sdk::BaseError), MatrixBaseError(#[from] matrix_sdk::BaseError),
#[error("future canceled")] #[error("future canceled")]

View File

@ -1,5 +1,6 @@
use crate::db::sqlite::errors::DataError; use crate::db::sqlite::errors::DataError;
use crate::db::sqlite::Rooms; use crate::db::sqlite::Rooms;
use crate::error::BotError;
use crate::matrix; use crate::matrix;
use crate::models::RoomInfo; use crate::models::RoomInfo;
use futures::stream::{self, StreamExt, TryStreamExt}; use futures::stream::{self, StreamExt, TryStreamExt};
@ -12,9 +13,12 @@ pub async fn record_room_information(
room_id: &RoomId, room_id: &RoomId,
room_display_name: &str, room_display_name: &str,
our_username: &str, our_username: &str,
) -> Result<(), DataError> { ) -> 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 room_id_str = room_id.as_str();
let usernames = matrix::get_users_in_room(&client, &room_id).await; let usernames = matrix::get_users_in_room(&client, &room_id).await?;
let info = RoomInfo { let info = RoomInfo {
room_id: room_id_str.to_owned(), room_id: room_id_str.to_owned(),
@ -29,12 +33,18 @@ pub async fn record_room_information(
.into_iter() .into_iter()
.filter(|username| username != our_username); .filter(|username| username != our_username);
println!("Users to add to room: {:?}", filtered_usernames);
// Async collect into vec of results, then use into_iter of result // Async collect into vec of results, then use into_iter of result
// to go to from Result<Vec<()>> to just Result<()>. Easier than // to go to from Result<Vec<()>> to just Result<()>. Easier than
// attempting to async-collect our way to a single Result<()>. // attempting to async-collect our way to a single Result<()>.
stream::iter(filtered_usernames) stream::iter(filtered_usernames)
.then(|username| async move { db.add_user_to_room(&username, &room_id_str).await }) .then(|username| async move {
.collect::<Vec<Result<(), DataError>>>() db.add_user_to_room(&username, &room_id_str)
.await
.map_err(|e| e.into())
})
.collect::<Vec<Result<(), BotError>>>()
.await .await
.into_iter() .into_iter()
.collect() .collect()

View File

@ -20,16 +20,19 @@ fn extract_error_message(error: MatrixError) -> String {
} }
/// Retrieve a list of users in a given room. /// Retrieve a list of users in a given room.
pub async fn get_users_in_room(client: &Client, room_id: &RoomId) -> Vec<String> { pub async fn get_users_in_room(
client: &Client,
room_id: &RoomId,
) -> Result<Vec<String>, MatrixError> {
if let Some(joined_room) = client.get_joined_room(room_id) { if let Some(joined_room) = client.get_joined_room(room_id) {
let members = joined_room.joined_members().await.ok().unwrap_or_default(); let members = joined_room.joined_members().await?;
members Ok(members
.into_iter() .into_iter()
.map(|member| member.user_id().to_string()) .map(|member| member.user_id().to_string())
.collect() .collect())
} else { } else {
vec![] Ok(vec![])
} }
} }

View File

@ -7,7 +7,6 @@ pub fn migration() -> String {
info!("Applying migration: {}", file!()); info!("Applying migration: {}", file!());
m.create_table("user_variables", |t| { m.create_table("user_variables", |t| {
t.add_column("id", types::primary());
t.add_column("room_id", types::text()); t.add_column("room_id", types::text());
t.add_column("user_id", types::text()); t.add_column("user_id", types::text());
t.add_column("key", types::text()); t.add_column("key", types::text());

View File

@ -1,31 +1,33 @@
use barrel::backend::Sqlite; use barrel::backend::Sqlite;
use barrel::{types, Migration}; use barrel::{types, types::Type, Migration};
use log::info; use log::info;
fn primary_uuid() -> Type {
types::text().unique(true).primary(true).nullable(false)
}
fn autoincrement_int() -> Type {
types::integer()
.increments(true)
.unique(true)
.primary(false)
}
pub fn migration() -> String { pub fn migration() -> String {
let mut m = Migration::new(); let mut m = Migration::new();
info!("Applying migration: {}", file!()); info!("Applying migration: {}", file!());
//Table for basic room information: room ID, room name //Table for basic room information: room ID, room name
m.create_table("room_info", move |t| { m.create_table("room_info", move |t| {
t.add_column("id", types::primary()); t.add_column("room_id", primary_uuid());
t.add_column("room_id", types::text());
t.add_column("room_name", types::text()); t.add_column("room_name", types::text());
}); });
//Table of users in rooms. //Table of users in rooms.
m.create_table("room_users", move |t| { m.create_table("room_users", move |t| {
t.add_column("id", types::primary()); t.add_column("room_id", autoincrement_int());
t.add_column("room_id", types::text());
t.add_column("username", types::text()); t.add_column("username", types::text());
}); });
//Table of room ID, event ID, event timestamp
m.create_table("room_events", move |t| {
t.add_column("id", types::primary());
t.add_column("room_id", types::text());
t.add_column("event_id", types::text());
t.add_column("event_timestamp", types::integer());
});
m.make::<Sqlite>() m.make::<Sqlite>()
} }

View File

@ -0,0 +1,38 @@
use barrel::backend::Sqlite;
use barrel::{types, types::Type, Migration};
use log::info;
fn primary_uuid() -> Type {
types::text().unique(true).nullable(false)
}
fn autoincrement_int() -> Type {
types::integer()
.increments(true)
.unique(true)
.primary(false)
}
pub fn migration() -> String {
let mut m = Migration::new();
info!("Applying migration: {}", file!());
//Table of room ID, event ID, event timestamp
m.create_table("room_events", move |t| {
t.add_column("room_id", types::text().nullable(false));
t.add_column("event_id", types::text().nullable(false));
t.add_column("event_timestamp", types::integer());
});
let mut res = m.make::<Sqlite>();
//This is a hack that gives us a composite primary key.
if res.ends_with(");") {
res.pop();
res.pop();
}
let x = format!("{}, PRIMARY KEY (room_id, event_id));", res);
println!("{}", x);
x
}