mirror of
http://shenjack.top:5100/shenjack/icalingua-python-bot.git
synced 2025-04-20 09:49:53 +08:00
Compare commits
No commits in common. "main" and "0.8.2" have entirely different histories.
575
Cargo.lock
generated
575
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
|
@ -5,6 +5,6 @@ members = [
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
rust_socketio = { git = "https://github.com/shenjackyuanjie/rust-socketio.git", branch = "main" }
|
# rust_socketio = { git = "https://github.com/shenjackyuanjie/rust-socketio.git", branch = "message_pack" }
|
||||||
# rust_socketio = { path = "../../rust-socketio/socketio" }
|
# rust_socketio = { path = "../../rust-socketio/socketio" }
|
||||||
# pyo3 = { git = "https://github.com/PyO3/pyo3.git", branch = "main" }
|
# pyo3 = { git = "https://github.com/PyO3/pyo3.git", branch = "main" }
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ica-rs"
|
name = "ica-rs"
|
||||||
version = "0.9.0"
|
version = "0.8.2"
|
||||||
edition = "2024"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ colored = "3.0"
|
||||||
# runtime
|
# runtime
|
||||||
tokio = { version = "1.43", features = ["rt-multi-thread", "time", "signal", "macros"] }
|
tokio = { version = "1.43", features = ["rt-multi-thread", "time", "signal", "macros"] }
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
pyo3 = { version = "0.24", features = ["experimental-async"] }
|
pyo3 = { version = "0.23", features = ["experimental-async", "py-clone"] }
|
||||||
anyhow = { version = "1.0", features = ["backtrace"] }
|
anyhow = { version = "1.0", features = ["backtrace"] }
|
||||||
# async 这玩意以后在搞
|
# async 这玩意以后在搞
|
||||||
# pyo3-async = "0.3.2"
|
# pyo3-async = "0.3.2"
|
||||||
|
|
|
@ -13,9 +13,7 @@ pub type MessageId = String;
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub trait RoomIdTrait {
|
pub trait RoomIdTrait {
|
||||||
/// 判断是否是群聊
|
|
||||||
fn is_room(&self) -> bool;
|
fn is_room(&self) -> bool;
|
||||||
/// 判断是否是私聊
|
|
||||||
fn is_chat(&self) -> bool { !self.is_room() }
|
fn is_chat(&self) -> bool { !self.is_room() }
|
||||||
fn as_room_id(&self) -> RoomId;
|
fn as_room_id(&self) -> RoomId;
|
||||||
fn as_chat_id(&self) -> RoomId;
|
fn as_chat_id(&self) -> RoomId;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::data_struct::ica::messages::{At, LastMessage, SendMessage};
|
use crate::data_struct::ica::messages::{At, LastMessage};
|
||||||
use crate::data_struct::ica::{RoomId, UserId};
|
use crate::data_struct::ica::RoomId;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{Number, Value as JsonValue};
|
use serde_json::{Number, Value as JsonValue};
|
||||||
|
@ -69,9 +69,6 @@ impl Room {
|
||||||
// download_path: inner.download_path,
|
// download_path: inner.download_path,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new_message_to(&self, content: String) -> SendMessage {
|
|
||||||
SendMessage::new(content, self.room_id, None)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn room_id_default() -> RoomId { -1 }
|
fn room_id_default() -> RoomId { -1 }
|
||||||
|
@ -101,37 +98,3 @@ struct InnerRoom {
|
||||||
// #[serde(rename = "downloadPath")]
|
// #[serde(rename = "downloadPath")]
|
||||||
// pub download_path: Option<String>,
|
// pub download_path: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ```json
|
|
||||||
/// {
|
|
||||||
/// "comment": "问题:从哪里了解到的本群\n答案:aaa",
|
|
||||||
/// "flag": "e4cd5a892ba34bed063196a0cc47a8",
|
|
||||||
/// "group_id": xxxxx,
|
|
||||||
/// "group_name": "Nuitka 和 Python 打包",
|
|
||||||
/// "nickname": "jashcken",
|
|
||||||
/// "post_type": "request",
|
|
||||||
/// "request_type": "group",
|
|
||||||
/// "self_id": 45620725,
|
|
||||||
/// "sub_type": "add",
|
|
||||||
/// "time": 1743372872,
|
|
||||||
/// "tips": "",
|
|
||||||
/// "user_id": 3838663305
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct JoinRequestRoom {
|
|
||||||
/// 问题+答案
|
|
||||||
pub comment: String,
|
|
||||||
pub group_id: RoomId,
|
|
||||||
pub group_name: String,
|
|
||||||
pub user_id: UserId,
|
|
||||||
pub nickname: String,
|
|
||||||
|
|
||||||
// 剩下的应该没用了……吧?
|
|
||||||
pub request_type: String,
|
|
||||||
pub post_type: String,
|
|
||||||
pub sub_type: String,
|
|
||||||
pub time: i64,
|
|
||||||
pub tips: String,
|
|
||||||
pub flag: String,
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::data_struct::ica::{MessageId, RoomId, UserId};
|
||||||
|
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{Value as JsonValue, json};
|
use serde_json::{json, Value as JsonValue};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
pub mod msg_trait;
|
pub mod msg_trait;
|
||||||
|
@ -331,7 +331,7 @@ impl SendMessage {
|
||||||
/// file_type: 图片类型(MIME) (image/png; image/jpeg)
|
/// file_type: 图片类型(MIME) (image/png; image/jpeg)
|
||||||
pub fn set_img(&mut self, file: &Vec<u8>, file_type: &str, as_sticker: bool) {
|
pub fn set_img(&mut self, file: &Vec<u8>, file_type: &str, as_sticker: bool) {
|
||||||
self.sticker = as_sticker;
|
self.sticker = as_sticker;
|
||||||
use base64::{Engine as _, engine::general_purpose};
|
use base64::{engine::general_purpose, Engine as _};
|
||||||
let base64_data = general_purpose::STANDARD.encode(file);
|
let base64_data = general_purpose::STANDARD.encode(file);
|
||||||
self.file_data = Some(format!("data:{};base64,{}", file_type, base64_data));
|
self.file_data = Some(format!("data:{};base64,{}", file_type, base64_data));
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,9 @@ use chrono::DateTime;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value as JsonValue;
|
use serde_json::Value as JsonValue;
|
||||||
|
|
||||||
use crate::MainStatus;
|
|
||||||
use crate::data_struct::ica::messages::{At, Message, NewMessage};
|
use crate::data_struct::ica::messages::{At, Message, NewMessage};
|
||||||
use crate::data_struct::ica::{MessageId, UserId};
|
use crate::data_struct::ica::{MessageId, UserId};
|
||||||
|
use crate::MainStatus;
|
||||||
|
|
||||||
impl Serialize for At {
|
impl Serialize for At {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{Value as JsonValue, json};
|
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};
|
||||||
|
|
||||||
|
|
|
@ -33,8 +33,6 @@ pub enum PyPluginError {
|
||||||
/// 插件内函数调用错误
|
/// 插件内函数调用错误
|
||||||
/// pyerr, func_name, module_name
|
/// pyerr, func_name, module_name
|
||||||
FuncCallError(pyo3::PyErr, String, String),
|
FuncCallError(pyo3::PyErr, String, String),
|
||||||
/// 插件停不下来!
|
|
||||||
PluginNotStopped,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<rust_socketio::Error> for IcaError {
|
impl From<rust_socketio::Error> for IcaError {
|
||||||
|
@ -83,9 +81,6 @@ impl std::fmt::Display for PyPluginError {
|
||||||
PyPluginError::FuncCallError(py_err, name, module) => {
|
PyPluginError::FuncCallError(py_err, name, module) => {
|
||||||
write!(f, "插件内函数调用错误: {:#?}|{} in {}", py_err, name, module)
|
write!(f, "插件内函数调用错误: {:#?}|{} in {}", py_err, name, module)
|
||||||
}
|
}
|
||||||
PyPluginError::PluginNotStopped => {
|
|
||||||
write!(f, "插件未停止")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +111,6 @@ impl std::error::Error for PyPluginError {
|
||||||
PyPluginError::CouldNotGetFunc(e, _, _) => Some(e),
|
PyPluginError::CouldNotGetFunc(e, _, _) => Some(e),
|
||||||
PyPluginError::FuncNotCallable(_, _) => None,
|
PyPluginError::FuncNotCallable(_, _) => None,
|
||||||
PyPluginError::FuncCallError(e, _, _) => Some(e),
|
PyPluginError::FuncCallError(e, _, _) => Some(e),
|
||||||
PyPluginError::PluginNotStopped => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,13 @@ pub mod events;
|
||||||
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use rust_socketio::asynchronous::{Client, ClientBuilder};
|
use rust_socketio::asynchronous::{Client, ClientBuilder};
|
||||||
use rust_socketio::{Event, Payload, TransportType};
|
|
||||||
use rust_socketio::{async_any_callback, async_callback};
|
use rust_socketio::{async_any_callback, async_callback};
|
||||||
use tracing::{Level, event, span};
|
use rust_socketio::{Event, Payload, TransportType};
|
||||||
|
use tracing::{event, span, Level};
|
||||||
|
|
||||||
use crate::config::IcaConfig;
|
use crate::config::IcaConfig;
|
||||||
use crate::error::{ClientResult, IcaError};
|
use crate::error::{ClientResult, IcaError};
|
||||||
use crate::{StopGetter, version_str};
|
use crate::{version_str, StopGetter};
|
||||||
|
|
||||||
/// icalingua 客户端的兼容版本号
|
/// icalingua 客户端的兼容版本号
|
||||||
pub const ICA_PROTOCOL_VERSION: &str = "2.12.28";
|
pub const ICA_PROTOCOL_VERSION: &str = "2.12.28";
|
||||||
|
@ -63,7 +63,6 @@ pub async fn start_ica(config: &IcaConfig, stop_reciver: StopGetter) -> ClientRe
|
||||||
.on("setMessages", async_callback!(events::set_messages))
|
.on("setMessages", async_callback!(events::set_messages))
|
||||||
.on("addMessage", async_callback!(events::add_message))
|
.on("addMessage", async_callback!(events::add_message))
|
||||||
.on("deleteMessage", async_callback!(events::delete_message))
|
.on("deleteMessage", async_callback!(events::delete_message))
|
||||||
.on("handleRequest", async_callback!(events::join_request))
|
|
||||||
.connect()
|
.connect()
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
@ -101,12 +100,12 @@ pub async fn start_ica(config: &IcaConfig, stop_reciver: StopGetter) -> ClientRe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 等待停止信号
|
// 等待停止信号
|
||||||
event!(Level::INFO, "{}", "ica client waiting for stop signal".purple());
|
event!(Level::INFO, "ica client waiting for stop signal");
|
||||||
stop_reciver.await.ok();
|
stop_reciver.await.ok();
|
||||||
event!(Level::INFO, "{}", "socketio client stopping".yellow());
|
event!(Level::INFO, "socketio client stopping");
|
||||||
match socket.disconnect().await {
|
match socket.disconnect().await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
event!(Level::INFO, "{}", "socketio client stopped".green());
|
event!(Level::INFO, "socketio client stopped");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -114,7 +113,7 @@ pub async fn start_ica(config: &IcaConfig, stop_reciver: StopGetter) -> ClientRe
|
||||||
match e {
|
match e {
|
||||||
rust_socketio::Error::IncompleteResponseFromEngineIo(inner_e) => {
|
rust_socketio::Error::IncompleteResponseFromEngineIo(inner_e) => {
|
||||||
if inner_e.to_string().contains("AlreadyClosed") {
|
if inner_e.to_string().contains("AlreadyClosed") {
|
||||||
event!(Level::INFO, "{}", "socketio client stopped".green());
|
event!(Level::INFO, "socketio client stopped");
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
event!(Level::ERROR, "socketio 客户端出现了 Error: {:?}", inner_e);
|
event!(Level::ERROR, "socketio 客户端出现了 Error: {:?}", inner_e);
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
use crate::MainStatus;
|
|
||||||
use crate::data_struct::ica::messages::{DeleteMessage, SendMessage};
|
use crate::data_struct::ica::messages::{DeleteMessage, SendMessage};
|
||||||
use crate::data_struct::ica::{RoomId, RoomIdTrait, UserId};
|
use crate::data_struct::ica::{RoomId, RoomIdTrait, UserId};
|
||||||
use crate::error::{ClientResult, IcaError};
|
use crate::error::{ClientResult, IcaError};
|
||||||
|
use crate::MainStatus;
|
||||||
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use ed25519_dalek::{Signature, Signer, SigningKey};
|
use ed25519_dalek::{Signature, Signer, SigningKey};
|
||||||
use rust_socketio::Payload;
|
|
||||||
use rust_socketio::asynchronous::Client;
|
use rust_socketio::asynchronous::Client;
|
||||||
use serde_json::{Value, json};
|
use rust_socketio::Payload;
|
||||||
use tracing::{Level, event, span};
|
use serde_json::{json, Value};
|
||||||
|
use tracing::{debug, event, span, warn, Level};
|
||||||
|
|
||||||
/// "安全" 的 发送一条消息
|
/// "安全" 的 发送一条消息
|
||||||
pub async fn send_message(client: &Client, message: &SendMessage) -> bool {
|
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(_) => {
|
Ok(_) => {
|
||||||
event!(Level::INFO, "send_message {}", format!("{:#?}", message).cyan());
|
debug!("send_message {}", format!("{:#?}", message).cyan());
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
event!(Level::WARN, "send_message faild:{}", format!("{:#?}", e).red());
|
warn!("send_message faild:{}", format!("{:#?}", e).red());
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,11 +30,11 @@ pub async fn delete_message(client: &Client, message: &DeleteMessage) -> bool {
|
||||||
let value = message.as_value();
|
let value = message.as_value();
|
||||||
match client.emit("deleteMessage", value).await {
|
match client.emit("deleteMessage", value).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
event!(Level::DEBUG, "delete_message {}", format!("{:#?}", message).yellow());
|
debug!("delete_message {}", format!("{:#?}", message).yellow());
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
event!(Level::WARN, "delete_message faild:{}", format!("{:#?}", e).red());
|
warn!("delete_message faild:{}", format!("{:#?}", e).red());
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,8 +71,7 @@ async fn inner_sign(payload: Payload, client: &Client) -> ClientResult<(), IcaEr
|
||||||
.as_str()
|
.as_str()
|
||||||
.unwrap_or("unknow");
|
.unwrap_or("unknow");
|
||||||
if server_protocol_version != crate::ica::ICA_PROTOCOL_VERSION {
|
if server_protocol_version != crate::ica::ICA_PROTOCOL_VERSION {
|
||||||
event!(
|
warn!(
|
||||||
Level::WARN,
|
|
||||||
"服务器版本与兼容版本不一致\n服务器协议版本:{:?}\n兼容版本:{}",
|
"服务器版本与兼容版本不一致\n服务器协议版本:{:?}\n兼容版本:{}",
|
||||||
version.get("protocolVersion"),
|
version.get("protocolVersion"),
|
||||||
crate::ica::ICA_PROTOCOL_VERSION
|
crate::ica::ICA_PROTOCOL_VERSION
|
||||||
|
|
|
@ -2,14 +2,14 @@ use colored::Colorize;
|
||||||
use rust_socketio::asynchronous::Client;
|
use rust_socketio::asynchronous::Client;
|
||||||
use rust_socketio::{Event, Payload};
|
use rust_socketio::{Event, Payload};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use tracing::{Level, event, info, span, warn};
|
use tracing::{event, info, span, warn, Level};
|
||||||
|
|
||||||
use crate::data_struct::ica::RoomId;
|
use crate::data_struct::ica::all_rooms::Room;
|
||||||
use crate::data_struct::ica::all_rooms::{JoinRequestRoom, Room};
|
|
||||||
use crate::data_struct::ica::messages::{Message, MessageTrait, NewMessage};
|
use crate::data_struct::ica::messages::{Message, MessageTrait, NewMessage};
|
||||||
use crate::data_struct::ica::online_data::OnlineData;
|
use crate::data_struct::ica::online_data::OnlineData;
|
||||||
|
use crate::data_struct::ica::RoomId;
|
||||||
use crate::ica::client::send_message;
|
use crate::ica::client::send_message;
|
||||||
use crate::{MainStatus, VERSION, client_id, help_msg, py, version_str};
|
use crate::{client_id, help_msg, py, version_str, MainStatus, VERSION};
|
||||||
|
|
||||||
/// 获取在线数据
|
/// 获取在线数据
|
||||||
pub async fn get_online_data(payload: Payload, _client: Client) {
|
pub async fn get_online_data(payload: Payload, _client: Client) {
|
||||||
|
@ -175,29 +175,6 @@ pub async fn failed_message(payload: Payload, _client: Client) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 处理加群申请
|
|
||||||
///
|
|
||||||
/// add: 2.0.1
|
|
||||||
pub async fn join_request(payload: Payload, _client: Client) {
|
|
||||||
if let Payload::Text(values) = payload {
|
|
||||||
if let Some(value) = values.first() {
|
|
||||||
match serde_json::from_value::<JoinRequestRoom>(value.clone()) {
|
|
||||||
Ok(join_room) => {
|
|
||||||
event!(Level::INFO, "{}", format!("收到加群申请 {:?}", join_room).on_blue());
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
event!(
|
|
||||||
Level::WARN,
|
|
||||||
"呼叫 shenjack! JoinRequestRoom 的 serde 没写好! {}\nraw: {:#?}",
|
|
||||||
e,
|
|
||||||
value
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn fetch_history(client: Client, room: RoomId) { let mut request_body = json!(room); }
|
pub async fn fetch_history(client: Client, room: RoomId) { let mut request_body = json!(room); }
|
||||||
|
|
||||||
pub async fn fetch_messages(client: &Client, room: RoomId) {
|
pub async fn fetch_messages(client: &Client, room: RoomId) {
|
||||||
|
@ -223,11 +200,11 @@ pub async fn any_event(event: Event, payload: Payload, _client: Client) {
|
||||||
"deleteMessage",
|
"deleteMessage",
|
||||||
"setAllRooms",
|
"setAllRooms",
|
||||||
"setMessages",
|
"setMessages",
|
||||||
"handleRequest", // 处理验证消息 (加入请求之类的)
|
|
||||||
// 也许以后会用到
|
// 也许以后会用到
|
||||||
"messageSuccess",
|
"messageSuccess",
|
||||||
"messageFailed",
|
"messageFailed",
|
||||||
"setAllChatGroups",
|
"setAllChatGroups",
|
||||||
|
"handleRequest", // 处理验证消息 (加入请求之类的)
|
||||||
// 忽略的
|
// 忽略的
|
||||||
"notify",
|
"notify",
|
||||||
"setShutUp", // 禁言
|
"setShutUp", // 禁言
|
||||||
|
|
|
@ -16,10 +16,8 @@ mod ica;
|
||||||
#[cfg(feature = "tailchat")]
|
#[cfg(feature = "tailchat")]
|
||||||
mod tailchat;
|
mod tailchat;
|
||||||
|
|
||||||
use colored::Colorize;
|
|
||||||
use config::BotConfig;
|
use config::BotConfig;
|
||||||
use error::PyPluginError;
|
use tracing::{event, span, Level};
|
||||||
use tracing::{Level, event, span};
|
|
||||||
|
|
||||||
pub static mut MAIN_STATUS: status::BotStatus = status::BotStatus {
|
pub static mut MAIN_STATUS: status::BotStatus = status::BotStatus {
|
||||||
config: None,
|
config: None,
|
||||||
|
@ -32,7 +30,7 @@ pub type MainStatus = status::BotStatus;
|
||||||
pub type StopGetter = tokio::sync::oneshot::Receiver<()>;
|
pub type StopGetter = tokio::sync::oneshot::Receiver<()>;
|
||||||
|
|
||||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
pub const ICA_VERSION: &str = "2.0.1";
|
pub const ICA_VERSION: &str = "2.0.0";
|
||||||
pub const TAILCHAT_VERSION: &str = "2.0.0";
|
pub const TAILCHAT_VERSION: &str = "2.0.0";
|
||||||
|
|
||||||
const HELP_MSG: &str = r#"/bot-rs
|
const HELP_MSG: &str = r#"/bot-rs
|
||||||
|
@ -82,7 +80,7 @@ pub fn version_str() -> String {
|
||||||
|
|
||||||
/// 是否为稳定版本
|
/// 是否为稳定版本
|
||||||
/// 会在 release 的时候设置为 true
|
/// 会在 release 的时候设置为 true
|
||||||
pub const STABLE: bool = false;
|
pub const STABLE: bool = true;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! async_callback_with_state {
|
macro_rules! async_callback_with_state {
|
||||||
|
@ -141,34 +139,20 @@ fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
tracing_subscriber::fmt().with_max_level(level).init();
|
tracing_subscriber::fmt().with_max_level(level).init();
|
||||||
|
|
||||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
let _ = tokio::runtime::Builder::new_multi_thread()
|
||||||
.enable_all()
|
.enable_all()
|
||||||
.thread_name("shenbot-rs")
|
.thread_name("shenbot-rs")
|
||||||
.worker_threads(10)
|
.worker_threads(10)
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap()
|
||||||
|
.block_on(inner_main());
|
||||||
let result = rt.block_on(inner_main());
|
|
||||||
|
|
||||||
event!(Level::INFO, "shenbot-rs v{} exiting", VERSION);
|
event!(Level::INFO, "shenbot-rs v{} exiting", VERSION);
|
||||||
|
|
||||||
match result {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => {
|
|
||||||
if let Some(PyPluginError::PluginNotStopped) = e.downcast_ref::<PyPluginError>() {
|
|
||||||
event!(Level::WARN, "Python 插件停不下来, 3s 后终止 tokio rt");
|
|
||||||
rt.shutdown_timeout(Duration::from_secs(3));
|
|
||||||
} else {
|
|
||||||
event!(Level::ERROR, "shenbot-rs v{} exiting with error: {}", VERSION, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn inner_main() -> anyhow::Result<()> {
|
async fn inner_main() -> anyhow::Result<()> {
|
||||||
let span = span!(Level::INFO, "bot-main");
|
let span = span!(Level::INFO, "Shenbot Main");
|
||||||
let _enter = span.enter();
|
let _enter = span.enter();
|
||||||
|
|
||||||
event!(Level::INFO, "shenbot-rs v{} starting", VERSION);
|
event!(Level::INFO, "shenbot-rs v{} starting", VERSION);
|
||||||
|
@ -185,28 +169,28 @@ async fn inner_main() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 准备一个用于停止 socket 的变量
|
// 准备一个用于停止 socket 的变量
|
||||||
|
event!(Level::INFO, "启动 ICA");
|
||||||
let (ica_send, ica_recv) = tokio::sync::oneshot::channel::<()>();
|
let (ica_send, ica_recv) = tokio::sync::oneshot::channel::<()>();
|
||||||
|
|
||||||
if bot_config.check_ica() {
|
if bot_config.check_ica() {
|
||||||
event!(Level::INFO, "{}", "开始启动 ICA".green());
|
|
||||||
let config = bot_config.ica();
|
let config = bot_config.ica();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
ica::start_ica(&config, ica_recv).await.unwrap();
|
ica::start_ica(&config, ica_recv).await.unwrap();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
event!(Level::INFO, "{}", "ica 未启用, 不管他".cyan());
|
event!(Level::INFO, "未启用 ica");
|
||||||
}
|
}
|
||||||
|
|
||||||
let (tailchat_send, tailchat_recv) = tokio::sync::oneshot::channel::<()>();
|
let (tailchat_send, tailchat_recv) = tokio::sync::oneshot::channel::<()>();
|
||||||
|
|
||||||
if bot_config.check_tailchat() {
|
if bot_config.check_tailchat() {
|
||||||
event!(Level::INFO, "{}", "开始启动 tailchat".green());
|
event!(Level::INFO, "启动 Tailchat");
|
||||||
let config = bot_config.tailchat();
|
let config = bot_config.tailchat();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
tailchat::start_tailchat(config, tailchat_recv).await.unwrap();
|
tailchat::start_tailchat(config, tailchat_recv).await.unwrap();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
event!(Level::INFO, "{}", "tailchat 未启用, 不管他".bright_magenta());
|
event!(Level::INFO, "未启用 Tailchat");
|
||||||
}
|
}
|
||||||
|
|
||||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||||
|
@ -233,8 +217,8 @@ async fn test_macro() {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
use rust_socketio::Payload;
|
|
||||||
use rust_socketio::asynchronous::{Client, ClientBuilder};
|
use rust_socketio::asynchronous::{Client, ClientBuilder};
|
||||||
|
use rust_socketio::Payload;
|
||||||
|
|
||||||
/// 一个简单的例子
|
/// 一个简单的例子
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
|
@ -4,13 +4,12 @@ use std::sync::LazyLock;
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use rust_socketio::asynchronous::Client;
|
use rust_socketio::asynchronous::Client;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tracing::{Level, event, info, warn};
|
use tracing::{event, info, warn, Level};
|
||||||
|
|
||||||
use crate::MainStatus;
|
|
||||||
use crate::data_struct::{ica, tailchat};
|
use crate::data_struct::{ica, tailchat};
|
||||||
use crate::error::PyPluginError;
|
use crate::error::PyPluginError;
|
||||||
use crate::py::consts::events_func;
|
use crate::py::{class, PyPlugin, PyStatus};
|
||||||
use crate::py::{PyPlugin, PyStatus, class};
|
use crate::MainStatus;
|
||||||
|
|
||||||
pub struct PyTasks {
|
pub struct PyTasks {
|
||||||
pub ica_new_message: Vec<tokio::task::JoinHandle<()>>,
|
pub ica_new_message: Vec<tokio::task::JoinHandle<()>>,
|
||||||
|
@ -175,6 +174,11 @@ pub fn verify_and_reload_plugins() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const ICA_NEW_MESSAGE_FUNC: &str = "on_ica_message";
|
||||||
|
pub const ICA_DELETE_MESSAGE_FUNC: &str = "on_ica_delete_message";
|
||||||
|
|
||||||
|
pub const TAILCHAT_NEW_MESSAGE_FUNC: &str = "on_tailchat_message";
|
||||||
|
|
||||||
macro_rules! call_py_func {
|
macro_rules! call_py_func {
|
||||||
($args:expr, $plugin:expr, $plugin_path:expr, $func_name:expr, $client:expr) => {
|
($args:expr, $plugin:expr, $plugin_path:expr, $func_name:expr, $client:expr) => {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
|
@ -220,7 +224,7 @@ pub async fn ica_new_message_py(message: &ica::messages::NewMessage, client: &Cl
|
||||||
let msg = class::ica::NewMessagePy::new(message);
|
let msg = class::ica::NewMessagePy::new(message);
|
||||||
let client = class::ica::IcaClientPy::new(client);
|
let client = class::ica::IcaClientPy::new(client);
|
||||||
let args = (msg, client);
|
let args = (msg, client);
|
||||||
let task = call_py_func!(args, plugin, path, events_func::ICA_NEW_MESSAGE, client);
|
let task = call_py_func!(args, plugin, path, ICA_NEW_MESSAGE_FUNC, client);
|
||||||
PY_TASKS.lock().await.push_ica_new_message(task);
|
PY_TASKS.lock().await.push_ica_new_message(task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,7 +237,7 @@ pub async fn ica_delete_message_py(msg_id: ica::MessageId, client: &Client) {
|
||||||
let msg_id = msg_id.clone();
|
let msg_id = msg_id.clone();
|
||||||
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);
|
||||||
let task = call_py_func!(args, plugin, path, events_func::ICA_DELETE_MESSAGE, client);
|
let task = call_py_func!(args, plugin, path, ICA_DELETE_MESSAGE_FUNC, client);
|
||||||
PY_TASKS.lock().await.push_ica_delete_message(task);
|
PY_TASKS.lock().await.push_ica_delete_message(task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -249,7 +253,7 @@ pub async fn tailchat_new_message_py(
|
||||||
let msg = class::tailchat::TailchatReceiveMessagePy::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);
|
||||||
let task = call_py_func!(args, plugin, path, events_func::TAILCHAT_NEW_MESSAGE, client);
|
let task = call_py_func!(args, plugin, path, TAILCHAT_NEW_MESSAGE_FUNC, client);
|
||||||
PY_TASKS.lock().await.push_tailchat_new_message(task);
|
PY_TASKS.lock().await.push_tailchat_new_message(task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,27 @@
|
||||||
pub mod commander;
|
|
||||||
pub mod config;
|
|
||||||
pub mod ica;
|
pub mod ica;
|
||||||
pub mod schdule;
|
|
||||||
pub mod tailchat;
|
pub mod tailchat;
|
||||||
|
|
||||||
use pyo3::{
|
use pyo3::{
|
||||||
Bound, IntoPyObject, PyAny, PyRef, PyResult, pyclass, pymethods, pymodule,
|
pyclass, pymethods,
|
||||||
types::{PyBool, PyModule, PyModuleMethods, PyString},
|
types::{PyBool, PyString},
|
||||||
|
Bound, IntoPyObject, PyAny, PyRef,
|
||||||
};
|
};
|
||||||
use toml::Value as TomlValue;
|
use toml::Value as TomlValue;
|
||||||
use tracing::{Level, event};
|
|
||||||
|
|
||||||
// #[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
#[pyclass]
|
||||||
|
#[pyo3(name = "ConfigRequest")]
|
||||||
|
pub struct ConfigRequestPy {
|
||||||
|
pub path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl ConfigRequestPy {
|
||||||
|
#[new]
|
||||||
|
pub fn py_new(path: String) -> Self { Self { path } }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
#[pyo3(name = "ConfigData")]
|
#[pyo3(name = "ConfigData")]
|
||||||
pub struct ConfigDataPy {
|
pub struct ConfigDataPy {
|
||||||
|
@ -51,35 +61,3 @@ impl ConfigDataPy {
|
||||||
impl ConfigDataPy {
|
impl ConfigDataPy {
|
||||||
pub fn new(data: TomlValue) -> Self { Self { data } }
|
pub fn new(data: TomlValue) -> Self { Self { data } }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rust 侧向 Python 侧提供的 api
|
|
||||||
#[pymodule]
|
|
||||||
#[pyo3(name = "shenbot_api")]
|
|
||||||
fn rs_api_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
||||||
m.add("__version__", crate::VERSION)?;
|
|
||||||
m.add("_version_", crate::VERSION)?;
|
|
||||||
m.add("_ica_version_", crate::ICA_VERSION)?;
|
|
||||||
m.add("_tailchat_version_", crate::TAILCHAT_VERSION)?;
|
|
||||||
m.add_class::<ConfigDataPy>()?;
|
|
||||||
m.add_class::<config::ConfigStoragePy>()?;
|
|
||||||
m.add_class::<schdule::SchedulerPy>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 在 python 初始化之前注册所有需要的类
|
|
||||||
///
|
|
||||||
/// WARNING: 这个函数需要在 Python 初始化之前调用,否则会导致报错
|
|
||||||
///
|
|
||||||
/// (pyo3 提供的宏会检查一遍, 不过我这里就直接用原始形式了)
|
|
||||||
pub fn regist_class() {
|
|
||||||
event!(Level::INFO, "向 Python 注册 Rust 侧模块/函数");
|
|
||||||
unsafe {
|
|
||||||
// 单纯没用 macro 而已
|
|
||||||
pyo3::ffi::PyImport_AppendInittab(
|
|
||||||
rs_api_module::__PYO3_NAME.as_ptr(),
|
|
||||||
Some(rs_api_module::__pyo3_init),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
event!(Level::INFO, "注册完成");
|
|
||||||
}
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
|
|
|
@ -1,343 +0,0 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use pyo3::{
|
|
||||||
Bound, PyAny, PyResult, pyclass, pymethods,
|
|
||||||
types::{
|
|
||||||
PyAnyMethods, PyBool, PyBoolMethods, PyDict, PyDictMethods, PyFloat, PyInt, PyList,
|
|
||||||
PyListMethods, PyString, PyStringMethods, PyTypeMethods,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use tracing::{Level, event};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum ConfigItem {
|
|
||||||
None,
|
|
||||||
String(String),
|
|
||||||
Int(i64),
|
|
||||||
Float(f64),
|
|
||||||
Bool(bool),
|
|
||||||
Array(Vec<ConfigItemPy>),
|
|
||||||
Table(HashMap<String, ConfigItemPy>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
#[pyclass]
|
|
||||||
#[pyo3(name = "ConfigItem")]
|
|
||||||
pub struct ConfigItemPy {
|
|
||||||
pub item: ConfigItem,
|
|
||||||
pub default_value: ConfigItem,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConfigItemPy {
|
|
||||||
pub fn new(item: ConfigItem, default_value: ConfigItem) -> Self {
|
|
||||||
Self {
|
|
||||||
item,
|
|
||||||
default_value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_uninit(default_value: ConfigItem) -> Self {
|
|
||||||
Self {
|
|
||||||
item: ConfigItem::None,
|
|
||||||
default_value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
#[pyclass]
|
|
||||||
#[pyo3(name = "ConfigStorage")]
|
|
||||||
pub struct ConfigStoragePy {
|
|
||||||
pub keys: HashMap<String, ConfigItemPy>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Storage 里允许的最大层级深度
|
|
||||||
///
|
|
||||||
/// 我也不知道为啥就突然有这玩意了(
|
|
||||||
pub const MAX_CFG_DEPTH: usize = 10;
|
|
||||||
|
|
||||||
fn parse_py_string(obj: &Bound<'_, PyAny>) -> PyResult<String> {
|
|
||||||
let py_str = obj.downcast::<PyString>()?;
|
|
||||||
let value = py_str.to_str()?;
|
|
||||||
Ok(value.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_py_bool(obj: &Bound<'_, PyAny>) -> PyResult<bool> {
|
|
||||||
let py_bool = obj.downcast::<PyBool>()?;
|
|
||||||
Ok(py_bool.is_true())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_py_int(obj: &Bound<'_, PyAny>) -> PyResult<i64> {
|
|
||||||
let py_int = obj.downcast::<PyInt>()?;
|
|
||||||
py_int.extract::<i64>()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_py_float(obj: &Bound<'_, PyAny>) -> PyResult<f64> {
|
|
||||||
let py_float = obj.downcast::<PyFloat>()?;
|
|
||||||
py_float.extract::<f64>()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConfigStoragePy {
|
|
||||||
/// 递归 list 解析配置
|
|
||||||
///
|
|
||||||
/// 用个 Result 来标记递归过深
|
|
||||||
fn parse_py_list(
|
|
||||||
args: &Bound<'_, PyList>,
|
|
||||||
list: &mut Vec<ConfigItemPy>,
|
|
||||||
current_deepth: usize,
|
|
||||||
) -> Result<(), usize> {
|
|
||||||
if current_deepth > MAX_CFG_DEPTH {
|
|
||||||
return Err(current_deepth);
|
|
||||||
} else {
|
|
||||||
for value in args.iter() {
|
|
||||||
// 匹配 item
|
|
||||||
let value_type = value.get_type();
|
|
||||||
if value_type.is_instance_of::<PyDict>() {
|
|
||||||
let py_dict = value.downcast::<PyDict>().unwrap();
|
|
||||||
let mut new_map = HashMap::new();
|
|
||||||
match Self::parse_py_dict(py_dict, &mut new_map, current_deepth + 1) {
|
|
||||||
Ok(_) => {
|
|
||||||
list.push(ConfigItemPy::new_uninit(ConfigItem::Table(new_map)));
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
event!(
|
|
||||||
Level::WARN,
|
|
||||||
"value(dict) 解析时出现错误: {}\nraw: {}",
|
|
||||||
e,
|
|
||||||
value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if value_type.is_instance_of::<PyList>() {
|
|
||||||
let py_list = value.downcast::<PyList>().unwrap();
|
|
||||||
let mut new_list = Vec::new();
|
|
||||||
match Self::parse_py_list(py_list, &mut new_list, current_deepth + 1) {
|
|
||||||
Ok(_) => {
|
|
||||||
list.push(ConfigItemPy::new_uninit(ConfigItem::Array(new_list)));
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
event!(
|
|
||||||
Level::WARN,
|
|
||||||
"value(list) 解析时出现错误: {}\nraw: {}",
|
|
||||||
e,
|
|
||||||
value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if value_type.is_instance_of::<PyString>() {
|
|
||||||
match parse_py_string(&value) {
|
|
||||||
Ok(value) => {
|
|
||||||
list.push(ConfigItemPy::new_uninit(ConfigItem::String(value)));
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
event!(
|
|
||||||
Level::WARN,
|
|
||||||
"value(string) 解析时出现错误: {}\nraw: {}",
|
|
||||||
e,
|
|
||||||
value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if value_type.is_instance_of::<PyBool>() {
|
|
||||||
match parse_py_bool(&value) {
|
|
||||||
Ok(value) => {
|
|
||||||
list.push(ConfigItemPy::new_uninit(ConfigItem::Bool(value)));
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
event!(
|
|
||||||
Level::WARN,
|
|
||||||
"value(bool) 解析时出现错误: {}\nraw: {}",
|
|
||||||
e,
|
|
||||||
value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if value_type.is_instance_of::<PyInt>() {
|
|
||||||
match parse_py_int(&value) {
|
|
||||||
Ok(value) => {
|
|
||||||
list.push(ConfigItemPy::new_uninit(ConfigItem::Int(value)));
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
event!(Level::WARN, "value(int) 解析时出现错误: {}\nraw: {}", e, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if value_type.is_instance_of::<PyFloat>() {
|
|
||||||
match parse_py_float(&value) {
|
|
||||||
Ok(value) => {
|
|
||||||
list.push(ConfigItemPy::new_uninit(ConfigItem::Float(value)));
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
event!(
|
|
||||||
Level::WARN,
|
|
||||||
"value(float) 解析时出现错误: {}\nraw: {}",
|
|
||||||
e,
|
|
||||||
value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 先丢个 warning 出去
|
|
||||||
match value_type.name() {
|
|
||||||
Ok(type_name) => {
|
|
||||||
event!(
|
|
||||||
Level::WARN,
|
|
||||||
"value 为不支持的 {} 类型\nraw: {}",
|
|
||||||
type_name,
|
|
||||||
value
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
event!(
|
|
||||||
Level::WARN,
|
|
||||||
"value 为不支持的类型 (获取类型名失败: {})\nraw: {}",
|
|
||||||
e,
|
|
||||||
value
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 递归 dict 解析配置
|
|
||||||
///
|
|
||||||
/// 用个 Result 来标记递归过深
|
|
||||||
fn parse_py_dict(
|
|
||||||
kwargs: &Bound<'_, PyDict>,
|
|
||||||
map: &mut HashMap<String, ConfigItemPy>,
|
|
||||||
current_deepth: usize,
|
|
||||||
) -> Result<(), usize> {
|
|
||||||
if current_deepth > MAX_CFG_DEPTH {
|
|
||||||
Err(current_deepth)
|
|
||||||
} else {
|
|
||||||
for (key, value) in kwargs.iter() {
|
|
||||||
if let Ok(name) = key.downcast::<PyString>() {
|
|
||||||
let name = name.to_string();
|
|
||||||
// 匹配 item
|
|
||||||
let value_type = value.get_type();
|
|
||||||
if value_type.is_instance_of::<PyDict>() {
|
|
||||||
let py_dict = value.downcast::<PyDict>().unwrap();
|
|
||||||
let mut new_map = HashMap::new();
|
|
||||||
match Self::parse_py_dict(py_dict, &mut new_map, current_deepth + 1) {
|
|
||||||
Ok(_) => {
|
|
||||||
map.insert(
|
|
||||||
name.clone(),
|
|
||||||
ConfigItemPy::new_uninit(ConfigItem::Table(new_map)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
event!(Level::WARN, "value(dict) {} 解析时出现错误: {}", name, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if value_type.is_instance_of::<PyList>() {
|
|
||||||
let py_list = value.downcast::<PyList>().unwrap();
|
|
||||||
let mut new_list = Vec::new();
|
|
||||||
match Self::parse_py_list(py_list, &mut new_list, current_deepth + 1) {
|
|
||||||
Ok(_) => {
|
|
||||||
map.insert(
|
|
||||||
name.clone(),
|
|
||||||
ConfigItemPy::new_uninit(ConfigItem::Array(new_list)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
event!(Level::WARN, "value(list) {} 解析时出现错误: {}", name, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if value_type.is_instance_of::<PyString>() {
|
|
||||||
match parse_py_string(&value) {
|
|
||||||
Ok(value) => {
|
|
||||||
map.insert(
|
|
||||||
name.clone(),
|
|
||||||
ConfigItemPy::new_uninit(ConfigItem::String(value)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
event!(Level::WARN, "value(string) {} 解析时出现错误: {}", name, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if value_type.is_instance_of::<PyBool>() {
|
|
||||||
match parse_py_bool(&value) {
|
|
||||||
Ok(value) => {
|
|
||||||
map.insert(
|
|
||||||
name.clone(),
|
|
||||||
ConfigItemPy::new_uninit(ConfigItem::Bool(value)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
event!(Level::WARN, "value(bool) {} 解析时出现错误: {}", name, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if value_type.is_instance_of::<PyInt>() {
|
|
||||||
match parse_py_int(&value) {
|
|
||||||
Ok(value) => {
|
|
||||||
map.insert(
|
|
||||||
name.clone(),
|
|
||||||
ConfigItemPy::new_uninit(ConfigItem::Int(value)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
event!(Level::WARN, "value(int) {} 解析时出现错误: {}", name, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if value_type.is_instance_of::<PyFloat>() {
|
|
||||||
match parse_py_float(&value) {
|
|
||||||
Ok(value) => {
|
|
||||||
map.insert(
|
|
||||||
name.clone(),
|
|
||||||
ConfigItemPy::new_uninit(ConfigItem::Float(value)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
event!(Level::WARN, "value(float) {} 解析时出现错误: {}", name, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 先丢个 warning 出去
|
|
||||||
match value_type.name() {
|
|
||||||
Ok(type_name) => {
|
|
||||||
event!(Level::WARN, "value {} 为不支持的 {} 类型", name, type_name)
|
|
||||||
}
|
|
||||||
Err(e) => event!(
|
|
||||||
Level::WARN,
|
|
||||||
"value {} 为不支持的类型 (获取类型名失败: {})",
|
|
||||||
name,
|
|
||||||
e
|
|
||||||
),
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl ConfigStoragePy {
|
|
||||||
#[new]
|
|
||||||
#[pyo3(signature = (**kwargs))]
|
|
||||||
pub fn new(kwargs: Option<&Bound<'_, PyDict>>) -> PyResult<Self> {
|
|
||||||
match kwargs {
|
|
||||||
Some(kwargs) => {
|
|
||||||
let mut keys = HashMap::new();
|
|
||||||
// 解析 kwargs
|
|
||||||
Self::parse_py_dict(kwargs, &mut keys, 0).map_err(|e| {
|
|
||||||
event!(Level::ERROR, "配置解析过深: {}", e);
|
|
||||||
pyo3::exceptions::PyValueError::new_err(format!("配置解析过深: {}", e))
|
|
||||||
})?;
|
|
||||||
// 解析完成
|
|
||||||
Ok(Self { keys })
|
|
||||||
}
|
|
||||||
None => Ok(Self {
|
|
||||||
keys: HashMap::new(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[getter]
|
|
||||||
/// 获取最大允许的层级深度
|
|
||||||
pub fn get_max_allowed_depth(&self) -> usize { MAX_CFG_DEPTH }
|
|
||||||
}
|
|
|
@ -3,15 +3,15 @@ use std::time::SystemTime;
|
||||||
use pyo3::{pyclass, pymethods};
|
use pyo3::{pyclass, pymethods};
|
||||||
use rust_socketio::asynchronous::Client;
|
use rust_socketio::asynchronous::Client;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
use tracing::{Level, event};
|
use tracing::{event, Level};
|
||||||
|
|
||||||
use crate::MainStatus;
|
|
||||||
use crate::data_struct::ica::messages::{
|
use crate::data_struct::ica::messages::{
|
||||||
DeleteMessage, MessageTrait, NewMessage, ReplyMessage, SendMessage,
|
DeleteMessage, MessageTrait, NewMessage, ReplyMessage, SendMessage,
|
||||||
};
|
};
|
||||||
use crate::data_struct::ica::{MessageId, RoomId, RoomIdTrait, UserId};
|
use crate::data_struct::ica::{MessageId, RoomId, RoomIdTrait, UserId};
|
||||||
use crate::ica::client::{delete_message, send_message, send_poke, send_room_sign_in};
|
use crate::ica::client::{delete_message, send_message, send_poke, send_room_sign_in};
|
||||||
use crate::py::PyStatus;
|
use crate::py::PyStatus;
|
||||||
|
use crate::MainStatus;
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
#[pyo3(name = "IcaStatus")]
|
#[pyo3(name = "IcaStatus")]
|
||||||
|
@ -63,27 +63,6 @@ impl IcaStatusPy {
|
||||||
pub fn get_load(&self) -> String {
|
pub fn get_load(&self) -> String {
|
||||||
MainStatus::global_ica_status().online_status.icalingua_info.load.clone()
|
MainStatus::global_ica_status().online_status.icalingua_info.load.clone()
|
||||||
}
|
}
|
||||||
#[getter]
|
|
||||||
/// 获取当前用户加入的所有房间
|
|
||||||
///
|
|
||||||
/// 添加自 2.0.1
|
|
||||||
pub fn get_rooms(&self) -> Vec<IcaRoomPy> {
|
|
||||||
MainStatus::global_ica_status().rooms.iter().map(|r| r.into()).collect()
|
|
||||||
}
|
|
||||||
#[getter]
|
|
||||||
/// 获取所有管理员
|
|
||||||
///
|
|
||||||
/// 添加自 2.0.1
|
|
||||||
pub fn get_admins(&self) -> Vec<UserId> { MainStatus::global_config().ica().admin_list.clone() }
|
|
||||||
#[getter]
|
|
||||||
/// 获取所有被屏蔽的人
|
|
||||||
///
|
|
||||||
/// (好像没啥用就是了, 反正被过滤的不会给到插件)
|
|
||||||
///
|
|
||||||
/// 添加自 2.0.1
|
|
||||||
pub fn get_filtered(&self) -> Vec<UserId> {
|
|
||||||
MainStatus::global_config().ica().filter_list.clone()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for IcaStatusPy {
|
impl Default for IcaStatusPy {
|
||||||
|
@ -94,47 +73,6 @@ impl IcaStatusPy {
|
||||||
pub fn new() -> Self { Self {} }
|
pub fn new() -> Self { Self {} }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
#[pyclass]
|
|
||||||
#[pyo3(name = "IcaRoom")]
|
|
||||||
/// Room api
|
|
||||||
///
|
|
||||||
/// 添加自 2.0.1
|
|
||||||
pub struct IcaRoomPy {
|
|
||||||
pub inner: crate::data_struct::ica::all_rooms::Room,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<crate::data_struct::ica::all_rooms::Room> for IcaRoomPy {
|
|
||||||
fn from(inner: crate::data_struct::ica::all_rooms::Room) -> Self { Self { inner } }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&crate::data_struct::ica::all_rooms::Room> for IcaRoomPy {
|
|
||||||
fn from(inner: &crate::data_struct::ica::all_rooms::Room) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: inner.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl IcaRoomPy {
|
|
||||||
#[getter]
|
|
||||||
pub fn get_room_id(&self) -> i64 { self.inner.room_id }
|
|
||||||
#[getter]
|
|
||||||
pub fn get_room_name(&self) -> String { self.inner.room_name.clone() }
|
|
||||||
#[getter]
|
|
||||||
pub fn get_unread_count(&self) -> u64 { self.inner.unread_count }
|
|
||||||
#[getter]
|
|
||||||
pub fn get_priority(&self) -> u8 { self.inner.priority }
|
|
||||||
#[getter]
|
|
||||||
pub fn get_utime(&self) -> i64 { self.inner.utime }
|
|
||||||
pub fn is_group(&self) -> bool { self.inner.room_id.is_room() }
|
|
||||||
pub fn is_chat(&self) -> bool { self.inner.room_id.is_chat() }
|
|
||||||
pub fn new_message_to(&self, content: String) -> SendMessagePy {
|
|
||||||
SendMessagePy::new(self.inner.new_message_to(content))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
#[pyo3(name = "NewMessage")]
|
#[pyo3(name = "NewMessage")]
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use pyo3::{Bound, Py, PyTraverseError, PyVisit, Python, pyclass, pymethods, types::PyFunction};
|
|
||||||
use tracing::{Level, event};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[pyclass]
|
|
||||||
#[pyo3(name = "Scheduler")]
|
|
||||||
/// 用于计划任务的类
|
|
||||||
///
|
|
||||||
/// 给 Python 侧使用
|
|
||||||
///
|
|
||||||
/// add: 0.9.0
|
|
||||||
pub struct SchedulerPy {
|
|
||||||
/// 回调函数
|
|
||||||
///
|
|
||||||
/// 你最好不要把他清理掉
|
|
||||||
pub callback: Py<PyFunction>,
|
|
||||||
/// 预计等待时间
|
|
||||||
pub schdule_time: Duration,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl SchedulerPy {
|
|
||||||
fn __traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError> {
|
|
||||||
visit.call(&self.callback)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[new]
|
|
||||||
pub fn new(func: Bound<'_, PyFunction>, schdule_time: Duration) -> Self {
|
|
||||||
Self {
|
|
||||||
callback: func.unbind(),
|
|
||||||
schdule_time,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 开始
|
|
||||||
pub fn start(&self, py: Python<'_>) {
|
|
||||||
let wait = self.schdule_time;
|
|
||||||
let cb = self.callback.clone_ref(py);
|
|
||||||
tokio::spawn(async move {
|
|
||||||
let second = Duration::from_secs(1);
|
|
||||||
if wait > second {
|
|
||||||
let big_sleep = wait.checked_sub(second).unwrap();
|
|
||||||
tokio::time::sleep(big_sleep).await;
|
|
||||||
tokio::time::sleep(second).await;
|
|
||||||
} else {
|
|
||||||
tokio::time::sleep(wait).await;
|
|
||||||
}
|
|
||||||
Python::with_gil(|py| {
|
|
||||||
event!(Level::INFO, "正在调用计划 {:?}", wait);
|
|
||||||
if let Err(e) = cb.call0(py) {
|
|
||||||
event!(Level::WARN, "调用时出现错误 {}", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +1,10 @@
|
||||||
use std::{path::Path, str::FromStr};
|
use std::{path::Path, str::FromStr};
|
||||||
|
|
||||||
use colored::Colorize;
|
use toml_edit::{value, DocumentMut, Key, Table, TomlError, Value};
|
||||||
use toml_edit::{DocumentMut, Key, Table, TomlError, Value, value};
|
use tracing::{event, Level};
|
||||||
use tracing::{Level, event};
|
|
||||||
|
|
||||||
use crate::MainStatus;
|
|
||||||
use crate::py::PyStatus;
|
use crate::py::PyStatus;
|
||||||
|
use crate::MainStatus;
|
||||||
|
|
||||||
/// ```toml
|
/// ```toml
|
||||||
/// # 这个文件是由 shenbot 自动生成的, 请 **谨慎** 修改
|
/// # 这个文件是由 shenbot 自动生成的, 请 **谨慎** 修改
|
||||||
|
@ -28,7 +27,6 @@ pub const DEFAULT_CONFIG: &str = r#"
|
||||||
[plugins]
|
[plugins]
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
impl PluginConfigFile {
|
impl PluginConfigFile {
|
||||||
pub fn from_str(data: &str) -> Result<Self, TomlError> {
|
pub fn from_str(data: &str) -> Result<Self, TomlError> {
|
||||||
let mut data = DocumentMut::from_str(data)?;
|
let mut data = DocumentMut::from_str(data)?;
|
||||||
|
@ -136,35 +134,18 @@ impl PluginConfigFile {
|
||||||
}
|
}
|
||||||
event!(Level::INFO, "同步插件状态");
|
event!(Level::INFO, "同步插件状态");
|
||||||
let plugins = PyStatus::get_mut();
|
let plugins = PyStatus::get_mut();
|
||||||
|
|
||||||
fn fmt_bool(b: bool) -> String {
|
|
||||||
if b {
|
|
||||||
"启用".green().to_string()
|
|
||||||
} else {
|
|
||||||
"禁用".red().to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
plugins.files.iter_mut().for_each(|(path, status)| {
|
plugins.files.iter_mut().for_each(|(path, status)| {
|
||||||
let plugin_id = status.get_id();
|
let plugin_id = status.get_id();
|
||||||
let config_status = self.get_status(&plugin_id);
|
let config_status = self.get_status(&plugin_id);
|
||||||
if config_status != status.enabled {
|
event!(
|
||||||
event!(
|
Level::INFO,
|
||||||
Level::INFO,
|
"插件状态: {}({:?}) {} -> {}",
|
||||||
"插件状态: {} {} -> {}",
|
status.get_id(),
|
||||||
status.get_id(),
|
path,
|
||||||
fmt_bool(status.enabled),
|
status.enabled,
|
||||||
fmt_bool(config_status)
|
config_status
|
||||||
);
|
);
|
||||||
status.enabled = config_status;
|
status.enabled = config_status;
|
||||||
} else {
|
|
||||||
event!(
|
|
||||||
Level::INFO,
|
|
||||||
"插件状态: {} {} (没变)",
|
|
||||||
status.get_id(),
|
|
||||||
fmt_bool(status.enabled)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
pub mod events_func {
|
|
||||||
|
|
||||||
/// icalingua 的 加群请求
|
|
||||||
///
|
|
||||||
/// added: 2.0.1
|
|
||||||
pub const ICA_JOIN_REQUEST: &str = "on_ica_join_request";
|
|
||||||
/// icalingua 的 新消息
|
|
||||||
pub const ICA_NEW_MESSAGE: &str = "on_ica_message";
|
|
||||||
/// icalingua 的 消息撤回
|
|
||||||
pub const ICA_DELETE_MESSAGE: &str = "on_ica_delete_message";
|
|
||||||
|
|
||||||
/// tailchat 的 新消息
|
|
||||||
pub const TAILCHAT_NEW_MESSAGE: &str = "on_tailchat_message";
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod config_func {
|
|
||||||
/// 请求配置用的函数
|
|
||||||
pub const REQUIRE_CONFIG: &str = "require_config";
|
|
||||||
/// 接受配置用的函数
|
|
||||||
pub const ON_CONFIG: &str = "on_config";
|
|
||||||
}
|
|
|
@ -1,9 +1,8 @@
|
||||||
pub mod call;
|
pub mod call;
|
||||||
pub mod class;
|
pub mod class;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod consts;
|
|
||||||
|
|
||||||
use std::ffi::CString;
|
use std::ffi::{CString, OsStr};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
@ -11,20 +10,17 @@ use std::time::SystemTime;
|
||||||
use std::{collections::HashMap, path::PathBuf};
|
use std::{collections::HashMap, path::PathBuf};
|
||||||
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use pyo3::{
|
use pyo3::exceptions::PyTypeError;
|
||||||
Bound, Py, PyErr, PyResult, Python,
|
use pyo3::types::PyTuple;
|
||||||
exceptions::PyTypeError,
|
use pyo3::{intern, prelude::*};
|
||||||
intern,
|
use tracing::{event, span, warn, Level};
|
||||||
types::{PyAnyMethods, PyModule, PyTracebackMethods, PyTuple},
|
|
||||||
};
|
|
||||||
use tracing::{Level, event, span, warn};
|
|
||||||
|
|
||||||
use crate::MainStatus;
|
use crate::MainStatus;
|
||||||
use crate::error::PyPluginError;
|
|
||||||
|
|
||||||
use consts::config_func;
|
const REQUIRE_CONFIG_FUNC_NAME: &str = "require_config";
|
||||||
|
const ON_CONFIG_FUNC_NAME: &str = "on_config";
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PyStatus {
|
pub struct PyStatus {
|
||||||
pub files: PyPlugins,
|
pub files: PyPlugins,
|
||||||
pub config: config::PluginConfigFile,
|
pub config: config::PluginConfigFile,
|
||||||
|
@ -36,7 +32,6 @@ pub type RawPyPlugin = (PathBuf, Option<SystemTime>, String);
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
static mut PyPluginStatus: OnceLock<PyStatus> = OnceLock::new();
|
static mut PyPluginStatus: OnceLock<PyStatus> = OnceLock::new();
|
||||||
|
|
||||||
#[allow(static_mut_refs)]
|
|
||||||
impl PyStatus {
|
impl PyStatus {
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
let config =
|
let config =
|
||||||
|
@ -121,24 +116,15 @@ pub fn get_py_err_traceback(py_err: &PyErr) -> String {
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PyPlugin {
|
pub struct PyPlugin {
|
||||||
pub file_path: PathBuf,
|
pub file_path: PathBuf,
|
||||||
pub modify_time: Option<SystemTime>,
|
pub changed_time: Option<SystemTime>,
|
||||||
pub py_module: Py<PyModule>,
|
pub py_module: Py<PyAny>,
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PyPlugin {
|
impl PyPlugin {
|
||||||
pub fn new(path: PathBuf, modify_time: Option<SystemTime>, module: Py<PyModule>) -> Self {
|
|
||||||
PyPlugin {
|
|
||||||
file_path: path.clone(),
|
|
||||||
modify_time,
|
|
||||||
py_module: module,
|
|
||||||
enabled: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 从文件创建一个新的
|
/// 从文件创建一个新的
|
||||||
pub fn new_from_path(path: &PathBuf) -> Option<Self> {
|
pub fn new_from_path(path: &PathBuf) -> Option<Self> {
|
||||||
let raw_file = load_py_file(path);
|
let raw_file = load_py_file(path);
|
||||||
|
@ -169,7 +155,7 @@ impl PyPlugin {
|
||||||
Ok(raw_file) => match Self::try_from(raw_file) {
|
Ok(raw_file) => match Self::try_from(raw_file) {
|
||||||
Ok(plugin) => {
|
Ok(plugin) => {
|
||||||
self.py_module = plugin.py_module;
|
self.py_module = plugin.py_module;
|
||||||
self.modify_time = plugin.modify_time;
|
self.changed_time = plugin.changed_time;
|
||||||
self.enabled = PyStatus::get().config.get_status(&self.get_id());
|
self.enabled = PyStatus::get().config.get_status(&self.get_id());
|
||||||
event!(Level::INFO, "更新 Python 插件文件 {:?} 完成", self.file_path);
|
event!(Level::INFO, "更新 Python 插件文件 {:?} 完成", self.file_path);
|
||||||
true
|
true
|
||||||
|
@ -196,7 +182,7 @@ impl PyPlugin {
|
||||||
match get_change_time(&self.file_path) {
|
match get_change_time(&self.file_path) {
|
||||||
None => false,
|
None => false,
|
||||||
Some(time) => {
|
Some(time) => {
|
||||||
if let Some(changed_time) = self.modify_time {
|
if let Some(changed_time) = self.changed_time {
|
||||||
time.eq(&changed_time)
|
time.eq(&changed_time)
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
|
@ -258,13 +244,13 @@ fn set_str_cfg_default_plugin(
|
||||||
}
|
}
|
||||||
|
|
||||||
// 给到 on config
|
// 给到 on config
|
||||||
if let Ok(attr) = module.getattr(intern!(module.py(), config_func::ON_CONFIG)) {
|
if let Ok(attr) = module.getattr(intern!(module.py(), ON_CONFIG_FUNC_NAME)) {
|
||||||
if !attr.is_callable() {
|
if !attr.is_callable() {
|
||||||
event!(
|
event!(
|
||||||
Level::WARN,
|
Level::WARN,
|
||||||
"Python 插件 {:?} 的 {} 函数不是 Callable",
|
"Python 插件 {:?} 的 {} 函数不是 Callable",
|
||||||
path,
|
path,
|
||||||
config_func::ON_CONFIG
|
ON_CONFIG_FUNC_NAME
|
||||||
);
|
);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -274,7 +260,7 @@ fn set_str_cfg_default_plugin(
|
||||||
Level::WARN,
|
Level::WARN,
|
||||||
"Python 插件 {:?} 的 {} 函数返回了一个报错 {}",
|
"Python 插件 {:?} 的 {} 函数返回了一个报错 {}",
|
||||||
path,
|
path,
|
||||||
config_func::ON_CONFIG,
|
ON_CONFIG_FUNC_NAME,
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -328,13 +314,13 @@ fn set_bytes_cfg_default_plugin(
|
||||||
}
|
}
|
||||||
|
|
||||||
// 给到 on config
|
// 给到 on config
|
||||||
if let Ok(attr) = module.getattr(intern!(module.py(), config_func::ON_CONFIG)) {
|
if let Ok(attr) = module.getattr(intern!(module.py(), ON_CONFIG_FUNC_NAME)) {
|
||||||
if !attr.is_callable() {
|
if !attr.is_callable() {
|
||||||
event!(
|
event!(
|
||||||
Level::WARN,
|
Level::WARN,
|
||||||
"Python 插件 {:?} 的 {} 函数不是 Callable",
|
"Python 插件 {:?} 的 {} 函数不是 Callable",
|
||||||
path,
|
path,
|
||||||
config_func::ON_CONFIG
|
ON_CONFIG_FUNC_NAME
|
||||||
);
|
);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -344,7 +330,7 @@ fn set_bytes_cfg_default_plugin(
|
||||||
Level::WARN,
|
Level::WARN,
|
||||||
"Python 插件 {:?} 的 {} 函数返回了一个报错 {}",
|
"Python 插件 {:?} 的 {} 函数返回了一个报错 {}",
|
||||||
path,
|
path,
|
||||||
config_func::ON_CONFIG,
|
ON_CONFIG_FUNC_NAME,
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -355,7 +341,7 @@ fn set_bytes_cfg_default_plugin(
|
||||||
impl TryFrom<RawPyPlugin> for PyPlugin {
|
impl TryFrom<RawPyPlugin> for PyPlugin {
|
||||||
type Error = PyErr;
|
type Error = PyErr;
|
||||||
fn try_from(value: RawPyPlugin) -> Result<Self, Self::Error> {
|
fn try_from(value: RawPyPlugin) -> Result<Self, Self::Error> {
|
||||||
let (path, modify_time, content) = value;
|
let (path, changed_time, content) = value;
|
||||||
let py_module: Py<PyModule> = match py_module_from_code(&content, &path) {
|
let py_module: Py<PyModule> = match py_module_from_code(&content, &path) {
|
||||||
Ok(module) => module,
|
Ok(module) => module,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -365,7 +351,7 @@ impl TryFrom<RawPyPlugin> for PyPlugin {
|
||||||
};
|
};
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
let module = py_module.bind(py);
|
let module = py_module.bind(py);
|
||||||
if let Ok(config_func) = call::get_func(module, config_func::REQUIRE_CONFIG) {
|
if let Ok(config_func) = call::get_func(module, REQUIRE_CONFIG_FUNC_NAME) {
|
||||||
match config_func.call0() {
|
match config_func.call0() {
|
||||||
Ok(config) => {
|
Ok(config) => {
|
||||||
if config.is_instance_of::<PyTuple>() {
|
if config.is_instance_of::<PyTuple>() {
|
||||||
|
@ -382,20 +368,32 @@ impl TryFrom<RawPyPlugin> for PyPlugin {
|
||||||
"加载 Python 插件 {:?} 的配置文件信息时失败:返回的不是 [str, bytes | str]",
|
"加载 Python 插件 {:?} 的配置文件信息时失败:返回的不是 [str, bytes | str]",
|
||||||
path
|
path
|
||||||
);
|
);
|
||||||
return Err(PyTypeError::new_err(
|
return Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
|
||||||
"返回的不是 [str, bytes | str]".to_string(),
|
"返回的不是 [str, bytes | str]".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Ok(PyPlugin::new(path, modify_time, module.clone().unbind()))
|
Ok(PyPlugin {
|
||||||
|
file_path: path,
|
||||||
|
changed_time,
|
||||||
|
py_module: module.clone().into_any().unbind(),
|
||||||
|
enabled: true,
|
||||||
|
})
|
||||||
} else if config.is_none() {
|
} else if config.is_none() {
|
||||||
// 没有配置文件
|
// 没有配置文件
|
||||||
Ok(PyPlugin::new(path, modify_time, module.clone().unbind()))
|
Ok(PyPlugin {
|
||||||
|
file_path: path,
|
||||||
|
changed_time,
|
||||||
|
py_module: module.clone().into_any().unbind(),
|
||||||
|
enabled: true,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
warn!(
|
warn!(
|
||||||
"加载 Python 插件 {:?} 的配置文件信息时失败:返回的不是 [str, str]",
|
"加载 Python 插件 {:?} 的配置文件信息时失败:返回的不是 [str, str]",
|
||||||
path
|
path
|
||||||
);
|
);
|
||||||
Err(PyTypeError::new_err("返回的不是 [str, str]".to_string()))
|
Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
|
||||||
|
"返回的不是 [str, str]".to_string(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -404,7 +402,12 @@ impl TryFrom<RawPyPlugin> for PyPlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(PyPlugin::new(path, modify_time, module.clone().unbind()))
|
Ok(PyPlugin {
|
||||||
|
file_path: path,
|
||||||
|
changed_time,
|
||||||
|
py_module: module.clone().into_any().unbind(),
|
||||||
|
enabled: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -467,8 +470,9 @@ pub fn py_module_from_code(content: &str, path: &Path) -> PyResult<Py<PyModule>>
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_c_str(),
|
.as_c_str(),
|
||||||
// !!!! 请注意, 一定要给他一个名字, cpython 会自动把后面的重名模块覆盖掉前面的
|
// !!!! 请注意, 一定要给他一个名字, cpython 会自动把后面的重名模块覆盖掉前面的
|
||||||
)?;
|
)
|
||||||
Ok(module.unbind())
|
.map(|module| module.unbind());
|
||||||
|
module
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,8 +486,8 @@ pub fn load_py_file(path: &PathBuf) -> std::io::Result<RawPyPlugin> {
|
||||||
|
|
||||||
fn init_py_with_env_path(path: &str) {
|
fn init_py_with_env_path(path: &str) {
|
||||||
unsafe {
|
unsafe {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "linux")]
|
||||||
use std::ffi::OsStr;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
use std::os::windows::ffi::OsStrExt;
|
use std::os::windows::ffi::OsStrExt;
|
||||||
|
|
||||||
|
@ -493,9 +497,6 @@ fn init_py_with_env_path(path: &str) {
|
||||||
// pyo3::ffi::PyConfig_InitIsolatedConfig(config_ptr);
|
// pyo3::ffi::PyConfig_InitIsolatedConfig(config_ptr);
|
||||||
pyo3::ffi::PyConfig_InitPythonConfig(config_ptr);
|
pyo3::ffi::PyConfig_InitPythonConfig(config_ptr);
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
let wide_path = path.as_bytes().iter().map(|i| *i as i32).collect::<Vec<i32>>();
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
let wide_path = OsStr::new(path).encode_wide().chain(Some(0)).collect::<Vec<u16>>();
|
let wide_path = OsStr::new(path).encode_wide().chain(Some(0)).collect::<Vec<u16>>();
|
||||||
|
|
||||||
// 设置 prefix 和 exec_prefix
|
// 设置 prefix 和 exec_prefix
|
||||||
|
@ -516,7 +517,7 @@ fn init_py_with_env_path(path: &str) {
|
||||||
event!(Level::INFO, "根据配置初始化 python 完成");
|
event!(Level::INFO, "根据配置初始化 python 完成");
|
||||||
}
|
}
|
||||||
pyo3::ffi::_PyStatus_TYPE::_PyStatus_TYPE_EXIT => {
|
pyo3::ffi::_PyStatus_TYPE::_PyStatus_TYPE_EXIT => {
|
||||||
event!(Level::ERROR, "不对啊, 怎么刚刚初始化 Python 就 EXIT 了");
|
event!(Level::ERROR, "初始化 python 时发生错误: EXIT");
|
||||||
}
|
}
|
||||||
pyo3::ffi::_PyStatus_TYPE::_PyStatus_TYPE_ERROR => {
|
pyo3::ffi::_PyStatus_TYPE::_PyStatus_TYPE_ERROR => {
|
||||||
event!(Level::ERROR, "初始化 python 时发生错误: ERROR");
|
event!(Level::ERROR, "初始化 python 时发生错误: ERROR");
|
||||||
|
@ -532,15 +533,9 @@ pub fn init_py() {
|
||||||
let span = span!(Level::INFO, "py init");
|
let span = span!(Level::INFO, "py init");
|
||||||
let _enter = span.enter();
|
let _enter = span.enter();
|
||||||
|
|
||||||
event!(Level::INFO, "开始初始化 python");
|
|
||||||
|
|
||||||
// 注册东西
|
|
||||||
class::regist_class();
|
|
||||||
|
|
||||||
let plugin_path = MainStatus::global_config().py().plugin_path;
|
let plugin_path = MainStatus::global_config().py().plugin_path;
|
||||||
|
|
||||||
let cli_args = std::env::args().collect::<Vec<String>>();
|
let cli_args = std::env::args().collect::<Vec<String>>();
|
||||||
|
|
||||||
if cli_args.contains(&"-env".to_string()) {
|
if cli_args.contains(&"-env".to_string()) {
|
||||||
let env_path = cli_args.iter().find(|&arg| arg != "-env").expect("未找到 -env 参数的值");
|
let env_path = cli_args.iter().find(|&arg| arg != "-env").expect("未找到 -env 参数的值");
|
||||||
event!(Level::INFO, "找到 -env 参数: {} 正在初始化", env_path);
|
event!(Level::INFO, "找到 -env 参数: {} 正在初始化", env_path);
|
||||||
|
@ -577,13 +572,18 @@ pub async fn post_py() -> anyhow::Result<()> {
|
||||||
status.config.sync_status_to_config();
|
status.config.sync_status_to_config();
|
||||||
status.config.write_to_default()?;
|
status.config.write_to_default()?;
|
||||||
|
|
||||||
stop_tasks().await?;
|
stop_tasks().await;
|
||||||
|
unsafe {
|
||||||
|
if !pyo3::ffi::Py_FinalizeEx() == 0 {
|
||||||
|
event!(Level::ERROR, "Python 退出失败 (不过应该无所谓)");
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn stop_tasks() -> Result<(), PyPluginError> {
|
async fn stop_tasks() {
|
||||||
if call::PY_TASKS.lock().await.is_empty() {
|
if call::PY_TASKS.lock().await.is_empty() {
|
||||||
return Ok(());
|
return;
|
||||||
}
|
}
|
||||||
let waiter = tokio::spawn(async {
|
let waiter = tokio::spawn(async {
|
||||||
call::PY_TASKS.lock().await.join_all().await;
|
call::PY_TASKS.lock().await.join_all().await;
|
||||||
|
@ -591,11 +591,10 @@ async fn stop_tasks() -> Result<(), PyPluginError> {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
_ = waiter => {
|
_ = waiter => {
|
||||||
event!(Level::INFO, "Python 任务完成");
|
event!(Level::INFO, "Python 任务完成");
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
_ = tokio::signal::ctrl_c() => {
|
_ = tokio::signal::ctrl_c() => {
|
||||||
event!(Level::WARN, "正在强制结束 Python 任务");
|
call::PY_TASKS.lock().await.cancel_all();
|
||||||
Err(PyPluginError::PluginNotStopped)
|
event!(Level::INFO, "Python 任务被中断");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::MAIN_STATUS;
|
|
||||||
use crate::config::BotConfig;
|
use crate::config::BotConfig;
|
||||||
|
use crate::MAIN_STATUS;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct BotStatus {
|
pub struct BotStatus {
|
||||||
|
|
|
@ -9,13 +9,13 @@ use reqwest::ClientBuilder as reqwest_ClientBuilder;
|
||||||
use rust_socketio::async_callback;
|
use rust_socketio::async_callback;
|
||||||
use rust_socketio::asynchronous::{Client, ClientBuilder};
|
use rust_socketio::asynchronous::{Client, ClientBuilder};
|
||||||
use rust_socketio::{Event, Payload, TransportType};
|
use rust_socketio::{Event, Payload, TransportType};
|
||||||
use serde_json::{Value, json};
|
use serde_json::{json, Value};
|
||||||
use tracing::{Level, event, span};
|
use tracing::{event, span, Level};
|
||||||
|
|
||||||
use crate::config::TailchatConfig;
|
use crate::config::TailchatConfig;
|
||||||
use crate::data_struct::tailchat::status::{BotStatus, LoginData};
|
use crate::data_struct::tailchat::status::{BotStatus, LoginData};
|
||||||
use crate::error::{ClientResult, TailchatError};
|
use crate::error::{ClientResult, TailchatError};
|
||||||
use crate::{StopGetter, async_any_callback_with_state, async_callback_with_state, version_str};
|
use crate::{async_any_callback_with_state, async_callback_with_state, version_str, StopGetter};
|
||||||
|
|
||||||
pub async fn start_tailchat(
|
pub async fn start_tailchat(
|
||||||
config: TailchatConfig,
|
config: TailchatConfig,
|
||||||
|
|
|
@ -4,8 +4,8 @@ use crate::data_struct::tailchat::messages::SendingMessage;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use reqwest::multipart;
|
use reqwest::multipart;
|
||||||
use rust_socketio::asynchronous::Client;
|
use rust_socketio::asynchronous::Client;
|
||||||
use serde_json::{Value, json};
|
use serde_json::{json, Value};
|
||||||
use tracing::{Level, event, span};
|
use tracing::{event, span, Level};
|
||||||
|
|
||||||
pub async fn send_message(client: &Client, message: &SendingMessage) -> bool {
|
pub async fn send_message(client: &Client, message: &SendingMessage) -> bool {
|
||||||
let span = span!(Level::INFO, "tailchat send message");
|
let span = span!(Level::INFO, "tailchat send message");
|
||||||
|
|
|
@ -3,14 +3,14 @@ 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::{Level, event, info};
|
use tracing::{event, info, Level};
|
||||||
|
|
||||||
use crate::data_struct::tailchat::messages::ReceiveMessage;
|
use crate::data_struct::tailchat::messages::ReceiveMessage;
|
||||||
use crate::data_struct::tailchat::status::{BotStatus, UpdateDMConverse};
|
use crate::data_struct::tailchat::status::{BotStatus, UpdateDMConverse};
|
||||||
use crate::py::PyStatus;
|
|
||||||
use crate::py::call::tailchat_new_message_py;
|
use crate::py::call::tailchat_new_message_py;
|
||||||
|
use crate::py::PyStatus;
|
||||||
use crate::tailchat::client::{emit_join_room, send_message};
|
use crate::tailchat::client::{emit_join_room, send_message};
|
||||||
use crate::{MainStatus, VERSION, client_id, help_msg, version_str};
|
use crate::{client_id, help_msg, version_str, MainStatus, VERSION};
|
||||||
|
|
||||||
/// 所有
|
/// 所有
|
||||||
pub async fn any_event(event: Event, payload: Payload, _client: Client, _status: Arc<BotStatus>) {
|
pub async fn any_event(event: Event, payload: Payload, _client: Client, _status: Arc<BotStatus>) {
|
||||||
|
|
382
news.md
382
news.md
|
@ -1,5 +1,383 @@
|
||||||
# 更新日志
|
# 更新日志
|
||||||
|
|
||||||
## [0.9](./news/0-9.md)
|
## 0.8.2
|
||||||
|
|
||||||
## [0.2 ~ 0.8](./news/old.md)
|
- ica 兼容版本号更新到 `2.12.28`
|
||||||
|
- 现在支持通过读取环境变量里的 `VIRTUAL_ENV` 来获取虚拟环境路径
|
||||||
|
- 用于在虚拟环境中运行插件
|
||||||
|
- 添加了 `-h` 参数
|
||||||
|
- 用于展示帮助信息
|
||||||
|
- 添加了 `-env` 参数
|
||||||
|
- 用于指定 python 插件的虚拟环境路径
|
||||||
|
- 会覆盖环境变量中的 `VIRTUAL_ENV`
|
||||||
|
- 现在加入了默认的配置文件路径 `./config.toml`
|
||||||
|
- 现在会记录所有的 python 运行中 task 了
|
||||||
|
- 也会在退出的时候等待所有的 task 结束
|
||||||
|
- 二次 ctrl-c 会立即退出
|
||||||
|
- 改进了一下 ica 的新消息显示
|
||||||
|
- 添加了 ica 链接用时的显示
|
||||||
|
|
||||||
|
### ica&tailchat 2.0.0
|
||||||
|
|
||||||
|
- BREAKING CHANGE
|
||||||
|
- 现在 `CONFIG_DATA` 为一个 `str | bytes`
|
||||||
|
- 用于存储插件的配置信息
|
||||||
|
- 需要自行解析
|
||||||
|
- 现在 `on_config` 函数签名为 `on_config = Callable[[bytes], None]`
|
||||||
|
- 用于接收配置信息
|
||||||
|
- 现在使用 `require_config = Callable[[None], str, bytes | str]` 函数来请求配置信息
|
||||||
|
- 用于请求配置信息
|
||||||
|
- `str` 为配置文件名
|
||||||
|
- `bytes | str` 为配置文件默认内容
|
||||||
|
- 以 `bytes` 形式或者 `str` 形式均可
|
||||||
|
|
||||||
|
### ica 1.6.7
|
||||||
|
|
||||||
|
- 为 `IcaClinet` 添加了 `py_tasks_count -> int` 属性
|
||||||
|
- 用于获取当前运行中的 python task 数量
|
||||||
|
|
||||||
|
### tailchat 1.2.6
|
||||||
|
|
||||||
|
- 为 `TailchatClient` 添加了 `py_tasks_count -> int` 属性
|
||||||
|
- 用于获取当前运行中的 python task 数量
|
||||||
|
|
||||||
|
## 0.8.1
|
||||||
|
|
||||||
|
- 修复了 Python 插件状态写入的时候写入路径错误的问题
|
||||||
|
- `ica-typing` 加入了 `from __future__ import annotations`
|
||||||
|
- 这样就可以随便用 typing 了
|
||||||
|
- 把 NewType 都扬了
|
||||||
|
|
||||||
|
### ica 1.6.6
|
||||||
|
|
||||||
|
- 修复了 `send_poke` api 的问题
|
||||||
|
- 现在可以正常使用了
|
||||||
|
|
||||||
|
## 0.8.0
|
||||||
|
|
||||||
|
- ica 兼容版本号更新到 ~~`2.12.24`~~ `2.12.26`
|
||||||
|
- 从 `py::PyStatus` 开始进行一个 `static mut` -> `static mut OnceLock` 的改造
|
||||||
|
- 用于看着更舒服(逃)
|
||||||
|
- 部分重构了一下 读取插件启用状态 的配置文件的代码
|
||||||
|
- 现在 `/bot-help` 会直接输出实际的 client id, 而不是给你一个默认的 `<client-id>`
|
||||||
|
|
||||||
|
### ica 1.6.5
|
||||||
|
|
||||||
|
- 添加了 `send_room_sign_in` api
|
||||||
|
- 用于发送群签到信息
|
||||||
|
- socketio event: `sendGroupSign`
|
||||||
|
- 添加了 `send_poke` api
|
||||||
|
- 用于发送戳一戳
|
||||||
|
- 可以指定群的某个人
|
||||||
|
- 或者指定好友
|
||||||
|
- 目前还是有点问题
|
||||||
|
- socketio event: `sendGroupPoke`
|
||||||
|
- 添加了 `reload_plugin_status` api
|
||||||
|
- 用于重新加载插件状态
|
||||||
|
- 添加了 `reload_plugin(plugin_name: str)` api
|
||||||
|
- 用于重新加载指定插件
|
||||||
|
- 添加了 `set_plugin_status(plugin_name: str, status: bool)` api
|
||||||
|
- 用于设置插件的启用状态
|
||||||
|
- 添加了 `get_plugin_status(plugin_name: str) -> bool` api
|
||||||
|
- 用于获取插件的启用状态
|
||||||
|
- 添加了 `sync_status_to_config` api
|
||||||
|
- 用于将内存中的插件状态同步到配置文件中
|
||||||
|
|
||||||
|
### tailchat 1.2.5
|
||||||
|
|
||||||
|
- 添加了 `reload_plugin_status` api
|
||||||
|
- 用于重新加载插件状态
|
||||||
|
- 添加了 `reload_plugin(plugin_name: str)` api
|
||||||
|
- 用于重新加载指定插件
|
||||||
|
- 添加了 `set_plugin_status(plugin_name: str, status: bool)` api
|
||||||
|
- 用于设置插件的启用状态
|
||||||
|
- 添加了 `get_plugin_status(plugin_name: str) -> bool` api
|
||||||
|
- 用于获取插件的启用状态
|
||||||
|
- 添加了 `sync_status_to_config` api
|
||||||
|
- 用于将内存中的插件状态同步到配置文件中
|
||||||
|
|
||||||
|
## 0.7.4
|
||||||
|
|
||||||
|
- ica 兼容版本号更新到 `2.12.23`
|
||||||
|
- 通过一个手动 json patch 修复了因为 icalingua 的奇怪类型问题导致的 bug
|
||||||
|
- [icalingua issue](https://github.com/Icalingua-plus-plus/Icalingua-plus-plus/issues/793)
|
||||||
|
|
||||||
|
## 0.7.3
|
||||||
|
|
||||||
|
- 也许修复了删除插件不会立即生效的问题
|
||||||
|
- ica 兼容版本号更新到 `2.12.21`
|
||||||
|
添加了一些新的 api
|
||||||
|
|
||||||
|
### ica 1.6.4
|
||||||
|
|
||||||
|
- 给 `SendMessagePy`
|
||||||
|
- 添加了 `remove_reply` 方法
|
||||||
|
- 用于取消回复状态
|
||||||
|
- 删除了 `Room` 的 `auto_download` 和 `download_path` 字段
|
||||||
|
- 因为这两个字段也没啥用
|
||||||
|
|
||||||
|
### tailcaht 1.2.4
|
||||||
|
|
||||||
|
- 给 `TailchatClientPy`
|
||||||
|
- 添加了 `new_message` 方法
|
||||||
|
- 用于创建新的消息
|
||||||
|
- 给 `TailchatSendingMessagePy`
|
||||||
|
- 添加了 `clear_meta` 功能
|
||||||
|
- 用于清除 `meta` 字段
|
||||||
|
- 可以用来取消回复状态
|
||||||
|
|
||||||
|
## 0.7.2
|
||||||
|
|
||||||
|
- 修复了一些 ica 和 tailchat 表现不一致的问题(捂脸)
|
||||||
|
|
||||||
|
## 0.7.1
|
||||||
|
|
||||||
|
- 两个 api 版本号分别升级到 `1.6.3(ica)` 和 `1.2.3(tailchat)`
|
||||||
|
- 加入了 `client_id`
|
||||||
|
- 用的 startup time hash 一遍取后六位
|
||||||
|
- 以及也有 python 侧的 `client_id` api
|
||||||
|
- 修复了上个版本其实没有写 python 侧 `version_str` api 的问题
|
||||||
|
|
||||||
|
## 0.7.0
|
||||||
|
|
||||||
|
> 我决定叫他 0.7.0
|
||||||
|
> 因为修改太多了.png
|
||||||
|
|
||||||
|
- 加入了 禁用/启用 插件功能
|
||||||
|
- 现在会在插件加载时警告你的插件原来定义了 `CONFIG_DATA` 这一项
|
||||||
|
- `IcaNewMessage` 添加了新的 api
|
||||||
|
- `get_sender_name` 获取发送人昵称
|
||||||
|
- `ica` 兼容版本号 `2.12.11` -> `2.12.12`
|
||||||
|
- 加入了 `STABLE` 信息, 用于标记稳定版本
|
||||||
|
- 不少配置文件项加上了默认值
|
||||||
|
- 添加了 `version_str() -> String` 用于方便的获取版本信息
|
||||||
|
- 同样在 `py` 侧也有 `version_str` 方法
|
||||||
|
- 加入了 `/help` 命令
|
||||||
|
- 用于获取帮助信息
|
||||||
|
- 加入了 `/bot-ls`
|
||||||
|
- 用于展示所有插件的信息
|
||||||
|
- 加入了 `/bot-enable` 和 `/bot-disable`
|
||||||
|
- 用于启用/禁用插件
|
||||||
|
|
||||||
|
## 0.6.10
|
||||||
|
|
||||||
|
- 加了点东西 (?)
|
||||||
|
|
||||||
|
## 0.6.9
|
||||||
|
|
||||||
|
我决定立即发布 0.6.9
|
||||||
|
|
||||||
|
- 添加了 `Client.startup_time() -> datetime` 方法
|
||||||
|
- 用于获取 bot 启动时间
|
||||||
|
- 这样就可以经常吹我 bot 跑了多久了 ( ˘•ω•˘ )
|
||||||
|
- 顺手加上了 `/bot-uptime` 命令
|
||||||
|
- 可以获取 bot 运行时间
|
||||||
|
- 谢谢 GitHub Copilot 的帮助
|
||||||
|
|
||||||
|
## 0.6.8
|
||||||
|
|
||||||
|
- 修复了一堆拼写错误
|
||||||
|
- 太难绷了
|
||||||
|
- `TailchatReciveMessagePy` -> `TailchatReceiveMessagePy`
|
||||||
|
- `ReciveMessage` -> `ReceiveMessage`
|
||||||
|
- `ReceiveMessage::meta`
|
||||||
|
- 从 `JsonValue` 改成 `Option<JsonValue>`
|
||||||
|
- 用来解决发图片的时候没有 `meta` 字段的问题
|
||||||
|
- 去除了自带的两个 macro
|
||||||
|
- `wrap_callback` 和 `wrap_any_callback`
|
||||||
|
- 因为现在他俩已经进到 `rust_socketio` 里啦
|
||||||
|
- 添加了新的 macro
|
||||||
|
- 支持了 `TailchatReceiveMessagePy` 的 `is_from_self` 方法
|
||||||
|
- 用于判断是否是自己发的消息
|
||||||
|
|
||||||
|
## 0.6.7
|
||||||
|
|
||||||
|
游学回来啦
|
||||||
|
|
||||||
|
- 处理了一些 tailchat 的特殊情况
|
||||||
|
- 比如 message 里面的 `GroupId` 实际上是可选的, 在私聊中没有这一项
|
||||||
|
- 忽略了所有的 `__v` (用于数据库记录信息的, bot不需要管)
|
||||||
|
- 作者原话 `不用管。数据库记录版本`
|
||||||
|
- 修复了如果没法解析新的信息, 会 panic 的问题
|
||||||
|
- `ica_typing.py`
|
||||||
|
- 补充了 `TailchatSendingMessage` 的 `group_id` 和 `converse_id` 字段
|
||||||
|
- 把 `group_id` 的设置和返回都改成了 `Optional[GroupId]`
|
||||||
|
- tailchat 的 API 也差点意思就是了(逃)
|
||||||
|
- 处理了 icalingua 的 `renewMessage` 事件 (其实就是直接忽略掉了)
|
||||||
|
|
||||||
|
## 0.6.6
|
||||||
|
|
||||||
|
游学之前最后一次更新
|
||||||
|
其实也就五天
|
||||||
|
|
||||||
|
正式支持了 tailchat 端
|
||||||
|
好耶!
|
||||||
|
|
||||||
|
[!note]
|
||||||
|
|
||||||
|
```text
|
||||||
|
notice_room = []
|
||||||
|
notice_start = true
|
||||||
|
|
||||||
|
admin_list = []
|
||||||
|
filter_list = []
|
||||||
|
```
|
||||||
|
|
||||||
|
的功能暂时不支持
|
||||||
|
|
||||||
|
## 0.6.5
|
||||||
|
|
||||||
|
怎么就突然 0.6.5 了
|
||||||
|
我也不造啊
|
||||||
|
|
||||||
|
- 反正支持了 tailchat 的信息接受
|
||||||
|
- 但是需要你在对面服务端打开 `DISABLE_MESSAGEPACK` 环境变量
|
||||||
|
- 能用就行
|
||||||
|
|
||||||
|
- 现在 `update_online_data` 不会再以 INFO 级别显示了
|
||||||
|
- `update_all_room` 同上
|
||||||
|
|
||||||
|
## 0.6.2
|
||||||
|
|
||||||
|
- 添加 API
|
||||||
|
- `NewMessage.set_img` 用于设置消息的图片
|
||||||
|
- `IcaSendMessage.set_img` 用于设置消息的图片 (python)
|
||||||
|
|
||||||
|
## 0.6.1
|
||||||
|
|
||||||
|
还是没写完 tailchat 支持
|
||||||
|
因为 rust_socketio 还是没写好 serdelizer 的支持
|
||||||
|
|
||||||
|
- 正在添加发送图片的 api
|
||||||
|
|
||||||
|
## 0.6.0-dev
|
||||||
|
|
||||||
|
- 去除了 matrix 的支持
|
||||||
|
- 淦哦
|
||||||
|
- 去除了相应代码和依赖
|
||||||
|
- 去除了 Python 侧代码
|
||||||
|
- 向 tailchat (typescript 低头)
|
||||||
|
|
||||||
|
- 修复了没法编译的问题(
|
||||||
|
|
||||||
|
## 0.5.3
|
||||||
|
|
||||||
|
修复了 Icalingua 断开时 如果 socketio 已经断开会导致程序 返回 Error 的问题
|
||||||
|
以及还有一些别的修复就是了
|
||||||
|
|
||||||
|
- Python 端修改
|
||||||
|
- `on_message` -> `on_ica_message`
|
||||||
|
- `on_delete_message` -> `on_ica_delete_message`
|
||||||
|
- 添加 `on_matrix_message`
|
||||||
|
|
||||||
|
## 0.5.1/2
|
||||||
|
|
||||||
|
重构了一整波, 还没改 `ica-typing.py` 的代码
|
||||||
|
但至少能用了
|
||||||
|
|
||||||
|
- Ica 版本号 `1.4.0`
|
||||||
|
- Matrix 版本号 `0.1.0`
|
||||||
|
|
||||||
|
## 0.5.0
|
||||||
|
|
||||||
|
准备接入 `Matrix`
|
||||||
|
|
||||||
|
去掉 `pyo3-async` 的依赖
|
||||||
|
|
||||||
|
## 0.4.12
|
||||||
|
|
||||||
|
把 0.4.11 的遗留问题修完了
|
||||||
|
|
||||||
|
## 0.4.11
|
||||||
|
|
||||||
|
这几天就是在刷版本号的感觉
|
||||||
|
|
||||||
|
- 添加
|
||||||
|
- `DeleteMessage` 用于删除消息
|
||||||
|
- `NewMessage.as_delete` 用于将消息转换为删除消息
|
||||||
|
- `client::delete_message` 用于删除消息
|
||||||
|
- `client::fetch_history` 用于获取历史消息 TODO
|
||||||
|
- `py::class::DeleteMessagePy` 用于删除消息 的 Python 侧 API
|
||||||
|
- `py::class::IcaClientPy.delete_message` 用于删除消息 的 Python 侧 API
|
||||||
|
- `IcalinguaStatus.current_loaded_messages_count`
|
||||||
|
- 用于以后加载信息计数
|
||||||
|
- 修改
|
||||||
|
- `py::class::IcaStatusPy`
|
||||||
|
- 大部分方法从手动 `unsafe` + `Option`
|
||||||
|
- 改成直接调用 `IcalinguaStatus` 的方法
|
||||||
|
- `IcalinguaStatus`
|
||||||
|
- 所有方法均改成 直接对着 `IcalinguaStatus` 的方法调用
|
||||||
|
- 补全没有的方法
|
||||||
|
|
||||||
|
## 0.4.10
|
||||||
|
|
||||||
|
好家伙, 我感觉都快能叫 0.5 了
|
||||||
|
修改了一些内部数据结构, 使得插件更加稳定
|
||||||
|
|
||||||
|
添加了 `rustfmt.toml` 用于格式化代码
|
||||||
|
**注意**: 请在提交代码前使用 `cargo +nightly fmt` 格式化代码
|
||||||
|
|
||||||
|
修复了 `Message` 解析 `replyMessage` 字段是 如果是 null 则会解析失败的问题
|
||||||
|
|
||||||
|
## 0.4.9
|
||||||
|
|
||||||
|
修复了 Python 插件运行错误会导致整个程序崩溃的问题
|
||||||
|
|
||||||
|
## 0.4.8
|
||||||
|
|
||||||
|
添加了 `filter_list` 用于过滤特定人的消息
|
||||||
|
|
||||||
|
## 0.4.7
|
||||||
|
|
||||||
|
修复了重载时如果代码有问题会直接 panic 的问题
|
||||||
|
|
||||||
|
## 0.4.6
|
||||||
|
|
||||||
|
现在更适合部署了
|
||||||
|
|
||||||
|
## 0.4.5
|
||||||
|
|
||||||
|
添加 `is_reply` api 到 `NewMessagePy`
|
||||||
|
|
||||||
|
## 0.4.4
|
||||||
|
|
||||||
|
现在正式支持 Python 插件了
|
||||||
|
`/bmcl` 也迁移到了 Python 插件版本
|
||||||
|
|
||||||
|
## 0.4.3
|
||||||
|
|
||||||
|
噫! 好! 我成了!
|
||||||
|
|
||||||
|
## 0.4.2
|
||||||
|
|
||||||
|
现在是 async 版本啦!
|
||||||
|
|
||||||
|
## 0.4.1
|
||||||
|
|
||||||
|
现在能发送登录信息啦
|
||||||
|
|
||||||
|
## 0.4.0
|
||||||
|
|
||||||
|
使用 Rust 从头实现一遍
|
||||||
|
\能登录啦/
|
||||||
|
|
||||||
|
## 0.3.3
|
||||||
|
|
||||||
|
适配 Rust 端的配置文件修改
|
||||||
|
|
||||||
|
## 0.3.1/2
|
||||||
|
|
||||||
|
改进 `/bmcl` 的细节
|
||||||
|
|
||||||
|
## 0.3.0
|
||||||
|
|
||||||
|
合并了 dongdigua 的代码, 把消息处理部分分离
|
||||||
|
现在代码更阳间了(喜
|
||||||
|
|
||||||
|
## 0.2.3
|
||||||
|
|
||||||
|
添加了 `/bmcl` 请求 bmclapi 状态
|
||||||
|
|
||||||
|
## 0.2.2
|
||||||
|
|
||||||
|
重构了一波整体代码
|
||||||
|
|
38
news/0-9.md
38
news/0-9.md
|
@ -1,38 +0,0 @@
|
||||||
# 0.9 更新日志
|
|
||||||
|
|
||||||
## 0.9.0
|
|
||||||
|
|
||||||
- 修复了 Python 插件停不下来就真的停不下来的问题
|
|
||||||
- 让初始化的时候 插件启/禁状态显示更明显了
|
|
||||||
- 有颜色啦!
|
|
||||||
- 加了不少颜色
|
|
||||||
|
|
||||||
### ica 2.0.1
|
|
||||||
|
|
||||||
> 添加了 `Room` 相关的 api
|
|
||||||
|
|
||||||
- `IcaStatus` 添加了 `rooms(self) -> list[IcaRoom]` 方法
|
|
||||||
- 用于获取当前所有的房间
|
|
||||||
|
|
||||||
- 添加了 `IcaRoom` 类
|
|
||||||
- 用于表示一个房间
|
|
||||||
- `room_id -> int` 群号 (i64)
|
|
||||||
|
|
||||||
- `def is_group(self) -> bool` 是否为群聊
|
|
||||||
- `def is_chat(self) -> bool` 是否为私聊
|
|
||||||
|
|
||||||
- `room_name -> int` 群名 (String)
|
|
||||||
- `unread_count -> int` 未读消息数 (u64)
|
|
||||||
- `priority -> int` 优先级 (u8)
|
|
||||||
- `utime -> int` 最后活跃时间 (unix sec * 1000)
|
|
||||||
|
|
||||||
- `def new_message_to(self, content: str) -> IcaSendMessage`
|
|
||||||
- 用于创建一条指向这个房间的消息
|
|
||||||
|
|
||||||
> 添加了 Ica 侧的相关配置获取
|
|
||||||
|
|
||||||
- `IcaStatus` 添加了 `admins(self) -> list[UserId]` 方法
|
|
||||||
- 用于获取当前所有的管理员
|
|
||||||
|
|
||||||
- `IcaStatus` 添加了 `blocked(self) -> list[UserId]` 方法
|
|
||||||
- 用于获取当前所有的被屏蔽的人
|
|
383
news/olds.md
383
news/olds.md
|
@ -1,383 +0,0 @@
|
||||||
# 更新日志 (0.2 ~ 0.8)
|
|
||||||
|
|
||||||
## 0.8.2
|
|
||||||
|
|
||||||
- ica 兼容版本号更新到 `2.12.28`
|
|
||||||
- 现在支持通过读取环境变量里的 `VIRTUAL_ENV` 来获取虚拟环境路径
|
|
||||||
- 用于在虚拟环境中运行插件
|
|
||||||
- 添加了 `-h` 参数
|
|
||||||
- 用于展示帮助信息
|
|
||||||
- 添加了 `-env` 参数
|
|
||||||
- 用于指定 python 插件的虚拟环境路径
|
|
||||||
- 会覆盖环境变量中的 `VIRTUAL_ENV`
|
|
||||||
- 现在加入了默认的配置文件路径 `./config.toml`
|
|
||||||
- 现在会记录所有的 python 运行中 task 了
|
|
||||||
- 也会在退出的时候等待所有的 task 结束
|
|
||||||
- 二次 ctrl-c 会立即退出
|
|
||||||
- 改进了一下 ica 的新消息显示
|
|
||||||
- 添加了 ica 链接用时的显示
|
|
||||||
|
|
||||||
### ica&tailchat 2.0.0
|
|
||||||
|
|
||||||
- BREAKING CHANGE
|
|
||||||
- 现在 `CONFIG_DATA` 为一个 `str | bytes`
|
|
||||||
- 用于存储插件的配置信息
|
|
||||||
- 需要自行解析
|
|
||||||
- 现在 `on_config` 函数签名为 `on_config = Callable[[bytes], None]`
|
|
||||||
- 用于接收配置信息
|
|
||||||
- 现在使用 `require_config = Callable[[None], str, bytes | str]` 函数来请求配置信息
|
|
||||||
- 用于请求配置信息
|
|
||||||
- `str` 为配置文件名
|
|
||||||
- `bytes | str` 为配置文件默认内容
|
|
||||||
- 以 `bytes` 形式或者 `str` 形式均可
|
|
||||||
|
|
||||||
### ica 1.6.7
|
|
||||||
|
|
||||||
- 为 `IcaClinet` 添加了 `py_tasks_count -> int` 属性
|
|
||||||
- 用于获取当前运行中的 python task 数量
|
|
||||||
|
|
||||||
### tailchat 1.2.6
|
|
||||||
|
|
||||||
- 为 `TailchatClient` 添加了 `py_tasks_count -> int` 属性
|
|
||||||
- 用于获取当前运行中的 python task 数量
|
|
||||||
|
|
||||||
## 0.8.1
|
|
||||||
|
|
||||||
- 修复了 Python 插件状态写入的时候写入路径错误的问题
|
|
||||||
- `ica-typing` 加入了 `from __future__ import annotations`
|
|
||||||
- 这样就可以随便用 typing 了
|
|
||||||
- 把 NewType 都扬了
|
|
||||||
|
|
||||||
### ica 1.6.6
|
|
||||||
|
|
||||||
- 修复了 `send_poke` api 的问题
|
|
||||||
- 现在可以正常使用了
|
|
||||||
|
|
||||||
## 0.8.0
|
|
||||||
|
|
||||||
- ica 兼容版本号更新到 ~~`2.12.24`~~ `2.12.26`
|
|
||||||
- 从 `py::PyStatus` 开始进行一个 `static mut` -> `static mut OnceLock` 的改造
|
|
||||||
- 用于看着更舒服(逃)
|
|
||||||
- 部分重构了一下 读取插件启用状态 的配置文件的代码
|
|
||||||
- 现在 `/bot-help` 会直接输出实际的 client id, 而不是给你一个默认的 `<client-id>`
|
|
||||||
|
|
||||||
### ica 1.6.5
|
|
||||||
|
|
||||||
- 添加了 `send_room_sign_in` api
|
|
||||||
- 用于发送群签到信息
|
|
||||||
- socketio event: `sendGroupSign`
|
|
||||||
- 添加了 `send_poke` api
|
|
||||||
- 用于发送戳一戳
|
|
||||||
- 可以指定群的某个人
|
|
||||||
- 或者指定好友
|
|
||||||
- 目前还是有点问题
|
|
||||||
- socketio event: `sendGroupPoke`
|
|
||||||
- 添加了 `reload_plugin_status` api
|
|
||||||
- 用于重新加载插件状态
|
|
||||||
- 添加了 `reload_plugin(plugin_name: str)` api
|
|
||||||
- 用于重新加载指定插件
|
|
||||||
- 添加了 `set_plugin_status(plugin_name: str, status: bool)` api
|
|
||||||
- 用于设置插件的启用状态
|
|
||||||
- 添加了 `get_plugin_status(plugin_name: str) -> bool` api
|
|
||||||
- 用于获取插件的启用状态
|
|
||||||
- 添加了 `sync_status_to_config` api
|
|
||||||
- 用于将内存中的插件状态同步到配置文件中
|
|
||||||
|
|
||||||
### tailchat 1.2.5
|
|
||||||
|
|
||||||
- 添加了 `reload_plugin_status` api
|
|
||||||
- 用于重新加载插件状态
|
|
||||||
- 添加了 `reload_plugin(plugin_name: str)` api
|
|
||||||
- 用于重新加载指定插件
|
|
||||||
- 添加了 `set_plugin_status(plugin_name: str, status: bool)` api
|
|
||||||
- 用于设置插件的启用状态
|
|
||||||
- 添加了 `get_plugin_status(plugin_name: str) -> bool` api
|
|
||||||
- 用于获取插件的启用状态
|
|
||||||
- 添加了 `sync_status_to_config` api
|
|
||||||
- 用于将内存中的插件状态同步到配置文件中
|
|
||||||
|
|
||||||
## 0.7.4
|
|
||||||
|
|
||||||
- ica 兼容版本号更新到 `2.12.23`
|
|
||||||
- 通过一个手动 json patch 修复了因为 icalingua 的奇怪类型问题导致的 bug
|
|
||||||
- [icalingua issue](https://github.com/Icalingua-plus-plus/Icalingua-plus-plus/issues/793)
|
|
||||||
|
|
||||||
## 0.7.3
|
|
||||||
|
|
||||||
- 也许修复了删除插件不会立即生效的问题
|
|
||||||
- ica 兼容版本号更新到 `2.12.21`
|
|
||||||
添加了一些新的 api
|
|
||||||
|
|
||||||
### ica 1.6.4
|
|
||||||
|
|
||||||
- 给 `SendMessagePy`
|
|
||||||
- 添加了 `remove_reply` 方法
|
|
||||||
- 用于取消回复状态
|
|
||||||
- 删除了 `Room` 的 `auto_download` 和 `download_path` 字段
|
|
||||||
- 因为这两个字段也没啥用
|
|
||||||
|
|
||||||
### tailcaht 1.2.4
|
|
||||||
|
|
||||||
- 给 `TailchatClientPy`
|
|
||||||
- 添加了 `new_message` 方法
|
|
||||||
- 用于创建新的消息
|
|
||||||
- 给 `TailchatSendingMessagePy`
|
|
||||||
- 添加了 `clear_meta` 功能
|
|
||||||
- 用于清除 `meta` 字段
|
|
||||||
- 可以用来取消回复状态
|
|
||||||
|
|
||||||
## 0.7.2
|
|
||||||
|
|
||||||
- 修复了一些 ica 和 tailchat 表现不一致的问题(捂脸)
|
|
||||||
|
|
||||||
## 0.7.1
|
|
||||||
|
|
||||||
- 两个 api 版本号分别升级到 `1.6.3(ica)` 和 `1.2.3(tailchat)`
|
|
||||||
- 加入了 `client_id`
|
|
||||||
- 用的 startup time hash 一遍取后六位
|
|
||||||
- 以及也有 python 侧的 `client_id` api
|
|
||||||
- 修复了上个版本其实没有写 python 侧 `version_str` api 的问题
|
|
||||||
|
|
||||||
## 0.7.0
|
|
||||||
|
|
||||||
> 我决定叫他 0.7.0
|
|
||||||
> 因为修改太多了.png
|
|
||||||
|
|
||||||
- 加入了 禁用/启用 插件功能
|
|
||||||
- 现在会在插件加载时警告你的插件原来定义了 `CONFIG_DATA` 这一项
|
|
||||||
- `IcaNewMessage` 添加了新的 api
|
|
||||||
- `get_sender_name` 获取发送人昵称
|
|
||||||
- `ica` 兼容版本号 `2.12.11` -> `2.12.12`
|
|
||||||
- 加入了 `STABLE` 信息, 用于标记稳定版本
|
|
||||||
- 不少配置文件项加上了默认值
|
|
||||||
- 添加了 `version_str() -> String` 用于方便的获取版本信息
|
|
||||||
- 同样在 `py` 侧也有 `version_str` 方法
|
|
||||||
- 加入了 `/help` 命令
|
|
||||||
- 用于获取帮助信息
|
|
||||||
- 加入了 `/bot-ls`
|
|
||||||
- 用于展示所有插件的信息
|
|
||||||
- 加入了 `/bot-enable` 和 `/bot-disable`
|
|
||||||
- 用于启用/禁用插件
|
|
||||||
|
|
||||||
## 0.6.10
|
|
||||||
|
|
||||||
- 加了点东西 (?)
|
|
||||||
|
|
||||||
## 0.6.9
|
|
||||||
|
|
||||||
我决定立即发布 0.6.9
|
|
||||||
|
|
||||||
- 添加了 `Client.startup_time() -> datetime` 方法
|
|
||||||
- 用于获取 bot 启动时间
|
|
||||||
- 这样就可以经常吹我 bot 跑了多久了 ( ˘•ω•˘ )
|
|
||||||
- 顺手加上了 `/bot-uptime` 命令
|
|
||||||
- 可以获取 bot 运行时间
|
|
||||||
- 谢谢 GitHub Copilot 的帮助
|
|
||||||
|
|
||||||
## 0.6.8
|
|
||||||
|
|
||||||
- 修复了一堆拼写错误
|
|
||||||
- 太难绷了
|
|
||||||
- `TailchatReciveMessagePy` -> `TailchatReceiveMessagePy`
|
|
||||||
- `ReciveMessage` -> `ReceiveMessage`
|
|
||||||
- `ReceiveMessage::meta`
|
|
||||||
- 从 `JsonValue` 改成 `Option<JsonValue>`
|
|
||||||
- 用来解决发图片的时候没有 `meta` 字段的问题
|
|
||||||
- 去除了自带的两个 macro
|
|
||||||
- `wrap_callback` 和 `wrap_any_callback`
|
|
||||||
- 因为现在他俩已经进到 `rust_socketio` 里啦
|
|
||||||
- 添加了新的 macro
|
|
||||||
- 支持了 `TailchatReceiveMessagePy` 的 `is_from_self` 方法
|
|
||||||
- 用于判断是否是自己发的消息
|
|
||||||
|
|
||||||
## 0.6.7
|
|
||||||
|
|
||||||
游学回来啦
|
|
||||||
|
|
||||||
- 处理了一些 tailchat 的特殊情况
|
|
||||||
- 比如 message 里面的 `GroupId` 实际上是可选的, 在私聊中没有这一项
|
|
||||||
- 忽略了所有的 `__v` (用于数据库记录信息的, bot不需要管)
|
|
||||||
- 作者原话 `不用管。数据库记录版本`
|
|
||||||
- 修复了如果没法解析新的信息, 会 panic 的问题
|
|
||||||
- `ica_typing.py`
|
|
||||||
- 补充了 `TailchatSendingMessage` 的 `group_id` 和 `converse_id` 字段
|
|
||||||
- 把 `group_id` 的设置和返回都改成了 `Optional[GroupId]`
|
|
||||||
- tailchat 的 API 也差点意思就是了(逃)
|
|
||||||
- 处理了 icalingua 的 `renewMessage` 事件 (其实就是直接忽略掉了)
|
|
||||||
|
|
||||||
## 0.6.6
|
|
||||||
|
|
||||||
游学之前最后一次更新
|
|
||||||
其实也就五天
|
|
||||||
|
|
||||||
正式支持了 tailchat 端
|
|
||||||
好耶!
|
|
||||||
|
|
||||||
[!note]
|
|
||||||
|
|
||||||
```text
|
|
||||||
notice_room = []
|
|
||||||
notice_start = true
|
|
||||||
|
|
||||||
admin_list = []
|
|
||||||
filter_list = []
|
|
||||||
```
|
|
||||||
|
|
||||||
的功能暂时不支持
|
|
||||||
|
|
||||||
## 0.6.5
|
|
||||||
|
|
||||||
怎么就突然 0.6.5 了
|
|
||||||
我也不造啊
|
|
||||||
|
|
||||||
- 反正支持了 tailchat 的信息接受
|
|
||||||
- 但是需要你在对面服务端打开 `DISABLE_MESSAGEPACK` 环境变量
|
|
||||||
- 能用就行
|
|
||||||
|
|
||||||
- 现在 `update_online_data` 不会再以 INFO 级别显示了
|
|
||||||
- `update_all_room` 同上
|
|
||||||
|
|
||||||
## 0.6.2
|
|
||||||
|
|
||||||
- 添加 API
|
|
||||||
- `NewMessage.set_img` 用于设置消息的图片
|
|
||||||
- `IcaSendMessage.set_img` 用于设置消息的图片 (python)
|
|
||||||
|
|
||||||
## 0.6.1
|
|
||||||
|
|
||||||
还是没写完 tailchat 支持
|
|
||||||
因为 rust_socketio 还是没写好 serdelizer 的支持
|
|
||||||
|
|
||||||
- 正在添加发送图片的 api
|
|
||||||
|
|
||||||
## 0.6.0-dev
|
|
||||||
|
|
||||||
- 去除了 matrix 的支持
|
|
||||||
- 淦哦
|
|
||||||
- 去除了相应代码和依赖
|
|
||||||
- 去除了 Python 侧代码
|
|
||||||
- 向 tailchat (typescript 低头)
|
|
||||||
|
|
||||||
- 修复了没法编译的问题(
|
|
||||||
|
|
||||||
## 0.5.3
|
|
||||||
|
|
||||||
修复了 Icalingua 断开时 如果 socketio 已经断开会导致程序 返回 Error 的问题
|
|
||||||
以及还有一些别的修复就是了
|
|
||||||
|
|
||||||
- Python 端修改
|
|
||||||
- `on_message` -> `on_ica_message`
|
|
||||||
- `on_delete_message` -> `on_ica_delete_message`
|
|
||||||
- 添加 `on_matrix_message`
|
|
||||||
|
|
||||||
## 0.5.1/2
|
|
||||||
|
|
||||||
重构了一整波, 还没改 `ica-typing.py` 的代码
|
|
||||||
但至少能用了
|
|
||||||
|
|
||||||
- Ica 版本号 `1.4.0`
|
|
||||||
- Matrix 版本号 `0.1.0`
|
|
||||||
|
|
||||||
## 0.5.0
|
|
||||||
|
|
||||||
准备接入 `Matrix`
|
|
||||||
|
|
||||||
去掉 `pyo3-async` 的依赖
|
|
||||||
|
|
||||||
## 0.4.12
|
|
||||||
|
|
||||||
把 0.4.11 的遗留问题修完了
|
|
||||||
|
|
||||||
## 0.4.11
|
|
||||||
|
|
||||||
这几天就是在刷版本号的感觉
|
|
||||||
|
|
||||||
- 添加
|
|
||||||
- `DeleteMessage` 用于删除消息
|
|
||||||
- `NewMessage.as_delete` 用于将消息转换为删除消息
|
|
||||||
- `client::delete_message` 用于删除消息
|
|
||||||
- `client::fetch_history` 用于获取历史消息 TODO
|
|
||||||
- `py::class::DeleteMessagePy` 用于删除消息 的 Python 侧 API
|
|
||||||
- `py::class::IcaClientPy.delete_message` 用于删除消息 的 Python 侧 API
|
|
||||||
- `IcalinguaStatus.current_loaded_messages_count`
|
|
||||||
- 用于以后加载信息计数
|
|
||||||
- 修改
|
|
||||||
- `py::class::IcaStatusPy`
|
|
||||||
- 大部分方法从手动 `unsafe` + `Option`
|
|
||||||
- 改成直接调用 `IcalinguaStatus` 的方法
|
|
||||||
- `IcalinguaStatus`
|
|
||||||
- 所有方法均改成 直接对着 `IcalinguaStatus` 的方法调用
|
|
||||||
- 补全没有的方法
|
|
||||||
|
|
||||||
## 0.4.10
|
|
||||||
|
|
||||||
好家伙, 我感觉都快能叫 0.5 了
|
|
||||||
修改了一些内部数据结构, 使得插件更加稳定
|
|
||||||
|
|
||||||
添加了 `rustfmt.toml` 用于格式化代码
|
|
||||||
**注意**: 请在提交代码前使用 `cargo +nightly fmt` 格式化代码
|
|
||||||
|
|
||||||
修复了 `Message` 解析 `replyMessage` 字段是 如果是 null 则会解析失败的问题
|
|
||||||
|
|
||||||
## 0.4.9
|
|
||||||
|
|
||||||
修复了 Python 插件运行错误会导致整个程序崩溃的问题
|
|
||||||
|
|
||||||
## 0.4.8
|
|
||||||
|
|
||||||
添加了 `filter_list` 用于过滤特定人的消息
|
|
||||||
|
|
||||||
## 0.4.7
|
|
||||||
|
|
||||||
修复了重载时如果代码有问题会直接 panic 的问题
|
|
||||||
|
|
||||||
## 0.4.6
|
|
||||||
|
|
||||||
现在更适合部署了
|
|
||||||
|
|
||||||
## 0.4.5
|
|
||||||
|
|
||||||
添加 `is_reply` api 到 `NewMessagePy`
|
|
||||||
|
|
||||||
## 0.4.4
|
|
||||||
|
|
||||||
现在正式支持 Python 插件了
|
|
||||||
`/bmcl` 也迁移到了 Python 插件版本
|
|
||||||
|
|
||||||
## 0.4.3
|
|
||||||
|
|
||||||
噫! 好! 我成了!
|
|
||||||
|
|
||||||
## 0.4.2
|
|
||||||
|
|
||||||
现在是 async 版本啦!
|
|
||||||
|
|
||||||
## 0.4.1
|
|
||||||
|
|
||||||
现在能发送登录信息啦
|
|
||||||
|
|
||||||
## 0.4.0
|
|
||||||
|
|
||||||
使用 Rust 从头实现一遍
|
|
||||||
\能登录啦/
|
|
||||||
|
|
||||||
## 0.3.3
|
|
||||||
|
|
||||||
适配 Rust 端的配置文件修改
|
|
||||||
|
|
||||||
## 0.3.1/2
|
|
||||||
|
|
||||||
改进 `/bmcl` 的细节
|
|
||||||
|
|
||||||
## 0.3.0
|
|
||||||
|
|
||||||
合并了 dongdigua 的代码, 把消息处理部分分离
|
|
||||||
现在代码更阳间了(喜
|
|
||||||
|
|
||||||
## 0.2.3
|
|
||||||
|
|
||||||
添加了 `/bmcl` 请求 bmclapi 状态
|
|
||||||
|
|
||||||
## 0.2.2
|
|
||||||
|
|
||||||
重构了一波整体代码
|
|
|
@ -1,8 +1,9 @@
|
||||||
# icalingua bot
|
# icalingua bot
|
||||||
|
|
||||||
这是一个基于 icalingua-bridge 的 bot
|
这是一个基于 icalingua docker 版的 bot
|
||||||
|
|
||||||
[插件市场(确信)](https://github.com/shenjackyuanjie/shenbot-plugins)
|
> 出于某个企鹅, 和保护 作者 和 原项目 ( ica ) 的原因 \
|
||||||
|
> 功能请自行理解
|
||||||
|
|
||||||
## 通用环境准备
|
## 通用环境准备
|
||||||
|
|
||||||
|
@ -14,14 +15,12 @@
|
||||||
choco install python
|
choco install python
|
||||||
# 或者
|
# 或者
|
||||||
scoop install python
|
scoop install python
|
||||||
# 又或者
|
|
||||||
uv venv
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- 启动 icalingua 后端
|
- 启动 icalingua 后端
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 用你自己的方法启动你的 icalingua-bridge
|
# 用你自己的方法启动你的 icalingua 后端
|
||||||
# 例如
|
# 例如
|
||||||
docker start icalingua
|
docker start icalingua
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
|
|
Loading…
Reference in New Issue
Block a user