Compare commits

..

8 commits

5 changed files with 996 additions and 653 deletions

1537
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -16,11 +16,11 @@ matrix-sdk = { version = "0.6.2", features = ["e2e-encryption"] }
never-say-never = "6.6.666" never-say-never = "6.6.666"
reqwest = { version = "0.11.12", features = ["json"] } reqwest = { version = "0.11.12", features = ["json"] }
serde = { version = "1.0.147", features = ["derive"] } serde = { version = "1.0.147", features = ["derive"] }
spaceapi = "0.8.1" spaceapi = "0.9.0"
thiserror = "1.0.37" thiserror = "1.0.37"
time = "0.3.17" time = { version = "0.3.17", features = ["local-offset", "formatting", "macros"] }
tokio = { version = "1.21.2", features = ["full"] } tokio = { version = "1.21.2", features = ["full"] }
toml = "0.5.9" toml = "0.7.6"
url = { version = "2.3.1", features = ["serde"] } url = { version = "2.3.1", features = ["serde"] }
xdg = "2.4.1" xdg = "2.4.1"
@ -29,7 +29,3 @@ color-eyre = "0.6.2"
tracing = "0.1.37" tracing = "0.1.37"
tracing-error = "0.2.0" tracing-error = "0.2.0"
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
[patch.crates-io]
# https://github.com/spaceapi-community/spaceapi-rs/pull/111
spaceapi = { git = "https://github.com/Xiretza/spaceapi-rs", branch = "icon-closed-fix" }

View file

@ -25,7 +25,7 @@ use matrix_sdk::{
use never_say_never::Never; use never_say_never::Never;
use reqwest::Url; use reqwest::Url;
use serde::Deserialize; use serde::Deserialize;
use time::OffsetDateTime; use time::{ext::NumericalDuration, macros::format_description, OffsetDateTime, UtcOffset};
use tokio::time::sleep; use tokio::time::sleep;
use tracing::{event, instrument, span, Level}; use tracing::{event, instrument, span, Level};
@ -179,6 +179,7 @@ impl Bot {
Ok(id) Ok(id)
} }
/// Collects the configured announcement rooms and joins them if necessary.
#[instrument(skip(self))] #[instrument(skip(self))]
async fn join_announce_rooms(&mut self) -> Result<()> { async fn join_announce_rooms(&mut self) -> Result<()> {
for room in &self.config.space.announce_rooms { for room in &self.config.space.announce_rooms {
@ -205,6 +206,59 @@ impl Bot {
Ok(()) Ok(())
} }
#[instrument(skip(self, ev, room))]
async fn handle_command(
self: &Arc<Self>,
ev: &OriginalRoomMessageEvent,
room: &Joined,
command: &str,
) -> Result<()> {
let reply = |msg: &str| {
// workaround for broken IRC bridge
// https://github.com/matrix-org/matrix-appservice-irc/issues/683#issuecomment-1312688727
let msg = format!("\n{msg}");
room.send(
RoomMessageEventContent::text_plain(msg).make_reply_to(ev),
None,
)
};
match command {
"isitopen" => {
match self.update_open_state().await? {
OpenState::Open { since } => {
if let Ok(offset) = UtcOffset::current_local_offset() {
let since = since.to_offset(offset);
let s = if OffsetDateTime::now_utc() - since > 20.hours() {
since.format(format_description!(
"[year]-[month]-[day] [hour]:[minute]:[second]"
))?
} else {
since.format(format_description!("[hour]:[minute]:[second]"))?
};
reply(&format!("positive! space has been open since {s}")).await?
} else {
reply(&format!("positive! space has been open since {since}")).await?
}
}
OpenState::Closed => reply("negative!").await?,
};
}
"spaceping" => {
self.api.ping().await?;
reply("Hello Space!").await?;
}
_ => {
reply("Unknown command").await?;
}
};
Ok(())
}
#[instrument(skip(self))] #[instrument(skip(self))]
async fn handle_message( async fn handle_message(
self: &Arc<Self>, self: &Arc<Self>,
@ -227,41 +281,24 @@ impl Bot {
return Ok(()); return Ok(());
}; };
let reply = |msg: &str| { if let Err(error) = self.handle_command(ev, room, command).await {
// workaround for broken IRC bridge event!(Level::WARN, ?error, "handling command failed");
// https://github.com/matrix-org/matrix-appservice-irc/issues/683#issuecomment-1312688727 let _ignore = room
let msg = format!("\n{msg}"); .send(
RoomMessageEventContent::text_plain("error handling command: {error}"),
room.send(
RoomMessageEventContent::text_plain(msg).make_reply_to(ev),
None, None,
) )
.await;
}; };
match command {
"isitopen" => {
match self.update_open_state().await? {
OpenState::Open { since } => {
reply(&format!("positive! space has been open since {since}")).await?
}
OpenState::Closed => reply("negative!").await?,
};
}
"spaceping" => {
self.api.ping().await?;
reply("Hello Space!").await?;
}
_ => {
reply("Unknown command").await?;
}
}
Ok(()) Ok(())
} }
/// Runs the bot. This function does not return except in case of error. /// Runs the bot. This function does not return except in case of error.
#[instrument(skip(self))] #[instrument(skip(self))]
pub async fn run(mut self) -> Result<Never> { pub async fn run(mut self) -> Result<Never> {
self.client.sync_once(SyncSettings::default()).await?;
self.join_announce_rooms() self.join_announce_rooms()
.await .await
.wrap_err("failed to join announcement rooms")?; .wrap_err("failed to join announcement rooms")?;
@ -297,7 +334,8 @@ impl Bot {
}, },
); );
this.client.sync(SyncSettings::default()).await?; // Box humongous future
Box::pin(this.client.sync(SyncSettings::default())).await?;
unreachable!("sync() returned unexpectedly") unreachable!("sync() returned unexpectedly")
} }
} }

View file

@ -9,7 +9,7 @@ use clap::Parser as _;
use color_eyre::{eyre::Context, Result}; use color_eyre::{eyre::Context, Result};
use matrix_sdk::Session; use matrix_sdk::Session;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tracing::instrument; use tracing::{instrument, event, Level};
use xdg::BaseDirectories; use xdg::BaseDirectories;
mod bot; mod bot;
@ -126,6 +126,8 @@ async fn main() -> Result<()> {
.wrap_err("Failed to load bot configuration")?; .wrap_err("Failed to load bot configuration")?;
let bot = Bot::new(config).await?; let bot = Bot::new(config).await?;
event!(Level::INFO, "logged in successfully, starting bot");
bot.run().await? bot.run().await?
} }
} }

View file

@ -58,7 +58,7 @@ pub(crate) async fn setup(setup: Setup) -> Result<()> {
homeserver_url: setup.homeserver_url, homeserver_url: setup.homeserver_url,
}; };
tokio::fs::write(crate::session_path()?, toml::to_vec(&data)?).await?; tokio::fs::write(crate::session_path()?, toml::to_string(&data)?).await?;
client.sync_once(SyncSettings::default()).await?; client.sync_once(SyncSettings::default()).await?;