Add and use active room value. #80
|
@ -1,4 +1,5 @@
|
||||||
use matrix_sdk::identifiers::room_id;
|
use matrix_sdk::identifiers::room_id;
|
||||||
|
use matrix_sdk::Client;
|
||||||
use tenebrous_dicebot::commands;
|
use tenebrous_dicebot::commands;
|
||||||
use tenebrous_dicebot::commands::ResponseExtractor;
|
use tenebrous_dicebot::commands::ResponseExtractor;
|
||||||
use tenebrous_dicebot::context::{Context, RoomContext};
|
use tenebrous_dicebot::context::{Context, RoomContext};
|
||||||
|
@ -26,11 +27,15 @@ async fn main() -> Result<(), BotError> {
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let context = Context {
|
let context = Context {
|
||||||
db: db,
|
db,
|
||||||
account: Account::default(),
|
account: Account::default(),
|
||||||
matrix_client: &matrix_sdk::Client::new(homeserver)
|
matrix_client: Client::new(homeserver).expect("Could not create matrix client"),
|
||||||
.expect("Could not create matrix client"),
|
origin_room: RoomContext {
|
||||||
room: RoomContext {
|
id: &room_id!("!fakeroomid:example.com"),
|
||||||
|
display_name: "fake room".to_owned(),
|
||||||
|
secure: false,
|
||||||
|
},
|
||||||
|
active_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(),
|
||||||
secure: false,
|
secure: false,
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
use crate::commands::{execute_command, ExecutionResult, ResponseExtractor};
|
|
||||||
use crate::context::{Context, RoomContext};
|
use crate::context::{Context, RoomContext};
|
||||||
use crate::db::sqlite::Database;
|
use crate::db::sqlite::Database;
|
||||||
use crate::error::BotError;
|
use crate::error::BotError;
|
||||||
use crate::logic;
|
use crate::logic;
|
||||||
use crate::matrix;
|
use crate::matrix;
|
||||||
|
use crate::{
|
||||||
|
commands::{execute_command, ExecutionResult, ResponseExtractor},
|
||||||
|
models::Account,
|
||||||
|
};
|
||||||
use futures::stream::{self, StreamExt};
|
use futures::stream::{self, StreamExt};
|
||||||
use matrix_sdk::{self, identifiers::EventId, room::Joined, Client};
|
use matrix_sdk::{
|
||||||
|
self,
|
||||||
|
identifiers::{EventId, RoomId},
|
||||||
|
room::Joined,
|
||||||
|
Client,
|
||||||
|
};
|
||||||
use std::clone::Clone;
|
use std::clone::Clone;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
/// Handle responding to a single command being executed. Wil print
|
/// Handle responding to a single command being executed. Wil print
|
||||||
/// out the full result of that command.
|
/// out the full result of that command.
|
||||||
|
@ -95,24 +104,57 @@ pub(super) async fn handle_multiple_results(
|
||||||
matrix::send_message(client, room.room_id(), (&message, &plain), None).await;
|
matrix::send_message(client, room.room_id(), (&message, &plain), None).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a context for command execution. Can fai if the room
|
/// Map an account's active room value to an actual matrix room, if
|
||||||
/// context creation fails.
|
/// the account has an active room. This only retrieves the
|
||||||
async fn create_context<'a>(
|
/// user-specified active room, and doesn't perform any further
|
||||||
db: &'a Database,
|
/// filtering.
|
||||||
client: &'a Client,
|
fn get_account_active_room(client: &Client, account: &Account) -> Result<Option<Joined>, BotError> {
|
||||||
room: &'a Joined,
|
let active_room = account
|
||||||
sender: &'a str,
|
.registered_user()
|
||||||
command: &'a str,
|
.and_then(|u| u.active_room.as_deref())
|
||||||
) -> Result<Context<'a>, BotError> {
|
.map(|room_id| RoomId::try_from(room_id))
|
||||||
let room_ctx = RoomContext::new(room, sender).await?;
|
.transpose()?
|
||||||
Ok(Context {
|
.and_then(|active_room_id| client.get_joined_room(&active_room_id));
|
||||||
|
|
||||||
|
Ok(active_room)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute a single command in the list of commands. Can fail if the
|
||||||
|
/// Account value cannot be created/fetched from the database, or if
|
||||||
|
/// room display names cannot be calculated. Otherwise, the success or
|
||||||
|
/// error of command execution itself is returned.
|
||||||
|
async fn execute_single_command(
|
||||||
|
command: &str,
|
||||||
|
db: &Database,
|
||||||
|
client: &Client,
|
||||||
|
origin_room: &Joined,
|
||||||
|
sender: &str,
|
||||||
|
) -> ExecutionResult {
|
||||||
|
let origin_ctx = RoomContext::new(origin_room, sender).await?;
|
||||||
|
let account = logic::get_account(db, sender).await?;
|
||||||
|
let active_room = get_account_active_room(client, &account)?;
|
||||||
|
|
||||||
|
// Active room is used in secure command-issuing rooms. In
|
||||||
|
// "public" rooms, where other users are, treat origin as the
|
||||||
|
// active room.
|
||||||
|
let active_room = active_room
|
||||||
|
.as_ref()
|
||||||
|
.filter(|_| origin_ctx.secure)
|
||||||
|
.unwrap_or(origin_room);
|
||||||
|
|
||||||
|
let active_ctx = RoomContext::new(active_room, sender).await?;
|
||||||
|
|
||||||
|
let ctx = Context {
|
||||||
|
account,
|
||||||
db: db.clone(),
|
db: db.clone(),
|
||||||
matrix_client: client,
|
matrix_client: client.clone(),
|
||||||
room: room_ctx,
|
origin_room: origin_ctx,
|
||||||
username: &sender,
|
username: &sender,
|
||||||
account: logic::get_account(db, &sender).await?,
|
active_room: active_ctx,
|
||||||
message_body: &command,
|
message_body: &command,
|
||||||
})
|
};
|
||||||
|
|
||||||
|
execute_command(&ctx).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to execute all commands sent to the bot in a message. This
|
/// Attempt to execute all commands sent to the bot in a message. This
|
||||||
|
@ -127,13 +169,8 @@ pub(super) async fn execute(
|
||||||
) -> Vec<(String, ExecutionResult)> {
|
) -> Vec<(String, ExecutionResult)> {
|
||||||
stream::iter(commands)
|
stream::iter(commands)
|
||||||
.then(|command| async move {
|
.then(|command| async move {
|
||||||
match create_context(db, client, room, sender, command).await {
|
let result = execute_single_command(command, db, client, room, sender).await;
|
||||||
Err(e) => (command.to_owned(), Err(e)),
|
(command.to_owned(), result)
|
||||||
Ok(ctx) => {
|
|
||||||
let cmd_result = execute_command(&ctx).await;
|
|
||||||
(command.to_owned(), cmd_result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -485,8 +485,9 @@ 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).unwrap(),
|
||||||
room: dummy_room!(),
|
origin_room: dummy_room!(),
|
||||||
|
active_room: dummy_room!(),
|
||||||
username: "username",
|
username: "username",
|
||||||
message_body: "message",
|
message_body: "message",
|
||||||
};
|
};
|
||||||
|
@ -526,8 +527,9 @@ 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).unwrap(),
|
||||||
room: dummy_room!(),
|
origin_room: dummy_room!(),
|
||||||
|
active_room: dummy_room!(),
|
||||||
username: "username",
|
username: "username",
|
||||||
message_body: "message",
|
message_body: "message",
|
||||||
};
|
};
|
||||||
|
@ -564,13 +566,19 @@ 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).unwrap(),
|
||||||
room: dummy_room!(),
|
origin_room: dummy_room!(),
|
||||||
|
active_room: dummy_room!(),
|
||||||
username: "username",
|
username: "username",
|
||||||
message_body: "message",
|
message_body: "message",
|
||||||
};
|
};
|
||||||
|
|
||||||
db.set_user_variable(&ctx.username, &ctx.room.id.as_str(), "myvariable", 10)
|
db.set_user_variable(
|
||||||
|
&ctx.username,
|
||||||
|
&ctx.origin_room.id.as_str(),
|
||||||
|
"myvariable",
|
||||||
|
10,
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.expect("could not set myvariable to 10");
|
.expect("could not set myvariable to 10");
|
||||||
|
|
||||||
|
|
|
@ -146,13 +146,13 @@ fn log_command(cmd: &(impl Command + ?Sized), ctx: &Context, result: &ExecutionR
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
info!(
|
info!(
|
||||||
"[{}] {} <{}{}> - success",
|
"[{}] {} <{}{}> - success",
|
||||||
ctx.room.display_name, ctx.username, command, dots
|
ctx.origin_room.display_name, ctx.username, command, dots
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(
|
error!(
|
||||||
"[{}] {} <{}{}> - {}",
|
"[{}] {} <{}{}> - {}",
|
||||||
ctx.room.display_name, ctx.username, command, dots, e
|
ctx.origin_room.display_name, ctx.username, command, dots, e
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -196,8 +196,9 @@ 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).unwrap(),
|
||||||
room: secure_room!(),
|
origin_room: secure_room!(),
|
||||||
|
active_room: secure_room!(),
|
||||||
username: "myusername",
|
username: "myusername",
|
||||||
message_body: "!notacommand",
|
message_body: "!notacommand",
|
||||||
};
|
};
|
||||||
|
@ -218,8 +219,9 @@ 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).unwrap(),
|
||||||
room: secure_room!(),
|
origin_room: secure_room!(),
|
||||||
|
active_room: secure_room!(),
|
||||||
username: "myusername",
|
username: "myusername",
|
||||||
message_body: "!notacommand",
|
message_body: "!notacommand",
|
||||||
};
|
};
|
||||||
|
@ -240,8 +242,9 @@ 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).unwrap(),
|
||||||
room: dummy_room!(),
|
origin_room: dummy_room!(),
|
||||||
|
active_room: dummy_room!(),
|
||||||
username: "myusername",
|
username: "myusername",
|
||||||
message_body: "!notacommand",
|
message_body: "!notacommand",
|
||||||
};
|
};
|
||||||
|
@ -262,8 +265,9 @@ 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).unwrap(),
|
||||||
room: dummy_room!(),
|
origin_room: dummy_room!(),
|
||||||
|
active_room: dummy_room!(),
|
||||||
username: "myusername",
|
username: "myusername",
|
||||||
message_body: "!notacommand",
|
message_body: "!notacommand",
|
||||||
};
|
};
|
||||||
|
@ -284,8 +288,9 @@ 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).unwrap(),
|
||||||
room: dummy_room!(),
|
origin_room: dummy_room!(),
|
||||||
|
active_room: dummy_room!(),
|
||||||
username: "myusername",
|
username: "myusername",
|
||||||
message_body: "!notacommand",
|
message_body: "!notacommand",
|
||||||
};
|
};
|
||||||
|
|
|
@ -110,7 +110,7 @@ impl Command for ListRoomsCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute(&self, ctx: &Context<'_>) -> ExecutionResult {
|
async fn execute(&self, ctx: &Context<'_>) -> ExecutionResult {
|
||||||
let rooms_for_user: Vec<String> = get_rooms_for_user(ctx.matrix_client, ctx.username)
|
let rooms_for_user: Vec<String> = get_rooms_for_user(&ctx.matrix_client, ctx.username)
|
||||||
.await
|
.await
|
||||||
.map(|rooms| {
|
.map(|rooms| {
|
||||||
rooms
|
rooms
|
||||||
|
@ -155,7 +155,7 @@ impl Command for SetRoomCommand {
|
||||||
return Err(BotError::AccountDoesNotExist);
|
return Err(BotError::AccountDoesNotExist);
|
||||||
}
|
}
|
||||||
|
|
||||||
let rooms_for_user = get_rooms_for_user(ctx.matrix_client, ctx.username).await?;
|
let rooms_for_user = get_rooms_for_user(&ctx.matrix_client, ctx.username).await?;
|
||||||
let room = search_for_room(&rooms_for_user, &self.0);
|
let room = search_for_room(&rooms_for_user, &self.0);
|
||||||
|
|
||||||
if let Some(room) = room {
|
if let Some(room) = room {
|
||||||
|
|
|
@ -11,8 +11,9 @@ use std::convert::TryFrom;
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Context<'a> {
|
pub struct Context<'a> {
|
||||||
pub db: Database,
|
pub db: Database,
|
||||||
pub matrix_client: &'a Client,
|
pub matrix_client: Client,
|
||||||
pub room: RoomContext<'a>,
|
pub origin_room: RoomContext<'a>,
|
||||||
|
pub active_room: RoomContext<'a>,
|
||||||
pub username: &'a str,
|
pub username: &'a str,
|
||||||
pub message_body: &'a str,
|
pub message_body: &'a str,
|
||||||
pub account: Account,
|
pub account: Account,
|
||||||
|
@ -20,11 +21,11 @@ pub struct Context<'a> {
|
||||||
|
|
||||||
impl Context<'_> {
|
impl Context<'_> {
|
||||||
pub fn room_id(&self) -> &RoomId {
|
pub fn room_id(&self) -> &RoomId {
|
||||||
self.room.id
|
self.origin_room.id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_secure(&self) -> bool {
|
pub fn is_secure(&self) -> bool {
|
||||||
self.room.secure
|
self.origin_room.secure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,15 +39,21 @@ pub struct RoomContext<'a> {
|
||||||
impl RoomContext<'_> {
|
impl RoomContext<'_> {
|
||||||
pub async fn new_with_name<'a>(
|
pub async fn new_with_name<'a>(
|
||||||
room: &'a Joined,
|
room: &'a Joined,
|
||||||
display_name: String,
|
|
||||||
sending_user: &str,
|
sending_user: &str,
|
||||||
) -> Result<RoomContext<'a>, BotError> {
|
) -> Result<RoomContext<'a>, BotError> {
|
||||||
// TODO is_direct is a hack; should set rooms to Direct
|
// TODO is_direct is a hack; the bot should set eligible rooms
|
||||||
// Message upon joining, if other contact has requested it.
|
// to Direct Message upon joining, if other contact has
|
||||||
// Waiting on SDK support.
|
// requested it. Waiting on SDK support.
|
||||||
|
let display_name = room
|
||||||
|
.display_name()
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
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.joined_members().await?.len() == 2;
|
let is_direct = room.active_members().await?.len() == 2;
|
||||||
|
|
||||||
Ok(RoomContext {
|
Ok(RoomContext {
|
||||||
id: room.room_id(),
|
id: room.room_id(),
|
||||||
|
@ -57,17 +64,8 @@ impl RoomContext<'_> {
|
||||||
|
|
||||||
pub async fn new<'a>(
|
pub async fn new<'a>(
|
||||||
room: &'a Joined,
|
room: &'a Joined,
|
||||||
sending_user: &str,
|
sending_user: &'a str,
|
||||||
) -> Result<RoomContext<'a>, BotError> {
|
) -> Result<RoomContext<'a>, BotError> {
|
||||||
Self::new_with_name(
|
Self::new_with_name(room, sending_user).await
|
||||||
&room,
|
|
||||||
room.display_name()
|
|
||||||
.await
|
|
||||||
.ok()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.to_string(),
|
|
||||||
sending_user,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -506,8 +506,9 @@ 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).unwrap(),
|
||||||
room: dummy_room!(),
|
origin_room: dummy_room!(),
|
||||||
|
active_room: dummy_room!(),
|
||||||
username: "username",
|
username: "username",
|
||||||
message_body: "message",
|
message_body: "message",
|
||||||
};
|
};
|
||||||
|
@ -543,8 +544,9 @@ 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).unwrap(),
|
||||||
room: dummy_room!(),
|
origin_room: dummy_room!(),
|
||||||
|
active_room: dummy_room!(),
|
||||||
username: "username",
|
username: "username",
|
||||||
message_body: "message",
|
message_body: "message",
|
||||||
};
|
};
|
||||||
|
@ -580,8 +582,9 @@ 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).unwrap(),
|
||||||
room: dummy_room!(),
|
origin_room: dummy_room!(),
|
||||||
|
active_room: dummy_room!(),
|
||||||
username: "username",
|
username: "username",
|
||||||
message_body: "message",
|
message_body: "message",
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue