Compare commits
4 Commits
243633a599
...
55ac1a20bb
Author | SHA1 | Date |
---|---|---|
projectmoon | 55ac1a20bb | |
projectmoon | 090ce9be45 | |
projectmoon | 2a6dff3e07 | |
projectmoon | 952f35d53a |
|
@ -3,7 +3,7 @@ name: build-and-test
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: test
|
- name: test
|
||||||
image: rust:1.54
|
image: rust:1.68
|
||||||
commands:
|
commands:
|
||||||
- apt-get update
|
- apt-get update
|
||||||
- apt-get install -y cmake
|
- apt-get install -y cmake
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "tenebrous-dicebot"
|
name = "tenebrous-dicebot"
|
||||||
version = "0.13.1"
|
version = "0.13.2"
|
||||||
|
rust-version = "1.68"
|
||||||
authors = ["projectmoon <projectmoon@agnos.is>", "Taylor C. Richberger <taywee@gmx.com>"]
|
authors = ["projectmoon <projectmoon@agnos.is>", "Taylor C. Richberger <taywee@gmx.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = 'AGPL-3.0-or-later'
|
license = 'AGPL-3.0-or-later'
|
||||||
|
@ -15,7 +16,7 @@ tonic-build = "0.4"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# indexmap version locked fixes a dependency cycle.
|
# indexmap version locked fixes a dependency cycle.
|
||||||
indexmap = "=1.6.2"
|
# indexmap = "=1.6.2"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
tracing-subscriber = "0.2"
|
tracing-subscriber = "0.2"
|
||||||
toml = "0.5"
|
toml = "0.5"
|
||||||
|
@ -32,7 +33,7 @@ combine = "4.5"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
html2text = "0.2"
|
html2text = "0.2"
|
||||||
phf = { version = "0.8", features = ["macros"] }
|
phf = { version = "0.8", features = ["macros"] }
|
||||||
matrix-sdk = { version = "0.4.1" }
|
matrix-sdk = { version = "0.6" }
|
||||||
refinery = { version = "0.8", features = ["rusqlite"]}
|
refinery = { version = "0.8", features = ["rusqlite"]}
|
||||||
barrel = { version = "0.7", features = ["sqlite3"] }
|
barrel = { version = "0.7", features = ["sqlite3"] }
|
||||||
strum = { version = "0.22", features = ["derive"] }
|
strum = { version = "0.22", features = ["derive"] }
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use matrix_sdk::ruma::identifiers::room_id;
|
use matrix_sdk::ruma::room_id;
|
||||||
use matrix_sdk::Client;
|
use matrix_sdk::Client;
|
||||||
use tenebrous_dicebot::commands;
|
use tenebrous_dicebot::commands;
|
||||||
use tenebrous_dicebot::commands::ResponseExtractor;
|
use tenebrous_dicebot::commands::ResponseExtractor;
|
||||||
|
@ -29,7 +29,7 @@ async fn main() -> Result<(), BotError> {
|
||||||
let context = Context {
|
let context = Context {
|
||||||
db,
|
db,
|
||||||
account: Account::default(),
|
account: Account::default(),
|
||||||
matrix_client: Client::new(homeserver).expect("Could not create matrix client"),
|
matrix_client: Client::new(homeserver).await.expect("Could not create matrix client"),
|
||||||
origin_room: RoomContext {
|
origin_room: RoomContext {
|
||||||
id: &room_id!("!fakeroomid:example.com"),
|
id: &room_id!("!fakeroomid:example.com"),
|
||||||
display_name: "fake room".to_owned(),
|
display_name: "fake room".to_owned(),
|
||||||
|
|
|
@ -21,7 +21,7 @@ async fn init(config_path: &str) -> Result<(Arc<Config>, Database, Client), BotE
|
||||||
let cfg = Arc::new(cfg);
|
let cfg = Arc::new(cfg);
|
||||||
let sqlite_path = format!("{}/dicebot.sqlite", cfg.database_path());
|
let sqlite_path = format!("{}/dicebot.sqlite", cfg.database_path());
|
||||||
let db = Database::new(&sqlite_path).await?;
|
let db = Database::new(&sqlite_path).await?;
|
||||||
let client = tenebrous_dicebot::matrix::create_client(&cfg)?;
|
let client = tenebrous_dicebot::matrix::create_client(&cfg).await?;
|
||||||
Ok((cfg, db, client))
|
Ok((cfg, db, client))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ async fn run() -> Result<(), BotError> {
|
||||||
|
|
||||||
match try_join!(bot, grpc) {
|
match try_join!(bot, grpc) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(e) => error!("Error: {}", e),
|
Err(e) => error!("Error: {:?}", e),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
models::Account,
|
models::Account,
|
||||||
};
|
};
|
||||||
use futures::stream::{self, StreamExt};
|
use futures::stream::{self, StreamExt};
|
||||||
use matrix_sdk::ruma::{EventId, RoomId};
|
use matrix_sdk::ruma::{OwnedEventId, RoomId};
|
||||||
use matrix_sdk::{self, room::Joined, Client};
|
use matrix_sdk::{self, room::Joined, Client};
|
||||||
use std::clone::Clone;
|
use std::clone::Clone;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
@ -20,7 +20,7 @@ pub(super) async fn handle_single_result(
|
||||||
cmd_result: &ExecutionResult,
|
cmd_result: &ExecutionResult,
|
||||||
respond_to: &str,
|
respond_to: &str,
|
||||||
room: &Joined,
|
room: &Joined,
|
||||||
event_id: EventId,
|
event_id: OwnedEventId,
|
||||||
) {
|
) {
|
||||||
let html = cmd_result.message_html(respond_to);
|
let html = cmd_result.message_html(respond_to);
|
||||||
let plain = cmd_result.message_plain(respond_to);
|
let plain = cmd_result.message_plain(respond_to);
|
||||||
|
@ -108,9 +108,9 @@ fn get_account_active_room(client: &Client, account: &Account) -> Result<Option<
|
||||||
let active_room = account
|
let active_room = account
|
||||||
.registered_user()
|
.registered_user()
|
||||||
.and_then(|u| u.active_room.as_deref())
|
.and_then(|u| u.active_room.as_deref())
|
||||||
.map(|room_id| RoomId::try_from(room_id))
|
.map(|room_id| <&RoomId>::try_from(room_id))
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.and_then(|active_room_id| client.get_joined_room(&active_room_id));
|
.and_then(|active_room_id| client.get_joined_room(active_room_id));
|
||||||
|
|
||||||
Ok(active_room)
|
Ok(active_room)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,25 +3,24 @@ use crate::db::sqlite::Database;
|
||||||
use crate::db::Rooms;
|
use crate::db::Rooms;
|
||||||
use crate::error::BotError;
|
use crate::error::BotError;
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use matrix_sdk::ruma::events::room::member::MemberEventContent;
|
use matrix_sdk::ruma::events::room::member::RoomMemberEventContent;
|
||||||
use matrix_sdk::ruma::events::room::message::{MessageType, TextMessageEventContent};
|
use matrix_sdk::ruma::events::{StrippedStateEvent, SyncMessageLikeEvent};
|
||||||
use matrix_sdk::ruma::events::{StrippedStateEvent, SyncMessageEvent};
|
use matrix_sdk::{Client, DisplayName};
|
||||||
use matrix_sdk::Client;
|
use matrix_sdk::{self, room::Room, ruma::events::room::message::RoomMessageEventContent};
|
||||||
use matrix_sdk::{self, room::Room, ruma::events::room::message::MessageEventContent};
|
|
||||||
use std::ops::Sub;
|
use std::ops::Sub;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
use std::{clone::Clone, time::UNIX_EPOCH};
|
use std::time::UNIX_EPOCH;
|
||||||
|
|
||||||
/// Check if a message is recent enough to actually process. If the
|
/// Check if a message is recent enough to actually process. If the
|
||||||
/// message is within "oldest_message_age" seconds, this function
|
/// message is within "oldest_message_age" seconds, this function
|
||||||
/// returns true. If it's older than that, it returns false and logs a
|
/// returns true. If it's older than that, it returns false and logs a
|
||||||
/// debug message.
|
/// debug message.
|
||||||
fn check_message_age(
|
fn check_message_age(
|
||||||
event: &SyncMessageEvent<MessageEventContent>,
|
event: &SyncMessageLikeEvent<RoomMessageEventContent>,
|
||||||
oldest_message_age: u64,
|
oldest_message_age: u64,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let sending_time = event
|
let sending_time = event
|
||||||
.origin_server_ts
|
.origin_server_ts()
|
||||||
.to_system_time()
|
.to_system_time()
|
||||||
.unwrap_or(UNIX_EPOCH);
|
.unwrap_or(UNIX_EPOCH);
|
||||||
|
|
||||||
|
@ -48,7 +47,7 @@ fn check_message_age(
|
||||||
/// the bot left and rejoined quickly.
|
/// the bot left and rejoined quickly.
|
||||||
async fn should_process_message<'a>(
|
async fn should_process_message<'a>(
|
||||||
bot: &DiceBot,
|
bot: &DiceBot,
|
||||||
event: &SyncMessageEvent<MessageEventContent>,
|
event: &SyncMessageLikeEvent<RoomMessageEventContent>,
|
||||||
) -> Result<(String, String), BotError> {
|
) -> Result<(String, String), BotError> {
|
||||||
//Ignore messages that are older than configured duration.
|
//Ignore messages that are older than configured duration.
|
||||||
if !check_message_age(event, bot.config.oldest_message_age()) {
|
if !check_message_age(event, bot.config.oldest_message_age()) {
|
||||||
|
@ -62,23 +61,13 @@ async fn should_process_message<'a>(
|
||||||
return Err(BotError::ShouldNotProcessError);
|
return Err(BotError::ShouldNotProcessError);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (msg_body, sender_username) = if let SyncMessageEvent {
|
let msg_body: String = event
|
||||||
content:
|
.as_original()
|
||||||
MessageEventContent {
|
.map(|e| e.content.body())
|
||||||
msgtype: MessageType::Text(TextMessageEventContent { body, .. }),
|
.map(str::to_string)
|
||||||
..
|
.unwrap_or_else(|| String::new());
|
||||||
},
|
|
||||||
sender,
|
let sender_username: String = format!("@{}:{}", event.sender().localpart(), event.sender().server_name());
|
||||||
..
|
|
||||||
} = event
|
|
||||||
{
|
|
||||||
(
|
|
||||||
body.clone(),
|
|
||||||
format!("@{}:{}", sender.localpart(), sender.server_name()),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(String::new(), String::new())
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((msg_body, sender_username))
|
Ok((msg_body, sender_username))
|
||||||
}
|
}
|
||||||
|
@ -96,7 +85,7 @@ async fn should_process_event(db: &Database, room_id: &str, event_id: &str) -> b
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn on_stripped_state_member(
|
pub(super) async fn on_stripped_state_member(
|
||||||
event: StrippedStateEvent<MemberEventContent>,
|
event: StrippedStateEvent<RoomMemberEventContent>,
|
||||||
client: Client,
|
client: Client,
|
||||||
room: Room,
|
room: Room,
|
||||||
) {
|
) {
|
||||||
|
@ -111,7 +100,7 @@ pub(super) async fn on_stripped_state_member(
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Autojoining room {}",
|
"Autojoining room {}",
|
||||||
room.display_name().await.ok().unwrap_or_default()
|
room.display_name().await.ok().unwrap_or_else(|| DisplayName::Named("[error]".to_string()))
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Err(e) = client.join_room_by_id(&room.room_id()).await {
|
if let Err(e) = client.join_room_by_id(&room.room_id()).await {
|
||||||
|
@ -120,7 +109,7 @@ pub(super) async fn on_stripped_state_member(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn on_room_message(
|
pub(super) async fn on_room_message(
|
||||||
event: SyncMessageEvent<MessageEventContent>,
|
event: SyncMessageLikeEvent<RoomMessageEventContent>,
|
||||||
room: Room,
|
room: Room,
|
||||||
bot: DiceBot,
|
bot: DiceBot,
|
||||||
) {
|
) {
|
||||||
|
@ -130,7 +119,7 @@ pub(super) async fn on_room_message(
|
||||||
};
|
};
|
||||||
|
|
||||||
let room_id = room.room_id().as_str();
|
let room_id = room.room_id().as_str();
|
||||||
if !should_process_event(&bot.db, room_id, event.event_id.as_str()).await {
|
if !should_process_event(&bot.db, room_id, event.event_id().as_str()).await {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +134,6 @@ pub(super) async fn on_room_message(
|
||||||
.execute_commands(&room, &sender_username, &msg_body)
|
.execute_commands(&room, &sender_username, &msg_body)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
bot.handle_results(&room, &sender_username, event.event_id.clone(), results)
|
bot.handle_results(&room, &sender_username, event.event_id().to_owned(), results)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,11 @@ use crate::error::BotError;
|
||||||
use crate::state::DiceBotState;
|
use crate::state::DiceBotState;
|
||||||
use log::info;
|
use log::info;
|
||||||
use matrix_sdk::room::Room;
|
use matrix_sdk::room::Room;
|
||||||
use matrix_sdk::ruma::events::room::message::MessageEventContent;
|
use matrix_sdk::ruma::events::room::message::RoomMessageEventContent;
|
||||||
use matrix_sdk::ruma::events::SyncMessageEvent;
|
use matrix_sdk::ruma::events::SyncMessageLikeEvent;
|
||||||
use matrix_sdk::ruma::EventId;
|
use matrix_sdk::ruma::OwnedEventId;
|
||||||
use matrix_sdk::{self, room::Joined, Client, SyncSettings};
|
use matrix_sdk::{self, room::Joined, Client};
|
||||||
|
use matrix_sdk::config::SyncSettings;
|
||||||
use std::clone::Clone;
|
use std::clone::Clone;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
@ -68,12 +69,14 @@ impl DiceBot {
|
||||||
let device_id: Option<String> = self.db.get_device_id().await?;
|
let device_id: Option<String> = self.db.get_device_id().await?;
|
||||||
let device_id: Option<&str> = device_id.as_deref();
|
let device_id: Option<&str> = device_id.as_deref();
|
||||||
|
|
||||||
client
|
let no_device_ld_login = || client.login_username(username, password);
|
||||||
.login(username, password, device_id, Some("matrix dice bot"))
|
let device_id_login = |id| client.login_username(username, password).device_id(id);
|
||||||
.await?;
|
let login = device_id.map_or_else(no_device_ld_login, device_id_login);
|
||||||
|
|
||||||
|
login.send().await?;
|
||||||
|
|
||||||
if device_id.is_none() {
|
if device_id.is_none() {
|
||||||
let device_id = client.device_id().await.ok_or(BotError::NoDeviceIdFound)?;
|
let device_id = client.device_id().ok_or(BotError::NoDeviceIdFound)?;
|
||||||
self.db.set_device_id(device_id.as_str()).await?;
|
self.db.set_device_id(device_id.as_str()).await?;
|
||||||
info!("Recorded new device ID: {}", device_id.as_str());
|
info!("Recorded new device ID: {}", device_id.as_str());
|
||||||
} else {
|
} else {
|
||||||
|
@ -87,19 +90,17 @@ impl DiceBot {
|
||||||
async fn bind_events(&self) {
|
async fn bind_events(&self) {
|
||||||
//on room message: need closure to pass bot ref in.
|
//on room message: need closure to pass bot ref in.
|
||||||
self.client
|
self.client
|
||||||
.register_event_handler({
|
.add_event_handler({
|
||||||
let bot: DiceBot = self.clone();
|
let bot: DiceBot = self.clone();
|
||||||
move |event: SyncMessageEvent<MessageEventContent>, room: Room| {
|
move |event: SyncMessageLikeEvent<RoomMessageEventContent>, room: Room| {
|
||||||
let bot = bot.clone();
|
let bot = bot.clone();
|
||||||
async move { event_handlers::on_room_message(event, room, bot).await }
|
async move { event_handlers::on_room_message(event, room, bot).await }
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.await;
|
|
||||||
|
|
||||||
//auto-join handler
|
//auto-join handler
|
||||||
self.client
|
self.client
|
||||||
.register_event_handler(event_handlers::on_stripped_state_member)
|
.add_event_handler(event_handlers::on_stripped_state_member);
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Logs the bot in to Matrix and listens for events until program
|
/// Logs the bot in to Matrix and listens for events until program
|
||||||
|
@ -114,7 +115,7 @@ impl DiceBot {
|
||||||
|
|
||||||
// TODO replace with sync_with_callback for cleaner shutdown
|
// TODO replace with sync_with_callback for cleaner shutdown
|
||||||
// process.
|
// process.
|
||||||
client.sync(SyncSettings::default()).await;
|
client.sync(SyncSettings::default()).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +145,7 @@ impl DiceBot {
|
||||||
&self,
|
&self,
|
||||||
room: &Joined,
|
room: &Joined,
|
||||||
sender_username: &str,
|
sender_username: &str,
|
||||||
event_id: EventId,
|
event_id: OwnedEventId,
|
||||||
results: Vec<(String, ExecutionResult)>,
|
results: Vec<(String, ExecutionResult)>,
|
||||||
) {
|
) {
|
||||||
if results.len() >= 1 {
|
if results.len() >= 1 {
|
||||||
|
|
|
@ -332,7 +332,7 @@ mod tests {
|
||||||
macro_rules! dummy_room {
|
macro_rules! dummy_room {
|
||||||
() => {
|
() => {
|
||||||
crate::context::RoomContext {
|
crate::context::RoomContext {
|
||||||
id: &matrix_sdk::ruma::identifiers::room_id!("!fakeroomid:example.com"),
|
id: &matrix_sdk::ruma::room_id!("!fakeroomid:example.com"),
|
||||||
display_name: "displayname".to_owned(),
|
display_name: "displayname".to_owned(),
|
||||||
secure: false,
|
secure: false,
|
||||||
}
|
}
|
||||||
|
@ -485,7 +485,7 @@ mod tests {
|
||||||
let ctx = Context {
|
let ctx = Context {
|
||||||
account: crate::models::Account::default(),
|
account: crate::models::Account::default(),
|
||||||
db: db,
|
db: db,
|
||||||
matrix_client: matrix_sdk::Client::new(homeserver).unwrap(),
|
matrix_client: matrix_sdk::Client::new(homeserver).await.unwrap(),
|
||||||
origin_room: dummy_room!(),
|
origin_room: dummy_room!(),
|
||||||
active_room: dummy_room!(),
|
active_room: dummy_room!(),
|
||||||
username: "username",
|
username: "username",
|
||||||
|
@ -527,7 +527,7 @@ mod tests {
|
||||||
let ctx = Context {
|
let ctx = Context {
|
||||||
account: crate::models::Account::default(),
|
account: crate::models::Account::default(),
|
||||||
db: db,
|
db: db,
|
||||||
matrix_client: matrix_sdk::Client::new(homeserver).unwrap(),
|
matrix_client: matrix_sdk::Client::new(homeserver).await.unwrap(),
|
||||||
origin_room: dummy_room!(),
|
origin_room: dummy_room!(),
|
||||||
active_room: dummy_room!(),
|
active_room: dummy_room!(),
|
||||||
username: "username",
|
username: "username",
|
||||||
|
@ -566,7 +566,7 @@ mod tests {
|
||||||
let ctx = Context {
|
let ctx = Context {
|
||||||
account: crate::models::Account::default(),
|
account: crate::models::Account::default(),
|
||||||
db: db.clone(),
|
db: db.clone(),
|
||||||
matrix_client: matrix_sdk::Client::new(homeserver).unwrap(),
|
matrix_client: matrix_sdk::Client::new(homeserver).await.unwrap(),
|
||||||
origin_room: dummy_room!(),
|
origin_room: dummy_room!(),
|
||||||
active_room: dummy_room!(),
|
active_room: dummy_room!(),
|
||||||
username: "username",
|
username: "username",
|
||||||
|
|
|
@ -162,11 +162,12 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use management::RegisterCommand;
|
use management::RegisterCommand;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
use matrix_sdk::ruma::room_id;
|
||||||
|
|
||||||
macro_rules! dummy_room {
|
macro_rules! dummy_room {
|
||||||
() => {
|
() => {
|
||||||
crate::context::RoomContext {
|
crate::context::RoomContext {
|
||||||
id: &matrix_sdk::ruma::identifiers::room_id!("!fakeroomid:example.com"),
|
id: &room_id!("!fakeroomid:example.com"),
|
||||||
display_name: "displayname".to_owned(),
|
display_name: "displayname".to_owned(),
|
||||||
secure: false,
|
secure: false,
|
||||||
}
|
}
|
||||||
|
@ -176,7 +177,7 @@ mod tests {
|
||||||
macro_rules! secure_room {
|
macro_rules! secure_room {
|
||||||
() => {
|
() => {
|
||||||
crate::context::RoomContext {
|
crate::context::RoomContext {
|
||||||
id: &matrix_sdk::ruma::identifiers::room_id!("!fakeroomid:example.com"),
|
id: &room_id!("!fakeroomid:example.com"),
|
||||||
display_name: "displayname".to_owned(),
|
display_name: "displayname".to_owned(),
|
||||||
secure: true,
|
secure: true,
|
||||||
}
|
}
|
||||||
|
@ -195,7 +196,7 @@ mod tests {
|
||||||
let ctx = Context {
|
let ctx = Context {
|
||||||
account: crate::models::Account::default(),
|
account: crate::models::Account::default(),
|
||||||
db: db,
|
db: db,
|
||||||
matrix_client: matrix_sdk::Client::new(homeserver).unwrap(),
|
matrix_client: matrix_sdk::Client::new(homeserver).await.unwrap(),
|
||||||
origin_room: secure_room!(),
|
origin_room: secure_room!(),
|
||||||
active_room: secure_room!(),
|
active_room: secure_room!(),
|
||||||
username: "myusername",
|
username: "myusername",
|
||||||
|
@ -218,7 +219,7 @@ mod tests {
|
||||||
let ctx = Context {
|
let ctx = Context {
|
||||||
account: crate::models::Account::default(),
|
account: crate::models::Account::default(),
|
||||||
db: db,
|
db: db,
|
||||||
matrix_client: matrix_sdk::Client::new(homeserver).unwrap(),
|
matrix_client: matrix_sdk::Client::new(homeserver).await.unwrap(),
|
||||||
origin_room: secure_room!(),
|
origin_room: secure_room!(),
|
||||||
active_room: secure_room!(),
|
active_room: secure_room!(),
|
||||||
username: "myusername",
|
username: "myusername",
|
||||||
|
@ -241,7 +242,7 @@ mod tests {
|
||||||
let ctx = Context {
|
let ctx = Context {
|
||||||
account: crate::models::Account::default(),
|
account: crate::models::Account::default(),
|
||||||
db: db,
|
db: db,
|
||||||
matrix_client: matrix_sdk::Client::new(homeserver).unwrap(),
|
matrix_client: matrix_sdk::Client::new(homeserver).await.unwrap(),
|
||||||
origin_room: dummy_room!(),
|
origin_room: dummy_room!(),
|
||||||
active_room: dummy_room!(),
|
active_room: dummy_room!(),
|
||||||
username: "myusername",
|
username: "myusername",
|
||||||
|
@ -264,7 +265,7 @@ mod tests {
|
||||||
let ctx = Context {
|
let ctx = Context {
|
||||||
account: crate::models::Account::default(),
|
account: crate::models::Account::default(),
|
||||||
db: db,
|
db: db,
|
||||||
matrix_client: matrix_sdk::Client::new(homeserver).unwrap(),
|
matrix_client: matrix_sdk::Client::new(homeserver).await.unwrap(),
|
||||||
origin_room: dummy_room!(),
|
origin_room: dummy_room!(),
|
||||||
active_room: dummy_room!(),
|
active_room: dummy_room!(),
|
||||||
username: "myusername",
|
username: "myusername",
|
||||||
|
@ -287,7 +288,7 @@ mod tests {
|
||||||
let ctx = Context {
|
let ctx = Context {
|
||||||
account: crate::models::Account::default(),
|
account: crate::models::Account::default(),
|
||||||
db: db,
|
db: db,
|
||||||
matrix_client: matrix_sdk::Client::new(homeserver).unwrap(),
|
matrix_client: matrix_sdk::Client::new(homeserver).await.unwrap(),
|
||||||
origin_room: dummy_room!(),
|
origin_room: dummy_room!(),
|
||||||
active_room: dummy_room!(),
|
active_room: dummy_room!(),
|
||||||
username: "myusername",
|
username: "myusername",
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::matrix;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use fuse_rust::{Fuse, FuseProperty, Fuseable};
|
use fuse_rust::{Fuse, FuseProperty, Fuseable};
|
||||||
use futures::stream::{self, StreamExt, TryStreamExt};
|
use futures::stream::{self, StreamExt, TryStreamExt};
|
||||||
use matrix_sdk::{ruma::UserId, Client};
|
use matrix_sdk::{ruma::OwnedUserId, Client};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
/// Holds matrix room ID and display name as strings, for use with
|
/// Holds matrix room ID and display name as strings, for use with
|
||||||
|
@ -62,13 +62,13 @@ async fn get_rooms_for_user(
|
||||||
client: &Client,
|
client: &Client,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
) -> Result<Vec<RoomNameAndId>, BotError> {
|
) -> Result<Vec<RoomNameAndId>, BotError> {
|
||||||
let user_id = UserId::try_from(user_id)?;
|
let user_id = OwnedUserId::try_from(user_id)?;
|
||||||
let rooms_for_user = matrix::get_rooms_for_user(client, &user_id).await?;
|
let rooms_for_user = matrix::get_rooms_for_user(client, &user_id).await?;
|
||||||
let mut rooms_for_user: Vec<RoomNameAndId> = stream::iter(rooms_for_user)
|
let mut rooms_for_user: Vec<RoomNameAndId> = stream::iter(rooms_for_user)
|
||||||
.filter_map(|room| async move {
|
.filter_map(|room| async move {
|
||||||
Some(room.display_name().await.map(|room_name| RoomNameAndId {
|
Some(room.display_name().await.map(|room_name| RoomNameAndId {
|
||||||
id: room.room_id().to_string(),
|
id: room.room_id().to_string(),
|
||||||
name: room_name,
|
name: room_name.to_string(),
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
.try_collect()
|
.try_collect()
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::db::sqlite::Database;
|
||||||
use crate::error::BotError;
|
use crate::error::BotError;
|
||||||
use crate::models::Account;
|
use crate::models::Account;
|
||||||
use matrix_sdk::room::Joined;
|
use matrix_sdk::room::Joined;
|
||||||
use matrix_sdk::ruma::identifiers::{RoomId, UserId};
|
use matrix_sdk::ruma::{RoomId, UserId};
|
||||||
use matrix_sdk::Client;
|
use matrix_sdk::Client;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
@ -48,15 +48,16 @@ impl RoomContext<'_> {
|
||||||
// TODO is_direct is a hack; the bot should set eligible rooms
|
// TODO is_direct is a hack; the bot should set eligible rooms
|
||||||
// to Direct Message upon joining, if other contact has
|
// to Direct Message upon joining, if other contact has
|
||||||
// requested it. Waiting on SDK support.
|
// requested it. Waiting on SDK support.
|
||||||
let display_name = room
|
let display_name =
|
||||||
|
room
|
||||||
.display_name()
|
.display_name()
|
||||||
.await
|
.await
|
||||||
.ok()
|
.ok()
|
||||||
.unwrap_or_default()
|
.map(|d| d.to_string())
|
||||||
.to_string();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let sending_user = UserId::try_from(sending_user)?;
|
let sending_user = <&UserId>::try_from(sending_user)?;
|
||||||
let user_in_room = room.get_member(&sending_user).await.ok().is_some();
|
let user_in_room = room.get_member(sending_user).await.ok().is_some();
|
||||||
let is_direct = room.active_members().await?.len() == 2;
|
let is_direct = room.active_members().await?.len() == 2;
|
||||||
|
|
||||||
Ok(RoomContext {
|
Ok(RoomContext {
|
||||||
|
|
|
@ -270,7 +270,7 @@ macro_rules! is_variable {
|
||||||
element: Element::Variable(_),
|
element: Element::Variable(_),
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,11 +427,12 @@ mod tests {
|
||||||
use crate::db::sqlite::Database;
|
use crate::db::sqlite::Database;
|
||||||
use crate::parser::dice::{Amount, Element, Operator};
|
use crate::parser::dice::{Amount, Element, Operator};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
use matrix_sdk::ruma::room_id;
|
||||||
|
|
||||||
macro_rules! dummy_room {
|
macro_rules! dummy_room {
|
||||||
() => {
|
() => {
|
||||||
crate::context::RoomContext {
|
crate::context::RoomContext {
|
||||||
id: &matrix_sdk::ruma::identifiers::room_id!("!fakeroomid:example.com"),
|
id: &room_id!("!fakeroomid:example.com"),
|
||||||
display_name: "displayname".to_owned(),
|
display_name: "displayname".to_owned(),
|
||||||
secure: false,
|
secure: false,
|
||||||
}
|
}
|
||||||
|
@ -511,7 +512,7 @@ mod tests {
|
||||||
let ctx = Context {
|
let ctx = Context {
|
||||||
account: crate::models::Account::default(),
|
account: crate::models::Account::default(),
|
||||||
db: db,
|
db: db,
|
||||||
matrix_client: matrix_sdk::Client::new(homeserver).unwrap(),
|
matrix_client: matrix_sdk::Client::new(homeserver).await.unwrap(),
|
||||||
origin_room: dummy_room!(),
|
origin_room: dummy_room!(),
|
||||||
active_room: dummy_room!(),
|
active_room: dummy_room!(),
|
||||||
username: "username",
|
username: "username",
|
||||||
|
@ -549,7 +550,7 @@ mod tests {
|
||||||
let ctx = Context {
|
let ctx = Context {
|
||||||
account: crate::models::Account::default(),
|
account: crate::models::Account::default(),
|
||||||
db: db,
|
db: db,
|
||||||
matrix_client: matrix_sdk::Client::new(homeserver).unwrap(),
|
matrix_client: matrix_sdk::Client::new(homeserver).await.unwrap(),
|
||||||
origin_room: dummy_room!(),
|
origin_room: dummy_room!(),
|
||||||
active_room: dummy_room!(),
|
active_room: dummy_room!(),
|
||||||
username: "username",
|
username: "username",
|
||||||
|
@ -587,7 +588,7 @@ mod tests {
|
||||||
let ctx = Context {
|
let ctx = Context {
|
||||||
account: crate::models::Account::default(),
|
account: crate::models::Account::default(),
|
||||||
db: db,
|
db: db,
|
||||||
matrix_client: matrix_sdk::Client::new(homeserver).unwrap(),
|
matrix_client: matrix_sdk::Client::new(homeserver).await.unwrap(),
|
||||||
origin_room: dummy_room!(),
|
origin_room: dummy_room!(),
|
||||||
active_room: dummy_room!(),
|
active_room: dummy_room!(),
|
||||||
username: "username",
|
username: "username",
|
||||||
|
|
|
@ -18,6 +18,12 @@ pub enum BotError {
|
||||||
#[error("could not retrieve device id")]
|
#[error("could not retrieve device id")]
|
||||||
NoDeviceIdFound,
|
NoDeviceIdFound,
|
||||||
|
|
||||||
|
#[error("could not build client: {0}")]
|
||||||
|
ClientBuildError(#[from] matrix_sdk::ClientBuildError),
|
||||||
|
|
||||||
|
#[error("could not open matrix store: {0}")]
|
||||||
|
OpenStoreError(#[from] matrix_sdk::store::OpenStoreError),
|
||||||
|
|
||||||
#[error("command error: {0}")]
|
#[error("command error: {0}")]
|
||||||
CommandError(#[from] CommandError),
|
CommandError(#[from] CommandError),
|
||||||
|
|
||||||
|
@ -33,6 +39,9 @@ pub enum BotError {
|
||||||
#[error("could not parse URL")]
|
#[error("could not parse URL")]
|
||||||
UrlParseError(#[from] url::ParseError),
|
UrlParseError(#[from] url::ParseError),
|
||||||
|
|
||||||
|
#[error("could not parse ID")]
|
||||||
|
IdParseError(#[from] matrix_sdk::ruma::IdParseError),
|
||||||
|
|
||||||
#[error("error in matrix state store: {0}")]
|
#[error("error in matrix state store: {0}")]
|
||||||
MatrixStateStoreError(#[from] matrix_sdk::StoreError),
|
MatrixStateStoreError(#[from] matrix_sdk::StoreError),
|
||||||
|
|
||||||
|
@ -76,8 +85,8 @@ pub enum BotError {
|
||||||
#[error("could not convert to proper integer type")]
|
#[error("could not convert to proper integer type")]
|
||||||
TryFromIntError(#[from] std::num::TryFromIntError),
|
TryFromIntError(#[from] std::num::TryFromIntError),
|
||||||
|
|
||||||
#[error("identifier error: {0}")]
|
// #[error("identifier error: {0}")]
|
||||||
IdentifierError(#[from] matrix_sdk::ruma::identifiers::Error),
|
// IdentifierError(#[from] matrix_sdk::ruma::Error),
|
||||||
|
|
||||||
#[error("password creation error: {0}")]
|
#[error("password creation error: {0}")]
|
||||||
PasswordCreationError(argon2::Error),
|
PasswordCreationError(argon2::Error),
|
||||||
|
|
|
@ -6,6 +6,9 @@ pub fn parse_help_topic(input: &str) -> Option<HelpTopic> {
|
||||||
"dicepool" => Some(HelpTopic::DicePool),
|
"dicepool" => Some(HelpTopic::DicePool),
|
||||||
"dice" => Some(HelpTopic::RollingDice),
|
"dice" => Some(HelpTopic::RollingDice),
|
||||||
"cthulhu" => Some(HelpTopic::Cthulhu),
|
"cthulhu" => Some(HelpTopic::Cthulhu),
|
||||||
|
"variables" => Some(HelpTopic::Variables),
|
||||||
|
"var" => Some(HelpTopic::Variables),
|
||||||
|
"variable" => Some(HelpTopic::Variables),
|
||||||
"" => Some(HelpTopic::General),
|
"" => Some(HelpTopic::General),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
@ -16,6 +19,7 @@ pub enum HelpTopic {
|
||||||
DicePool,
|
DicePool,
|
||||||
Cthulhu,
|
Cthulhu,
|
||||||
RollingDice,
|
RollingDice,
|
||||||
|
Variables,
|
||||||
General,
|
General,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,6 +105,34 @@ Note: If !cthadv is given a variable, and the roll is successful, it will
|
||||||
update the variable with the new skill.
|
update the variable with the new skill.
|
||||||
"};
|
"};
|
||||||
|
|
||||||
|
const VARIABLES_HELP: &'static str = indoc! {"
|
||||||
|
Variables
|
||||||
|
|
||||||
|
Commands: !get, !set, !variables
|
||||||
|
|
||||||
|
Manage variables that can be substituted into roll commands.
|
||||||
|
|
||||||
|
Examples: !get myvar, !set myvar 10
|
||||||
|
|
||||||
|
!get <variable> = show variable of the given name
|
||||||
|
!set <variable> <num> = set a variable to a number
|
||||||
|
|
||||||
|
The !variables command will list all variables for the room. The
|
||||||
|
variables command cna be used in a secure room to avoid spamming the
|
||||||
|
actual room that the variable is set in.
|
||||||
|
|
||||||
|
Variable names can be used in all types of dice rolls:
|
||||||
|
|
||||||
|
!pool myvar + 3
|
||||||
|
!roll myvar
|
||||||
|
|
||||||
|
There are some limitations on variables: they cannot themselves be
|
||||||
|
dice expressions (i.e. can only be numbers), and they must be uniquely
|
||||||
|
parseable in an expression (i.e 'myvard6' does not work for the !roll
|
||||||
|
command).
|
||||||
|
|
||||||
|
"};
|
||||||
|
|
||||||
const GENERAL_HELP: &'static str = indoc! {"
|
const GENERAL_HELP: &'static str = indoc! {"
|
||||||
General Help
|
General Help
|
||||||
|
|
||||||
|
@ -117,6 +149,7 @@ impl HelpTopic {
|
||||||
HelpTopic::DicePool => DICEPOOL_HELP,
|
HelpTopic::DicePool => DICEPOOL_HELP,
|
||||||
HelpTopic::Cthulhu => CTHULHU_HELP,
|
HelpTopic::Cthulhu => CTHULHU_HELP,
|
||||||
HelpTopic::RollingDice => DICE_HELP,
|
HelpTopic::RollingDice => DICE_HELP,
|
||||||
|
HelpTopic::Variables => VARIABLES_HELP,
|
||||||
HelpTopic::General => GENERAL_HELP,
|
HelpTopic::General => GENERAL_HELP,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,12 @@ use std::path::PathBuf;
|
||||||
|
|
||||||
use futures::stream::{self, StreamExt, TryStreamExt};
|
use futures::stream::{self, StreamExt, TryStreamExt};
|
||||||
use log::error;
|
use log::error;
|
||||||
use matrix_sdk::ruma::events::room::message::{InReplyTo, MessageEventContent, Relation};
|
use matrix_sdk::ruma::events::room::message::{InReplyTo, RoomMessageEventContent, Relation};
|
||||||
use matrix_sdk::ruma::events::AnyMessageEventContent;
|
use matrix_sdk::ruma::events::AnyMessageLikeEventContent;
|
||||||
use matrix_sdk::ruma::{EventId, RoomId, UserId};
|
use matrix_sdk::ruma::{RoomId, OwnedEventId, OwnedUserId};
|
||||||
use matrix_sdk::Client;
|
use matrix_sdk::Client;
|
||||||
use matrix_sdk::Error as MatrixError;
|
use matrix_sdk::Error as MatrixError;
|
||||||
use matrix_sdk::{room::Joined, ClientConfig};
|
use matrix_sdk::room::Joined;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::{config::Config, error::BotError};
|
use crate::{config::Config, error::BotError};
|
||||||
|
@ -29,12 +29,16 @@ fn extract_error_message(error: MatrixError) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates the matrix client.
|
/// Creates the matrix client.
|
||||||
pub fn create_client(config: &Config) -> Result<Client, BotError> {
|
pub async fn create_client(config: &Config) -> Result<Client, BotError> {
|
||||||
let cache_dir = cache_dir()?;
|
let cache_dir = cache_dir()?;
|
||||||
let client_config = ClientConfig::new().store_path(cache_dir);
|
|
||||||
let homeserver_url = Url::parse(&config.matrix_homeserver())?;
|
let homeserver_url = Url::parse(&config.matrix_homeserver())?;
|
||||||
|
|
||||||
Ok(Client::new_with_config(homeserver_url, client_config)?)
|
let client = Client::builder()
|
||||||
|
.sled_store(cache_dir, None)?
|
||||||
|
.homeserver_url(homeserver_url).build()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve a list of users in a given room.
|
/// Retrieve a list of users in a given room.
|
||||||
|
@ -56,7 +60,7 @@ pub async fn get_users_in_room(
|
||||||
|
|
||||||
pub async fn get_rooms_for_user(
|
pub async fn get_rooms_for_user(
|
||||||
client: &Client,
|
client: &Client,
|
||||||
user: &UserId,
|
user: &OwnedUserId,
|
||||||
) -> Result<Vec<Joined>, MatrixError> {
|
) -> Result<Vec<Joined>, MatrixError> {
|
||||||
// Carries errors through, in case we cannot load joined user IDs
|
// Carries errors through, in case we cannot load joined user IDs
|
||||||
// from the room for some reason.
|
// from the room for some reason.
|
||||||
|
@ -84,7 +88,7 @@ pub async fn send_message(
|
||||||
client: &Client,
|
client: &Client,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
message: (&str, &str),
|
message: (&str, &str),
|
||||||
reply_to: Option<EventId>,
|
reply_to: Option<OwnedEventId>,
|
||||||
) {
|
) {
|
||||||
let (html, plain) = message;
|
let (html, plain) = message;
|
||||||
let room = match client.get_joined_room(room_id) {
|
let room = match client.get_joined_room(room_id) {
|
||||||
|
@ -92,13 +96,13 @@ pub async fn send_message(
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut content = MessageEventContent::notice_html(plain.trim(), html);
|
let mut content = RoomMessageEventContent::notice_html(plain.trim(), html);
|
||||||
|
|
||||||
content.relates_to = reply_to.map(|event_id| Relation::Reply {
|
content.relates_to = reply_to.map(|event_id| Relation::Reply {
|
||||||
in_reply_to: InReplyTo::new(event_id),
|
in_reply_to: InReplyTo::new(event_id)
|
||||||
});
|
});
|
||||||
|
|
||||||
let content = AnyMessageEventContent::RoomMessage(content);
|
let content = AnyMessageLikeEventContent::RoomMessage(content);
|
||||||
|
|
||||||
let result = room.send(content, None).await;
|
let result = room.send(content, None).await;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::matrix;
|
||||||
use crate::{config::Config, db::sqlite::Database};
|
use crate::{config::Config, db::sqlite::Database};
|
||||||
use futures::stream;
|
use futures::stream;
|
||||||
use futures::{StreamExt, TryFutureExt, TryStreamExt};
|
use futures::{StreamExt, TryFutureExt, TryStreamExt};
|
||||||
use matrix_sdk::ruma::UserId;
|
use matrix_sdk::ruma::OwnedUserId;
|
||||||
use matrix_sdk::{room::Joined, Client};
|
use matrix_sdk::{room::Joined, Client};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -85,7 +85,7 @@ impl Dicebot for DicebotRpcService {
|
||||||
request: Request<UserIdRequest>,
|
request: Request<UserIdRequest>,
|
||||||
) -> Result<Response<RoomsListReply>, Status> {
|
) -> Result<Response<RoomsListReply>, Status> {
|
||||||
let UserIdRequest { user_id } = request.into_inner();
|
let UserIdRequest { user_id } = request.into_inner();
|
||||||
let user_id = UserId::try_from(user_id).map_err(BotError::from)?;
|
let user_id = OwnedUserId::try_from(user_id).map_err(BotError::from)?;
|
||||||
|
|
||||||
let rooms_for_user = matrix::get_rooms_for_user(&self.client, &user_id)
|
let rooms_for_user = matrix::get_rooms_for_user(&self.client, &user_id)
|
||||||
.err_into::<BotError>()
|
.err_into::<BotError>()
|
||||||
|
@ -95,7 +95,7 @@ impl Dicebot for DicebotRpcService {
|
||||||
.filter_map(|room: Joined| async move {
|
.filter_map(|room: Joined| async move {
|
||||||
let room: Result<Room, _> = room.display_name().await.map(|room_name| Room {
|
let room: Result<Room, _> = room.display_name().await.map(|room_name| Room {
|
||||||
room_id: room.room_id().to_string(),
|
room_id: room.room_id().to_string(),
|
||||||
display_name: room_name,
|
display_name: room_name.to_string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
Some(room)
|
Some(room)
|
||||||
|
|
Loading…
Reference in New Issue