Skip to content

Commit 2f4a835

Browse files
committed
feat(event): add GroupReaction & GroupNudge
1 parent b9ba1cd commit 2f4a835

File tree

4 files changed

+133
-23
lines changed

4 files changed

+133
-23
lines changed

lagrange/client/events/group.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
from dataclasses import dataclass, field
4-
from typing import TYPE_CHECKING, List, Optional
4+
from typing import TYPE_CHECKING, List, Optional, Union, Dict
55

66
from . import BaseEvent
77

@@ -42,6 +42,16 @@ class GroupRecall(GroupEvent, MessageInfo):
4242
suffix: str
4343

4444

45+
@dataclass
46+
class GroupNudge(GroupEvent):
47+
sender_uin: int
48+
target_uin: int
49+
action: str
50+
suffix: str
51+
attrs: Dict[str, Union[str, int]] = field(repr=False)
52+
attrs_xml: str = field(repr=False)
53+
54+
4555
@dataclass
4656
class GroupMuteMember(GroupEvent):
4757
"""when target_uid is empty, mute all member"""
@@ -86,8 +96,26 @@ class GroupMemberGotSpecialTitle(GroupEvent):
8696

8797

8898
@dataclass
89-
class GroupNameChanged:
90-
grp_id: int
99+
class GroupNameChanged(GroupEvent):
91100
name_new: str
92101
timestamp: int
93102
operator_uid: str
103+
104+
105+
@dataclass
106+
class GroupReaction(GroupEvent):
107+
uid: str
108+
seq: int
109+
emoji_id: int
110+
emoji_type: int
111+
emoji_count: int
112+
type: int
113+
total_operations: int
114+
115+
@property
116+
def is_increase(self) -> bool:
117+
return self.type == 1
118+
119+
@property
120+
def is_emoji(self) -> bool:
121+
return self.emoji_type == 2

lagrange/client/server_push/msg.py

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import json
22
import re
3-
from typing import TYPE_CHECKING
3+
from typing import TYPE_CHECKING, Type, Tuple, TypeVar, Union, Dict
44

55
from lagrange.client.message.decoder import parse_grp_msg, parse_friend_msg
66
from lagrange.pb.message.msg_push import MsgPush
@@ -11,27 +11,37 @@
1111
MemberGotTitleBody,
1212
MemberInviteRequest,
1313
MemberJoinRequest,
14-
MemberRecallMsg,
14+
MemberRecallMsg, GroupSub20Head,
1515
)
16-
from lagrange.utils.binary.protobuf import proto_decode
16+
from lagrange.utils.binary.protobuf import proto_decode, ProtoStruct
1717
from lagrange.utils.binary.reader import Reader
1818
from lagrange.utils.operator import unpack_dict
1919

20+
from ..events import BaseEvent
2021
from ..events.group import (
2122
GroupMemberGotSpecialTitle,
2223
GroupMemberJoined,
2324
GroupMemberJoinRequest,
2425
GroupMemberQuit,
2526
GroupMuteMember,
2627
GroupNameChanged,
27-
GroupRecall,
28+
GroupRecall, GroupNudge, GroupReaction,
2829
)
2930
from ..wtlogin.sso import SSOPacket
3031
from .log import logger
3132

3233
if TYPE_CHECKING:
3334
from lagrange.client.client import Client
3435

36+
T = TypeVar("T", bound=ProtoStruct)
37+
38+
39+
def unpack(buf2: bytes, decoder: Type[T]) -> Tuple[int, T]:
40+
reader = Reader(buf2)
41+
grp_id = reader.read_u32()
42+
reader.read_u8()
43+
return grp_id, decoder.decode(reader.read_bytes_with_length("u16", False))
44+
3545

