diff --git a/Cargo.lock b/Cargo.lock index 6c6fea6..a5299c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -910,7 +910,7 @@ dependencies = [ [[package]] name = "ica-rs" -version = "0.5.2" +version = "0.5.3" dependencies = [ "anyhow", "chrono", diff --git a/ica-rs/Cargo.toml b/ica-rs/Cargo.toml index f16fb44..5887a02 100644 --- a/ica-rs/Cargo.toml +++ b/ica-rs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ica-rs" -version = "0.5.2" +version = "0.5.3" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/ica-rs/ica_typing.py b/ica-rs/ica_typing.py index f0083ee..659d757 100644 --- a/ica-rs/ica_typing.py +++ b/ica-rs/ica_typing.py @@ -47,18 +47,18 @@ class IcaStatus: ... -class ReplyMessage: +class IcaReplyMessage: ... -class SendMessage: +class IcaSendMessage: @property def content(self) -> str: ... @content.setter def content(self, value: str) -> None: ... - def with_content(self, content: str) -> "SendMessage": + def with_content(self, content: str) -> "IcaSendMessage": """ 为了链式调用, 返回自身 """ @@ -66,15 +66,15 @@ class SendMessage: return self -class DeleteMessage: +class IcaDeleteMessage: def __str__(self): ... -class NewMessage: - def reply_with(self, message: str) -> SendMessage: +class IcaNewMessage: + def reply_with(self, message: str) -> IcaSendMessage: ... - def as_deleted(self) -> DeleteMessage: + def as_deleted(self) -> IcaDeleteMessage: ... def __str__(self) -> str: ... @@ -93,6 +93,15 @@ class NewMessage: @property def is_reply(self) -> bool: ... + @property + def is_room_msg(self) -> bool: + ... + @property + def is_chat_msg(self) -> bool: + ... + @property + def room_id(self) -> RoomId: + ... class IcaClient: @@ -103,13 +112,13 @@ class IcaClient: # (因为目前来说, rust调用 Python端没法启动一个异步运行时 # 所以只能 tokio::task::block_in_place 转换成同步调用) # """ - def send_message(self, message: SendMessage) -> bool: + def send_message(self, message: IcaSendMessage) -> bool: ... - def send_and_warn(self, message: SendMessage) -> bool: + def send_and_warn(self, message: IcaSendMessage) -> bool: """发送消息, 并在日志中输出警告信息""" self.warn(message.content) return self.send_message(message) - def delete_message(self, message: DeleteMessage) -> bool: + def delete_message(self, message: IcaDeleteMessage) -> bool: ... @property @@ -144,11 +153,11 @@ on_load = Callable[[IcaClient], None] # def on_load(client: IcaClient) -> None: # ... -on_message = Callable[[NewMessage, IcaClient], None] +on_ica_message = Callable[[IcaNewMessage, IcaClient], None] # def on_message(msg: NewMessage, client: IcaClient) -> None: # ... -on_delete_message = Callable[[MessageId, IcaClient], None] +on_ica_delete_message = Callable[[MessageId, IcaClient], None] # def on_delete_message(msg_id: MessageId, client: IcaClient) -> None: # ... diff --git a/ica-rs/plugins/base.py b/ica-rs/plugins/base.py index a462d6f..e923155 100644 --- a/ica-rs/plugins/base.py +++ b/ica-rs/plugins/base.py @@ -1,12 +1,12 @@ from typing import TYPE_CHECKING, TypeVar if TYPE_CHECKING: - from ica_typing import NewMessage, IcaClient + from ica_typing import IcaNewMessage, IcaClient else: - NewMessage = TypeVar("NewMessage") + IcaNewMessage = TypeVar("NewMessage") IcaClient = TypeVar("IcaClient") -def on_message(msg: NewMessage, client: IcaClient) -> None: +def on_ica_message(msg: IcaNewMessage, client: IcaClient) -> None: if not (msg.is_from_self or msg.is_reply): if msg.content == "/bot": reply = msg.reply_with(f"ica-async-rs({client.version})-sync-py {client.ica_version}") diff --git a/ica-rs/plugins/bmcl.py b/ica-rs/plugins/bmcl.py index 90bf12e..5f3cfa1 100644 --- a/ica-rs/plugins/bmcl.py +++ b/ica-rs/plugins/bmcl.py @@ -7,11 +7,11 @@ import traceback from typing import TYPE_CHECKING, TypeVar, Optional, Tuple if TYPE_CHECKING: - from ica_typing import NewMessage, IcaClient, ConfigData + from ica_typing import IcaNewMessage, IcaClient, ConfigData CONFIG_DATA: ConfigData else: CONFIG_DATA = None - NewMessage = TypeVar("NewMessage") + IcaNewMessage = TypeVar("NewMessage") IcaClient = TypeVar("IcaClient") _version_ = "2.2.0-rs" @@ -57,7 +57,7 @@ def format_hit_count(count: int) -> str: return count_str -def wrap_request(url: str, msg: NewMessage, client: IcaClient) -> Optional[dict]: +def wrap_request(url: str, msg: IcaNewMessage, client: IcaClient) -> Optional[dict]: # if CONFIG_DATA try: cookie = CONFIG_DATA["cookie"] @@ -83,7 +83,7 @@ def wrap_request(url: str, msg: NewMessage, client: IcaClient) -> Optional[dict] return response.json() -def bmcl_dashboard(msg: NewMessage, client: IcaClient) -> None: +def bmcl_dashboard(msg: IcaNewMessage, client: IcaClient) -> None: req_time = time.time() # 记录请求时间 data = wrap_request("https://bd.bangbang93.com/openbmclapi/metric/dashboard", msg, client) @@ -196,7 +196,7 @@ def bmcl_rank_general(msg, client): client.send_message(reply) -def bmcl_rank(msg: NewMessage, client: IcaClient, name: str) -> None: +def bmcl_rank(msg: IcaNewMessage, client: IcaClient, name: str) -> None: req_time = time.time() # 记录请求时间 rank_data = wrap_request("https://bd.bangbang93.com/openbmclapi/metric/rank", msg, client) @@ -247,7 +247,7 @@ help = """/bmcl -> dashboard """ -def on_message(msg: NewMessage, client: IcaClient) -> None: +def on_ica_message(msg: IcaNewMessage, client: IcaClient) -> None: if not (msg.is_from_self or msg.is_reply): if '\n' in msg.content: return diff --git a/ica-rs/plugins/save_eval.pyi b/ica-rs/plugins/save_eval.pyi index e4fb333..b82e044 100644 --- a/ica-rs/plugins/save_eval.pyi +++ b/ica-rs/plugins/save_eval.pyi @@ -5,12 +5,12 @@ import traceback from typing import TYPE_CHECKING, TypeVar if TYPE_CHECKING: - from ica_typing import NewMessage, IcaClient + from ica_typing import IcaNewMessage, IcaClient else: - NewMessage = TypeVar("NewMessage") + IcaNewMessage = TypeVar("NewMessage") IcaClient = TypeVar("IcaClient") -def safe_eval(code: str, msg: NewMessage) -> str: +def safe_eval(code: str, msg: IcaNewMessage) -> str: try: # code = code.replace('help', '坏东西!\n') # code = code.replace('bytes', '坏东西!\n') @@ -71,7 +71,7 @@ def safe_eval(code: str, msg: NewMessage) -> str: return result -def on_message(message: NewMessage, client: IcaClient) -> None: +def on_message(message: IcaNewMessage, client: IcaClient) -> None: if not (message.is_from_self or message.is_reply): if message.content.startswith("/="): code = message.content[2:] diff --git a/ica-rs/src/error.rs b/ica-rs/src/error.rs index 702de7d..863e55e 100644 --- a/ica-rs/src/error.rs +++ b/ica-rs/src/error.rs @@ -3,7 +3,7 @@ pub type ClientResult = Result; #[derive(Debug)] pub enum IcaError { /// Socket IO 链接错误 - SocketIoError(rust_socketio::Error), + SocketIoError(rust_socketio::error::Error), } #[derive(Debug)] diff --git a/ica-rs/src/ica.rs b/ica-rs/src/ica.rs index 3939a07..b654f09 100644 --- a/ica-rs/src/ica.rs +++ b/ica-rs/src/ica.rs @@ -64,18 +64,30 @@ pub async fn start_ica(config: &IcaConfig, stop_reciver: StopGetter) -> ClientRe // 等待停止信号 stop_reciver.await.ok(); event!(Level::INFO, "socketio client stopping"); - let disconnect = socket.disconnect().await; - match disconnect { + match socket.disconnect().await { Ok(_) => { event!(Level::INFO, "socketio client stopped"); Ok(()) } Err(e) => { - event!(Level::ERROR, "socketio client stopped with error: {}", e); - Err(IcaError::SocketIoError(e)) + // 单独处理 SocketIoError(IncompleteResponseFromEngineIo(WebsocketError(AlreadyClosed))) + match e { + rust_socketio::Error::IncompleteResponseFromEngineIo(inner_e) => { + if inner_e.to_string().contains("AlreadyClosed") { + event!(Level::INFO, "socketio client stopped"); + return Ok(()); + } else { + event!(Level::ERROR, "socketio client stopped with error: {:?}", inner_e); + Err(IcaError::SocketIoError( + rust_socketio::Error::IncompleteResponseFromEngineIo(inner_e), + )) + } + } + e => { + event!(Level::ERROR, "socketio client stopped with error: {}", e); + Err(IcaError::SocketIoError(e)) + } + } } } - - // event!(Level::INFO, "socketio client stopped"); - // disconnect.into() } diff --git a/ica-rs/src/py/call.rs b/ica-rs/src/py/call.rs index 5ca988c..fc1b1a5 100644 --- a/ica-rs/src/py/call.rs +++ b/ica-rs/src/py/call.rs @@ -73,6 +73,11 @@ pub fn verify_plugins() { } } +pub const ICA_NEW_MESSAGE_FUNC: &str = "on_ica_message"; +pub const ICA_DELETE_MESSAGE_FUNC: &str = "on_ica_delete_message"; + +pub const MATRIX_NEW_MESSAGE_FUNC: &str = "on_matrix_message"; + /// 执行 new message 的 python 插件 pub async fn ica_new_message_py(message: &NewMessage, client: &Client) { // 验证插件是否改变 @@ -80,15 +85,17 @@ pub async fn ica_new_message_py(message: &NewMessage, client: &Client) { let plugins = PyStatus::get_files(); for (path, plugin) in plugins.iter() { - let msg = class::NewMessagePy::new(message); - let client = class::IcaClientPy::new(client); + let msg = class::ica::NewMessagePy::new(message); + let client = class::ica::IcaClientPy::new(client); let args = (msg, client); // 甚至实际上压根不需要await这个spawn, 直接让他自己跑就好了(离谱) tokio::spawn(async move { Python::with_gil(|py| { - if let Some(py_func) = get_func(plugin.py_module.as_ref(py), path, "on_message") { + if let Some(py_func) = + get_func(plugin.py_module.as_ref(py), path, ICA_NEW_MESSAGE_FUNC) + { if let Err(e) = py_func.call1(args) { - warn!("failed to call function: {:?}", e); + warn!("failed to call function<{}>: {:?}", ICA_NEW_MESSAGE_FUNC, e); } } }) @@ -102,15 +109,34 @@ pub async fn ica_delete_message_py(msg_id: MessageId, client: &Client) { let plugins = PyStatus::get_files(); for (path, plugin) in plugins.iter() { let msg_id = msg_id.clone(); - let client = class::IcaClientPy::new(client); + let client = class::ica::IcaClientPy::new(client); let args = (msg_id.clone(), client); tokio::spawn(async move { Python::with_gil(|py| { if let Some(py_func) = - get_func(plugin.py_module.as_ref(py), path, "on_delete_message") + get_func(plugin.py_module.as_ref(py), path, ICA_DELETE_MESSAGE_FUNC) { if let Err(e) = py_func.call1(args) { - warn!("failed to call function: {:?}", e); + warn!("failed to call function<{}>: {:?}", ICA_DELETE_MESSAGE_FUNC, e); + } + } + }) + }); + } +} + +pub async fn matrix_new_message_py() { + verify_plugins(); + + let plugins = PyStatus::get_files(); + for (path, plugin) in plugins.iter() { + tokio::spawn(async move { + Python::with_gil(|py| { + if let Some(py_func) = + get_func(plugin.py_module.as_ref(py), path, MATRIX_NEW_MESSAGE_FUNC) + { + if let Err(e) = py_func.call0() { + warn!("failed to call function<{}>: {:?}", MATRIX_NEW_MESSAGE_FUNC, e); } } }) diff --git a/ica-rs/src/py/class.rs b/ica-rs/src/py/class.rs index 4b695c9..81b956d 100644 --- a/ica-rs/src/py/class.rs +++ b/ica-rs/src/py/class.rs @@ -1,237 +1,7 @@ +pub mod ica; + use pyo3::prelude::*; -use rust_socketio::asynchronous::Client; -use tokio::runtime::Runtime; use toml::Value as TomlValue; -use tracing::{debug, info, warn}; - -use crate::data_struct::ica::messages::{ - DeleteMessage, MessageTrait, NewMessage, ReplyMessage, SendMessage, -}; -use crate::data_struct::ica::MessageId; -use crate::ica::client::{delete_message, send_message}; -use crate::MainStatus; - -#[pyclass] -#[pyo3(name = "IcaStatus")] -pub struct IcaStatusPy {} - -#[pymethods] -impl IcaStatusPy { - #[new] - pub fn py_new() -> Self { Self {} } - #[getter] - pub fn get_qq_login(&self) -> bool { MainStatus::global_ica_status().qq_login } - #[getter] - pub fn get_online(&self) -> bool { MainStatus::global_ica_status().online_status.online } - #[getter] - pub fn get_self_id(&self) -> i64 { MainStatus::global_ica_status().online_status.qqid } - #[getter] - pub fn get_nick_name(&self) -> String { - MainStatus::global_ica_status().online_status.nick.clone() - } - #[getter] - pub fn get_loaded_messages_count(&self) -> u64 { - MainStatus::global_ica_status().current_loaded_messages_count - } - #[getter] - pub fn get_ica_version(&self) -> String { - MainStatus::global_ica_status().online_status.icalingua_info.ica_version.clone() - } - - #[getter] - pub fn get_os_info(&self) -> String { - MainStatus::global_ica_status().online_status.icalingua_info.os_info.clone() - } - - #[getter] - pub fn get_resident_set_size(&self) -> String { - MainStatus::global_ica_status() - .online_status - .icalingua_info - .resident_set_size - .clone() - } - - #[getter] - pub fn get_heap_used(&self) -> String { - MainStatus::global_ica_status().online_status.icalingua_info.heap_used.clone() - } - - #[getter] - pub fn get_load(&self) -> String { - MainStatus::global_ica_status().online_status.icalingua_info.load.clone() - } -} - -impl Default for IcaStatusPy { - fn default() -> Self { Self::new() } -} - -impl IcaStatusPy { - pub fn new() -> Self { Self {} } -} - -#[derive(Clone)] -#[pyclass] -#[pyo3(name = "NewMessage")] -pub struct NewMessagePy { - pub msg: NewMessage, -} - -#[pymethods] -impl NewMessagePy { - pub fn reply_with(&self, content: String) -> SendMessagePy { - SendMessagePy::new(self.msg.reply_with(&content)) - } - pub fn as_deleted(&self) -> DeleteMessagePy { DeleteMessagePy::new(self.msg.as_deleted()) } - pub fn __str__(&self) -> String { format!("{:?}", self.msg) } - #[getter] - pub fn get_id(&self) -> MessageId { self.msg.msg_id().clone() } - #[getter] - pub fn get_content(&self) -> String { self.msg.content().clone() } - #[getter] - pub fn get_sender_id(&self) -> i64 { self.msg.sender_id() } - #[getter] - pub fn get_is_from_self(&self) -> bool { self.msg.is_from_self() } - #[getter] - pub fn get_is_reply(&self) -> bool { self.msg.is_reply() } -} - -impl NewMessagePy { - pub fn new(msg: &NewMessage) -> Self { Self { msg: msg.clone() } } -} - -#[pyclass] -#[pyo3(name = "ReplyMessage")] -pub struct ReplyMessagePy { - pub msg: ReplyMessage, -} - -#[pymethods] -impl ReplyMessagePy { - pub fn __str__(&self) -> String { format!("{:?}", self.msg) } -} - -impl ReplyMessagePy { - pub fn new(msg: ReplyMessage) -> Self { Self { msg } } -} - -#[derive(Clone)] -#[pyclass] -#[pyo3(name = "SendMessage")] -pub struct SendMessagePy { - pub msg: SendMessage, -} - -#[pymethods] -impl SendMessagePy { - pub fn __str__(&self) -> String { format!("{:?}", self.msg) } - /// 设置消息内容 - /// 用于链式调用 - pub fn with_content(&mut self, content: String) -> Self { - self.msg.content = content; - self.clone() - } - #[getter] - pub fn get_content(&self) -> String { self.msg.content.clone() } - #[setter] - pub fn set_content(&mut self, content: String) { self.msg.content = content; } -} - -impl SendMessagePy { - pub fn new(msg: SendMessage) -> Self { Self { msg } } -} - -#[derive(Clone)] -#[pyclass] -#[pyo3(name = "DeleteMessage")] -pub struct DeleteMessagePy { - pub msg: DeleteMessage, -} - -#[pymethods] -impl DeleteMessagePy { - pub fn __str__(&self) -> String { format!("{:?}", self.msg) } -} - -impl DeleteMessagePy { - pub fn new(msg: DeleteMessage) -> Self { Self { msg } } -} - -#[derive(Clone)] -#[pyclass] -#[pyo3(name = "IcaClient")] -pub struct IcaClientPy { - pub client: Client, -} - -#[pymethods] -impl IcaClientPy { - pub fn send_message(&self, message: SendMessagePy) -> bool { - tokio::task::block_in_place(|| { - let rt = Runtime::new().unwrap(); - rt.block_on(send_message(&self.client, &message.msg)) - }) - } - - pub fn send_and_warn(&self, message: SendMessagePy) -> bool { - warn!(message.msg.content); - tokio::task::block_in_place(|| { - let rt = Runtime::new().unwrap(); - rt.block_on(send_message(&self.client, &message.msg)) - }) - } - - pub fn delete_message(&self, message: DeleteMessagePy) -> bool { - tokio::task::block_in_place(|| { - let rt = Runtime::new().unwrap(); - rt.block_on(delete_message(&self.client, &message.msg)) - }) - } - - /// 仅作占位 - /// (因为目前来说, rust调用 Python端没法启动一个异步运行时 - /// 所以只能 tokio::task::block_in_place 转换成同步调用) - // #[staticmethod] - // pub fn send_message_a( - // py: Python, - // client: IcaClientPy, - // message: SendMessagePy, - // ) -> PyResult<&PyAny> { - // pyo3_asyncio::tokio::future_into_py(py, async move { - // Ok(send_message(&client.client, &message.msg).await) - // }) - // } - - #[getter] - pub fn get_status(&self) -> IcaStatusPy { IcaStatusPy::new() } - #[getter] - pub fn get_version(&self) -> String { crate::VERSION.to_string() } - #[getter] - pub fn get_ica_version(&self) -> String { crate::ICA_VERSION.to_string() } - #[getter] - pub fn get_matrix_version(&self) -> String { crate::MATRIX_VERSION.to_string() } - - pub fn debug(&self, content: String) { - debug!("{}", content); - } - - pub fn info(&self, content: String) { - info!("{}", content); - } - - pub fn warn(&self, content: String) { - warn!("{}", content); - } -} - -impl IcaClientPy { - pub fn new(client: &Client) -> Self { - Self { - client: client.clone(), - } - } -} #[derive(Clone)] #[pyclass] diff --git a/ica-rs/src/py/class/ica.rs b/ica-rs/src/py/class/ica.rs new file mode 100644 index 0000000..1bcc85c --- /dev/null +++ b/ica-rs/src/py/class/ica.rs @@ -0,0 +1,239 @@ +use pyo3::prelude::*; +use rust_socketio::asynchronous::Client; +use tokio::runtime::Runtime; +use tracing::{debug, info, warn}; + +use crate::data_struct::ica::messages::{ + DeleteMessage, MessageTrait, NewMessage, ReplyMessage, SendMessage, +}; +use crate::data_struct::ica::{MessageId, RoomId, RoomIdTrait}; +use crate::ica::client::{delete_message, send_message}; +use crate::MainStatus; + +#[pyclass] +#[pyo3(name = "IcaStatus")] +pub struct IcaStatusPy {} + +#[pymethods] +impl IcaStatusPy { + #[new] + pub fn py_new() -> Self { Self {} } + #[getter] + pub fn get_qq_login(&self) -> bool { MainStatus::global_ica_status().qq_login } + #[getter] + pub fn get_online(&self) -> bool { MainStatus::global_ica_status().online_status.online } + #[getter] + pub fn get_self_id(&self) -> i64 { MainStatus::global_ica_status().online_status.qqid } + #[getter] + pub fn get_nick_name(&self) -> String { + MainStatus::global_ica_status().online_status.nick.clone() + } + #[getter] + pub fn get_loaded_messages_count(&self) -> u64 { + MainStatus::global_ica_status().current_loaded_messages_count + } + #[getter] + pub fn get_ica_version(&self) -> String { + MainStatus::global_ica_status().online_status.icalingua_info.ica_version.clone() + } + + #[getter] + pub fn get_os_info(&self) -> String { + MainStatus::global_ica_status().online_status.icalingua_info.os_info.clone() + } + + #[getter] + pub fn get_resident_set_size(&self) -> String { + MainStatus::global_ica_status() + .online_status + .icalingua_info + .resident_set_size + .clone() + } + + #[getter] + pub fn get_heap_used(&self) -> String { + MainStatus::global_ica_status().online_status.icalingua_info.heap_used.clone() + } + + #[getter] + pub fn get_load(&self) -> String { + MainStatus::global_ica_status().online_status.icalingua_info.load.clone() + } +} + +impl Default for IcaStatusPy { + fn default() -> Self { Self::new() } +} + +impl IcaStatusPy { + pub fn new() -> Self { Self {} } +} + +#[derive(Clone)] +#[pyclass] +#[pyo3(name = "NewMessage")] +pub struct NewMessagePy { + pub msg: NewMessage, +} + +#[pymethods] +impl NewMessagePy { + pub fn reply_with(&self, content: String) -> SendMessagePy { + SendMessagePy::new(self.msg.reply_with(&content)) + } + pub fn as_deleted(&self) -> DeleteMessagePy { DeleteMessagePy::new(self.msg.as_deleted()) } + pub fn __str__(&self) -> String { format!("{:?}", self.msg) } + #[getter] + pub fn get_id(&self) -> MessageId { self.msg.msg_id().clone() } + #[getter] + pub fn get_content(&self) -> String { self.msg.content().clone() } + #[getter] + pub fn get_sender_id(&self) -> i64 { self.msg.sender_id() } + #[getter] + pub fn get_is_from_self(&self) -> bool { self.msg.is_from_self() } + #[getter] + pub fn get_is_reply(&self) -> bool { self.msg.is_reply() } + #[getter] + pub fn get_is_room_msg(&self) -> bool { self.msg.room_id.is_room() } + #[getter] + pub fn get_is_chat_msg(&self) -> bool { self.msg.room_id.is_chat() } + #[getter] + pub fn get_room_id(&self) -> RoomId { self.msg.room_id.clone() } +} + +impl NewMessagePy { + pub fn new(msg: &NewMessage) -> Self { Self { msg: msg.clone() } } +} + +#[pyclass] +#[pyo3(name = "ReplyMessage")] +pub struct ReplyMessagePy { + pub msg: ReplyMessage, +} + +#[pymethods] +impl ReplyMessagePy { + pub fn __str__(&self) -> String { format!("{:?}", self.msg) } +} + +impl ReplyMessagePy { + pub fn new(msg: ReplyMessage) -> Self { Self { msg } } +} + +#[derive(Clone)] +#[pyclass] +#[pyo3(name = "SendMessage")] +pub struct SendMessagePy { + pub msg: SendMessage, +} + +#[pymethods] +impl SendMessagePy { + pub fn __str__(&self) -> String { format!("{:?}", self.msg) } + /// 设置消息内容 + /// 用于链式调用 + pub fn with_content(&mut self, content: String) -> Self { + self.msg.content = content; + self.clone() + } + #[getter] + pub fn get_content(&self) -> String { self.msg.content.clone() } + #[setter] + pub fn set_content(&mut self, content: String) { self.msg.content = content; } +} + +impl SendMessagePy { + pub fn new(msg: SendMessage) -> Self { Self { msg } } +} + +#[derive(Clone)] +#[pyclass] +#[pyo3(name = "DeleteMessage")] +pub struct DeleteMessagePy { + pub msg: DeleteMessage, +} + +#[pymethods] +impl DeleteMessagePy { + pub fn __str__(&self) -> String { format!("{:?}", self.msg) } +} + +impl DeleteMessagePy { + pub fn new(msg: DeleteMessage) -> Self { Self { msg } } +} + +#[derive(Clone)] +#[pyclass] +#[pyo3(name = "IcaClient")] +pub struct IcaClientPy { + pub client: Client, +} + +#[pymethods] +impl IcaClientPy { + pub fn send_message(&self, message: SendMessagePy) -> bool { + tokio::task::block_in_place(|| { + let rt = Runtime::new().unwrap(); + rt.block_on(send_message(&self.client, &message.msg)) + }) + } + + pub fn send_and_warn(&self, message: SendMessagePy) -> bool { + warn!(message.msg.content); + tokio::task::block_in_place(|| { + let rt = Runtime::new().unwrap(); + rt.block_on(send_message(&self.client, &message.msg)) + }) + } + + pub fn delete_message(&self, message: DeleteMessagePy) -> bool { + tokio::task::block_in_place(|| { + let rt = Runtime::new().unwrap(); + rt.block_on(delete_message(&self.client, &message.msg)) + }) + } + + /// 仅作占位 + /// (因为目前来说, rust调用 Python端没法启动一个异步运行时 + /// 所以只能 tokio::task::block_in_place 转换成同步调用) + // #[staticmethod] + // pub fn send_message_a( + // py: Python, + // client: IcaClientPy, + // message: SendMessagePy, + // ) -> PyResult<&PyAny> { + // pyo3_asyncio::tokio::future_into_py(py, async move { + // Ok(send_message(&client.client, &message.msg).await) + // }) + // } + + #[getter] + pub fn get_status(&self) -> IcaStatusPy { IcaStatusPy::new() } + #[getter] + pub fn get_version(&self) -> String { crate::VERSION.to_string() } + #[getter] + pub fn get_ica_version(&self) -> String { crate::ICA_VERSION.to_string() } + #[getter] + pub fn get_matrix_version(&self) -> String { crate::MATRIX_VERSION.to_string() } + + pub fn debug(&self, content: String) { + debug!("{}", content); + } + + pub fn info(&self, content: String) { + info!("{}", content); + } + + pub fn warn(&self, content: String) { + warn!("{}", content); + } +} + +impl IcaClientPy { + pub fn new(client: &Client) -> Self { + Self { + client: client.clone(), + } + } +} diff --git a/news.md b/news.md index d16b5fc..fde45cb 100644 --- a/news.md +++ b/news.md @@ -1,5 +1,15 @@ # 更新日志 +## 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` 的代码 @@ -32,7 +42,7 @@ - `IcalinguaStatus.current_loaded_messages_count` - 用于以后加载信息计数 - 修改 - - `py::class::IcaStatusPy` + - `py::class::IcaStatusPy` - 大部分方法从手动 `unsafe` + `Option` - 改成直接调用 `IcalinguaStatus` 的方法 - `IcalinguaStatus`