This commit is contained in:
shenjack 2024-06-15 00:35:21 +08:00
parent a606db5cb0
commit ca9033a23f
Signed by: shenjack
GPG Key ID: 7B1134A979775551
11 changed files with 81 additions and 73 deletions

1
.gitignore vendored
View File

@ -3,6 +3,7 @@ env*
config.toml config.toml
.idea
*.pyc *.pyc
*__pycache__/ *__pycache__/

View File

@ -6,7 +6,7 @@ use serde_json::{json, Value as JsonValue};
use crate::data_struct::tailchat::{ConverseId, GroupId, MessageId, UserId}; use crate::data_struct::tailchat::{ConverseId, GroupId, MessageId, UserId};
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ReciveMessage { pub struct ReceiveMessage {
/// 消息ID /// 消息ID
#[serde(rename = "_id")] #[serde(rename = "_id")]
pub msg_id: MessageId, pub msg_id: MessageId,
@ -27,7 +27,7 @@ pub struct ReciveMessage {
pub has_recall: bool, pub has_recall: bool,
/// 暂时懒得解析这玩意 /// 暂时懒得解析这玩意
/// 准确来说是不确定内容, 毕竟没细看 API /// 准确来说是不确定内容, 毕竟没细看 API
pub meta: JsonValue, pub meta: Option<JsonValue>,
/// 也懒得解析这玩意 /// 也懒得解析这玩意
pub reactions: Vec<JsonValue>, pub reactions: Vec<JsonValue>,
/// 创建时间 /// 创建时间
@ -38,8 +38,14 @@ pub struct ReciveMessage {
pub updated_at: String, pub updated_at: String,
} }
impl ReciveMessage { impl ReceiveMessage {
pub fn is_reply(&self) -> bool { self.meta.get("reply").is_some() } pub fn is_reply(&self) -> bool {
if let Some(meta) = &self.meta {
meta.get("reply").is_some()
} else {
false
}
}
/// 创建一个对这条消息的回复 /// 创建一个对这条消息的回复
pub fn as_reply(&self) -> SendingMessage { pub fn as_reply(&self) -> SendingMessage {
@ -62,7 +68,7 @@ impl ReciveMessage {
} }
} }
impl Display for ReciveMessage { impl Display for ReceiveMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// msgid|groupid-converseid|senderid|content // msgid|groupid-converseid|senderid|content
write!( write!(
@ -90,6 +96,8 @@ pub struct SendingMessage {
/// 消息内容 /// 消息内容
/// ///
/// 其实还有个 plain, 就是不知道干啥的 /// 其实还有个 plain, 就是不知道干啥的
///
/// [img height=1329 width=1918]{BACKEND}/static/files/6602e20d7b8d10675758e36b/8db505b87bdf9fb309467abcec4d8e2a.png[/img]
pub content: String, pub content: String,
/// 会话ID /// 会话ID
#[serde(rename = "converseId")] #[serde(rename = "converseId")]
@ -99,6 +107,9 @@ pub struct SendingMessage {
pub group_id: Option<GroupId>, pub group_id: Option<GroupId>,
/// 消息的元数据 /// 消息的元数据
pub meta: Option<ReplyMeta>, pub meta: Option<ReplyMeta>,
/// 额外携带的文件
#[serde(skip)]
pub file: Option<Vec<u8>>,
} }
impl SendingMessage { impl SendingMessage {
@ -113,6 +124,7 @@ impl SendingMessage {
converse_id, converse_id,
group_id, group_id,
meta, meta,
file: None,
} }
} }
pub fn new_without_meta( pub fn new_without_meta(
@ -125,8 +137,13 @@ impl SendingMessage {
converse_id, converse_id,
group_id, group_id,
meta: None, meta: None,
file: None,
} }
} }
pub fn contain_file(&self) -> bool { self.file.is_some() }
pub fn add_img(&mut self, file: Vec<u8>, ) { self.file = Some(file); }
pub fn as_value(&self) -> JsonValue { serde_json::to_value(self).unwrap() } pub fn as_value(&self) -> JsonValue { serde_json::to_value(self).unwrap() }
} }
@ -143,7 +160,7 @@ pub struct ReplyMeta {
} }
impl ReplyMeta { impl ReplyMeta {
pub fn from_recive_message(msg: &ReciveMessage) -> Self { pub fn from_recive_message(msg: &ReceiveMessage) -> Self {
Self { Self {
mentions: vec![msg.sender_id.clone()], mentions: vec![msg.sender_id.clone()],
reply_id: msg.msg_id.clone(), reply_id: msg.msg_id.clone(),

View File

@ -33,32 +33,18 @@ pub struct UpdateDMConverse {
pub updated_at: String, pub updated_at: String,
} }
#[allow(unused)]
pub type Writeable<T> = Arc<RwLock<T>>; pub type Writeable<T> = Arc<RwLock<T>>;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct BotStatus { pub struct BotStatus {
user_id: Writeable<UserId>, user_id: UserId,
} }
impl BotStatus { impl BotStatus {
pub fn new(user_id: UserId) -> Self { pub fn new(user_id: UserId) -> Self { Self { user_id } }
Self {
user_id: Arc::new(RwLock::new(user_id)),
}
}
pub async fn get_user_id(&self) -> UserId { self.user_id.read().await.clone() } pub fn get_user_id(&self) -> UserId { self.user_id.clone() }
pub fn block_get_user_id(&self) -> UserId {
tokio::task::block_in_place(|| {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(self.get_user_id())
})
}
pub async fn set_user_id(&self, user_id: UserId) {
self.user_id.write().await.clone_from(&user_id);
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View File

@ -31,7 +31,7 @@ pub async fn add_message(payload: Payload, client: Client) {
return; return;
} }
info!("add_message {}", message.to_string().cyan()); event!(Level::INFO, "add_message {}", message.to_string().cyan());
// 就在这里处理掉最基本的消息 // 就在这里处理掉最基本的消息
// 之后的处理交给插件 // 之后的处理交给插件
if !message.is_from_self() && !message.is_reply() { if !message.is_from_self() && !message.is_reply() {
@ -68,7 +68,7 @@ pub async fn delete_message(payload: Payload, client: Client) {
// 消息 id // 消息 id
if let Some(value) = values.first() { if let Some(value) = values.first() {
if let Some(msg_id) = value.as_str() { if let Some(msg_id) = value.as_str() {
info!("delete_message {}", msg_id.to_string().yellow()); event!(Level::INFO, "delete_message {}", msg_id.to_string().yellow());
py::call::ica_delete_message_py(msg_id.to_string(), &client).await; py::call::ica_delete_message_py(msg_id.to_string(), &client).await;
} }

View File

@ -28,6 +28,7 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub const ICA_VERSION: &str = "1.6.0"; pub const ICA_VERSION: &str = "1.6.0";
pub const TAILCHAT_VERSION: &str = "1.1.0"; pub const TAILCHAT_VERSION: &str = "1.1.0";
#[macro_export]
macro_rules! async_callback_with_state { macro_rules! async_callback_with_state {
($f:expr, $state:expr) => {{ ($f:expr, $state:expr) => {{
use futures_util::FutureExt; use futures_util::FutureExt;

View File

@ -1,9 +1,11 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc;
use pyo3::prelude::*; use pyo3::prelude::*;
use rust_socketio::asynchronous::Client; use rust_socketio::asynchronous::Client;
use tracing::{info, warn}; use tracing::{event, info, warn, Level};
use crate::data_struct::tailchat::status::BotStatus;
use crate::data_struct::{ica, tailchat}; use crate::data_struct::{ica, tailchat};
use crate::error::PyPluginError; use crate::error::PyPluginError;
use crate::py::{class, PyPlugin, PyStatus}; use crate::py::{class, PyPlugin, PyStatus};
@ -106,7 +108,7 @@ macro_rules! call_py_func {
$func_name.to_string(), $func_name.to_string(),
$plugin_path.to_string_lossy().to_string(), $plugin_path.to_string_lossy().to_string(),
); );
warn!("failed to call function<{}>: {:?}", $func_name, e); event!(Level::WARN, "failed to call function<{}>: {:?}", $func_name, e);
} }
} }
}) })
@ -126,20 +128,6 @@ pub async fn ica_new_message_py(message: &ica::messages::NewMessage, client: &Cl
let args = (msg, client); let args = (msg, client);
// 甚至实际上压根不需要await这个spawn, 直接让他自己跑就好了(离谱) // 甚至实际上压根不需要await这个spawn, 直接让他自己跑就好了(离谱)
call_py_func!(args, plugin, path, ICA_NEW_MESSAGE_FUNC, client); call_py_func!(args, plugin, path, ICA_NEW_MESSAGE_FUNC, client);
// tokio::spawn(async move {
// Python::with_gil(|py| {
// if let Ok(py_func) = get_func(plugin.py_module.bind(py), ICA_NEW_MESSAGE_FUNC) {
// 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);
// }
// }
// })
// });
} }
} }
@ -152,29 +140,19 @@ pub async fn ica_delete_message_py(msg_id: ica::MessageId, client: &Client) {
let client = class::ica::IcaClientPy::new(client); let client = class::ica::IcaClientPy::new(client);
let args = (msg_id.clone(), client); let args = (msg_id.clone(), client);
call_py_func!(args, plugin, path, ICA_DELETE_MESSAGE_FUNC, client); call_py_func!(args, plugin, path, ICA_DELETE_MESSAGE_FUNC, client);
// tokio::spawn(async move {
// Python::with_gil(|py| {
// if let Ok(py_func) = get_func(plugin.py_module.bind(py), ICA_DELETE_MESSAGE_FUNC) {
// 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);
// }
// }
// })
// });
} }
} }
pub async fn tailchat_new_message_py(message: &tailchat::messages::ReciveMessage, client: &Client) { pub async fn tailchat_new_message_py(
message: &tailchat::messages::ReceiveMessage,
client: &Client,
status: Arc<BotStatus>,
) {
verify_plugins(); verify_plugins();
let plugins = PyStatus::get_files(); let plugins = PyStatus::get_files();
for (path, plugin) in plugins.iter() { for (path, plugin) in plugins.iter() {
let msg = class::tailchat::TailchatReciveMessagePy::from_recive_message(message); let msg = class::tailchat::TailchatReceiveMessagePy::from_recive_message(message);
let client = class::tailchat::TailchatClientPy::new(client); let client = class::tailchat::TailchatClientPy::new(client);
let args = (msg, client); let args = (msg, client);
call_py_func!(args, plugin, path, TAILCHAT_NEW_MESSAGE_FUNC, client); call_py_func!(args, plugin, path, TAILCHAT_NEW_MESSAGE_FUNC, client);

View File

@ -3,7 +3,7 @@ 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::tailchat::messages::{ReciveMessage, SendingMessage}; use crate::data_struct::tailchat::messages::{ReceiveMessage, SendingMessage};
use crate::data_struct::tailchat::{ConverseId, GroupId, MessageId, UserId}; use crate::data_struct::tailchat::{ConverseId, GroupId, MessageId, UserId};
use crate::tailchat::client::send_message; use crate::tailchat::client::send_message;
@ -27,13 +27,13 @@ impl TailchatClientPy {
pub struct TailchatStatusPy {} pub struct TailchatStatusPy {}
#[pyclass] #[pyclass]
#[pyo3(name = "TailchatReciveMessage")] #[pyo3(name = "TailchatReceiveMessage")]
pub struct TailchatReciveMessagePy { pub struct TailchatReceiveMessagePy {
pub message: ReciveMessage, pub message: ReceiveMessage,
} }
impl TailchatReciveMessagePy { impl TailchatReceiveMessagePy {
pub fn from_recive_message(msg: &ReciveMessage) -> Self { pub fn from_recive_message(msg: &ReceiveMessage) -> Self {
Self { Self {
message: msg.clone(), message: msg.clone(),
} }
@ -76,7 +76,7 @@ impl TailchatClientPy {
} }
#[pymethods] #[pymethods]
impl TailchatReciveMessagePy { impl TailchatReceiveMessagePy {
#[getter] #[getter]
pub fn get_is_reply(&self) -> bool { self.message.is_reply() } pub fn get_is_reply(&self) -> bool { self.message.is_reply() }
#[getter] #[getter]

View File

@ -1,6 +1,8 @@
pub mod client; pub mod client;
pub mod events; pub mod events;
use std::sync::Arc;
use colored::Colorize; use colored::Colorize;
use md5::{Digest, Md5}; use md5::{Digest, Md5};
use reqwest::ClientBuilder as reqwest_ClientBuilder; use reqwest::ClientBuilder as reqwest_ClientBuilder;
@ -11,9 +13,9 @@ use serde_json::{json, Value};
use tracing::{event, span, Level}; use tracing::{event, span, Level};
use crate::config::TailchatConfig; use crate::config::TailchatConfig;
use crate::data_struct::tailchat::status::LoginData; use crate::data_struct::tailchat::status::{BotStatus, LoginData};
use crate::error::{ClientResult, TailchatError}; use crate::error::{ClientResult, TailchatError};
use crate::StopGetter; use crate::{async_callback_with_state, StopGetter};
pub async fn start_tailchat( pub async fn start_tailchat(
config: TailchatConfig, config: TailchatConfig,
@ -60,11 +62,17 @@ pub async fn start_tailchat(
Err(e) => return Err(TailchatError::LoginFailed(e.to_string())), Err(e) => return Err(TailchatError::LoginFailed(e.to_string())),
}; };
let sharded_status = BotStatus::new(status.user_id.clone());
let sharded_status = Arc::new(sharded_status);
let socket = ClientBuilder::new(config.host) let socket = ClientBuilder::new(config.host)
.auth(json!({"token": status.jwt.clone()})) .auth(json!({"token": status.jwt.clone()}))
.transport_type(TransportType::Websocket) .transport_type(TransportType::Websocket)
.on_any(async_any_callback!(events::any_event)) .on_any(async_any_callback!(events::any_event))
.on("notify:chat.message.add", async_callback!(events::on_message)) .on(
"notify:chat.message.add",
async_callback_with_state!(events::on_message, sharded_status.clone()),
)
.on("notify:chat.message.delete", async_callback!(events::on_msg_delete)) .on("notify:chat.message.delete", async_callback!(events::on_msg_delete))
.on( .on(
"notify:chat.converse.updateDMConverse", "notify:chat.converse.updateDMConverse",

View File

@ -8,6 +8,10 @@ use serde_json::{json, Value};
use tracing::{debug, info, warn}; use tracing::{debug, info, warn};
pub async fn send_message(client: &Client, message: &SendingMessage) -> bool { pub async fn send_message(client: &Client, message: &SendingMessage) -> bool {
if message.contain_file() {
// 处理文件
}
let value: Value = message.as_value(); let value: Value = message.as_value();
match client.emit("chat.message.sendMessage", value).await { match client.emit("chat.message.sendMessage", value).await {
Ok(_) => { Ok(_) => {

View File

@ -1,10 +1,12 @@
use std::sync::Arc;
use colored::Colorize; use colored::Colorize;
use rust_socketio::asynchronous::Client; use rust_socketio::asynchronous::Client;
use rust_socketio::{Event, Payload}; use rust_socketio::{Event, Payload};
use tracing::info; use tracing::info;
use crate::data_struct::tailchat::messages::ReciveMessage; use crate::data_struct::tailchat::messages::ReceiveMessage;
use crate::data_struct::tailchat::status::UpdateDMConverse; use crate::data_struct::tailchat::status::{BotStatus, UpdateDMConverse};
use crate::tailchat::client::{emit_join_room, send_message}; use crate::tailchat::client::{emit_join_room, send_message};
/// 所有 /// 所有
@ -60,10 +62,10 @@ pub async fn any_event(event: Event, payload: Payload, _client: Client) {
} }
#[allow(clippy::collapsible_if)] #[allow(clippy::collapsible_if)]
pub async fn on_message(payload: Payload, client: Client) { pub async fn on_message(payload: Payload, client: Client, status: Arc<BotStatus>) {
if let Payload::Text(values) = payload { if let Payload::Text(values) = payload {
if let Some(value) = values.first() { if let Some(value) = values.first() {
let message: ReciveMessage = match serde_json::from_value(value.clone()) { let message: ReceiveMessage = match serde_json::from_value(value.clone()) {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
info!("tailchat_msg {}", value.to_string().red()); info!("tailchat_msg {}", value.to_string().red());
@ -83,7 +85,7 @@ pub async fn on_message(payload: Payload, client: Client) {
send_message(&client, &reply).await; send_message(&client, &reply).await;
} }
} }
crate::py::call::tailchat_new_message_py(&message, &client).await; crate::py::call::tailchat_new_message_py(&message, &client, status.clone()).await;
} }
} }
} }

11
news.md
View File

@ -1,5 +1,16 @@
# 更新日志 # 更新日志
## 0.6.8
- 修复了一堆拼写错误
- 太难绷了
- `TailchatReciveMessagePy` -> `TailchatReceiveMessagePy`
- `ReciveMessage` -> `ReceiveMessage`
- `ReceiveMessage::meta`
- 从 `JsonValue` 改成 `Option<JsonValue>`
- 用来解决发图片的时候没有 `meta` 字段的问题
- 去除了
## 0.6.7 ## 0.6.7
游学回来啦 游学回来啦