From 974368166b507ee5ebb0c38d4b4866a3beb038dc Mon Sep 17 00:00:00 2001 From: shenjack <3695888@qq.com> Date: Thu, 22 Feb 2024 02:40:39 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A5=BD=E6=AD=B9=E8=BF=87=E7=BC=96=E8=AF=91+t?= =?UTF-8?q?est=E4=BA=86=E2=80=A6=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ica-rs/Cargo.toml | 7 +-- ica-rs/ica_typing.py | 6 +++ ica-rs/src/client.rs | 12 +++-- ica-rs/src/events.rs | 2 +- ica-rs/src/py/class.rs | 36 +++++++++++++ ica-rs/src/py/mod.rs | 111 ++++++++++++++++++++++++++++------------- 6 files changed, 132 insertions(+), 42 deletions(-) diff --git a/ica-rs/Cargo.toml b/ica-rs/Cargo.toml index 0cceec7..e16e8b2 100644 --- a/ica-rs/Cargo.toml +++ b/ica-rs/Cargo.toml @@ -20,12 +20,13 @@ colored = "2.1.0" tokio = { version = "1.0", features = ["full"] } futures-util = "0.3.30" +pyo3 = "0.20.2" +# pyo3-async = "0.3.2" +pyo3-asyncio = { version = "0.20.0", features = ["attributes", "tokio-runtime"] } tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["time"] } -[dependencies.pyo3] -version = "0.20.2" - [patch.crates-io] rust_socketio = { git = "https://github.com/shenjackyuanjie/rust-socketio.git", branch = "mult_payload" } +# pyo3 = { git = "https://github.com/PyO3/pyo3.git", branch = "main" } diff --git a/ica-rs/ica_typing.py b/ica-rs/ica_typing.py index 078209a..6d002cc 100644 --- a/ica-rs/ica_typing.py +++ b/ica-rs/ica_typing.py @@ -44,3 +44,9 @@ class SendMessage: class NewMessage: def reply_with(self, message: str) -> SendMessage: ... + + +class IcaClient: + @staticmethod + async def send_message(client: "IcaClient", message: SendMessage) -> bool: + ... diff --git a/ica-rs/src/client.rs b/ica-rs/src/client.rs index 8e6ab4f..f96434e 100644 --- a/ica-rs/src/client.rs +++ b/ica-rs/src/client.rs @@ -11,11 +11,17 @@ use serde_json::Value; use tracing::{debug, warn}; /// "安全" 的 发送一条消息 -pub async fn send_message(client: Client, message: SendMessage) { +pub async fn send_message(client: &Client, message: &SendMessage) -> bool { let value = message.as_value(); match client.emit("sendMessage", value).await { - Ok(_) => debug!("send_message {}", format!("{:#?}", message).cyan()), - Err(e) => warn!("send_message faild:{}", format!("{:#?}", e).red()), + Ok(_) => { + debug!("send_message {}", format!("{:#?}", message).cyan()); + true + } + Err(e) => { + warn!("send_message faild:{}", format!("{:#?}", e).red()); + false + } } } diff --git a/ica-rs/src/events.rs b/ica-rs/src/events.rs index 065064d..3abe6f2 100644 --- a/ica-rs/src/events.rs +++ b/ica-rs/src/events.rs @@ -41,7 +41,7 @@ pub async fn add_message(payload: Payload, client: Client) { // 之后的处理交给插件 if message.content.eq("/bot-rs") { let reply = message.reply_with(&format!("ica-async-rs pong v{}", VERSION)); - send_message(client, reply).await; + send_message(&client, &reply).await; } } } diff --git a/ica-rs/src/py/class.rs b/ica-rs/src/py/class.rs index 559acbf..e946c1e 100644 --- a/ica-rs/src/py/class.rs +++ b/ica-rs/src/py/class.rs @@ -1,5 +1,7 @@ use pyo3::prelude::*; +use rust_socketio::asynchronous::Client; +use crate::client::send_message; use crate::data_struct::messages::{NewMessage, ReplyMessage, SendMessage}; use crate::ClientStatus; @@ -137,6 +139,7 @@ impl ReplyMessagePy { } } +#[derive(Clone)] #[pyclass] #[pyo3(name = "SendMessage")] pub struct SendMessagePy { @@ -148,3 +151,36 @@ impl SendMessagePy { Self { msg } } } + +#[derive(Clone)] +#[pyclass] +#[pyo3(name = "IcaClient")] +pub struct IcaClientPy { + pub client: Client, +} + +#[pymethods] +impl IcaClientPy { + // fn send_message(&self, message: SendMessagePy) -> bool { + // // Some(send_message(&self.client, &message.msg).await) + // true + // // // Ok(send_message(&self.client, &message.msg).await) + // // let mut future; + // // Python::with_gil(|gil| { + // // let this = self_.borrow(gil); + // // future = send_message(&this.client, &message.msg); + // // }); + // // Ok(future.await) + // } + #[staticmethod] + pub fn send_message( + py: Python, + client: IcaClientPy, + message: SendMessagePy, + ) -> PyResult<&PyAny> { + // send_message(&client.client, &message.msg).await + pyo3_asyncio::tokio::future_into_py(py, async move { + Ok(send_message(&client.client, &message.msg).await) + }) + } +} diff --git a/ica-rs/src/py/mod.rs b/ica-rs/src/py/mod.rs index 3907b78..57c3872 100644 --- a/ica-rs/src/py/mod.rs +++ b/ica-rs/src/py/mod.rs @@ -1,20 +1,22 @@ pub mod class; +use std::time::SystemTime; use std::{collections::HashMap, path::PathBuf}; -use blake3::Hasher; use pyo3::{prelude::*, types::IntoPyDict}; +use rust_socketio::asynchronous::Client; use tracing::{debug, info, warn}; use crate::config::IcaConfig; +use crate::data_struct::messages::NewMessage; #[derive(Debug, Clone)] pub struct PyStatus { - pub files: Option, String)>>, + pub files: Option, Py)>>, } impl PyStatus { - pub fn get_files() -> &'static HashMap, String)> { + pub fn get_files() -> &'static HashMap, Py)> { unsafe { match PYSTATUS.files.as_ref() { Some(files) => files, @@ -27,26 +29,26 @@ impl PyStatus { } } - pub fn add_file(path: PathBuf, content: Vec, hash: String) { + pub fn add_file(path: PathBuf, changed_time: Option, py_module: Py) { unsafe { match PYSTATUS.files.as_mut() { Some(files) => { - files.insert(path, (content, hash)); + files.insert(path, (changed_time, py_module)); } None => { let mut files = HashMap::new(); - files.insert(path, (content, hash)); + files.insert(path, (changed_time, py_module)); PYSTATUS.files = Some(files); } } } } - pub fn verify_file(path: &PathBuf, hash: &String) -> bool { + pub fn verify_file(path: &PathBuf, change_time: &Option) -> bool { unsafe { match PYSTATUS.files.as_ref() { Some(files) => match files.get(path) { - Some((_, file_hash)) => file_hash == hash, + Some((changed_time, _)) => change_time == changed_time, None => false, }, None => false, @@ -72,12 +74,65 @@ pub fn run() { }); } -pub fn load_py_file(path: &PathBuf) -> (Vec, String) { - let mut hasher = Hasher::new(); - let content = std::fs::read(path).unwrap(); - hasher.update(&content); - let hash = hasher.finalize().as_bytes().to_vec(); - (content, hex::encode(hash)) +pub fn load_py_plugins(path: &PathBuf) { + if path.exists() { + info!("finding plugins in: {:?}", path); + // 搜索所有的 py 文件 和 文件夹单层下面的 py 文件 + match path.read_dir() { + Err(e) => { + warn!("failed to read plugin path: {:?}", e); + } + Ok(dir) => { + for entry in dir { + if let Ok(entry) = entry { + let path = entry.path(); + if let Some(ext) = path.extension() { + if ext == "py" { + match load_py_file(&path) { + Ok((changed_time, content)) => { + let py_module = Python::with_gil(|py| -> Py { + let module: Py = PyModule::from_code( + py, + &content, + &path.to_string_lossy(), + "", + ) + .unwrap() + .into(); + module + }); + PyStatus::add_file(path, changed_time, py_module); + } + Err(e) => { + warn!("failed to load file: {:?} | e: {:?}", path, e); + } + } + } + } + } + } + } + } + } else { + warn!("plugin path not exists: {:?}", path); + } + info!( + "python 插件目录: {:?} 加载完成, 加载到 {} 个插件", + path, + PyStatus::get_files().len() + ); +} + +pub fn get_change_time(path: &PathBuf) -> Option { + path.metadata().ok()?.modified().ok() +} + +/// 传入文件路径 +/// 返回 hash 和 文件内容 +pub fn load_py_file(path: &PathBuf) -> std::io::Result<(Option, String)> { + let changed_time = get_change_time(&path); + let content = std::fs::read_to_string(path)?; + Ok((changed_time, content)) } pub fn init_py(config: &IcaConfig) { @@ -85,28 +140,14 @@ pub fn init_py(config: &IcaConfig) { pyo3::prepare_freethreaded_python(); if let Some(plugin_path) = &config.py_plugin_path { let path = PathBuf::from(plugin_path); - if path.exists() { - info!("finding plugins in: {:?}", path); - // 搜索所有的 py 文件 和 文件夹单层下面的 py 文件 - match path.read_dir() { - Err(e) => { - warn!("failed to read plugin path: {:?}", e); - } - Ok(dir) => { - for entry in dir { - if let Ok(entry) = entry { - let path = entry.path(); - if let Some(ext) = path.extension() { - if ext == "py" {} - } - } - } - } - } - } else { - warn!("plugin path not exists: {:?}", path); - } + load_py_plugins(&path); + debug!("python 插件列表: {:#?}", PyStatus::get_files()); } info!("python inited") } + +/// 执行 new message 的 python 插件 +pub fn new_message_py(message: &NewMessage, client: &Client) { + let msg = class::NewMessagePy::new(message); +}