Compare commits

...

2 Commits

Author SHA1 Message Date
2f535cc960
tailchat support p2 2024-03-30 12:54:52 +08:00
eaae60902d
tailchat support p1 2024-03-30 12:52:49 +08:00
13 changed files with 519 additions and 59 deletions

227
Cargo.lock generated
View File

@ -499,7 +499,26 @@ dependencies = [
"futures-core", "futures-core",
"futures-sink", "futures-sink",
"futures-util", "futures-util",
"http", "http 0.2.12",
"indexmap",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "h2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51ee2dd2e4f378392eeff5d51618cd9a63166a2513846bbc55f21cfacd9199d4"
dependencies = [
"bytes",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http 1.1.0",
"indexmap", "indexmap",
"slab", "slab",
"tokio", "tokio",
@ -542,6 +561,17 @@ dependencies = [
"itoa", "itoa",
] ]
[[package]]
name = "http"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]] [[package]]
name = "http-body" name = "http-body"
version = "0.4.6" version = "0.4.6"
@ -549,7 +579,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
dependencies = [ dependencies = [
"bytes", "bytes",
"http", "http 0.2.12",
"pin-project-lite",
]
[[package]]
name = "http-body"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
dependencies = [
"bytes",
"http 1.1.0",
]
[[package]]
name = "http-body-util"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d"
dependencies = [
"bytes",
"futures-core",
"http 1.1.0",
"http-body 1.0.0",
"pin-project-lite", "pin-project-lite",
] ]
@ -575,9 +628,9 @@ dependencies = [
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2", "h2 0.3.25",
"http", "http 0.2.12",
"http-body", "http-body 0.4.6",
"httparse", "httparse",
"httpdate", "httpdate",
"itoa", "itoa",
@ -589,6 +642,26 @@ dependencies = [
"want", "want",
] ]
[[package]]
name = "hyper"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a"
dependencies = [
"bytes",
"futures-channel",
"futures-util",
"h2 0.4.3",
"http 1.1.0",
"http-body 1.0.0",
"httparse",
"itoa",
"pin-project-lite",
"smallvec",
"tokio",
"want",
]
[[package]] [[package]]
name = "hyper-tls" name = "hyper-tls"
version = "0.5.0" version = "0.5.0"
@ -596,12 +669,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [ dependencies = [
"bytes", "bytes",
"hyper", "hyper 0.14.28",
"native-tls", "native-tls",
"tokio", "tokio",
"tokio-native-tls", "tokio-native-tls",
] ]
[[package]]
name = "hyper-tls"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
dependencies = [
"bytes",
"http-body-util",
"hyper 1.2.0",
"hyper-util",
"native-tls",
"tokio",
"tokio-native-tls",
"tower-service",
]
[[package]]
name = "hyper-util"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa"
dependencies = [
"bytes",
"futures-channel",
"futures-util",
"http 1.1.0",
"http-body 1.0.0",
"hyper 1.2.0",
"pin-project-lite",
"socket2",
"tokio",
"tower",
"tower-service",
"tracing",
]
[[package]] [[package]]
name = "iana-time-zone" name = "iana-time-zone"
version = "0.1.60" version = "0.1.60"
@ -638,6 +747,7 @@ dependencies = [
"hex", "hex",
"md-5", "md-5",
"pyo3", "pyo3",
"reqwest 0.12.2",
"rust_socketio", "rust_socketio",
"serde", "serde",
"serde_json", "serde_json",
@ -890,9 +1000,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.101" version = "0.9.102"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@ -935,6 +1045,26 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pin-project"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.13" version = "0.2.13"
@ -1118,11 +1248,11 @@ dependencies = [
"encoding_rs", "encoding_rs",
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2", "h2 0.3.25",
"http", "http 0.2.12",
"http-body", "http-body 0.4.6",
"hyper", "hyper 0.14.28",
"hyper-tls", "hyper-tls 0.5.0",
"ipnet", "ipnet",
"js-sys", "js-sys",
"log", "log",
@ -1149,6 +1279,48 @@ dependencies = [
"winreg", "winreg",
] ]
[[package]]
name = "reqwest"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d66674f2b6fb864665eea7a3c1ac4e3dfacd2fda83cf6f935a612e01b0e3338"
dependencies = [
"base64 0.21.7",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2 0.4.3",
"http 1.1.0",
"http-body 1.0.0",
"http-body-util",
"hyper 1.2.0",
"hyper-tls 0.6.0",
"hyper-util",
"ipnet",
"js-sys",
"log",
"mime",
"native-tls",
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls-pemfile",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
"system-configuration",
"tokio",
"tokio-native-tls",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"winreg",
]
[[package]] [[package]]
name = "rust_engineio" name = "rust_engineio"
version = "0.4.4" version = "0.4.4"
@ -1160,9 +1332,9 @@ dependencies = [
"base64 0.21.7", "base64 0.21.7",
"bytes", "bytes",
"futures-util", "futures-util",
"http", "http 0.2.12",
"native-tls", "native-tls",
"reqwest", "reqwest 0.11.27",
"serde", "serde",
"serde_json", "serde_json",
"thiserror", "thiserror",
@ -1657,6 +1829,28 @@ dependencies = [
"winnow", "winnow",
] ]
[[package]]
name = "tower"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
dependencies = [
"futures-core",
"futures-util",
"pin-project",
"pin-project-lite",
"tokio",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower-layer"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
[[package]] [[package]]
name = "tower-service" name = "tower-service"
version = "0.3.2" version = "0.3.2"
@ -1669,6 +1863,7 @@ version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [ dependencies = [
"log",
"pin-project-lite", "pin-project-lite",
"tracing-attributes", "tracing-attributes",
"tracing-core", "tracing-core",
@ -1736,7 +1931,7 @@ dependencies = [
"byteorder", "byteorder",
"bytes", "bytes",
"data-encoding", "data-encoding",
"http", "http 0.2.12",
"httparse", "httparse",
"log", "log",
"native-tls", "native-tls",

View File

@ -8,7 +8,7 @@ edition = "2021"
[features] [features]
default = ["ica", "tailchat"] default = ["ica", "tailchat"]
ica = ["dep:ed25519", "dep:ed25519-dalek", "dep:hex", "dep:rust_socketio"] ica = ["dep:ed25519", "dep:ed25519-dalek", "dep:hex", "dep:rust_socketio"]
tailchat = ["dep:rust_socketio", "dep:md-5"] tailchat = ["dep:rust_socketio", "dep:md-5", "dep:reqwest"]
[dependencies] [dependencies]
@ -18,6 +18,7 @@ ed25519-dalek = { version = "2.1", optional = true }
hex = { version = "0.4", optional = true } hex = { version = "0.4", optional = true }
# tailchat # tailchat
reqwest = { version = "0.12.2", optional = true }
md-5 = { version = "0.10.6", optional = true } md-5 = { version = "0.10.6", optional = true }
# ica & tailchat (socketio) # ica & tailchat (socketio)
@ -26,9 +27,9 @@ rust_socketio = { version = "0.4.4", features = ["async"], optional = true }
# data # data
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
chrono = "0.4.37" chrono = "0.4"
toml = "0.8.12" toml = "0.8"
colored = "2.1.0" colored = "2.1"
# runtime # runtime
tokio = { version = "1.37", features = ["full"] } tokio = { version = "1.37", features = ["full"] }

View File

@ -24,7 +24,6 @@ pub struct IcaConfig {
pub filter_list: Vec<i64>, pub filter_list: Vec<i64>,
} }
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
pub struct PyConfig { pub struct PyConfig {
/// 插件路径 /// 插件路径

View File

@ -1 +1,2 @@
pub mod ica; pub mod ica;
pub mod tailchat;

View File

@ -0,0 +1,6 @@
pub mod messages;
pub type GroupId = String;
pub type ConverseId = String;
pub type UserId = String;
pub type MessageId = String;

View 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,
}

View File

@ -4,6 +4,31 @@ pub type ClientResult<T, E> = Result<T, E>;
pub enum IcaError { pub enum IcaError {
/// Socket IO 链接错误 /// Socket IO 链接错误
SocketIoError(rust_socketio::error::Error), SocketIoError(rust_socketio::error::Error),
/// 登录失败
LoginFailed(String),
}
#[derive(Debug)]
pub enum TailchatError {
/// Socket IO 链接错误
SocketIoError(rust_socketio::error::Error),
/// 登录失败
LoginFailed(String),
}
#[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 { impl From<rust_socketio::Error> for IcaError {
@ -14,6 +39,35 @@ impl std::fmt::Display for IcaError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
IcaError::SocketIoError(e) => write!(f, "Socket IO 链接错误: {}", e), IcaError::SocketIoError(e) => write!(f, "Socket IO 链接错误: {}", e),
IcaError::LoginFailed(e) => write!(f, "登录失败: {}", e),
}
}
}
impl std::fmt::Display for TailchatError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TailchatError::SocketIoError(e) => write!(f, "Socket IO 链接错误: {}", e),
TailchatError::LoginFailed(e) => write!(f, "登录失败: {}", e),
}
}
}
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)
}
} }
} }
} }
@ -22,6 +76,27 @@ impl std::error::Error for IcaError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self { match self {
IcaError::SocketIoError(e) => Some(e), IcaError::SocketIoError(e) => Some(e),
IcaError::LoginFailed(_) => None,
}
}
}
impl std::error::Error for TailchatError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
TailchatError::SocketIoError(e) => Some(e),
TailchatError::LoginFailed(_) => None,
}
}
}
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(e, _, _) => Some(e),
} }
} }
} }

