Compare commits
2 Commits
b3cd7266e4
...
14f8bc8b39
Author | SHA1 | Date |
---|---|---|
projectmoon | 14f8bc8b39 | |
projectmoon | 7db639f16c |
|
@ -245,7 +245,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "chronicle-dicebot"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bincode",
|
||||
|
|
40
src/bot.rs
40
src/bot.rs
|
@ -120,20 +120,40 @@ impl DiceBot {
|
|||
})
|
||||
}
|
||||
|
||||
/// Logs the bot into Matrix and listens for events until program
|
||||
/// Logs in to matrix and potentially records a new device ID. If
|
||||
/// no device ID is found in the database, a new one will be
|
||||
/// generated by the matrix SDK, and we will store it.
|
||||
async fn login(&self, client: &Client) -> Result<(), BotError> {
|
||||
let username = self.config.matrix_username();
|
||||
let password = self.config.matrix_password();
|
||||
|
||||
// Pull device ID from database, if it exists. Then write it
|
||||
// to DB if the library generated one for us.
|
||||
let device_id: Option<String> = self.db.state.get_device_id()?;
|
||||
let device_id: Option<&str> = device_id.as_deref();
|
||||
|
||||
client
|
||||
.login(username, password, device_id, Some("matrix dice bot"))
|
||||
.await?;
|
||||
|
||||
if device_id.is_none() {
|
||||
let device_id = client.device_id().await.ok_or(BotError::NoDeviceIdFound)?;
|
||||
self.db.state.set_device_id(device_id.as_str())?;
|
||||
info!("Recorded new device ID: {}", device_id.as_str());
|
||||
} else {
|
||||
info!("Using existing device ID: {}", device_id.unwrap());
|
||||
}
|
||||
|
||||
info!("Logged in as {}", username);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Logs the bot in to Matrix and listens for events until program
|
||||
/// terminated, or a panic occurs. Originally adapted from the
|
||||
/// matrix-rust-sdk command bot example.
|
||||
pub async fn run(self) -> Result<(), BotError> {
|
||||
let username = &self.config.matrix_username();
|
||||
let password = &self.config.matrix_password();
|
||||
|
||||
//TODO provide a device id from config.
|
||||
let client = self.client.clone();
|
||||
client
|
||||
.login(username, password, None, Some("matrix dice bot"))
|
||||
.await?;
|
||||
|
||||
info!("Logged in as {}", username);
|
||||
self.login(&client).await?;
|
||||
|
||||
// Initial sync without event handler prevents responding to
|
||||
// messages received while bot was offline. TODO: selectively
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::db::errors::{DataError, MigrationError};
|
||||
use crate::db::migrations::{get_migration_version, Migrations};
|
||||
use crate::db::rooms::Rooms;
|
||||
use crate::db::state::DbState;
|
||||
use crate::db::variables::Variables;
|
||||
use log::info;
|
||||
use sled::{Config, Db};
|
||||
|
@ -11,6 +12,7 @@ pub mod errors;
|
|||
pub mod migrations;
|
||||
pub mod rooms;
|
||||
pub mod schema;
|
||||
pub mod state;
|
||||
pub mod variables;
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -19,6 +21,7 @@ pub struct Database {
|
|||
pub(crate) variables: Variables,
|
||||
pub(crate) migrations: Migrations,
|
||||
pub(crate) rooms: Rooms,
|
||||
pub(crate) state: DbState,
|
||||
}
|
||||
|
||||
impl Database {
|
||||
|
@ -30,6 +33,7 @@ impl Database {
|
|||
variables: Variables::new(&db)?,
|
||||
migrations: Migrations(migrations),
|
||||
rooms: Rooms::new(&db)?,
|
||||
state: DbState::new(&db)?,
|
||||
};
|
||||
|
||||
//Start any event handlers.
|
||||
|
|
|
@ -29,8 +29,11 @@ pub enum DataError {
|
|||
#[error("unexpected or corruptd data bytes")]
|
||||
InvalidValue,
|
||||
|
||||
#[error("expected string ref, but utf8 schema was violated: {0}")]
|
||||
Utf8RefSchemaViolation(#[from] std::str::Utf8Error),
|
||||
|
||||
#[error("expected string, but utf8 schema was violated: {0}")]
|
||||
Utf8chemaViolation(#[from] std::str::Utf8Error),
|
||||
Utf8SchemaViolation(#[from] std::string::FromUtf8Error),
|
||||
|
||||
#[error("internal database error: {0}")]
|
||||
InternalError(#[from] sled::Error),
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
use crate::db::errors::DataError;
|
||||
use sled::Tree;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DbState {
|
||||
/// Tree of simple key-values for global state values that persist
|
||||
/// between restarts (e.g. device ID).
|
||||
pub(in crate::db) global_metadata: Tree,
|
||||
}
|
||||
|
||||
const DEVICE_ID_KEY: &'static [u8] = b"device_id";
|
||||
|
||||
impl DbState {
|
||||
pub(in crate::db) fn new(db: &sled::Db) -> Result<DbState, sled::Error> {
|
||||
Ok(DbState {
|
||||
global_metadata: db.open_tree("global_metadata")?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_device_id(&self) -> Result<Option<String>, DataError> {
|
||||
self.global_metadata
|
||||
.get(DEVICE_ID_KEY)?
|
||||
.map(|v| String::from_utf8(v.to_vec()))
|
||||
.transpose()
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
pub fn set_device_id(&self, device_id: &str) -> Result<(), DataError> {
|
||||
self.global_metadata
|
||||
.insert(DEVICE_ID_KEY, device_id.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use sled::Config;
|
||||
|
||||
fn create_test_instance() -> DbState {
|
||||
let config = Config::new().temporary(true);
|
||||
let db = config.open().unwrap();
|
||||
DbState::new(&db).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_device_id_works() {
|
||||
let state = create_test_instance();
|
||||
let result = state.set_device_id("test-device");
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_device_id_can_overwrite() {
|
||||
let state = create_test_instance();
|
||||
state.set_device_id("test-device").expect("insert 1 failed");
|
||||
let result = state.set_device_id("test-device2");
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_device_id_returns_some_when_set() {
|
||||
let state = create_test_instance();
|
||||
|
||||
state
|
||||
.set_device_id("test-device")
|
||||
.expect("could not store device id properly");
|
||||
|
||||
let device_id = state.get_device_id();
|
||||
|
||||
assert!(device_id.is_ok());
|
||||
|
||||
let device_id = device_id.unwrap();
|
||||
assert!(device_id.is_some());
|
||||
assert_eq!("test-device", device_id.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_device_id_returns_none_when_unset() {
|
||||
let state = create_test_instance();
|
||||
let device_id = state.get_device_id();
|
||||
assert!(device_id.is_ok());
|
||||
|
||||
let device_id = device_id.unwrap();
|
||||
assert!(device_id.is_none());
|
||||
}
|
||||
}
|
|
@ -12,6 +12,9 @@ pub enum BotError {
|
|||
#[error("the sync token could not be retrieved")]
|
||||
SyncTokenRequired,
|
||||
|
||||
#[error("could not retrieve device id")]
|
||||
NoDeviceIdFound,
|
||||
|
||||
#[error("command error: {0}")]
|
||||
CommandError(#[from] CommandError),
|
||||
|
||||
|
|
Loading…
Reference in New Issue