socket-bot/ica-rs/src/py/call.rs
2024-03-16 16:58:18 +08:00

146 lines
4.8 KiB
Rust

use std::path::PathBuf;
use pyo3::prelude::*;
use rust_socketio::asynchronous::Client;
use tracing::{debug, info, warn};
use crate::data_struct::ica::messages::NewMessage;
use crate::data_struct::ica::MessageId;
use crate::py::{class, PyPlugin, PyStatus};
use crate::MainStatus;
pub fn get_func<'py>(py_module: &'py PyAny, path: &PathBuf, name: &'py str) -> Option<&'py PyAny> {
// 要处理的情况:
// 1. 有这个函数
// 2. 没有这个函数
// 3. 函数不是 Callable
match py_module.hasattr(name) {
Ok(contain) => {
if contain {
match py_module.getattr(name) {
Ok(func) => {
if func.is_callable() {
Some(func)
} else {
warn!("function<{}>: {:#?} in {:?} is not callable", name, func, path);
None
}
}
Err(e) => {
warn!("failed to get function<{}> from {:?}: {:?}", name, path, e);
None
}
}
} else {
debug!("no function<{}> in module {:?}", name, path);
None
}
}
Err(e) => {
warn!("failed to check function<{}> from {:?}: {:?}", name, path, e);
None
}
}
}
pub fn verify_plugins() {
let mut need_reload_files: Vec<PathBuf> = Vec::new();
let plugin_path = MainStatus::global_config().py().plugin_path.clone();
for entry in std::fs::read_dir(plugin_path).unwrap().flatten() {
let path = entry.path();
if let Some(ext) = path.extension() {
if ext == "py" && !PyStatus::verify_file(&path) {
need_reload_files.push(path);
}
}
}
if need_reload_files.is_empty() {
return;
}
info!("file change list: {:?}", need_reload_files);
for reload_file in need_reload_files {
match PyPlugin::new_from_path(&reload_file) {
Some(plugin) => {
PyStatus::add_file(reload_file.clone(), plugin);
info!("重载 Python 插件: {:?}", reload_file);
}
None => {
warn!("重载 Python 插件: {:?} 失败", reload_file);
}
}
}
}
pub const ICA_NEW_MESSAGE_FUNC: &str = "on_ica_message";
pub const ICA_DELETE_MESSAGE_FUNC: &str = "on_ica_delete_message";
pub const MATRIX_NEW_MESSAGE_FUNC: &str = "on_matrix_message";
/// 执行 new message 的 python 插件
pub async fn ica_new_message_py(message: &NewMessage, client: &Client) {
// 验证插件是否改变
verify_plugins();
let plugins = PyStatus::get_files();
for (path, plugin) in plugins.iter() {
let msg = class::ica::NewMessagePy::new(message);
let client = class::ica::IcaClientPy::new(client);
let args = (msg, client);
// 甚至实际上压根不需要await这个spawn, 直接让他自己跑就好了(离谱)
tokio::spawn(async move {
Python::with_gil(|py| {
if let Some(py_func) =
get_func(plugin.py_module.as_ref(py), path, ICA_NEW_MESSAGE_FUNC)
{
if let Err(e) = py_func.call1(args) {
warn!("failed to call function<{}>: {:?}", ICA_NEW_MESSAGE_FUNC, e);
}
}
})
});
}
}
pub async fn ica_delete_message_py(msg_id: MessageId, client: &Client) {
verify_plugins();
let plugins = PyStatus::get_files();
for (path, plugin) in plugins.iter() {
let msg_id = msg_id.clone();
let client = class::ica::IcaClientPy::new(client);
let args = (msg_id.clone(), client);
tokio::spawn(async move {
Python::with_gil(|py| {
if let Some(py_func) =
get_func(plugin.py_module.as_ref(py), path, ICA_DELETE_MESSAGE_FUNC)
{
if let Err(e) = py_func.call1(args) {
warn!("failed to call function<{}>: {:?}", ICA_DELETE_MESSAGE_FUNC, e);
}
}
})
});
}
}
pub async fn matrix_new_message_py() {
verify_plugins();
let plugins = PyStatus::get_files();
for (path, plugin) in plugins.iter() {
tokio::spawn(async move {
Python::with_gil(|py| {
if let Some(py_func) =
get_func(plugin.py_module.as_ref(py), path, MATRIX_NEW_MESSAGE_FUNC)
{
if let Err(e) = py_func.call0() {
warn!("failed to call function<{}>: {:?}", MATRIX_NEW_MESSAGE_FUNC, e);
}
}
})
});
}
}