View File

@ -1,4 +1,5 @@
use crate::data_struct::ica::messages::{DeleteMessage, SendMessage}; use crate::data_struct::ica::messages::{DeleteMessage, SendMessage};
use crate::error::{ClientResult, IcaError};
use crate::MainStatus; use crate::MainStatus;
use colored::Colorize; use colored::Colorize;
@ -43,24 +44,24 @@ pub async fn delete_message(client: &Client, message: &DeleteMessage) -> bool {
// #[allow(dead_code)] // #[allow(dead_code)]
// pub async fn fetch_history(client: &Client, roomd_id: RoomId) -> bool { false } // pub async fn fetch_history(client: &Client, roomd_id: RoomId) -> bool { false }
pub async fn sign_callback(payload: Payload, client: Client) { async fn inner_sign(payload: Payload, client: Client) -> ClientResult<(), IcaError> {
let span = span!(Level::INFO, "signing icalingua"); let span = span!(Level::INFO, "signing icalingua");
let _guard = span.enter(); let _guard = span.enter();
// 获取数据 // 获取数据
let require_data = match payload { let require_data = match payload {
Payload::Text(json_value) => Some(json_value), Payload::Text(json_value) => Ok(json_value),
_ => None, _ => Err(IcaError::LoginFailed("Got a invalid payload".to_string())),
} }?;
.expect("Payload should be Json data");
let (auth_key, version) = (&require_data[0], &require_data[1]); let (auth_key, version) = (&require_data[0], &require_data[1]);
debug!("auth_key: {:?}, server_version: {:?}", auth_key, version); debug!("auth_key: {:?}, server_version: {:?}", auth_key, version);
let auth_key = match &require_data.first() { let auth_key = match &require_data.first() {
Some(Value::String(auth_key)) => Some(auth_key), Some(Value::String(auth_key)) => Ok(auth_key),
_ => None, _ => Err(IcaError::LoginFailed("Got a invalid auth_key".to_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");
// 签名 // 签名
@ -76,4 +77,11 @@ pub async fn sign_callback(payload: Payload, client: Client) {
// 发送签名 // 发送签名
let sign = signature.to_bytes().to_vec(); let sign = signature.to_bytes().to_vec();
client.emit("auth", sign).await.expect("Faild to send signin data"); client.emit("auth", sign).await.expect("Faild to send signin data");
Ok(())
}
/// 签名回调
/// 失败的时候得 panic
pub async fn sign_callback(payload: Payload, client: Client) {
inner_sign(payload, client).await.expect("Faild to sign");
} }

View File

@ -3,13 +3,14 @@ use std::time::Duration;
mod config; mod config;
mod data_struct; mod data_struct;
mod error; mod error;
#[cfg(feature = "ica")]
mod ica;
// #[cfg(feature = "tailchat")]
// mod tailchat;
mod py; mod py;
mod status; mod status;
#[cfg(feature = "ica")]
mod ica;
#[cfg(feature = "tailchat")]
mod tailchat;
use config::BotConfig; use config::BotConfig;
use tracing::{event, info, span, Level}; use tracing::{event, info, span, Level};

View File

@ -4,12 +4,15 @@ use pyo3::prelude::*;
use rust_socketio::asynchronous::Client; use rust_socketio::asynchronous::Client;
use tracing::{debug, info, warn}; use tracing::{debug, info, warn};
use crate::data_struct::ica::messages::NewMessage; use crate::data_struct::{ica, tailchat};
use crate::data_struct::ica::MessageId; use crate::error::PyPluginError;
use crate::py::{class, PyPlugin, PyStatus}; use crate::py::{class, PyPlugin, PyStatus};
use crate::MainStatus; 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. 有这个函数 // 1. 有这个函数
// 2. 没有这个函数 // 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) { match py_module.getattr(name) {
Ok(func) => { Ok(func) => {
if func.is_callable() { if func.is_callable() {
Some(func) Ok(func)
} else { } else {
warn!("function<{}>: {:#?} in {:?} is not callable", name, func, path); // warn!("function<{}>: {:#?} in {:?} is not callable", name, func, path);
None Err(PyPluginError::FuncNotCallable(
name.to_string(),
py_module.getattr("__name__").unwrap().extract::<String>().unwrap(),
))
} }
} }
Err(e) => { Err(e) => {
warn!("failed to get function<{}> from {:?}: {:?}", name, path, e); // warn!("failed to get function<{}> from {:?}: {:?}", name, path, e);
None Err(PyPluginError::CouldNotGetFunc(
e,
name.to_string(),
py_module.getattr("__name__").unwrap().extract::<String>().unwrap(),
))
} }
} }
} else { } else {
debug!("no function<{}> in module {:?}", name, path); // debug!("no function<{}> in module {:?}", name, path);
None Err(PyPluginError::FuncNotFound(
name.to_string(),
py_module.getattr("__name__").unwrap().extract::<String>().unwrap(),
))
} }
} }
Err(e) => { Err(e) => {
warn!("failed to check function<{}> from {:?}: {:?}", name, path, e); // warn!("failed to check function<{}> from {:?}: {:?}", name, path, e);
None Err(PyPluginError::CouldNotGetFunc(
e,
name.to_string(),
py_module.getattr("__name__").unwrap().extract::<String>().unwrap(),
))
} }
} }
} }
@ -78,8 +95,14 @@ pub const ICA_DELETE_MESSAGE_FUNC: &str = "on_ica_delete_message";
pub const TAILCHAT_NEW_MESSAGE_FUNC: &str = "on_tailchat_message"; pub const TAILCHAT_NEW_MESSAGE_FUNC: &str = "on_tailchat_message";
macro_rules! call_py_func {
($args:expr, $func_name:expr, $client:expr) => {
};
}
/// 执行 new message 的 python 插件 /// 执行 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(); verify_plugins();
@ -91,10 +114,13 @@ pub async fn ica_new_message_py(message: &NewMessage, client: &Client) {
// 甚至实际上压根不需要await这个spawn, 直接让他自己跑就好了(离谱) // 甚至实际上压根不需要await这个spawn, 直接让他自己跑就好了(离谱)
tokio::spawn(async move { tokio::spawn(async move {
Python::with_gil(|py| { Python::with_gil(|py| {
if let Some(py_func) = if let Ok(py_func) = get_func(plugin.py_module.bind(py), ICA_NEW_MESSAGE_FUNC) {
get_func(plugin.py_module.as_ref(py), path, ICA_NEW_MESSAGE_FUNC)
{
if let Err(e) = py_func.call1(args) { if let Err(e) = py_func.call1(args) {
let e = PyPluginError::FuncCallError(
e,
ICA_NEW_MESSAGE_FUNC.to_string(),
path.to_string_lossy().to_string(),
);
warn!("failed to call function<{}>: {:?}", ICA_NEW_MESSAGE_FUNC, e); warn!("failed to call function<{}>: {:?}", ICA_NEW_MESSAGE_FUNC, e);
} }
} }
@ -103,7 +129,7 @@ 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(); verify_plugins();
let plugins = PyStatus::get_files(); let plugins = PyStatus::get_files();
@ -113,10 +139,13 @@ pub async fn ica_delete_message_py(msg_id: MessageId, client: &Client) {
let args = (msg_id.clone(), client); let args = (msg_id.clone(), client);
tokio::spawn(async move { tokio::spawn(async move {
Python::with_gil(|py| { Python::with_gil(|py| {
if let Some(py_func) = if let Ok(py_func) = get_func(plugin.py_module.bind(py), ICA_DELETE_MESSAGE_FUNC) {
get_func(plugin.py_module.as_ref(py), path, ICA_DELETE_MESSAGE_FUNC)
{
if let Err(e) = py_func.call1(args) { if let Err(e) = py_func.call1(args) {
let e = PyPluginError::FuncCallError(
e,
ICA_DELETE_MESSAGE_FUNC.to_string(),
path.to_string_lossy().to_string(),
);
warn!("failed to call function<{}>: {:?}", ICA_DELETE_MESSAGE_FUNC, e); warn!("failed to call function<{}>: {:?}", ICA_DELETE_MESSAGE_FUNC, e);
} }
} }
@ -124,3 +153,7 @@ pub async fn ica_delete_message_py(msg_id: MessageId, client: &Client) {
}); });
} }
} }
pub async fn tailchat_new_message_py(message: tailchat::messages::ReciveMessage, client: &Client) {
}

View File

@ -68,8 +68,8 @@ impl TryFrom<RawPyPlugin> for PyPlugin {
}; };
let py_module = py_module.unwrap(); let py_module = py_module.unwrap();
Python::with_gil(|py| { Python::with_gil(|py| {
let module = py_module.as_ref(py); let module = py_module.bind(py);
if let Some(config_func) = call::get_func(module, &path, "on_config") { if let Ok(config_func) = call::get_func(module, "on_config") {
match config_func.call0() { match config_func.call0() {
Ok(config) => { Ok(config) => {
if config.is_instance_of::<PyTuple>() { if config.is_instance_of::<PyTuple>() {
@ -96,8 +96,7 @@ impl TryFrom<RawPyPlugin> for PyPlugin {
}; };
match config_value { match config_value {
Ok(config) => { Ok(config) => {
let py_config = class::ConfigDataPy::new(config); let py_config = Bound::new(py, class::ConfigDataPy::new(config)).unwrap();
let py_config = PyCell::new(py, py_config).unwrap();
module.setattr("CONFIG_DATA", py_config).unwrap(); module.setattr("CONFIG_DATA", py_config).unwrap();
Ok(PyPlugin { Ok(PyPlugin {
file_path: path, 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>> { pub fn py_module_from_code(content: &str, path: &Path) -> PyResult<Py<PyAny>> {
Python::with_gil(|py| -> 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, py,
content, content,
&path.to_string_lossy(), &path.to_string_lossy(),

23
ica-rs/src/tailchat.rs Normal file
View File

@ -0,0 +1,23 @@
pub mod events;
use futures_util::FutureExt;
use rust_socketio::asynchronous::{Client, ClientBuilder};
use rust_socketio::{Event, Payload, TransportType};
use tracing::{event, span, Level};
// use crate::config::IcaConfig;
use crate::error::{ClientResult, TailchatError};
pub async fn start_tailchat() -> ClientResult<(), TailchatError> {
let span = span!(Level::INFO, "Tailchat Client");
let _enter = span.enter();
event!(Level::INFO, "tailchat-async-rs v{} initing", crate::TAILCHAT_VERSION);
// let socket = match ClientBuilder::new() {
// };
Ok(())
}

View File