From 2dcd9ad9064ed3b04997b63b5aaf52e45b6e3b22 Mon Sep 17 00:00:00 2001 From: BlueGlassBlock Date: Sun, 19 Mar 2023 13:15:09 +0800 Subject: [PATCH] feat: group mute See #17 --- news/+group-mute.added.md | 1 + python/ichika/core/events/__init__.pyi | 23 ++++++++- src/events/converter.rs | 66 ++++++++++++++++++++++++-- src/events/mod.rs | 25 ++++++++++ src/events/structs.rs | 3 +- src/lib.rs | 3 ++ src/utils.rs | 17 ++++++- 7 files changed, 130 insertions(+), 8 deletions(-) create mode 100644 news/+group-mute.added.md diff --git a/news/+group-mute.added.md b/news/+group-mute.added.md new file mode 100644 index 0000000..86cc97d --- /dev/null +++ b/news/+group-mute.added.md @@ -0,0 +1 @@ +支持处理全体禁言和群员禁言事件。 diff --git a/python/ichika/core/events/__init__.pyi b/python/ichika/core/events/__init__.pyi index a05b2c7..2d21980 100644 --- a/python/ichika/core/events/__init__.pyi +++ b/python/ichika/core/events/__init__.pyi @@ -1,8 +1,11 @@ from dataclasses import dataclass -from datetime import datetime +from datetime import datetime, timedelta +from typing import Literal from graia.amnesia.message import MessageChain +from ichika.core import Group + from . import structs as structs from .structs import FriendInfo, MemberInfo, MessageSource @@ -77,5 +80,23 @@ class GroupDisband: class FriendDeleted: friend_uin: int +@internal_repr +class GroupMute: + group: Group + operator: MemberInfo + status: bool + +@internal_repr +class MemberMute: + operator: MemberInfo + target: MemberInfo + duration: timedelta | Literal[False] + +@internal_repr +class BotMute: + group: Group + operator: MemberInfo + duration: timedelta | Literal[False] + @internal_repr class UnknownEvent: ... diff --git a/src/events/converter.rs b/src/events/converter.rs index 6f3d36a..ece9ecb 100644 --- a/src/events/converter.rs +++ b/src/events/converter.rs @@ -6,16 +6,19 @@ use ricq::handler::QEvent; use super::structs::{FriendInfo, MemberInfo, MessageSource}; use super::{ BotLeaveGroup, + BotMute, FriendDeleted, FriendMessage, FriendNudge, FriendRecallMessage, GroupDisband, GroupMessage, + GroupMute, GroupNudge, GroupRecallMessage, LoginEvent, MemberLeaveGroup, + MemberMute, NewFriend, NewMember, TempMessage, @@ -24,8 +27,8 @@ use super::{ use crate::client::cache; use crate::exc::MapPyErr; use crate::message::convert::{serialize_as_py_chain, serialize_audio}; -use crate::utils::{datetime_from_ts, py_none, py_try, AsPython}; -use crate::{call_static_py, PyRet}; +use crate::utils::{datetime_from_ts, py_none, py_try, timedelta_from_secs, AsPython}; +use crate::PyRet; pub async fn convert(event: QEvent) -> PyRet { match event { @@ -44,6 +47,7 @@ pub async fn convert(event: QEvent) -> PyRet { QEvent::GroupLeave(event) => Ok(handle_group_leave(event).await), QEvent::GroupDisband(event) => Ok(handle_group_disband(event)), QEvent::DeleteFriend(event) => Ok(handle_friend_delete(event)), + QEvent::GroupMute(event) => handle_mute(event).await, unknown => Ok(UnknownEvent { inner: unknown }.obj()), } } @@ -91,7 +95,7 @@ async fn handle_group_recall(event: rce::GroupMessageRecallEvent) -> PyRet { .fetch_member(event.group_code, event.operator_uin) .await .py_res()?; - let time = py_try(|py| Ok(call_static_py!(datetime_from_ts, py, (event.time))?.into_py(py)))?; + let time = py_try(|py| Ok(datetime_from_ts(py, event.time)?.into_py(py)))?; Ok(GroupRecallMessage { time, author: MemberInfo::new(&author, (*group_info).clone()), @@ -149,7 +153,7 @@ async fn handle_friend_recall(event: rce::FriendMessageRecallEvent) -> PyRet { .ok_or_else(|| { PyValueError::new_err(format!("Unable to find friend {}", event.friend_uin)) })?; - let time = py_try(|py| Ok(call_static_py!(datetime_from_ts, py, (event.time))?.into_py(py)))?; + let time = py_try(|py| Ok(datetime_from_ts(py, event.time)?.into_py(py)))?; Ok(FriendRecallMessage { time, author: FriendInfo { @@ -296,3 +300,57 @@ fn handle_friend_delete(event: rce::DeleteFriendEvent) -> PyObject { } .obj() } + +async fn handle_mute(event: rce::GroupMuteEvent) -> PyRet { + let uin = event.client.uin().await; + let mut cache = cache(event.client).await; + let event = event.inner; + let group = cache + .fetch_group(event.group_code) + .await + .py_res()? + .as_ref() + .clone(); + let operator = cache + .fetch_member(event.group_code, event.operator_uin) + .await + .py_res()?; + let operator = MemberInfo::new(&operator, group.clone()); + + if event.target_uin == 0 { + return Ok(GroupMute { + group, + operator, + status: event.duration.as_secs() == 0, + } + .obj()); + } + let duration = event.duration.as_secs(); + let duration = py_try(|py| { + Ok(if duration != 0 { + timedelta_from_secs(py, duration)?.into_py(py) + } else { + false.into_py(py) + }) + })?; + if event.target_uin == uin { + Ok(BotMute { + group, + operator, + duration, + } + .obj()) + } else { + let target = cache + .fetch_member(event.group_code, event.target_uin) + .await + .py_res()?; + let target = MemberInfo::new(&target, group.clone()); + Ok(MemberMute { + operator, + target, + duration, + } + .obj()) + } +} diff --git a/src/events/mod.rs b/src/events/mod.rs index 8f99ee7..ffa3433 100644 --- a/src/events/mod.rs +++ b/src/events/mod.rs @@ -10,6 +10,7 @@ pub mod structs; use structs::MessageSource; use self::structs::{FriendInfo, MemberInfo}; +use crate::client::group::Group; use crate::utils::py_try; #[pyclass(get_all)] @@ -110,6 +111,30 @@ pub struct FriendDeleted { friend_uin: i64, } +#[pyclass(get_all)] +#[derive(PyRepr, Clone)] +pub struct MemberMute { + target: MemberInfo, + operator: MemberInfo, + duration: PyObject, // datetime.timedelta | Literal[False] +} + +#[pyclass(get_all)] +#[derive(PyRepr, Clone)] +pub struct BotMute { + group: Group, + operator: MemberInfo, + duration: PyObject, // datetime.timedelta | Literal[False] +} + +#[pyclass(get_all)] +#[derive(PyRepr, Clone)] +pub struct GroupMute { + group: Group, + operator: MemberInfo, + status: bool, +} + #[pyclass] #[derive(PyRepr, Clone)] pub struct UnknownEvent { diff --git a/src/events/structs.rs b/src/events/structs.rs index 1c94f1f..53d9e7e 100644 --- a/src/events/structs.rs +++ b/src/events/structs.rs @@ -2,7 +2,6 @@ use pyo3::prelude::*; use pyo3::types::PyTuple; use pyo3_repr::PyRepr; -use crate::call_static_py; use crate::client::group::{Group, Member}; use crate::utils::datetime_from_ts; #[pyclass(get_all)] @@ -18,7 +17,7 @@ impl MessageSource { Ok(Self { seqs: PyTuple::new(py, seqs).into_py(py), rands: PyTuple::new(py, rands).into_py(py), - time: call_static_py!(datetime_from_ts, py, (time))?.into(), + time: datetime_from_ts(py, time)?.into_py(py), }) } } diff --git a/src/lib.rs b/src/lib.rs index 85a5b70..0ca2326 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,6 +74,9 @@ fn register_event_module(py: Python, parent: &PyModule) -> PyResult<()> { crate::events::BotLeaveGroup, crate::events::GroupDisband, crate::events::FriendDeleted, + crate::events::GroupMute, + crate::events::MemberMute, + crate::events::BotMute, crate::events::UnknownEvent ); parent.add_submodule(m)?; diff --git a/src/utils.rs b/src/utils.rs index 273343c..b52c3a0 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -163,10 +163,25 @@ macro_rules! call_static_py { } static_py_fn!( - datetime_from_ts, + _datetime_from_ts, __DT_CELL, "datetime", ["datetime", "fromtimestamp"] ); +pub fn datetime_from_ts(py: Python<'_>, time: impl IntoPy) -> PyResult<&PyAny> { + call_static_py!(_datetime_from_ts, py, (time)) +} + +static_py_fn!( + _timedelta_from_secs, + __TDELTA_CELL, + "datetime", + ["timedelta"] +); + +pub fn timedelta_from_secs(py: Python<'_>, delta: impl IntoPy) -> PyResult<&PyAny> { + _timedelta_from_secs(py).call((), kwargs!(py, "seconds" => delta.into_py(py))) +} + static_py_fn!(partial, __PARTIAL_CELL, "functools", ["partial"]);