split into files
This commit is contained in:
parent
8beae909b8
commit
3bdee6ed89
60
src/autojoin.rs
Normal file
60
src/autojoin.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use matrix_sdk::{
|
||||||
|
self, async_trait,
|
||||||
|
events::{room::member::MemberEventContent, StrippedStateEvent},
|
||||||
|
Client, EventEmitter, RoomState,
|
||||||
|
};
|
||||||
|
use tokio::time::sleep;
|
||||||
|
|
||||||
|
pub struct AutoJoinHandler {
|
||||||
|
client: Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AutoJoinHandler {
|
||||||
|
pub fn new(client: Client) -> Self {
|
||||||
|
Self { client }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl EventEmitter for AutoJoinHandler {
|
||||||
|
async fn on_stripped_state_member(
|
||||||
|
&self,
|
||||||
|
room: RoomState,
|
||||||
|
room_member: &StrippedStateEvent<MemberEventContent>,
|
||||||
|
_: Option<MemberEventContent>,
|
||||||
|
) {
|
||||||
|
if room_member.state_key != self.client.user_id().await.unwrap() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let RoomState::Invited(room) = room {
|
||||||
|
// TODO: only join room if it's the room specified in the configuration
|
||||||
|
|
||||||
|
println!("Autojoining room {}", room.room_id());
|
||||||
|
let mut delay = 2;
|
||||||
|
|
||||||
|
while let Err(err) = self.client.join_room_by_id(room.room_id()).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 {} ({:?}), retrying in {}s",
|
||||||
|
room.room_id(),
|
||||||
|
err,
|
||||||
|
delay
|
||||||
|
);
|
||||||
|
|
||||||
|
sleep(Duration::from_secs(delay)).await;
|
||||||
|
delay *= 2;
|
||||||
|
|
||||||
|
if delay > 3600 {
|
||||||
|
eprintln!("Can't join room {} ({:?})", room.room_id(), err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("Successfully joined room {}", room.room_id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
src/bot.rs
Normal file
74
src/bot.rs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::{BufReader, BufWriter},
|
||||||
|
};
|
||||||
|
|
||||||
|
use matrix_sdk::{Client, ClientConfig, Session, SyncSettings};
|
||||||
|
|
||||||
|
use crate::autojoin::AutoJoinHandler;
|
||||||
|
use crate::Config;
|
||||||
|
|
||||||
|
pub struct BadNewsBot {
|
||||||
|
client: Client,
|
||||||
|
config: Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BadNewsBot {
|
||||||
|
pub fn new(config: Config) -> anyhow::Result<Self> {
|
||||||
|
let client_config = ClientConfig::new().store_path(config.state_dir.join("store"));
|
||||||
|
let client = Client::new_with_config(config.homeserver.clone(), client_config)?;
|
||||||
|
|
||||||
|
Ok(Self { client, config })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn init(&self) -> anyhow::Result<()> {
|
||||||
|
load_or_init_session(&self).await?;
|
||||||
|
|
||||||
|
self.client
|
||||||
|
.add_event_emitter(Box::new(AutoJoinHandler::new(self.client.clone())))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run(&self) {
|
||||||
|
self.client.sync(SyncSettings::default()).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn load_or_init_session(bot: &BadNewsBot) -> anyhow::Result<()> {
|
||||||
|
let session_file = bot.config.state_dir.join("session.yaml");
|
||||||
|
|
||||||
|
if session_file.is_file() {
|
||||||
|
let reader = BufReader::new(File::open(session_file)?);
|
||||||
|
|
||||||
|
let session: Session = serde_yaml::from_reader(reader)?;
|
||||||
|
|
||||||
|
bot.client.restore_login(session.clone()).await?;
|
||||||
|
|
||||||
|
println!("Reused session: {}, {}", session.user_id, session.device_id);
|
||||||
|
} else {
|
||||||
|
let response = bot
|
||||||
|
.client
|
||||||
|
.login(
|
||||||
|
&bot.config.username,
|
||||||
|
&bot.config.password,
|
||||||
|
None,
|
||||||
|
Some("autojoin bot"),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
println!("logged in as {}", bot.config.username);
|
||||||
|
|
||||||
|
let session = Session {
|
||||||
|
access_token: response.access_token,
|
||||||
|
user_id: response.user_id,
|
||||||
|
device_id: response.device_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
let writer = BufWriter::new(File::create(session_file)?);
|
||||||
|
serde_yaml::to_writer(writer, &session)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
129
src/main.rs
129
src/main.rs
|
@ -1,137 +1,18 @@
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{self, BufReader, BufWriter},
|
io::{self, BufReader},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
time::Duration,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use clap::Clap;
|
use clap::Clap;
|
||||||
use matrix_sdk::{
|
|
||||||
self, async_trait,
|
|
||||||
events::{room::member::MemberEventContent, StrippedStateEvent},
|
|
||||||
Client, ClientConfig, EventEmitter, RoomState, Session, SyncSettings,
|
|
||||||
};
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::time::sleep;
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
struct BadNewsBot {
|
mod autojoin;
|
||||||
client: Client,
|
mod bot;
|
||||||
config: Config,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BadNewsBot {
|
use bot::BadNewsBot;
|
||||||
pub fn new(config: Config) -> anyhow::Result<Self> {
|
|
||||||
let client_config = ClientConfig::new().store_path(config.state_dir.join("store"));
|
|
||||||
let client = Client::new_with_config(config.homeserver.clone(), client_config)?;
|
|
||||||
|
|
||||||
Ok(Self { client, config })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn init(&self) -> anyhow::Result<()> {
|
|
||||||
load_or_init_session(&self).await?;
|
|
||||||
|
|
||||||
self.client
|
|
||||||
.add_event_emitter(Box::new(AutoJoinHandler::new(self.client.clone())))
|
|
||||||
.await;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run(&self) {
|
|
||||||
self.client.sync(SyncSettings::default()).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn load_or_init_session(bot: &BadNewsBot) -> anyhow::Result<()> {
|
|
||||||
let session_file = bot.config.state_dir.join("session.yaml");
|
|
||||||
|
|
||||||
if session_file.is_file() {
|
|
||||||
let reader = BufReader::new(File::open(session_file)?);
|
|
||||||
|
|
||||||
let session: Session = serde_yaml::from_reader(reader)?;
|
|
||||||
|
|
||||||
bot.client.restore_login(session.clone()).await?;
|
|
||||||
|
|
||||||
println!("Reused session: {}, {}", session.user_id, session.device_id);
|
|
||||||
} else {
|
|
||||||
let response = bot
|
|
||||||
.client
|
|
||||||
.login(
|
|
||||||
&bot.config.username,
|
|
||||||
&bot.config.password,
|
|
||||||
None,
|
|
||||||
Some("autojoin bot"),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
println!("logged in as {}", bot.config.username);
|
|
||||||
|
|
||||||
let session = Session {
|
|
||||||
access_token: response.access_token,
|
|
||||||
user_id: response.user_id,
|
|
||||||
device_id: response.device_id,
|
|
||||||
};
|
|
||||||
|
|
||||||
let writer = BufWriter::new(File::create(session_file)?);
|
|
||||||
serde_yaml::to_writer(writer, &session)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AutoJoinHandler {
|
|
||||||
client: Client,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AutoJoinHandler {
|
|
||||||
fn new(client: Client) -> Self {
|
|
||||||
Self { client }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl EventEmitter for AutoJoinHandler {
|
|
||||||
async fn on_stripped_state_member(
|
|
||||||
&self,
|
|
||||||
room: RoomState,
|
|
||||||
room_member: &StrippedStateEvent<MemberEventContent>,
|
|
||||||
_: Option<MemberEventContent>,
|
|
||||||
) {
|
|
||||||
if room_member.state_key != self.client.user_id().await.unwrap() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let RoomState::Invited(room) = room {
|
|
||||||
// TODO: only join room if it's the room specified in the configuration
|
|
||||||
|
|
||||||
println!("Autojoining room {}", room.room_id());
|
|
||||||
let mut delay = 2;
|
|
||||||
|
|
||||||
while let Err(err) = self.client.join_room_by_id(room.room_id()).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 {} ({:?}), retrying in {}s",
|
|
||||||
room.room_id(),
|
|
||||||
err,
|
|
||||||
delay
|
|
||||||
);
|
|
||||||
|
|
||||||
sleep(Duration::from_secs(delay)).await;
|
|
||||||
delay *= 2;
|
|
||||||
|
|
||||||
if delay > 3600 {
|
|
||||||
eprintln!("Can't join room {} ({:?})", room.room_id(), err);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!("Successfully joined room {}", room.room_id());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
enum BadNewsError {
|
enum BadNewsError {
|
||||||
|
@ -151,7 +32,7 @@ struct Opts {
|
||||||
|
|
||||||
/// Holds the configuration for the bot.
|
/// Holds the configuration for the bot.
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct Config {
|
pub struct Config {
|
||||||
/// The URL for the homeserver we should connect to
|
/// The URL for the homeserver we should connect to
|
||||||
homeserver: Url,
|
homeserver: Url,
|
||||||
/// The bot's account username
|
/// The bot's account username
|
||||||
|
|
Loading…
Reference in a new issue