diff --git a/ica-rs/Cargo.toml b/ica-rs/Cargo.toml index bbe7c7f..ac23c59 100644 --- a/ica-rs/Cargo.toml +++ b/ica-rs/Cargo.toml @@ -10,15 +10,17 @@ ed25519 = "2.2.3" ed25519-dalek = "2.1.1" hex = "0.4.3" blake3 = "1.5.0" -rust_socketio = "0.4.4" +rust_socketio = { version = "0.4.4", features = ["async"]} serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" chrono = "0.4.34" toml = "0.8.10" - colored = "2.1.0" +tokio = { version = "1.0", features = ["full"] } +futures-util = "0.3.30" + tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["time"] } diff --git a/ica-rs/src/client.rs b/ica-rs/src/client.rs index 2087896..8e6ab4f 100644 --- a/ica-rs/src/client.rs +++ b/ica-rs/src/client.rs @@ -1,17 +1,19 @@ use crate::config::IcaConfig; use crate::data_struct::messages::SendMessage; use crate::data_struct::{all_rooms::Room, online_data::OnlineData}; +use crate::ClientStatus; -use ed25519_dalek::{Signature, Signer, SigningKey}; -use rust_socketio::{Payload, RawClient}; -use serde_json::Value; use colored::Colorize; +use ed25519_dalek::{Signature, Signer, SigningKey}; +use rust_socketio::asynchronous::Client; +use rust_socketio::Payload; +use serde_json::Value; use tracing::{debug, warn}; /// "安全" 的 发送一条消息 -pub fn send_message(client: RawClient, message: SendMessage) { +pub async fn send_message(client: Client, message: SendMessage) { let value = message.as_value(); - match client.emit("sendMessage", value) { + match client.emit("sendMessage", value).await { Ok(_) => debug!("send_message {}", format!("{:#?}", message).cyan()), Err(e) => warn!("send_message faild:{}", format!("{:#?}", e).red()), } @@ -51,64 +53,48 @@ impl IcalinguaStatus { self.config = Some(config); } - pub fn get_online_data(&self) -> &OnlineData { - self.online_data.as_ref().unwrap() - } - - pub fn get_config(&self) -> &IcaConfig { - self.config.as_ref().unwrap() - } -} - -pub struct IcalinguaSinger { - pub host: String, - pub private_key: SigningKey, -} - -impl IcalinguaSinger { - pub fn new_from_config(config: &IcaConfig) -> Self { - let host = config.host.clone(); - let pub_key = config.private_key.clone(); - Self::new_from_raw(host, pub_key) - } - - pub fn new_from_raw(host: String, pub_key: String) -> Self { - let array_key: [u8; 32] = hex::decode(pub_key) - .expect("Not a vaild pub key") - .try_into() - .expect("Not a vaild pub key"); - - let signing_key: SigningKey = SigningKey::from_bytes(&array_key); - Self { - host, - private_key: signing_key, + pub fn get_online_data() -> &'static OnlineData { + unsafe { + ClientStatus + .online_data + .as_ref() + .expect("online_data should be set") } } - /// 最痛苦的一集 - /// - /// 签名的回调函数 - pub fn sign_callback(&self, payload: Payload, client: RawClient) { - let require_data = match payload { - Payload::Text(json_value) => Some(json_value), - _ => None, - } - .expect("Payload should be Json data"); - - let (auth_key, version) = (&require_data[0], &require_data[1]); - debug!("auth_key: {:?}, version: {:?}", auth_key, version); - let auth_key = match &require_data.get(0) { - Some(Value::String(auth_key)) => Some(auth_key), - _ => None, - } - .expect("auth_key should be string"); - - let salt = hex::decode(auth_key).expect("Got an invalid salt from the server"); - let signature: Signature = self.private_key.sign(salt.as_slice()); - - let sign = signature.to_bytes().to_vec(); - client - .emit("auth", sign) - .expect("Faild to send signin data"); + pub fn get_config() -> &'static IcaConfig { + unsafe { ClientStatus.config.as_ref().expect("config should be set") } } } + +pub async fn sign_callback(payload: Payload, client: Client) { + // 获取数据 + let require_data = match payload { + Payload::Text(json_value) => Some(json_value), + _ => None, + } + .expect("Payload should be Json data"); + + let (auth_key, version) = (&require_data[0], &require_data[1]); + debug!("auth_key: {:?}, version: {:?}", auth_key, version); + let auth_key = match &require_data.get(0) { + Some(Value::String(auth_key)) => Some(auth_key), + _ => None, + } + .expect("auth_key should be string"); + let salt = hex::decode(auth_key).expect("Got an invalid salt from the server"); + // 签名 + let private_key = IcalinguaStatus::get_config().private_key.clone(); + let array_key: [u8; 32] = hex::decode(private_key) + .expect("Not a vaild pub key") + .try_into() + .expect("Not a vaild pub key"); + let signing_key: SigningKey = SigningKey::from_bytes(&array_key); + let signature: Signature = signing_key.sign(salt.as_slice()); + + let sign = signature.to_bytes().to_vec(); + client + .emit("auth", sign) + .await + .expect("Faild to send signin data"); +} diff --git a/ica-rs/src/data_struct/messages.rs b/ica-rs/src/data_struct/messages.rs index 6a71929..7375604 100644 --- a/ica-rs/src/data_struct/messages.rs +++ b/ica-rs/src/data_struct/messages.rs @@ -1,6 +1,6 @@ +use crate::client::IcalinguaStatus; use crate::data_struct::files::MessageFile; use crate::data_struct::{MessageId, RoomId, UserId}; -use crate::ClientStatus; use chrono::NaiveDateTime; use serde::{Deserialize, Serialize}; @@ -217,9 +217,7 @@ impl NewMessage { } pub fn is_from_self(&self) -> bool { - let qq_id = unsafe { - ClientStatus.get_online_data().qqid - }; + let qq_id = IcalinguaStatus::get_online_data().qqid; self.sender_id == qq_id } diff --git a/ica-rs/src/events.rs b/ica-rs/src/events.rs index d352fa7..c98eea5 100644 --- a/ica-rs/src/events.rs +++ b/ica-rs/src/events.rs @@ -1,5 +1,6 @@ use colored::Colorize; -use rust_socketio::{Event, Payload, RawClient}; +use rust_socketio::asynchronous::Client; +use rust_socketio::{Event, Payload}; use tracing::{info, warn}; use crate::client::send_message; @@ -9,7 +10,7 @@ use crate::data_struct::online_data::OnlineData; use crate::{py, VERSION}; /// 获取在线数据 -pub fn get_online_data(payload: Payload, _client: RawClient) { +pub async fn get_online_data(payload: Payload, _client: Client) { if let Payload::Text(values) = payload { if let Some(value) = values.first() { let online_data = OnlineData::new_from_json(value); @@ -25,7 +26,7 @@ pub fn get_online_data(payload: Payload, _client: RawClient) { } /// 接收消息 -pub fn add_message(payload: Payload, client: RawClient) { +pub async fn add_message(payload: Payload, client: Client) { if let Payload::Text(values) = payload { if let Some(value) = values.first() { let message = NewMessage::new_from_json(value); @@ -40,14 +41,14 @@ pub fn add_message(payload: Payload, client: RawClient) { // 之后的处理交给插件 if message.content.eq("/bot-rs") { let reply = message.reply_with(&format!("ica-rs pong v{}", VERSION)); - send_message(client, reply) + send_message(client, reply).await; } } } } /// 撤回消息 -pub fn delete_message(payload: Payload, _client: RawClient) { +pub async fn delete_message(payload: Payload, _client: Client) { if let Payload::Text(values) = payload { // 消息 id if let Some(value) = values.first() { @@ -58,7 +59,7 @@ pub fn delete_message(payload: Payload, _client: RawClient) { } } -pub fn update_all_room(payload: Payload, _client: RawClient) { +pub async fn update_all_room(payload: Payload, _client: Client) { if let Payload::Text(values) = payload { if let Some(value) = values.first() { if let Some(raw_rooms) = value.as_array() { @@ -76,7 +77,7 @@ pub fn update_all_room(payload: Payload, _client: RawClient) { } /// 所有 -pub fn any_event(event: Event, payload: Payload, _client: RawClient) { +pub async fn any_event(event: Event, payload: Payload, _client: Client) { let handled = vec![ // 真正处理过的 "authSucceed", @@ -128,7 +129,7 @@ pub fn any_event(event: Event, payload: Payload, _client: RawClient) { } } -pub fn connect_callback(payload: Payload, _client: RawClient) { +pub async fn connect_callback(payload: Payload, _client: Client) { match payload { Payload::Text(values) => { if let Some(value) = values.first() { diff --git a/ica-rs/src/main.rs b/ica-rs/src/main.rs index e5c46f1..77c7490 100644 --- a/ica-rs/src/main.rs +++ b/ica-rs/src/main.rs @@ -1,6 +1,8 @@ use std::time::Duration; -use rust_socketio::ClientBuilder; +use futures_util::FutureExt; +use rust_socketio::asynchronous::{Client, ClientBuilder}; +use rust_socketio::{Event, Payload, TransportType}; use tracing::info; mod client; @@ -19,7 +21,20 @@ pub static mut ClientStatus: client::IcalinguaStatus = client::IcalinguaStatus { pub const VERSION: &str = env!("CARGO_PKG_VERSION"); -fn main() { +macro_rules! wrap_callback { + ($f:expr) => { + |payload: Payload, client: Client| $f(payload, client).boxed() + }; +} + +macro_rules! wrap_any_callback { + ($f:expr) => { + |event: Event, payload: Payload, client: Client| $f(event, payload, client).boxed() + }; +} + +#[tokio::main] +async fn main() { tracing_subscriber::fmt() .with_max_level(tracing::Level::DEBUG) .init(); @@ -31,20 +46,20 @@ fn main() { ClientStatus.update_config(ica_config.clone()); } py::init_py(&ica_config); - let ica_singer = client::IcalinguaSinger::new_from_config(&ica_config); - let socket = ClientBuilder::new(ica_singer.host.clone()) - .transport_type(rust_socketio::TransportType::Websocket) - .on_any(events::any_event) - .on("requireAuth", move |a, b| ica_singer.sign_callback(a, b)) - .on("message", events::connect_callback) - .on("authSucceed", events::connect_callback) - .on("authFailed", events::connect_callback) - .on("onlineData", events::get_online_data) - .on("setAllRooms", events::update_all_room) - .on("addMessage", events::add_message) - .on("deleteMessage", events::delete_message) + let socket = ClientBuilder::new(ica_config.host.clone()) + .transport_type(TransportType::Websocket) + .on_any(wrap_any_callback!(events::any_event)) + .on("requireAuth", wrap_callback!(client::sign_callback)) + .on("message", wrap_callback!(events::connect_callback)) + .on("authSucceed", wrap_callback!(events::connect_callback)) + .on("authFailed", wrap_callback!(events::connect_callback)) + .on("onlineData", wrap_callback!(events::get_online_data)) + .on("setAllRooms", wrap_callback!(events::update_all_room)) + .on("addMessage", wrap_callback!(events::add_message)) + .on("deleteMessage", wrap_callback!(events::delete_message)) .connect() + .await .expect("Connection failed"); info!("Connected"); @@ -58,7 +73,10 @@ fn main() { ); std::thread::sleep(Duration::from_secs(1)); info!("发送启动消息到房间: {}", room); - if let Err(e) = socket.emit("sendMessage", serde_json::to_value(startup_msg).unwrap()) { + if let Err(e) = socket + .emit("sendMessage", serde_json::to_value(startup_msg).unwrap()) + .await + { info!("启动信息发送失败 房间:{}|e:{}", room, e); } } @@ -69,6 +87,7 @@ fn main() { info!("Press any key to exit"); let mut input = String::new(); std::io::stdin().read_line(&mut input).unwrap(); - socket.disconnect().expect("Disconnect failed"); + + socket.disconnect().await.expect("Disconnect failed"); info!("Disconnected"); } diff --git a/ica-rs/src/py/mod.rs b/ica-rs/src/py/mod.rs index 57e0d49..3907b78 100644 --- a/ica-rs/src/py/mod.rs +++ b/ica-rs/src/py/mod.rs @@ -2,9 +2,9 @@ pub mod class; use std::{collections::HashMap, path::PathBuf}; +use blake3::Hasher; use pyo3::{prelude::*, types::IntoPyDict}; use tracing::{debug, info, warn}; -use blake3::Hasher; use crate::config::IcaConfig; @@ -22,7 +22,7 @@ impl PyStatus { debug!("No files in py status"); PYSTATUS.files = Some(HashMap::new()); PYSTATUS.files.as_ref().unwrap() - }, + } } } } @@ -32,12 +32,12 @@ impl PyStatus { match PYSTATUS.files.as_mut() { Some(files) => { files.insert(path, (content, hash)); - }, + } None => { let mut files = HashMap::new(); files.insert(path, (content, hash)); PYSTATUS.files = Some(files); - }, + } } } } @@ -45,11 +45,9 @@ impl PyStatus { pub fn verify_file(path: &PathBuf, hash: &String) -> bool { unsafe { match PYSTATUS.files.as_ref() { - Some(files) => { - match files.get(path) { - Some((_, file_hash)) => file_hash == hash, - None => false, - } + Some(files) => match files.get(path) { + Some((_, file_hash)) => file_hash == hash, + None => false, }, None => false, } @@ -65,7 +63,12 @@ pub fn run() { let _bot_status: &PyCell<_> = PyCell::new(py, bot_status).unwrap(); let locals = [("state", _bot_status)].into_py_dict(py); - py.run("from pathlib import Path\nprint(Path.cwd())\nprint(state)", None, Some(locals)).unwrap(); + py.run( + "from pathlib import Path\nprint(Path.cwd())\nprint(state)", + None, + Some(locals), + ) + .unwrap(); }); } @@ -94,9 +97,7 @@ pub fn init_py(config: &IcaConfig) { if let Ok(entry) = entry { let path = entry.path(); if let Some(ext) = path.extension() { - if ext == "py" { - - } + if ext == "py" {} } } }