update libs
This commit is contained in:
parent
653592649d
commit
c270953ab2
4 changed files with 133 additions and 167 deletions
22
Cargo.toml
22
Cargo.toml
|
@ -7,26 +7,26 @@ edition = "2018"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0.53"
|
||||||
bincode = "1.3.3"
|
bincode = "1.3.3"
|
||||||
clap = "2.33.3"
|
clap = "3.0.10"
|
||||||
env_logger = "0.8.4"
|
env_logger = "0.9.0"
|
||||||
log = "0.4.13"
|
log = "0.4.14"
|
||||||
mime = "0.3.16"
|
mime = "0.3.16"
|
||||||
mime_guess = "2.0.3"
|
mime_guess = "2.0.3"
|
||||||
rand = "0.8.4"
|
rand = "0.8.4"
|
||||||
serde_json = "1.0.61"
|
serde_json = "1.0.78"
|
||||||
sled = "0.34.6"
|
sled = "0.34.7"
|
||||||
structopt = "0.3.21"
|
structopt = "0.3.26"
|
||||||
toml = "0.5.8"
|
toml = "0.5.8"
|
||||||
|
|
||||||
[dependencies.libjens]
|
[dependencies.libjens]
|
||||||
git = "https://tilera.xyz/git/lordmzte/libjens.git"
|
git = "https://tilera.xyz/git/lordmzte/libjens.git"
|
||||||
rev = "d4a8b3"
|
rev = "1.1.0"
|
||||||
|
|
||||||
[dependencies.matrix-sdk]
|
[dependencies.matrix-sdk]
|
||||||
git = "https://github.com/matrix-org/matrix-rust-sdk.git"
|
git = "https://github.com/matrix-org/matrix-rust-sdk.git"
|
||||||
tag = "0.3.0"
|
rev = "c79e62d"
|
||||||
features = ["encryption"]
|
features = ["encryption"]
|
||||||
|
|
||||||
[dependencies.url]
|
[dependencies.url]
|
||||||
|
@ -34,9 +34,9 @@ version = "2.2.2"
|
||||||
features = ["serde"]
|
features = ["serde"]
|
||||||
|
|
||||||
[dependencies.serde]
|
[dependencies.serde]
|
||||||
version = "1.0"
|
version = "1.0.135"
|
||||||
features = ["derive"]
|
features = ["derive"]
|
||||||
|
|
||||||
[dependencies.tokio]
|
[dependencies.tokio]
|
||||||
version = "1.7.0"
|
version = "1.15.0"
|
||||||
features = ["macros"]
|
features = ["macros"]
|
||||||
|
|
228
src/main.rs
228
src/main.rs
|
@ -2,27 +2,37 @@ use anyhow::{anyhow, bail, Context};
|
||||||
use libjens::JMClient;
|
use libjens::JMClient;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use matrix_sdk::{
|
use matrix_sdk::{
|
||||||
api::r0::session::login,
|
config::SyncSettings,
|
||||||
async_trait,
|
|
||||||
deserialized_responses::SyncResponse,
|
deserialized_responses::SyncResponse,
|
||||||
events::{
|
encryption::verification::Verification,
|
||||||
room::{
|
|
||||||
member::MemberEventContent,
|
|
||||||
message::{MessageEventContent, MessageType, TextMessageEventContent},
|
|
||||||
},
|
|
||||||
AnyToDeviceEvent,
|
|
||||||
StrippedStateEvent,
|
|
||||||
SyncMessageEvent,
|
|
||||||
},
|
|
||||||
room::Room,
|
room::Room,
|
||||||
verification::Verification,
|
ruma::{
|
||||||
EventHandler,
|
api::client::r0::{
|
||||||
|
session::login,
|
||||||
|
uiaa::{AuthData, Password, UserIdentifier},
|
||||||
|
},
|
||||||
|
assign,
|
||||||
|
events::{
|
||||||
|
room::{
|
||||||
|
member::StrippedRoomMemberEvent,
|
||||||
|
message::{
|
||||||
|
MessageType,
|
||||||
|
RoomMessageEventContent,
|
||||||
|
SyncRoomMessageEvent,
|
||||||
|
TextMessageEventContent,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
AnyToDeviceEvent,
|
||||||
|
SyncMessageEvent,
|
||||||
|
},
|
||||||
|
UserId,
|
||||||
|
},
|
||||||
|
Client,
|
||||||
LoopCtrl,
|
LoopCtrl,
|
||||||
};
|
};
|
||||||
use rand::{rngs::StdRng, SeedableRng};
|
use rand::{rngs::StdRng, SeedableRng};
|
||||||
use sled::Db;
|
use sled::Db;
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, AtomicU32},
|
atomic::{AtomicBool, AtomicU32},
|
||||||
|
@ -35,8 +45,6 @@ use tokio::sync::{Mutex, RwLock};
|
||||||
|
|
||||||
use config::Config;
|
use config::Config;
|
||||||
|
|
||||||
use matrix_sdk::{self, api::r0::uiaa::AuthData, identifiers::UserId, Client, SyncSettings};
|
|
||||||
use serde_json::json;
|
|
||||||
mod config;
|
mod config;
|
||||||
mod meme;
|
mod meme;
|
||||||
mod responder;
|
mod responder;
|
||||||
|
@ -63,37 +71,41 @@ async fn main() -> anyhow::Result<()> {
|
||||||
toml::from_slice::<Config>(&config).map_err(|e| anyhow!("Error parsing config: {}", e))?;
|
toml::from_slice::<Config>(&config).map_err(|e| anyhow!("Error parsing config: {}", e))?;
|
||||||
let config = Arc::new(config);
|
let config = Arc::new(config);
|
||||||
|
|
||||||
let client = Arc::new(RwLock::new(Client::new(config.homeserver_url.clone())?));
|
let client = Client::new(config.homeserver_url.clone())?;
|
||||||
|
|
||||||
let device_name = config.device_name.as_ref().map(String::as_ref);
|
let device_name = config.device_name.as_ref().map(String::as_ref);
|
||||||
let login::Response { user_id, .. } = client
|
|
||||||
.read()
|
let bot = Arc::new(Bot {
|
||||||
.await
|
client: client.clone(),
|
||||||
.login(&config.user_id, &config.password, device_name, device_name)
|
jm_client: RwLock::new(JMClient::new()),
|
||||||
.await?;
|
memecache: sled::open(config.store_path.join("memecache"))
|
||||||
|
.map_err(|e| anyhow!("error opening memecache: {}", e))?,
|
||||||
|
config: Arc::clone(&config),
|
||||||
|
meme_count: AtomicU32::new(0),
|
||||||
|
rng: Mutex::new(StdRng::from_rng(rand::thread_rng())?),
|
||||||
|
});
|
||||||
|
|
||||||
client
|
client
|
||||||
.write()
|
.register_event_handler(on_stripped_state_member)
|
||||||
.await
|
|
||||||
.set_event_handler(Box::new(Bot {
|
|
||||||
client: Arc::clone(&client),
|
|
||||||
jm_client: RwLock::new(JMClient::new()),
|
|
||||||
memecache: sled::open(config.store_path.join("memecache"))
|
|
||||||
.map_err(|e| anyhow!("error opening memecache: {}", e))?,
|
|
||||||
config: Arc::clone(&config),
|
|
||||||
meme_count: AtomicU32::new(0),
|
|
||||||
rng: Mutex::new(StdRng::from_rng(rand::thread_rng())?),
|
|
||||||
}))
|
|
||||||
.await;
|
.await;
|
||||||
|
let bot_ = Arc::clone(&bot);
|
||||||
|
client
|
||||||
|
.register_event_handler(move |ev, client, room| {
|
||||||
|
on_room_message(ev, client, room, Arc::clone(&bot_))
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let login::Response { user_id, .. } = client
|
||||||
|
.login(&config.user_id, &config.password, device_name, Some("ruff"))
|
||||||
|
.await?;
|
||||||
|
|
||||||
let initial = AtomicBool::from(true);
|
let initial = AtomicBool::from(true);
|
||||||
let initial_ref = &initial;
|
let initial_ref = &initial;
|
||||||
let client_ref = &client.read().await;
|
let client_ref = &client;
|
||||||
let config_ref = &config;
|
let config_ref = &config;
|
||||||
let user_id_ref = &user_id;
|
let user_id_ref = &user_id;
|
||||||
|
|
||||||
client
|
client
|
||||||
.read()
|
|
||||||
.await
|
|
||||||
.sync_with_callback(SyncSettings::new(), |response| async move {
|
.sync_with_callback(SyncSettings::new(), |response| async move {
|
||||||
if let Err(e) = on_response(&response, client_ref).await {
|
if let Err(e) = on_response(&response, client_ref).await {
|
||||||
error!("Error processing response: {}", e);
|
error!("Error processing response: {}", e);
|
||||||
|
@ -103,7 +115,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
if initial.load(std::sync::atomic::Ordering::SeqCst) {
|
if initial.load(std::sync::atomic::Ordering::SeqCst) {
|
||||||
if let Err(e) =
|
if let Err(e) =
|
||||||
on_initial_response(client_ref, &user_id_ref, &config_ref.password).await
|
on_initial_response(client_ref, user_id_ref, &config_ref.password).await
|
||||||
{
|
{
|
||||||
error!("Error processing initial response: {}", e);
|
error!("Error processing initial response: {}", e);
|
||||||
}
|
}
|
||||||
|
@ -119,7 +131,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Bot {
|
pub struct Bot {
|
||||||
client: Arc<RwLock<Client>>,
|
client: Client,
|
||||||
jm_client: RwLock<JMClient>,
|
jm_client: RwLock<JMClient>,
|
||||||
memecache: Db,
|
memecache: Db,
|
||||||
config: Arc<Config>,
|
config: Arc<Config>,
|
||||||
|
@ -130,77 +142,60 @@ pub struct Bot {
|
||||||
rng: Mutex<StdRng>,
|
rng: Mutex<StdRng>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
async fn on_stripped_state_member(event: StrippedRoomMemberEvent, client: Client, room: Room) {
|
||||||
impl EventHandler for Bot {
|
if event.state_key == client.user_id().await.unwrap() {
|
||||||
async fn on_stripped_state_member(
|
return;
|
||||||
&self,
|
|
||||||
room: Room,
|
|
||||||
room_member: &StrippedStateEvent<MemberEventContent>,
|
|
||||||
_: Option<MemberEventContent>,
|
|
||||||
) {
|
|
||||||
if room_member.state_key == self.client.read().await.user_id().await.unwrap() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Room::Invited(room) = room {
|
|
||||||
info!("Autojoining room {}", room.room_id());
|
|
||||||
let mut delay = 2;
|
|
||||||
|
|
||||||
while let Err(err) = self
|
|
||||||
.client
|
|
||||||
.read()
|
|
||||||
.await
|
|
||||||
.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
|
|
||||||
warn!(
|
|
||||||
"Failed to join room {} ({:?}), retrying in {}s",
|
|
||||||
room.room_id(),
|
|
||||||
err,
|
|
||||||
delay
|
|
||||||
);
|
|
||||||
|
|
||||||
tokio::time::sleep(Duration::from_secs(delay)).await;
|
|
||||||
delay *= 2;
|
|
||||||
|
|
||||||
if delay > 3600 {
|
|
||||||
error!("Can't join room {} ({:?})", room.room_id(), err);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("Successfully joined room {}", room.room_id());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_room_message(&self, room: Room, msg: &SyncMessageEvent<MessageEventContent>) {
|
if let Room::Invited(room) = room {
|
||||||
if self
|
info!("Autojoining room {}", room.room_id());
|
||||||
.client
|
let mut delay = 2;
|
||||||
.read()
|
|
||||||
.await
|
while let Err(err) = client.join_room_by_id(room.room_id()).await {
|
||||||
.user_id()
|
// retry autojoin due to synapse sending invites, before the
|
||||||
.await
|
// invited user can join for more information see
|
||||||
.map(|u| u == msg.sender)
|
// https://github.com/matrix-org/synapse/issues/4345
|
||||||
.unwrap_or(true)
|
warn!(
|
||||||
{
|
"Failed to join room {} ({:?}), retrying in {}s",
|
||||||
return;
|
room.room_id(),
|
||||||
|
err,
|
||||||
|
delay
|
||||||
|
);
|
||||||
|
|
||||||
|
tokio::time::sleep(Duration::from_secs(delay)).await;
|
||||||
|
delay *= 2;
|
||||||
|
|
||||||
|
if delay > 3600 {
|
||||||
|
error!("Can't join room {} ({:?})", room.room_id(), err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let SyncMessageEvent {
|
info!("Successfully joined room {}", room.room_id());
|
||||||
content:
|
}
|
||||||
MessageEventContent {
|
}
|
||||||
msgtype: MessageType::Text(TextMessageEventContent { body: msg_body, .. }),
|
|
||||||
..
|
async fn on_room_message(msg: SyncRoomMessageEvent, client: Client, room: Room, bot: Arc<Bot>) {
|
||||||
},
|
if client
|
||||||
..
|
.user_id()
|
||||||
} = msg
|
.await
|
||||||
{
|
.map(|u| u == msg.sender)
|
||||||
if let Err(e) = responder::on_msg(msg_body, room, self).await {
|
.unwrap_or(true)
|
||||||
error!("Responder error: {}", e);
|
{
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let SyncMessageEvent {
|
||||||
|
content:
|
||||||
|
RoomMessageEventContent {
|
||||||
|
msgtype: MessageType::Text(TextMessageEventContent { body: msg_body, .. }),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
} = msg
|
||||||
|
{
|
||||||
|
if let Err(e) = responder::on_msg(&msg_body, room, &bot).await {
|
||||||
|
error!("Responder error: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,23 +248,6 @@ async fn on_response(response: &SyncResponse, client: &Client) -> anyhow::Result
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn auth_data<'a>(user: &UserId, password: &str, session: Option<&'a str>) -> AuthData<'a> {
|
|
||||||
let mut auth_parameters = BTreeMap::new();
|
|
||||||
let identifier = json!({
|
|
||||||
"type": "m.id.user",
|
|
||||||
"user": user,
|
|
||||||
});
|
|
||||||
|
|
||||||
auth_parameters.insert("identifier".to_owned(), identifier);
|
|
||||||
auth_parameters.insert("password".to_owned(), password.to_owned().into());
|
|
||||||
|
|
||||||
AuthData::DirectRequest {
|
|
||||||
kind: "m.login.password",
|
|
||||||
auth_parameters,
|
|
||||||
session,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn bootstrap_cross_signing(
|
async fn bootstrap_cross_signing(
|
||||||
client: &Client,
|
client: &Client,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
|
@ -278,7 +256,11 @@ async fn bootstrap_cross_signing(
|
||||||
info!("bootstrapping e2e");
|
info!("bootstrapping e2e");
|
||||||
if let Err(e) = client.bootstrap_cross_signing(None).await {
|
if let Err(e) = client.bootstrap_cross_signing(None).await {
|
||||||
if let Some(response) = e.uiaa_response() {
|
if let Some(response) = e.uiaa_response() {
|
||||||
let auth_data = auth_data(&user_id, &password, response.session.as_deref());
|
let auth_data = AuthData::Password(assign!(
|
||||||
|
Password::new(UserIdentifier::MatrixId(user_id.as_str()), password),
|
||||||
|
{ session: response.session.as_deref() }
|
||||||
|
));
|
||||||
|
|
||||||
client
|
client
|
||||||
.bootstrap_cross_signing(Some(auth_data))
|
.bootstrap_cross_signing(Some(auth_data))
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -2,25 +2,24 @@ use crate::{util, Bot};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use matrix_sdk::{
|
use matrix_sdk::{
|
||||||
api::r0::media::create_content,
|
room::{Joined, Room},
|
||||||
events::{
|
ruma::{
|
||||||
room::{
|
api::client::r0::media::create_content,
|
||||||
|
events::room::{
|
||||||
message::{
|
message::{
|
||||||
FileInfo,
|
FileInfo,
|
||||||
FileMessageEventContent,
|
FileMessageEventContent,
|
||||||
ImageMessageEventContent,
|
ImageMessageEventContent,
|
||||||
MessageEventContent,
|
|
||||||
MessageType,
|
MessageType,
|
||||||
|
RoomMessageEventContent,
|
||||||
VideoInfo,
|
VideoInfo,
|
||||||
VideoMessageEventContent,
|
VideoMessageEventContent,
|
||||||
},
|
},
|
||||||
ImageInfo,
|
ImageInfo,
|
||||||
},
|
},
|
||||||
AnyMessageEventContent,
|
MxcUri,
|
||||||
|
UInt,
|
||||||
},
|
},
|
||||||
identifiers::MxcUri,
|
|
||||||
room::{Joined, Room},
|
|
||||||
UInt,
|
|
||||||
};
|
};
|
||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -30,7 +29,7 @@ use std::{io::Cursor, sync::atomic::Ordering};
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
struct CachedMeme {
|
struct CachedMeme {
|
||||||
/// mxc url of the meme
|
/// mxc url of the meme
|
||||||
mxc: MxcUri,
|
mxc: Box<MxcUri>,
|
||||||
/// MIME type of the meme
|
/// MIME type of the meme
|
||||||
#[serde(with = "util::mime_serialize")]
|
#[serde(with = "util::mime_serialize")]
|
||||||
mime: Mime,
|
mime: Mime,
|
||||||
|
@ -104,13 +103,8 @@ pub async fn on_msg(msg: &str, room: Room, bot: &Bot) -> anyhow::Result<()> {
|
||||||
let id = if let Ok(id) = id.parse::<u32>() {
|
let id = if let Ok(id) = id.parse::<u32>() {
|
||||||
id
|
id
|
||||||
} else {
|
} else {
|
||||||
room.send(
|
room.send(RoomMessageEventContent::text_plain("Invalid ID!"), None)
|
||||||
AnyMessageEventContent::RoomMessage(MessageEventContent::text_plain(
|
.await?;
|
||||||
"Invalid ID!",
|
|
||||||
)),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -161,12 +155,8 @@ async fn cache_send_meme(meme_id: u32, bot: &Bot, room: Joined) -> anyhow::Resul
|
||||||
|
|
||||||
if let Some(mime) = mime_guess::from_path(&meme.link).first() {
|
if let Some(mime) = mime_guess::from_path(&meme.link).first() {
|
||||||
let size = resp.len();
|
let size = resp.len();
|
||||||
let create_content::Response { content_uri, .. } = bot
|
let create_content::Response { content_uri, .. } =
|
||||||
.client
|
bot.client.upload(&mime, &mut Cursor::new(resp)).await?;
|
||||||
.read()
|
|
||||||
.await
|
|
||||||
.upload(&mime, &mut Cursor::new(resp))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let cached = CachedMeme {
|
let cached = CachedMeme {
|
||||||
mxc: content_uri,
|
mxc: content_uri,
|
||||||
|
@ -202,10 +192,7 @@ async fn cache_send_meme(meme_id: u32, bot: &Bot, room: Joined) -> anyhow::Resul
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
room.send(
|
room.send(
|
||||||
AnyMessageEventContent::RoomMessage(MessageEventContent::text_plain(&format!(
|
RoomMessageEventContent::text_plain(format!("No meme with id '{}'", meme_id)),
|
||||||
"No meme with id '{}'",
|
|
||||||
meme_id
|
|
||||||
))),
|
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -216,12 +203,9 @@ async fn cache_send_meme(meme_id: u32, bot: &Bot, room: Joined) -> anyhow::Resul
|
||||||
|
|
||||||
async fn send_meme(room: &Joined, cached: CachedMeme) -> anyhow::Result<()> {
|
async fn send_meme(room: &Joined, cached: CachedMeme) -> anyhow::Result<()> {
|
||||||
let msg_ty = cached.into_message_type();
|
let msg_ty = cached.into_message_type();
|
||||||
room.send(
|
room.send(RoomMessageEventContent::new(msg_ty), None)
|
||||||
AnyMessageEventContent::RoomMessage(MessageEventContent::new(msg_ty)),
|
.await
|
||||||
None,
|
.context("Failed to send meme")?;
|
||||||
)
|
|
||||||
.await
|
|
||||||
.context("Failed to send meme")?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ pub mod mime_serialize {
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
serializer.serialize_str(&data.to_string())
|
serializer.serialize_str(data.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Mime, D::Error>
|
pub fn deserialize<'de, D>(deserializer: D) -> Result<Mime, D::Error>
|
||||||
|
|
Loading…
Reference in a new issue