Compare commits

..

No commits in common. "8c87cad42f45ca71bed7595ff2d97095a7124c84" and "a5f916542e996be9de6f8a6b689d1e5c0c0077ae" have entirely different histories.

7 changed files with 105 additions and 126 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ica-rs" name = "ica-rs"
version = "0.4.2" version = "0.4.1"
edition = "2021" edition = "2021"
# 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
@ -10,16 +10,14 @@ ed25519 = "2.2.3"
ed25519-dalek = "2.1.1" ed25519-dalek = "2.1.1"
hex = "0.4.3" hex = "0.4.3"
blake3 = "1.5.0" blake3 = "1.5.0"
rust_socketio = { version = "0.4.4", features = ["async"]} rust_socketio = "0.4.4"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
chrono = "0.4.34" chrono = "0.4.34"
toml = "0.8.10" toml = "0.8.10"
colored = "2.1.0"
tokio = { version = "1.0", features = ["full"] } colored = "2.1.0"
futures-util = "0.3.30"
tracing = "0.1.40" tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["time"] } tracing-subscriber = { version = "0.3.18", features = ["time"] }

View File

@ -1,19 +1,17 @@
use crate::config::IcaConfig; use crate::config::IcaConfig;
use crate::data_struct::messages::SendMessage; use crate::data_struct::messages::SendMessage;
use crate::data_struct::{all_rooms::Room, online_data::OnlineData}; use crate::data_struct::{all_rooms::Room, online_data::OnlineData};
use crate::ClientStatus;
use colored::Colorize;
use ed25519_dalek::{Signature, Signer, SigningKey}; use ed25519_dalek::{Signature, Signer, SigningKey};
use rust_socketio::asynchronous::Client; use rust_socketio::{Payload, RawClient};
use rust_socketio::Payload;
use serde_json::Value; use serde_json::Value;
use colored::Colorize;
use tracing::{debug, warn}; use tracing::{debug, warn};
/// "安全" 的 发送一条消息 /// "安全" 的 发送一条消息
pub async fn send_message(client: Client, message: SendMessage) { pub fn send_message(client: RawClient, message: SendMessage) {
let value = message.as_value(); let value = message.as_value();
match client.emit("sendMessage", value).await { match client.emit("sendMessage", value) {
Ok(_) => debug!("send_message {}", format!("{:#?}", message).cyan()), Ok(_) => debug!("send_message {}", format!("{:#?}", message).cyan()),
Err(e) => warn!("send_message faild:{}", format!("{:#?}", e).red()), Err(e) => warn!("send_message faild:{}", format!("{:#?}", e).red()),
} }
@ -53,22 +51,44 @@ impl IcalinguaStatus {
self.config = Some(config); self.config = Some(config);
} }
pub fn get_online_data() -> &'static OnlineData { pub fn get_online_data(&self) -> &OnlineData {
unsafe { self.online_data.as_ref().unwrap()
ClientStatus }
.online_data
.as_ref() pub fn get_config(&self) -> &IcaConfig {
.expect("online_data should be set") self.config.as_ref().unwrap()
} }
} }
pub fn get_config() -> &'static IcaConfig { pub struct IcalinguaSinger {
unsafe { ClientStatus.config.as_ref().expect("config should be set") } 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 async fn sign_callback(payload: Payload, client: Client) { /// 最痛苦的一集
// 获取数据 ///
/// 签名的回调函数
pub fn sign_callback(&self, payload: Payload, client: RawClient) {
let require_data = match payload { let require_data = match payload {
Payload::Text(json_value) => Some(json_value), Payload::Text(json_value) => Some(json_value),
_ => None, _ => None,
@ -82,19 +102,13 @@ pub async fn sign_callback(payload: Payload, client: Client) {
_ => None, _ => None,
} }
.expect("auth_key should be string"); .expect("auth_key should be string");
let salt = hex::decode(auth_key).expect("Got an invalid salt from the server"); 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 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(); let sign = signature.to_bytes().to_vec();
client client
.emit("auth", sign) .emit("auth", sign)
.await
.expect("Faild to send signin data"); .expect("Faild to send signin data");
} }
}

