mirror of
http://shenjack.top:5100/shenjack/icalingua-python-bot.git
synced 2024-11-23 04:31:05 +08:00
tailchat support p1
This commit is contained in:
parent
6dfbc4e879
commit
eaae60902d
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -890,9 +890,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
|||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.101"
|
||||
version = "0.9.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff"
|
||||
checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
|
|
|
@ -26,9 +26,9 @@ rust_socketio = { version = "0.4.4", features = ["async"], optional = true }
|
|||
# data
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
chrono = "0.4.37"
|
||||
toml = "0.8.12"
|
||||
colored = "2.1.0"
|
||||
chrono = "0.4"
|
||||
toml = "0.8"
|
||||
colored = "2.1"
|
||||
|
||||
# runtime
|
||||
tokio = { version = "1.37", features = ["full"] }
|
||||
|
|
|
@ -24,7 +24,6 @@ pub struct IcaConfig {
|
|||
pub filter_list: Vec<i64>,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct PyConfig {
|
||||
/// 插件路径
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
pub mod ica;
|
||||
pub mod tailchat;
|
||||
|
|
6
ica-rs/src/data_struct/tailchat.rs
Normal file
6
ica-rs/src/data_struct/tailchat.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
pub mod messages;
|
||||
|
||||
pub type GroupId = String;
|
||||
pub type ConverseId = String;
|
||||
pub type UserId = String;
|
||||
pub type MessageId = String;
|
119
ica-rs/src/data_struct/tailchat/messages.rs
Normal file
119
ica-rs/src/data_struct/tailchat/messages.rs
Normal file
|
@ -0,0 +1,119 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value as JsonValue};
|
||||
|
||||
use crate::data_struct::tailchat::{ConverseId, GroupId, MessageId, UserId};
|
||||
|
||||
/*{'_id': '6606b3075163504389a6fc47',
|
||||
'content': '光速!(',
|
||||
'author': '6602e20d7b8d10675758e36b',
|
||||
'groupId': '6602e331d31fd31b04aa0693',
|
||||
'converseId': '6602f785928c4254f17726b2',
|
||||
'hasRecall': False,
|
||||
'meta': {'mentions': []},
|
||||
'reactions': [],
|
||||
'createdAt': ExtType(code=0, data=b'\x00\x00\x01\x8e\x8a+TJ'),
|
||||
'updatedAt': ExtType(code=0, data=b'\x00\x00\x01\x8e\x8a+TJ'),
|
||||
'__v': 0} */
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ReciveMessage {
|
||||
/// 消息ID
|
||||
#[serde(rename = "_id")]
|
||||
pub msg_id: MessageId,
|
||||
/// 消息内容
|
||||
pub content: String,
|
||||
/// 发送者ID
|
||||
#[serde(rename = "author")]
|
||||
pub sender_id: UserId,
|
||||
/// 服务器ID
|
||||
#[serde(rename = "groupId")]
|
||||
pub group_id: GroupId,
|
||||
/// 会话ID
|
||||
#[serde(rename = "converseId")]
|
||||
pub converse_id: ConverseId,
|
||||
/// 是否有回复?
|
||||
#[serde(rename = "hasRecall")]
|
||||
pub has_recall: bool,
|
||||
/// 暂时懒得解析这玩意
|
||||
pub meta: JsonValue,
|
||||
/// 也懒得解析这玩意
|
||||
pub reactions: Vec<JsonValue>,
|
||||
/// 创建时间
|
||||
#[serde(rename = "createdAt")]
|
||||
pub created_at: JsonValue,
|
||||
/// 更新时间
|
||||
#[serde(rename = "updatedAt")]
|
||||
pub updated_at: JsonValue,
|
||||
/// 未知
|
||||
#[serde(rename = "__v")]
|
||||
pub v: JsonValue,
|
||||
}
|
||||
|
||||
impl ReciveMessage {
|
||||
pub fn as_reply(&self) -> ReplyMessage {
|
||||
ReplyMessage {
|
||||
content: self.content.clone(),
|
||||
converse_id: self.converse_id.clone(),
|
||||
group_id: self.group_id.clone(),
|
||||
reply_id: self.msg_id.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SendingMessage {
|
||||
/// 消息内容
|
||||
pub content: String,
|
||||
/// 会话ID
|
||||
#[serde(rename = "converseId")]
|
||||
pub converse_id: ConverseId,
|
||||
/// 服务器ID
|
||||
#[serde(rename = "groupId")]
|
||||
pub group_id: GroupId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ReplyMeta {
|
||||
/// 被回复的人的ID (可以是多个?)
|
||||
pub mentions: Vec<UserId>,
|
||||
/// 被回复的消息ID
|
||||
pub reply_id: MessageId,
|
||||
/// 被回复的消息的发送者ID
|
||||
pub reply_author: UserId,
|
||||
/// 被回复的消息内容
|
||||
pub reply_content: String,
|
||||
}
|
||||
|
||||
impl Serialize for ReplyMeta {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::ser::Serializer,
|
||||
{
|
||||
let reply = json! {
|
||||
{
|
||||
"replyId": self.reply_id,
|
||||
"replyAuthor": self.reply_author,
|
||||
"replyContent": self.reply_content,
|
||||
}
|
||||
};
|
||||
let mut map = serde_json::Map::new();
|
||||
map.insert("mentions".to_string(), serde_json::to_value(&self.mentions).unwrap());
|
||||
map.insert("reply".to_string(), reply);
|
||||
map.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ReplyMessage {
|
||||
/// 消息内容
|
||||
pub content: String,
|
||||
/// 会话ID
|
||||
#[serde(rename = "converseId")]
|
||||
pub converse_id: ConverseId,
|
||||
/// 服务器ID
|
||||
#[serde(rename = "groupId")]
|
||||
pub group_id: GroupId,
|
||||
/// 回复的消息ID
|
||||
#[serde(rename = "replyId")]
|
||||
pub reply_id: MessageId,
|
||||
}
|
|
@ -6,6 +6,21 @@ pub enum IcaError {
|
|||
SocketIoError(rust_socketio::error::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PyPluginError {
|
||||
/// 插件内未找到指定函数
|
||||
/// 函数名, 模块名
|
||||
FuncNotFound(String, String),
|
||||
/// 插件内函数获取错误
|
||||
/// pyerr, func_name, module_name
|
||||
CouldNotGetFunc(pyo3::PyErr, String, String),
|
||||
/// 插件内函数不可调用
|
||||
FuncNotCallable(String, String),
|
||||
/// 插件内函数调用错误
|
||||
/// pyerr, func_name, module_name
|
||||
FuncCallError(pyo3::PyErr, String, String),
|
||||
}
|
||||
|
||||
impl From<rust_socketio::Error> for IcaError {
|
||||
fn from(e: rust_socketio::Error) -> Self { IcaError::SocketIoError(e) }
|
||||
}
|
||||
|
@ -18,6 +33,25 @@ impl std::fmt::Display for IcaError {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for PyPluginError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
PyPluginError::FuncNotFound(name, module) => {
|
||||
write!(f, "插件内未找到函数: {} in {}", name, module)
|
||||
}
|
||||
PyPluginError::CouldNotGetFunc(py_err, name, module) => {
|
||||
write!(f, "插件内函数获取错误: {:#?}|{} in {}", py_err, name, module)
|
||||
}
|
||||
PyPluginError::FuncNotCallable(name, module) => {
|
||||
write!(f, "插件内函数不可调用: {} in {}", name, module)
|
||||
}
|
||||
PyPluginError::FuncCallError(py_err, name, module) => {
|
||||
write!(f, "插件内函数调用错误: {:#?}|{} in {}", py_err, name, module)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for IcaError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
|
@ -25,3 +59,14 @@ impl std::error::Error for IcaError {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for PyPluginError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
PyPluginError::FuncNotFound(_, _) => None,
|
||||
PyPluginError::CouldNotGetFunc(e, _, _) => Some(e),
|
||||
PyPluginError::FuncNotCallable(_, _) => None,
|
||||
PyPluginError::FuncCallError(_, _) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,13 +3,14 @@ use std::time::Duration;
|
|||
mod config;
|
||||
mod data_struct;
|
||||
mod error;
|
||||
#[cfg(feature = "ica")]
|
||||
mod ica;
|
||||
// #[cfg(feature = "tailchat")]
|
||||
// mod tailchat;
|
||||
mod py;
|
||||
mod status;
|
||||
|
||||
#[cfg(feature = "ica")]
|
||||
mod ica;
|
||||
#[cfg(feature = "tailchat")]
|
||||
mod tailchat;
|
||||
|
||||
use config::BotConfig;
|
||||
use tracing::{event, info, span, Level};
|
||||
|
||||
|
|
|
@ -4,12 +4,15 @@ 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::data_struct::{ica, tailchat};
|
||||
use crate::error::PyPluginError;
|
||||
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> {
|
||||
pub fn get_func<'py>(
|
||||
py_module: &Bound<'py, PyAny>,
|
||||
name: &'py str,
|
||||
) -> Result<Bound<'py, PyAny>, PyPluginError> {
|
||||
// 要处理的情况:
|
||||
// 1. 有这个函数
|
||||
// 2. 没有这个函数
|
||||
|
@ -20,25 +23,39 @@ pub fn get_func<'py>(py_module: &'py PyAny, path: &PathBuf, name: &'py str) -> O
|
|||
match py_module.getattr(name) {
|
||||
Ok(func) => {
|
||||
if func.is_callable() {
|
||||
Some(func)
|
||||
Ok(func)
|
||||
} else {
|
||||
warn!("function<{}>: {:#?} in {:?} is not callable", name, func, path);
|
||||
None
|
||||
// warn!("function<{}>: {:#?} in {:?} is not callable", name, func, path);
|
||||
Err(PyPluginError::FuncNotCallable(
|
||||
name.to_string(),
|
||||
py_module.getattr("__name__").unwrap().extract::<String>().unwrap(),
|
||||
))
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("failed to get function<{}> from {:?}: {:?}", name, path, e);
|
||||
None
|
||||
// warn!("failed to get function<{}> from {:?}: {:?}", name, path, e);
|
||||
Err(PyPluginError::CouldNotGetFunc(
|
||||
e,
|
||||
name.to_string(),
|
||||
py_module.getattr("__name__").unwrap().extract::<String>().unwrap(),
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debug!("no function<{}> in module {:?}", name, path);
|
||||
None
|
||||
// debug!("no function<{}> in module {:?}", name, path);
|
||||
Err(PyPluginError::FuncNotFound(
|
||||
name.to_string(),
|
||||
py_module.getattr("__name__").unwrap().extract::<String>().unwrap(),
|
||||
))
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("failed to check function<{}> from {:?}: {:?}", name, path, e);
|
||||
None
|
||||
// warn!("failed to check function<{}> from {:?}: {:?}", name, path, e);
|
||||
Err(PyPluginError::CouldNotGetFunc(
|
||||
e,
|
||||
name.to_string(),
|
||||
py_module.getattr("__name__").unwrap().extract::<String>().unwrap(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,23 +96,22 @@ pub const ICA_DELETE_MESSAGE_FUNC: &str = "on_ica_delete_message";
|
|||
pub const TAILCHAT_NEW_MESSAGE_FUNC: &str = "on_tailchat_message";
|
||||
|
||||
/// 执行 new message 的 python 插件
|
||||
pub async fn ica_new_message_py(message: &NewMessage, client: &Client) {
|
||||
pub async fn ica_new_message_py(message: &ica::messages::NewMessage, client: &Client) {
|
||||
// 验证插件是否改变
|
||||
verify_plugins();
|
||||
|
||||
let plugins = PyStatus::get_files();
|
||||
for (path, plugin) in plugins.iter() {
|
||||
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 Ok(py_func) = get_func(plugin.py_module.bind(py), ICA_NEW_MESSAGE_FUNC) {
|
||||
if let Err(e) = py_func.call1(args) {
|
||||
warn!("failed to call function<{}>: {:?}", ICA_NEW_MESSAGE_FUNC, e);
|
||||
let e = PyPluginError::FuncCallError(e, ICA_NEW_MESSAGE_FUNC.to_string());
|
||||
// warn!("failed to call function<{}>: {:?}", ICA_NEW_MESSAGE_FUNC, e);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -103,19 +119,17 @@ pub async fn ica_new_message_py(message: &NewMessage, client: &Client) {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn ica_delete_message_py(msg_id: MessageId, client: &Client) {
|
||||
pub async fn ica_delete_message_py(msg_id: ica::MessageId, client: &Client) {
|
||||
verify_plugins();
|
||||
|
||||
let plugins = PyStatus::get_files();
|
||||
for (path, plugin) in plugins.iter() {
|
||||
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 Ok(py_func) = get_func(plugin.py_module.bind(py), ICA_DELETE_MESSAGE_FUNC) {
|
||||
if let Err(e) = py_func.call1(args) {
|
||||
warn!("failed to call function<{}>: {:?}", ICA_DELETE_MESSAGE_FUNC, e);
|
||||
}
|
||||
|
@ -124,3 +138,5 @@ pub async fn ica_delete_message_py(msg_id: MessageId, client: &Client) {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
// pub async fn tailchat_new_message_py(message: )
|
||||
|
|
|
@ -68,8 +68,8 @@ impl TryFrom<RawPyPlugin> for PyPlugin {
|
|||
};
|
||||
let py_module = py_module.unwrap();
|
||||
Python::with_gil(|py| {
|
||||
let module = py_module.as_ref(py);
|
||||
if let Some(config_func) = call::get_func(module, &path, "on_config") {
|
||||
let module = py_module.bind(py);
|
||||
if let Ok(config_func) = call::get_func(module, "on_config") {
|
||||
match config_func.call0() {
|
||||
Ok(config) => {
|
||||
if config.is_instance_of::<PyTuple>() {
|
||||
|
@ -96,8 +96,7 @@ impl TryFrom<RawPyPlugin> for PyPlugin {
|
|||
};
|
||||
match config_value {
|
||||
Ok(config) => {
|
||||
let py_config = class::ConfigDataPy::new(config);
|
||||
let py_config = PyCell::new(py, py_config).unwrap();
|
||||
let py_config = Bound::new(py, class::ConfigDataPy::new(config)).unwrap();
|
||||
module.setattr("CONFIG_DATA", py_config).unwrap();
|
||||
Ok(PyPlugin {
|
||||
file_path: path,
|
||||
|
@ -228,7 +227,7 @@ pub fn get_change_time(path: &Path) -> Option<SystemTime> { path.metadata().ok()
|
|||
|
||||
pub fn py_module_from_code(content: &str, path: &Path) -> PyResult<Py<PyAny>> {
|
||||
Python::with_gil(|py| -> PyResult<Py<PyAny>> {
|
||||
let module: PyResult<Py<PyAny>> = PyModule::from_code(
|
||||
let module: PyResult<Py<PyAny>> = PyModule::from_code_bound(
|
||||
py,
|
||||
content,
|
||||
&path.to_string_lossy(),
|
||||
|
|
1
ica-rs/src/tailchat.rs
Normal file
1
ica-rs/src/tailchat.rs
Normal file
|
@ -0,0 +1 @@
|
|||
|
Loading…
Reference in New Issue
Block a user