command-bot: add comments, use timestamp to filter old messages
parent
49e962e9c4
commit
3f9243a326
|
@ -1,23 +1,31 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
use std::{env, process::exit};
|
use std::{env, process::exit};
|
||||||
|
|
||||||
use url::Url;
|
use js_int::UInt;
|
||||||
|
|
||||||
use matrix_sdk::{
|
use matrix_sdk::{
|
||||||
self,
|
self,
|
||||||
events::room::message::{MessageEvent, MessageEventContent, TextMessageEventContent},
|
events::room::message::{MessageEvent, MessageEventContent, TextMessageEventContent},
|
||||||
AsyncClient, AsyncClientConfig, EventEmitter, Room, SyncSettings,
|
AsyncClient, AsyncClientConfig, EventEmitter, Room, SyncSettings,
|
||||||
};
|
};
|
||||||
use tokio::sync::{Mutex, RwLock};
|
use tokio::sync::{Mutex, RwLock};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
struct CommandBot {
|
struct CommandBot {
|
||||||
|
/// This clone of the `AsyncClient` will send requests to the server,
|
||||||
|
/// while the other keeps us in sync with the server using `sync_forever`.
|
||||||
client: Mutex<AsyncClient>,
|
client: Mutex<AsyncClient>,
|
||||||
|
/// A timestamp so we only respond to messages sent after the bot is running.
|
||||||
|
start_time: UInt,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommandBot {
|
impl CommandBot {
|
||||||
pub fn new(client: AsyncClient) -> Self {
|
pub fn new(client: AsyncClient) -> Self {
|
||||||
|
let now = SystemTime::now();
|
||||||
|
let timestamp = now.duration_since(UNIX_EPOCH).unwrap().as_millis();
|
||||||
Self {
|
Self {
|
||||||
client: Mutex::new(client),
|
client: Mutex::new(client),
|
||||||
|
start_time: UInt::new(timestamp as u64).unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,30 +33,34 @@ impl CommandBot {
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl EventEmitter for CommandBot {
|
impl EventEmitter for CommandBot {
|
||||||
async fn on_room_message(&self, room: Arc<RwLock<Room>>, event: &MessageEvent) {
|
async fn on_room_message(&self, room: Arc<RwLock<Room>>, event: &MessageEvent) {
|
||||||
let msg_body = if let MessageEvent {
|
let (msg_body, timestamp) = if let MessageEvent {
|
||||||
content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }),
|
content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }),
|
||||||
|
origin_server_ts,
|
||||||
..
|
..
|
||||||
} = event
|
} = event
|
||||||
{
|
{
|
||||||
msg_body.clone()
|
(msg_body.clone(), *origin_server_ts)
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
(String::new(), UInt::min_value())
|
||||||
};
|
};
|
||||||
|
|
||||||
if msg_body.contains("!party") {
|
if msg_body.contains("!party") && timestamp > self.start_time {
|
||||||
let content = MessageEventContent::Text(TextMessageEventContent {
|
let content = MessageEventContent::Text(TextMessageEventContent {
|
||||||
body: "🎉🎊🥳 let's PARTY!! 🥳🎊🎉".to_string(),
|
body: "🎉🎊🥳 let's PARTY!! 🥳🎊🎉".to_string(),
|
||||||
format: None,
|
format: None,
|
||||||
formatted_body: None,
|
formatted_body: None,
|
||||||
relates_to: None,
|
relates_to: None,
|
||||||
});
|
});
|
||||||
let room_id = { room.read().await.room_id.clone() };
|
// we clone here to hold the lock for as little time as possible.
|
||||||
|
let room_id = room.read().await.room_id.clone();
|
||||||
|
|
||||||
println!("sending");
|
println!("sending");
|
||||||
|
|
||||||
self.client
|
self.client
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
|
// send our message to the room we found the "!party" command in
|
||||||
|
// the last parameter is an optional Uuid which we don't care about.
|
||||||
.room_send(&room_id, content, None)
|
.room_send(&room_id, content, None)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -58,7 +70,6 @@ impl EventEmitter for CommandBot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::for_loop_over_option)]
|
|
||||||
async fn login_and_sync(
|
async fn login_and_sync(
|
||||||
homeserver_url: String,
|
homeserver_url: String,
|
||||||
username: String,
|
username: String,
|
||||||
|
@ -69,8 +80,9 @@ async fn login_and_sync(
|
||||||
.disable_ssl_verification();
|
.disable_ssl_verification();
|
||||||
|
|
||||||
let homeserver_url = Url::parse(&homeserver_url)?;
|
let homeserver_url = Url::parse(&homeserver_url)?;
|
||||||
|
// create a new AsyncClient with the given homeserver url and config
|
||||||
let mut client = AsyncClient::new_with_config(homeserver_url, None, client_config).unwrap();
|
let mut client = AsyncClient::new_with_config(homeserver_url, None, client_config).unwrap();
|
||||||
|
// add our CommandBot to be notified of incoming messages
|
||||||
client
|
client
|
||||||
.add_event_emitter(Box::new(CommandBot::new(client.clone())))
|
.add_event_emitter(Box::new(CommandBot::new(client.clone())))
|
||||||
.await;
|
.await;
|
||||||
|
@ -86,6 +98,7 @@ async fn login_and_sync(
|
||||||
|
|
||||||
println!("logged in as {}", username);
|
println!("logged in as {}", username);
|
||||||
|
|
||||||
|
// this keeps state from the server streaming in to CommandBot via the EventEmitter trait
|
||||||
client.sync_forever(SyncSettings::new(), |_| async {}).await;
|
client.sync_forever(SyncSettings::new(), |_| async {}).await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -21,7 +21,8 @@ impl EventEmitter for EventCallback {
|
||||||
} = event
|
} = event
|
||||||
{
|
{
|
||||||
let name = {
|
let name = {
|
||||||
// any reads or
|
// any reads should be held for the shortest time possible to
|
||||||
|
// avoid dead locks
|
||||||
let room = room.read().await;
|
let room = room.read().await;
|
||||||
let member = room.members.get(&sender).unwrap();
|
let member = room.members.get(&sender).unwrap();
|
||||||
member
|
member
|
||||||
|
|
|
@ -53,6 +53,8 @@ const DEFAULT_SYNC_TIMEOUT: Duration = Duration::from_secs(30);
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
/// An async/await enabled Matrix client.
|
/// An async/await enabled Matrix client.
|
||||||
|
///
|
||||||
|
/// All of the state is held in an `Arc` so the `AsyncClient` can be cloned freely.
|
||||||
pub struct AsyncClient {
|
pub struct AsyncClient {
|
||||||
/// The URL of the homeserver to connect to.
|
/// The URL of the homeserver to connect to.
|
||||||
homeserver: Url,
|
homeserver: Url,
|
||||||
|
|
|
@ -50,28 +50,29 @@ use tokio::sync::RwLock;
|
||||||
/// },
|
/// },
|
||||||
/// AsyncClient, AsyncClientConfig, EventEmitter, Room, SyncSettings,
|
/// AsyncClient, AsyncClientConfig, EventEmitter, Room, SyncSettings,
|
||||||
/// };
|
/// };
|
||||||
/// use tokio::sync::Mutex;
|
/// use tokio::sync::RwLock;
|
||||||
///
|
///
|
||||||
/// struct EventCallback;
|
/// struct EventCallback;
|
||||||
///
|
///
|
||||||
/// #[async_trait::async_trait]
|
/// #[async_trait::async_trait]
|
||||||
/// impl EventEmitter for EventCallback {
|
/// impl EventEmitter for EventCallback {
|
||||||
/// async fn on_room_message(&self, room: &Room, event: &MessageEvent) {
|
/// async fn on_room_message(&self, room: Arc<RwLock<Room>>, event: &MessageEvent) {
|
||||||
/// if let MessageEvent {
|
/// if let MessageEvent {
|
||||||
/// content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }),
|
/// content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }),
|
||||||
/// sender,
|
/// sender,
|
||||||
/// ..
|
/// ..
|
||||||
/// } = event
|
/// } = event
|
||||||
/// {
|
/// {
|
||||||
|
/// let name = {
|
||||||
|
/// let room = room.read().await;
|
||||||
/// let member = room.members.get(&sender).unwrap();
|
/// let member = room.members.get(&sender).unwrap();
|
||||||
/// println!(
|
|
||||||
/// "{}: {}",
|
|
||||||
/// member
|
/// member
|
||||||
/// .display_name
|
/// .display_name
|
||||||
/// .as_ref()
|
/// .as_ref()
|
||||||
/// .unwrap_or(&sender.to_string()),
|
/// .map(ToString::to_string)
|
||||||
/// msg_body
|
/// .unwrap_or(sender.to_string())
|
||||||
/// );
|
/// };
|
||||||
|
/// println!("{}: {}", name, msg_body);
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
|
|
Loading…
Reference in New Issue