3646
async def msg_push_handler(client: "Client", sso: SSOPacket):
3747
pkg = MsgPush.decode(sso.data).body
@@ -76,13 +86,30 @@ async def msg_push_handler(client: "Client", sso: SSOPacket):
7686
logger.debug("unhandled friend event: %s" % pkg)
7787
elif typ == 0x2DC: # grp event, 732
7888
if sub_typ == 20: # nudget(grp_id only)
79-
return
80-
elif sub_typ == 16: # rename and special_title and reaction(server not impl)
81-
if pkg.message: # rename and special_title
82-
reader = Reader(pkg.message.buf2)
83-
grp_id = reader.read_u32()
84-
reader.read_u8() # reserve
85-
pb = GroupSub16Head.decode(reader.read_bytes_with_length("u16", False))
89+
if pkg.message:
90+
grp_id, pb = unpack(pkg.message.buf2, GroupSub20Head)
91+
attrs: Dict[str, Union[str, int]] = {}
92+
for x in pb.body.attrs: # type: dict[bytes, bytes]
93+
k, v = x.values()
94+
if v.isdigit():
95+
attrs[k.decode()] = int(v.decode())
96+
else:
97+
attrs[k.decode()] = v.decode()
98+
return GroupNudge(
99+
grp_id,
100+
attrs["uin_str1"],
101+
attrs["uin_str2"],
102+
attrs["action_str"],
103+
attrs["suffix_str"],
104+
attrs,
105+
pb.body.attrs_xml
106+
)
107+
else:
108+
# print(pkg.encode().hex(), 2)
109+
return
110+
elif sub_typ == 16: # rename and special_title and reaction
111+
if pkg.message:
112+
grp_id, pb = unpack(pkg.message.buf2, GroupSub16Head)
86113
if pb.flag == 6: # special_title
87114
body = MemberGotTitleBody.decode(pb.body)
88115
for el in re.findall(r"<(\{.*?})>", body.string):
@@ -107,15 +134,24 @@ async def msg_push_handler(client: "Client", sso: SSOPacket):
107134
timestamp=pb.timestamp,
108135
operator_uid=pb.operator_uid,
109136
)
137+
elif pb.flag == 35: # add reaction
138+
body = pb.f44.inner.body
139+
return GroupReaction(
140+
grp_id=grp_id,
141+
uid=body.detail.sender_uid,
142+
seq=body.msg.id,
143+
emoji_id=int(body.detail.emo_id),
144+
emoji_type=body.detail.emo_type,
145+
emoji_count=body.detail.count,
146+
type=body.detail.send_type,
147+
total_operations=body.msg.total_operations
148+
)
110149
else:
111150
raise ValueError(
112151
f"Unknown subtype_12 flag: {pb.flag}: {pb.body.hex() if pb.body else pb}"
113152
)
114153
elif sub_typ == 17: # recall
115-
reader = Reader(pkg.message.buf2)
116-
grp_id = reader.read_u32()
117-
reader.read_u8() # reserve
118-
pb = MemberRecallMsg.decode(reader.read_bytes_with_length("u16", False))
154+
grp_id, pb = unpack(pkg.message.buf2, MemberRecallMsg)
119155

120156
info = pb.body.info
121157
return GroupRecall(

lagrange/pb/status/group.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,55 @@ class GroupRenamedBody(ProtoStruct):
7373
grp_name: str = proto_field(2)
7474

7575

76+
class GroupReactionMsg(ProtoStruct):
77+
id: int = proto_field(1)
78+
total_operations: int = proto_field(2)
79+
# f3: int = proto_field(3) # 4
80+
81+
82+
class GroupReactionDetail(ProtoStruct):
83+
emo_id: str = proto_field(1) # string type Unicode
84+
emo_type: int = proto_field(2) # 1: qq internal emoji, 2: unicode emoji
85+
count: int = proto_field(3, default=0)
86+
send_type: int = proto_field(5) # 1: set, 2: remove
87+
sender_uid: str = proto_field(4)
88+
89+
90+
class GroupReactionBody(ProtoStruct):
91+
op_id: int = proto_field(1)
92+
msg: GroupReactionMsg = proto_field(2)
93+
detail: GroupReactionDetail = proto_field(3)
94+
95+
96+
class GroupReactionInner(ProtoStruct):
97+
body: GroupReactionBody = proto_field(1)
98+
99+
100+
class GroupReaction(ProtoStruct):
101+
inner: GroupReactionInner = proto_field(1)
102+
103+
76104
class GroupSub16Head(ProtoStruct):
77105
timestamp: int = proto_field(2, default=0)
78106
uin: Optional[int] = proto_field(4, default=None)
79107
body: Optional[bytes] = proto_field(5, default=None)
80-
flag: int = proto_field(13) # 12: renamed, 6: set special_title, 13: unknown
108+
flag: int = proto_field(13) # 12: renamed, 6: set special_title, 13: unknown, 35: set reaction
81109
operator_uid: str = proto_field(21, default="")
110+
f44: Optional[GroupReaction] = proto_field(44, default=None) # set reaction only
111+
112+
113+
class GroupSub20Body(ProtoStruct):
114+
# f1: int = proto_field(1) # 12
115+
# f2: int = proto_field(2) # 1061
116+
# f3: int = proto_field(3) # 7
117+
# f6: int = proto_field(6) # 1132
118+
attrs: list[dict] = proto_field(7, default={})
119+
attrs_xml: str = proto_field(8, default=None)
120+
f10: int = proto_field(10)
121+
122+
123+
class GroupSub20Head(ProtoStruct):
124+
f1: int = proto_field(1) # 20
125+
grp_id: int = proto_field(4)
126+
f13: int = proto_field(13) # 19
127+
body: GroupSub20Body = proto_field(26)

main.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
from lagrange.client.client import Client
66
from lagrange.client.events.group import GroupMessage
77
from lagrange.client.events.service import ServerKick
8-
from lagrange.client.message.elems import At, Raw, Text
8+
from lagrange.client.message.elems import At, Text
99

1010

1111
async def msg_handler(client: Client, event: GroupMessage):
12-
print(event)
12+
#print(event)
1313
if event.msg.startswith("114514"):
14-
p = await client.send_grp_msg([Text("1919810")], event.grp_id)
14+
msg_seq = await client.send_grp_msg([At.build(event), Text("1919810")], event.grp_id)
1515
await asyncio.sleep(5)
16-
await client.recall_grp_msg(event.grp_id, p)
16+
await client.recall_grp_msg(event.grp_id, msg_seq)
1717
elif event.msg.startswith("imgs"):
1818
await client.send_grp_msg(
1919
[

0 commit comments

Comments
 (0)