View File

@ -1,6 +1,6 @@
use crate::client::IcalinguaStatus;
use crate::data_struct::files::MessageFile; use crate::data_struct::files::MessageFile;
use crate::data_struct::{MessageId, RoomId, UserId}; use crate::data_struct::{MessageId, RoomId, UserId};
use crate::ClientStatus;
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -217,7 +217,9 @@ impl NewMessage {
} }
pub fn is_from_self(&self) -> bool { pub fn is_from_self(&self) -> bool {
let qq_id = IcalinguaStatus::get_online_data().qqid; let qq_id = unsafe {
ClientStatus.get_online_data().qqid
};
self.sender_id == qq_id self.sender_id == qq_id
} }

View File

@ -1,6 +1,5 @@
use colored::Colorize; use colored::Colorize;
use rust_socketio::asynchronous::Client; use rust_socketio::{Event, Payload, RawClient};
use rust_socketio::{Event, Payload};
use tracing::{info, warn}; use tracing::{info, warn};
use crate::client::send_message; use crate::client::send_message;
@ -10,7 +9,7 @@ use crate::data_struct::online_data::OnlineData;
use crate::{py, VERSION}; use crate::{py, VERSION};
/// 获取在线数据 /// 获取在线数据
pub async fn get_online_data(payload: Payload, _client: Client) { pub fn get_online_data(payload: Payload, _client: RawClient) {
if let Payload::Text(values) = payload { if let Payload::Text(values) = payload {
if let Some(value) = values.first() { if let Some(value) = values.first() {
let online_data = OnlineData::new_from_json(value); let online_data = OnlineData::new_from_json(value);
@ -26,7 +25,7 @@ pub async fn get_online_data(payload: Payload, _client: Client) {
} }
/// 接收消息 /// 接收消息
pub async fn add_message(payload: Payload, client: Client) { pub fn add_message(payload: Payload, client: RawClient) {
if let Payload::Text(values) = payload { if let Payload::Text(values) = payload {
if let Some(value) = values.first() { if let Some(value) = values.first() {
let message = NewMessage::new_from_json(value); let message = NewMessage::new_from_json(value);
@ -41,14 +40,14 @@ pub async fn add_message(payload: Payload, client: Client) {
// 之后的处理交给插件 // 之后的处理交给插件
if message.content.eq("/bot-rs") { if message.content.eq("/bot-rs") {
let reply = message.reply_with(&format!("ica-rs pong v{}", VERSION)); let reply = message.reply_with(&format!("ica-rs pong v{}", VERSION));
send_message(client, reply).await; send_message(client, reply)
} }
} }
} }
} }
/// 撤回消息 /// 撤回消息
pub async fn delete_message(payload: Payload, _client: Client) { pub fn delete_message(payload: Payload, _client: RawClient) {
if let Payload::Text(values) = payload { if let Payload::Text(values) = payload {
// 消息 id // 消息 id
if let Some(value) = values.first() { if let Some(value) = values.first() {
@ -59,7 +58,7 @@ pub async fn delete_message(payload: Payload, _client: Client) {
} }
} }
pub async fn update_all_room(payload: Payload, _client: Client) { pub fn update_all_room(payload: Payload, _client: RawClient) {
if let Payload::Text(values) = payload { if let Payload::Text(values) = payload {
if let Some(value) = values.first() { if let Some(value) = values.first() {
if let Some(raw_rooms) = value.as_array() { if let Some(raw_rooms) = value.as_array() {
@ -77,7 +76,7 @@ pub async fn update_all_room(payload: Payload, _client: Client) {
} }
/// 所有 /// 所有
pub async fn any_event(event: Event, payload: Payload, _client: Client) { pub fn any_event(event: Event, payload: Payload, _client: RawClient) {
let handled = vec![ let handled = vec![
// 真正处理过的 // 真正处理过的
"authSucceed", "authSucceed",
@ -129,7 +128,7 @@ pub async fn any_event(event: Event, payload: Payload, _client: Client) {
} }
} }
pub async fn connect_callback(payload: Payload, _client: Client) { pub fn connect_callback(payload: Payload, _client: RawClient) {
match payload { match payload {
Payload::Text(values) => { Payload::Text(values) => {
if let Some(value) = values.first() { if let Some(value) = values.first() {

View File

@ -1,8 +1,6 @@
use std::time::Duration; use std::time::Duration;
use futures_util::FutureExt; use rust_socketio::ClientBuilder;
use rust_socketio::asynchronous::{Client, ClientBuilder};
use rust_socketio::{Event, Payload, TransportType};
use tracing::info; use tracing::info;
mod client; mod client;
@ -21,20 +19,7 @@ pub static mut ClientStatus: client::IcalinguaStatus = client::IcalinguaStatus {
pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub const VERSION: &str = env!("CARGO_PKG_VERSION");
macro_rules! wrap_callback { fn main() {
($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() tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG) .with_max_level(tracing::Level::DEBUG)
.init(); .init();
@ -46,20 +31,20 @@ async fn main() {
ClientStatus.update_config(ica_config.clone()); ClientStatus.update_config(ica_config.clone());
} }
py::init_py(&ica_config); py::init_py(&ica_config);
let ica_singer = client::IcalinguaSinger::new_from_config(&ica_config);
let socket = ClientBuilder::new(ica_config.host.clone()) let socket = ClientBuilder::new(ica_singer.host.clone())
.transport_type(TransportType::Websocket) .transport_type(rust_socketio::TransportType::Websocket)
.on_any(wrap_any_callback!(events::any_event)) .on_any(events::any_event)
.on("requireAuth", wrap_callback!(client::sign_callback)) .on("requireAuth", move |a, b| ica_singer.sign_callback(a, b))
.on("message", wrap_callback!(events::connect_callback)) .on("message", events::connect_callback)
.on("authSucceed", wrap_callback!(events::connect_callback)) .on("authSucceed", events::connect_callback)
.on("authFailed", wrap_callback!(events::connect_callback)) .on("authFailed", events::connect_callback)
.on("onlineData", wrap_callback!(events::get_online_data)) .on("onlineData", events::get_online_data)
.on("setAllRooms", wrap_callback!(events::update_all_room)) .on("setAllRooms", events::update_all_room)
.on("addMessage", wrap_callback!(events::add_message)) .on("addMessage", events::add_message)
.on("deleteMessage", wrap_callback!(events::delete_message)) .on("deleteMessage", events::delete_message)
.connect() .connect()
.await
.expect("Connection failed"); .expect("Connection failed");
info!("Connected"); info!("Connected");
@ -67,16 +52,13 @@ async fn main() {
if ica_config.notice_start { if ica_config.notice_start {
for room in ica_config.notice_room.iter() { for room in ica_config.notice_room.iter() {
let startup_msg = crate::data_struct::messages::SendMessage::new( let startup_msg = crate::data_struct::messages::SendMessage::new(
format!("ica-async-rs bot v{}", VERSION), format!("ica-rs bot v{}", VERSION),
room.clone(), room.clone(),
None, None,
); );
std::thread::sleep(Duration::from_secs(1)); std::thread::sleep(Duration::from_secs(1));
info!("发送启动消息到房间: {}", room); info!("发送启动消息到房间: {}", room);
if let Err(e) = socket if let Err(e) = socket.emit("sendMessage", serde_json::to_value(startup_msg).unwrap()) {
.emit("sendMessage", serde_json::to_value(startup_msg).unwrap())
.await
{
info!("启动信息发送失败 房间:{}|e:{}", room, e); info!("启动信息发送失败 房间:{}|e:{}", room, e);
} }
} }
@ -87,8 +69,6 @@ async fn main() {
info!("Press any key to exit"); info!("Press any key to exit");
let mut input = String::new(); let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap(); std::io::stdin().read_line(&mut input).unwrap();
socket.disconnect().expect("Disconnect failed");
socket.disconnect().await.expect("Disconnect failed");
info!("Disconnected"); info!("Disconnected");
} }

View File

@ -2,9 +2,9 @@ pub mod class;
use std::{collections::HashMap, path::PathBuf}; use std::{collections::HashMap, path::PathBuf};
use blake3::Hasher;
use pyo3::{prelude::*, types::IntoPyDict}; use pyo3::{prelude::*, types::IntoPyDict};
use tracing::{debug, info, warn}; use tracing::{debug, info, warn};
use blake3::Hasher;
use crate::config::IcaConfig; use crate::config::IcaConfig;
@ -22,7 +22,7 @@ impl PyStatus {
debug!("No files in py status"); debug!("No files in py status");
PYSTATUS.files = Some(HashMap::new()); PYSTATUS.files = Some(HashMap::new());
PYSTATUS.files.as_ref().unwrap() PYSTATUS.files.as_ref().unwrap()
} },
} }
} }
} }
@ -32,12 +32,12 @@ impl PyStatus {
match PYSTATUS.files.as_mut() { match PYSTATUS.files.as_mut() {
Some(files) => { Some(files) => {
files.insert(path, (content, hash)); files.insert(path, (content, hash));
} },
None => { None => {
let mut files = HashMap::new(); let mut files = HashMap::new();
files.insert(path, (content, hash)); files.insert(path, (content, hash));
PYSTATUS.files = Some(files); PYSTATUS.files = Some(files);
} },
} }
} }
} }
@ -45,9 +45,11 @@ impl PyStatus {
pub fn verify_file(path: &PathBuf, hash: &String) -> bool { pub fn verify_file(path: &PathBuf, hash: &String) -> bool {
unsafe { unsafe {
match PYSTATUS.files.as_ref() { match PYSTATUS.files.as_ref() {
Some(files) => match files.get(path) { Some(files) => {
match files.get(path) {
Some((_, file_hash)) => file_hash == hash, Some((_, file_hash)) => file_hash == hash,
None => false, None => false,
}
}, },
None => false, None => false,
} }
@ -63,12 +65,7 @@ pub fn run() {
let _bot_status: &PyCell<_> = PyCell::new(py, bot_status).unwrap(); let _bot_status: &PyCell<_> = PyCell::new(py, bot_status).unwrap();
let locals = [("state", _bot_status)].into_py_dict(py); let locals = [("state", _bot_status)].into_py_dict(py);
py.run( py.run("from pathlib import Path\nprint(Path.cwd())\nprint(state)", None, Some(locals)).unwrap();
"from pathlib import Path\nprint(Path.cwd())\nprint(state)",
None,
Some(locals),
)
.unwrap();
}); });
} }
@ -97,7 +94,9 @@ pub fn init_py(config: &IcaConfig) {
if let Ok(entry) = entry { if let Ok(entry) = entry {
let path = entry.path(); let path = entry.path();
if let Some(ext) = path.extension() { if let Some(ext) = path.extension() {
if ext == "py" {} if ext == "py" {
}
} }
} }
} }

13
news.md
View File

@ -1,18 +1,5 @@
# 更新日志 # 更新日志
## 0.4.2
现在是 async 版本啦!
## 0.4.1
现在能发送登录信息啦
## 0.4.0
使用 Rust 从头实现一遍
\能登录啦/
## 0.3.3 ## 0.3.3
适配 Rust 端的配置文件修改 适配 Rust 端的配置文件修改