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();
|
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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(())
|
||||||
|
|
|
@ -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")]
|
||||||
|
|
18
src/logic.rs
18
src/logic.rs
|
@ -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()
|
||||||
|
|
|
@ -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![])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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>()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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