Convert to SQLx and SQLite #64
18
src/bot.rs
18
src/bot.rs
|
@ -161,19 +161,25 @@ impl DiceBot {
|
|||
let client = self.client.clone();
|
||||
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;
|
||||
info!("Listening for commands");
|
||||
|
||||
let token = client
|
||||
.sync_token()
|
||||
.await
|
||||
.ok_or(BotError::SyncTokenRequired)?;
|
||||
// let token = client
|
||||
// .sync_token()
|
||||
// .await
|
||||
// .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
|
||||
// process.
|
||||
client.sync(settings).await;
|
||||
client.sync(SyncSettings::default()).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ impl EventHandler for DiceBot {
|
|||
|
||||
let result = if event_affects_us && !adding_user {
|
||||
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 {
|
||||
info!("Joined room {}; recording room information", room_id);
|
||||
record_room_information(
|
||||
|
@ -149,10 +149,16 @@ impl EventHandler for DiceBot {
|
|||
.await
|
||||
} else if !event_affects_us && adding_user {
|
||||
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 {
|
||||
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 {
|
||||
debug!("Ignoring a room member event: {:#?}", event);
|
||||
Ok(())
|
||||
|
|
|
@ -36,10 +36,10 @@ pub enum BotError {
|
|||
#[error("error in matrix state store: {0}")]
|
||||
MatrixStateStoreError(#[from] matrix_sdk::StoreError),
|
||||
|
||||
#[error("uncategorized matrix SDK error")]
|
||||
#[error("uncategorized matrix SDK error: {0}")]
|
||||
MatrixError(#[from] matrix_sdk::Error),
|
||||
|
||||
#[error("uncategorized matrix SDK base error")]
|
||||
#[error("uncategorized matrix SDK base error: {0}")]
|
||||
MatrixBaseError(#[from] matrix_sdk::BaseError),
|
||||
|
||||
#[error("future canceled")]
|
||||
|
|
18
src/logic.rs
18
src/logic.rs
|
@ -1,5 +1,6 @@
|
|||
use crate::db::sqlite::errors::DataError;
|
||||
use crate::db::sqlite::Rooms;
|
||||
use crate::error::BotError;
|
||||
use crate::matrix;
|
||||
use crate::models::RoomInfo;
|
||||
use futures::stream::{self, StreamExt, TryStreamExt};
|
||||
|
@ -12,9 +13,12 @@ pub async fn record_room_information(
|
|||
room_id: &RoomId,
|
||||
room_display_name: &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 usernames = matrix::get_users_in_room(&client, &room_id).await;
|
||||
let usernames = matrix::get_users_in_room(&client, &room_id).await?;
|
||||
|
||||
let info = RoomInfo {
|
||||
room_id: room_id_str.to_owned(),
|
||||
|
@ -29,12 +33,18 @@ pub async fn record_room_information(
|
|||
.into_iter()
|
||||
.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
|
||||
// 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 })
|
||||
.collect::<Vec<Result<(), DataError>>>()
|
||||
.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()
|
||||
|
|
|
@ -20,16 +20,19 @@ fn extract_error_message(error: MatrixError) -> String {
|
|||
}
|
||||
|
||||
/// 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) {
|
||||
let members = joined_room.joined_members().await.ok().unwrap_or_default();
|
||||
let members = joined_room.joined_members().await?;
|
||||
|
||||
members
|
||||
Ok(members
|
||||
.into_iter()
|
||||
.map(|member| member.user_id().to_string())
|
||||
.collect()
|
||||
.collect())
|
||||
} else {
|
||||
vec![]
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ pub fn migration() -> String {
|
|||
info!("Applying migration: {}", file!());
|
||||
|
||||
m.create_table("user_variables", |t| {
|
||||
t.add_column("id", types::primary());
|
||||
t.add_column("room_id", types::text());
|
||||
t.add_column("user_id", types::text());
|
||||
t.add_column("key", types::text());
|
||||
|
|
|
@ -1,31 +1,33 @@
|
|||
use barrel::backend::Sqlite;
|
||||
use barrel::{types, Migration};
|
||||
use barrel::{types, types::Type, Migration};
|
||||
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 {
|
||||
let mut m = Migration::new();
|
||||
info!("Applying migration: {}", file!());
|
||||
|
||||
//Table for basic room information: room ID, room name
|
||||
m.create_table("room_info", move |t| {
|
||||
t.add_column("id", types::primary());
|
||||
t.add_column("room_id", types::text());
|
||||
t.add_column("room_id", primary_uuid());
|
||||
t.add_column("room_name", types::text());
|
||||
});
|
||||
|
||||
//Table of users in rooms.
|
||||
m.create_table("room_users", move |t| {
|
||||
t.add_column("id", types::primary());
|
||||
t.add_column("room_id", types::text());
|
||||
t.add_column("room_id", autoincrement_int());
|
||||
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>()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue