simple ping bot functionality
This commit is contained in:
197
Cargo.lock
generated
197
Cargo.lock
generated
@@ -22,6 +22,21 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
|
||||
|
||||
[[package]]
|
||||
name = "anymap2"
|
||||
version = "0.13.0"
|
||||
@@ -153,6 +168,18 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloudabi"
|
||||
version = "0.0.3"
|
||||
@@ -162,6 +189,23 @@ dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
||||
|
||||
[[package]]
|
||||
name = "cron"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ff76b51e4c068c52bfd2866e1567bee7c567ae8f24ada09fd4307019e25eab7"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"nom",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.14.4"
|
||||
@@ -437,6 +481,15 @@ dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.9"
|
||||
@@ -508,6 +561,29 @@ dependencies = [
|
||||
"tokio-rustls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
@@ -627,8 +703,12 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||
name = "matrix-federation-notifier"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"js_int",
|
||||
"matrix-sdk",
|
||||
"serde",
|
||||
"tokio",
|
||||
"tokio-cron-scheduler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -637,6 +717,7 @@ version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbeafb4809f33f377165f2fbcf10e0613053ad206762194c3050a727fd3abcb2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anymap2",
|
||||
"async-once-cell",
|
||||
"async-stream",
|
||||
@@ -717,6 +798,12 @@ version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.6"
|
||||
@@ -729,6 +816,56 @@ dependencies = [
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.58",
|
||||
"quote 1.0.27",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
@@ -746,6 +883,16 @@ dependencies = [
|
||||
"parking_lot_core 0.8.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core 0.9.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.6"
|
||||
@@ -1281,6 +1428,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.8"
|
||||
@@ -1396,11 +1552,41 @@ dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"parking_lot 0.12.1",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-cron-scheduler"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de2c1fd54a857b29c6cd1846f31903d0ae8e28175615c14a277aed45c58d8e27"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"cron",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"uuid 1.3.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.58",
|
||||
"quote 1.0.27",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.24.0"
|
||||
@@ -1653,7 +1839,7 @@ checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"js-sys",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
"pin-utils",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
@@ -1717,6 +1903,15 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
|
||||
@@ -6,5 +6,9 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
matrix-sdk = { version = "0.6.2", default-features = false, features = ["rustls-tls"] }
|
||||
tokio = "1.28.1"
|
||||
anyhow = "1.0.71"
|
||||
js_int = { version = "0.2.2", features = ["serde"] }
|
||||
matrix-sdk = { version = "0.6.2", default-features = false, features = ["rustls-tls", "anyhow"] }
|
||||
serde = { version = "1.0.163", features = ["derive"] }
|
||||
tokio = { version = "1.28.1", features = ["full"] }
|
||||
tokio-cron-scheduler = "0.9.4"
|
||||
|
||||
120
src/main.rs
120
src/main.rs
@@ -1,3 +1,119 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
// Parts of this file was lifted from https://github.com/matrix-org/matrix-rust-sdk/commit/3db90fbe026c222167000fcfdee8b35b44ae5694
|
||||
// And are thus licensed under Apache
|
||||
|
||||
use matrix_sdk::{
|
||||
config::SyncSettings,
|
||||
room::Room,
|
||||
ruma::events::room::message::{MessageType, OriginalSyncRoomMessageEvent},
|
||||
ruma::events::{macros::EventContent, room::member::StrippedRoomMemberEvent},
|
||||
Client,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{env, process::exit};
|
||||
use tokio::time::{sleep, Duration};
|
||||
|
||||
mod ping;
|
||||
|
||||
async fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) {
|
||||
if let Room::Joined(room) = room {
|
||||
let MessageType::Text(text_content) = &event.content.msgtype else {
|
||||
return;
|
||||
};
|
||||
|
||||
if text_content.body.starts_with("!ping ") {
|
||||
ping::ping(event, room).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_stripped_state_member(
|
||||
room_member: StrippedRoomMemberEvent,
|
||||
client: Client,
|
||||
room: Room,
|
||||
) {
|
||||
if room_member.state_key != client.user_id().unwrap() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Room::Invited(room) = room {
|
||||
tokio::spawn(async move {
|
||||
println!("Autojoining room {}", room.room_id());
|
||||
let mut delay = 2;
|
||||
|
||||
while let Err(err) = room.accept_invitation().await {
|
||||
// retry autojoin due to synapse sending invites, before the
|
||||
// invited user can join for more information see
|
||||
// https://github.com/matrix-org/synapse/issues/4345
|
||||
eprintln!(
|
||||
"Failed to join room {} ({err:?}), retrying in {delay}s",
|
||||
room.room_id()
|
||||
);
|
||||
|
||||
sleep(Duration::from_secs(delay)).await;
|
||||
delay *= 2;
|
||||
|
||||
if delay > 3600 {
|
||||
eprintln!("Can't join room {} ({err:?})", room.room_id());
|
||||
break;
|
||||
}
|
||||
}
|
||||
println!("Successfully joined room {}", room.room_id());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn login_and_sync(
|
||||
homeserver_url: String,
|
||||
username: String,
|
||||
password: String,
|
||||
) -> anyhow::Result<()> {
|
||||
// Note that when encryption is enabled, you should use a persistent store to be
|
||||
// able to restore the session with a working encryption setup.
|
||||
// See the `persist_session` example.
|
||||
let client = Client::builder()
|
||||
.homeserver_url(homeserver_url)
|
||||
.build()
|
||||
.await
|
||||
.unwrap();
|
||||
client
|
||||
.login_username(&username, &password)
|
||||
.initial_device_display_name("ping bot")
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
println!("logged in as {username}");
|
||||
|
||||
// An initial sync to set up state and so our bot doesn't respond to old
|
||||
// messages.
|
||||
let response = client.sync_once(SyncSettings::default()).await.unwrap();
|
||||
|
||||
println!("Finished initial sync");
|
||||
|
||||
client.add_event_handler(on_room_message);
|
||||
client.add_event_handler(on_stripped_state_member);
|
||||
|
||||
// since we called `sync_once` before we entered our sync loop we must pass
|
||||
// that sync token to `sync`
|
||||
let settings = SyncSettings::default().token(response.next_batch);
|
||||
|
||||
client.sync(settings).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let (homeserver_url, username, password) =
|
||||
match (env::args().nth(1), env::args().nth(2), env::args().nth(3)) {
|
||||
(Some(a), Some(b), Some(c)) => (a, b, c),
|
||||
_ => {
|
||||
eprintln!(
|
||||
"Usage: {} <homeserver_url> <username> <password>",
|
||||
env::args().next().unwrap()
|
||||
);
|
||||
exit(1)
|
||||
}
|
||||
};
|
||||
|
||||
login_and_sync(homeserver_url, username, password).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
97
src/ping.rs
Normal file
97
src/ping.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use matrix_sdk::{
|
||||
room::Joined,
|
||||
ruma::{
|
||||
events::room::message::OriginalSyncRoomMessageEvent, exports::ruma_macros::EventContent,
|
||||
OwnedEventId, UInt,
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "rel_type", rename = "xyz.maubot.pong")]
|
||||
struct PongRelation {
|
||||
event_id: OwnedEventId,
|
||||
from: String,
|
||||
ms: UInt,
|
||||
}
|
||||
|
||||
impl PongRelation {
|
||||
fn new(event: &OriginalSyncRoomMessageEvent, diff: &Duration) -> Self {
|
||||
let millis = diff.as_millis();
|
||||
let clamped_ms: u64 = if millis < js_int::MAX_SAFE_UINT as u128 {
|
||||
millis as u64
|
||||
} else {
|
||||
js_int::MAX_SAFE_UINT
|
||||
};
|
||||
PongRelation {
|
||||
event_id: event.event_id.to_owned(),
|
||||
from: event.sender.server_name().to_string(),
|
||||
ms: UInt::new_saturating(clamped_ms),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
struct PongMixin {
|
||||
from: String,
|
||||
ms: UInt,
|
||||
ping: OwnedEventId,
|
||||
}
|
||||
|
||||
impl PongMixin {
|
||||
fn new(event: &OriginalSyncRoomMessageEvent, diff: &Duration) -> Self {
|
||||
let millis = diff.as_millis();
|
||||
let clamped_ms: u64 = if millis < js_int::MAX_SAFE_UINT as u128 {
|
||||
millis as u64
|
||||
} else {
|
||||
js_int::MAX_SAFE_UINT
|
||||
};
|
||||
PongMixin {
|
||||
ping: event.event_id.to_owned(),
|
||||
from: event.sender.server_name().to_string(),
|
||||
ms: UInt::new_saturating(clamped_ms),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
|
||||
#[ruma_event(type = "m.room.message", kind = MessageLike)]
|
||||
struct AckEventContent {
|
||||
body: String,
|
||||
msgtype: String,
|
||||
#[serde(rename = "m.relates_to")]
|
||||
relates_to: PongRelation,
|
||||
pong: PongMixin,
|
||||
}
|
||||
|
||||
impl AckEventContent {
|
||||
fn new(event: &OriginalSyncRoomMessageEvent, diff: &Duration) -> Self {
|
||||
let plain = format!("ping took {:?} to arrive", &diff);
|
||||
|
||||
Self {
|
||||
body: plain,
|
||||
msgtype: "m.notice".to_string(),
|
||||
relates_to: PongRelation::new(&event, &diff),
|
||||
pong: PongMixin::new(&event, &diff),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn ping(event: OriginalSyncRoomMessageEvent, room: Joined) {
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("timetravel");
|
||||
let then = event
|
||||
.origin_server_ts
|
||||
.to_system_time()
|
||||
.expect("timestamp was not a real time")
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("timetravel");
|
||||
let diff = now - then;
|
||||
|
||||
let content = AckEventContent::new(&event, &diff);
|
||||
|
||||
room.send(content, None).await.unwrap();
|
||||
}
|
||||
Reference in New Issue
Block a user