mirror of
http://shenjack.top:5100/shenjack/icalingua-python-bot.git
synced 2024-11-23 12:41:05 +08:00
好歹过编译+test了……
This commit is contained in:
parent
9ce466f000
commit
974368166b
|
@ -20,12 +20,13 @@ colored = "2.1.0"
|
||||||
|
|
||||||
tokio = { version = "1.0", features = ["full"] }
|
tokio = { version = "1.0", features = ["full"] }
|
||||||
futures-util = "0.3.30"
|
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 = "0.1.40"
|
||||||
tracing-subscriber = { version = "0.3.18", features = ["time"] }
|
tracing-subscriber = { version = "0.3.18", features = ["time"] }
|
||||||
|
|
||||||
[dependencies.pyo3]
|
|
||||||
version = "0.20.2"
|
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
rust_socketio = { git = "https://github.com/shenjackyuanjie/rust-socketio.git", branch = "mult_payload" }
|
rust_socketio = { git = "https://github.com/shenjackyuanjie/rust-socketio.git", branch = "mult_payload" }
|
||||||
|
# pyo3 = { git = "https://github.com/PyO3/pyo3.git", branch = "main" }
|
||||||
|
|
|
@ -44,3 +44,9 @@ class SendMessage:
|
||||||
class NewMessage:
|
class NewMessage:
|
||||||
def reply_with(self, message: str) -> SendMessage:
|
def reply_with(self, message: str) -> SendMessage:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class IcaClient:
|
||||||
|
@staticmethod
|
||||||
|
async def send_message(client: "IcaClient", message: SendMessage) -> bool:
|
||||||
|
...
|
||||||
|
|
|
@ -11,11 +11,17 @@ use serde_json::Value;
|
||||||
use tracing::{debug, warn};
|
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();
|
let value = message.as_value();
|
||||||
match client.emit("sendMessage", value).await {
|
match client.emit("sendMessage", value).await {
|
||||||
Ok(_) => debug!("send_message {}", format!("{:#?}", message).cyan()),
|
Ok(_) => {
|
||||||
Err(e) => warn!("send_message faild:{}", format!("{:#?}", e).red()),
|
debug!("send_message {}", format!("{:#?}", message).cyan());
|
||||||
|
true
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
warn!("send_message faild:{}", format!("{:#?}", e).red());
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ 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-async-rs pong v{}", VERSION));
|
let reply = message.reply_with(&format!("ica-async-rs pong v{}", VERSION));
|
||||||
send_message(client, reply).await;
|
send_message(&client, &reply).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
use rust_socketio::asynchronous::Client;
|
||||||
|
|
||||||
|
use crate::client::send_message;
|
||||||
use crate::data_struct::messages::{NewMessage, ReplyMessage, SendMessage};
|
use crate::data_struct::messages::{NewMessage, ReplyMessage, SendMessage};
|
||||||
use crate::ClientStatus;
|
use crate::ClientStatus;
|
||||||
|
|
||||||
|
@ -137,6 +139,7 @@ impl ReplyMessagePy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
#[pyo3(name = "SendMessage")]
|
#[pyo3(name = "SendMessage")]
|
||||||
pub struct SendMessagePy {
|
pub struct SendMessagePy {
|
||||||
|
@ -148,3 +151,36 @@ impl SendMessagePy {
|
||||||
Self { msg }
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,20 +1,22 @@
|
||||||
pub mod class;
|
pub mod class;
|
||||||
|
|
||||||
|
use std::time::SystemTime;
|
||||||
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 rust_socketio::asynchronous::Client;
|
||||||
use tracing::{debug, info, warn};
|
use tracing::{debug, info, warn};
|
||||||
|
|
||||||
use crate::config::IcaConfig;
|
use crate::config::IcaConfig;
|
||||||
|
use crate::data_struct::messages::NewMessage;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PyStatus {
|
pub struct PyStatus {
|
||||||
pub files: Option<HashMap<PathBuf, (Vec<u8>, String)>>,
|
pub files: Option<HashMap<PathBuf, (Option<SystemTime>, Py<PyAny>)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PyStatus {
|
impl PyStatus {
|
||||||
pub fn get_files() -> &'static HashMap<PathBuf, (Vec<u8>, String)> {
|
pub fn get_files() -> &'static HashMap<PathBuf, (Option<SystemTime>, Py<PyAny>)> {
|
||||||
unsafe {
|
unsafe {
|
||||||
match PYSTATUS.files.as_ref() {
|
match PYSTATUS.files.as_ref() {
|
||||||
Some(files) => files,
|
Some(files) => files,
|
||||||
|
@ -27,26 +29,26 @@ impl PyStatus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_file(path: PathBuf, content: Vec<u8>, hash: String) {
|
pub fn add_file(path: PathBuf, changed_time: Option<SystemTime>, py_module: Py<PyAny>) {
|
||||||
unsafe {
|
unsafe {
|
||||||
match PYSTATUS.files.as_mut() {
|
match PYSTATUS.files.as_mut() {
|
||||||
Some(files) => {
|
Some(files) => {
|
||||||
files.insert(path, (content, hash));
|
files.insert(path, (changed_time, py_module));
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let mut files = HashMap::new();
|
let mut files = HashMap::new();
|
||||||
files.insert(path, (content, hash));
|
files.insert(path, (changed_time, py_module));
|
||||||
PYSTATUS.files = Some(files);
|
PYSTATUS.files = Some(files);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_file(path: &PathBuf, hash: &String) -> bool {
|
pub fn verify_file(path: &PathBuf, change_time: &Option<SystemTime>) -> 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((changed_time, _)) => change_time == changed_time,
|
||||||
None => false,
|
None => false,
|
||||||
},
|
},
|
||||||
None => false,
|
None => false,
|
||||||
|
@ -72,12 +74,65 @@ pub fn run() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_py_file(path: &PathBuf) -> (Vec<u8>, String) {
|
pub fn load_py_plugins(path: &PathBuf) {
|
||||||
let mut hasher = Hasher::new();
|
if path.exists() {
|
||||||
let content = std::fs::read(path).unwrap();
|
info!("finding plugins in: {:?}", path);
|
||||||
hasher.update(&content);
|
// 搜索所有的 py 文件 和 文件夹单层下面的 py 文件
|
||||||
let hash = hasher.finalize().as_bytes().to_vec();
|
match path.read_dir() {
|
||||||
(content, hex::encode(hash))
|
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<PyAny> {
|
||||||
|
let module: Py<PyAny> = 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<SystemTime> {
|
||||||
|
path.metadata().ok()?.modified().ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 传入文件路径
|
||||||
|
/// 返回 hash 和 文件内容
|
||||||
|
pub fn load_py_file(path: &PathBuf) -> std::io::Result<(Option<SystemTime>, 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) {
|
pub fn init_py(config: &IcaConfig) {
|
||||||
|
@ -85,28 +140,14 @@ pub fn init_py(config: &IcaConfig) {
|
||||||
pyo3::prepare_freethreaded_python();
|
pyo3::prepare_freethreaded_python();
|
||||||
if let Some(plugin_path) = &config.py_plugin_path {
|
if let Some(plugin_path) = &config.py_plugin_path {
|
||||||
let path = PathBuf::from(plugin_path);
|
let path = PathBuf::from(plugin_path);
|
||||||
if path.exists() {
|
load_py_plugins(&path);
|
||||||
info!("finding plugins in: {:?}", path);
|
debug!("python 插件列表: {:#?}", PyStatus::get_files());
|
||||||
// 搜索所有的 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("python inited")
|
info!("python inited")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 执行 new message 的 python 插件
|
||||||
|
pub fn new_message_py(message: &NewMessage, client: &Client) {
|
||||||
|
let msg = class::NewMessagePy::new(message);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user