From 30cad6218e750efef7e2f6d6182f7a5b93e68bd5 Mon Sep 17 00:00:00 2001 From: Decrabbit Date: Sun, 13 Oct 2024 23:09:21 +0800 Subject: [PATCH 1/5] fix: fix group notice detail --- lagrange/client/events/group.py | 35 ++++++++++++++- lagrange/client/server_push/msg.py | 68 +++++++++++++++++++++--------- lagrange/pb/status/group.py | 49 ++++++++++++++++++--- 3 files changed, 126 insertions(+), 26 deletions(-) diff --git a/lagrange/client/events/group.py b/lagrange/client/events/group.py index 14b2584..c9d4156 100644 --- a/lagrange/client/events/group.py +++ b/lagrange/client/events/group.py @@ -81,7 +81,7 @@ class GroupMemberJoinRequest(GroupEvent): @dataclass class GroupMemberJoined(GroupEvent): - uin: int + # uin: int //it cant get uid: str join_type: int @@ -152,4 +152,35 @@ class GroupInvite(GroupEvent): @dataclass class GroupMemberJoinedByInvite(GroupEvent): invitor_uin: int - uin: int \ No newline at end of file + uin: int + + +@dataclass +class GroupSelfJoined(GroupEvent): + grp_id: int + op_uid: str + + +@dataclass +class GroupSelfRequireReject(GroupEvent): + grp_id: int + message: str + + +@dataclass +class GroupBotAdded(GroupEvent): + bot_uid: str + + +@dataclass +class BotGrayTip(GroupEvent): + content: str + + +@dataclass +class GroupBotJoined(GroupEvent): + opqq_uin: int + nick_name: str + robot_name: str + robot_schema: str + user_schema: str diff --git a/lagrange/client/server_push/msg.py b/lagrange/client/server_push/msg.py index 9ef034f..31edfbc 100644 --- a/lagrange/client/server_push/msg.py +++ b/lagrange/client/server_push/msg.py @@ -14,17 +14,21 @@ MemberJoinRequest, MemberRecallMsg, GroupSub20Head, + PBBotGrayTip, PBGroupAlbumUpdate, + PBGroupBotAdded, PBGroupInvite, + PBSelfJoinInGroup, ) -from lagrange.pb.status.friend import ( - PBFriendRecall -) +from lagrange.pb.status.friend import PBFriendRecall from lagrange.utils.binary.protobuf import proto_decode, ProtoStruct, proto_encode from lagrange.utils.binary.reader import Reader from lagrange.utils.operator import unpack_dict, timestamp from ..events.group import ( + BotGrayTip, + GroupBotAdded, + GroupBotJoined, GroupInvite, GroupMemberGotSpecialTitle, GroupMemberJoined, @@ -35,13 +39,13 @@ GroupRecall, GroupNudge, GroupReaction, + GroupSelfJoined, + GroupSelfRequireReject, GroupSign, GroupAlbumUpdate, - GroupMemberJoinedByInvite -) -from ..events.friend import ( - FriendRecall + GroupMemberJoinedByInvite, ) +from ..events.friend import FriendRecall from ..wtlogin.sso import SSOPacket from .log import logger @@ -72,10 +76,9 @@ async def msg_push_handler(client: "Client", sso: SSOPacket): elif typ == 33: # member joined pb = MemberChanged.decode(pkg.message.buf2) return GroupMemberJoined( - grp_id=pkg.response_head.from_uin, - uin=pb.uin, - uid=pb.uid, - join_type=pb.join_type, + grp_id=pb.uin, + uid=pb.uid, # right u cant get uin + join_type=pb.join_type_new, ) elif typ == 34: # member exit pb = MemberChanged.decode(pkg.message.buf2) @@ -89,16 +92,25 @@ async def msg_push_handler(client: "Client", sso: SSOPacket): elif typ == 84: pb = MemberJoinRequest.decode(pkg.message.buf2) return GroupMemberJoinRequest(grp_id=pb.grp_id, uid=pb.uid, answer=pb.request_field) + elif typ == 85: + pb = PBSelfJoinInGroup.decode(pkg.message.buf2) + return GroupSelfJoined(grp_id=pb.gid, op_uid=pb.operator_uid) + elif typ == 86: + reader = Reader(pkg.message.buf2) + grp_id = reader.read_u32() # it should have more infomation,but i cant guess it + return GroupSelfRequireReject(grp_id, "") elif typ == 87: pb = PBGroupInvite.decode(pkg.message.buf2) return GroupInvite(grp_id=pb.gid, invitor_uid=pb.invitor_uid) + elif typ == 167: + print(pkg.message.encode().hex()) elif typ == 525: pb = MemberInviteRequest.decode(pkg.message.buf2) if pb.cmd == 87: inn = pb.info.inner return GroupMemberJoinRequest(grp_id=inn.grp_id, uid=inn.uid, invitor_uid=inn.invitor_uid) elif typ == 0x210: # friend event, 528 / group file upload notice event - if sub_typ == 138: # friend recall + if sub_typ == 138: # friend recall pb = PBFriendRecall.decode(pkg.message.buf2) return FriendRecall( pkg.response_head.from_uin, @@ -107,10 +119,16 @@ async def msg_push_handler(client: "Client", sso: SSOPacket): pb.info.to_uid, pb.info.seq, pb.info.random, - pb.info.time + pb.info.time, ) + if sub_typ == 368: + pass + # print(pkg.message.encode().hex()) logger.debug(f"unhandled friend event / group file upload notice event: {pkg}") # TODO: paste elif typ == 0x2DC: # grp event, 732 + if sub_typ == 1: + # print(pkg.encode().hex()) + pass if sub_typ == 20: # nudge and group_sign(群打卡) if pkg.message: grp_id, pb = unpack(pkg.message.buf2, GroupSub20Head) @@ -124,14 +142,19 @@ async def msg_push_handler(client: "Client", sso: SSOPacket): attrs[k.decode()] = int(v.decode()) else: attrs[k.decode()] = v.decode() + if pb.body.f2 == 19217: + return GroupBotJoined( + grp_id, + attrs["mqq_uin"], + attrs["nick_name"], + attrs["robot_name"], + attrs["robot_schema"], + attrs["user_schema"], + ) if pb.body.type == 1: if "invitor" in attrs: # reserve: attrs["msg_nums"] - return GroupMemberJoinedByInvite( - grp_id, - attrs["invitor"], - attrs["invitee"] - ) + return GroupMemberJoinedByInvite(grp_id, attrs["invitor"], attrs["invitee"]) elif "user" in attrs and "uin" in attrs: # todo: 群代办 pass @@ -157,13 +180,17 @@ async def msg_push_handler(client: "Client", sso: SSOPacket): pb.body.attrs_xml, ) else: - raise ValueError(f"unknown type({pb.body.type}) on GroupSub20: {attrs}") + raise ValueError(f"unknown type({pb.body.type}) f2({pb.body.f2}) on GroupSub20: {attrs}") else: # print(pkg.encode().hex(), 2) return elif sub_typ == 16: # rename & special_title & reaction + # print(sso.data.hex()) if pkg.message: grp_id, pb = unpack(pkg.message.buf2, GroupSub16Head) + if pb.flag is None: + _, pb = unpack(pkg.message.buf2, PBBotGrayTip) # 傻逼tx,我13号位呢 + return BotGrayTip(grp_id, pb.body.message) if pb.flag == 6: # special_title body = MemberGotTitleBody.decode(pb.body) for el in re.findall(r"<(\{.*?})>", body.string): @@ -210,6 +237,9 @@ async def msg_push_handler(client: "Client", sso: SSOPacket): timestamp=pb.timestamp, image_id=q["i"], ) + elif pb.flag == 38: + _, pb = unpack(pkg.message.buf2, PBGroupBotAdded) + return GroupBotAdded(pb.body.grp_id, pb.body.bot_uid_1 or pb.body.bot_uid_2) else: raise ValueError(f"Unknown subtype_12 flag: {pb.flag}: {pb.body.hex() if pb.body else pb}") elif sub_typ == 17: # recall diff --git a/lagrange/pb/status/group.py b/lagrange/pb/status/group.py index 0f17ad3..674f7ad 100644 --- a/lagrange/pb/status/group.py +++ b/lagrange/pb/status/group.py @@ -12,7 +12,10 @@ class MemberChanged(ProtoStruct): uid: str = proto_field(3) exit_type: Optional[int] = proto_field(4, default=None) # 3kick_me, 131kick, 130exit operator_uid: str = proto_field(5, default="") - join_type: Optional[int] = proto_field(6, default=None) # 6scanqr, + join_type: Optional[int] = proto_field(6, default=None) # 6other, 0slef_invite + join_type_new: Optional[int] = proto_field( + 4, default=None + ) # 130 by_other(click url,scan qr,input grpid), 131 by_invite class MemberJoinRequest(ProtoStruct): @@ -106,21 +109,23 @@ class GroupSub16Head(ProtoStruct): timestamp: int = proto_field(2, default=0) uin: Optional[int] = proto_field(4, default=None) body: Optional[bytes] = proto_field(5, default=None) - flag: int = proto_field(13) # 12: renamed, 6: set special_title, 13: unknown, 35: set reaction + flag: Optional[int] = proto_field( + 13, default=None + ) # 12: renamed, 6: set special_title, 13: unknown, 35: set reaction, 38: bot add operator_uid: str = proto_field(21, default="") f44: Optional[PBGroupReaction] = proto_field(44, default=None) # set reaction only class GroupSub20Head(ProtoStruct): - f1: int = proto_field(1) # 20 + f1: int = proto_field(1, default=None) # 20 grp_id: int = proto_field(4) f13: int = proto_field(13) # 19 body: "GroupSub20Body" = proto_field(26) class GroupSub20Body(ProtoStruct): - type: int = proto_field(1) # 12: nudge, 14: group_sign - # f2: int = proto_field(2) # 1061 + type: Optional[int] = proto_field(1, default=None) # 12: nudge, 14: group_sign + f2: int = proto_field(2) # 1061 , bot added group:19217 # f3: int = proto_field(3) # 7 # f6: int = proto_field(6) # 1132 attrs: list[dict] = proto_field(7, default_factory=list) @@ -160,3 +165,37 @@ class PBGroupInvite(ProtoStruct): f4: int = proto_field(4) # 0 invitor_uid: str = proto_field(5) invite_info: bytes = proto_field(6) + + +class PBSelfJoinInGroup(ProtoStruct): + gid: int = proto_field(1) + f2: int = proto_field(2) + f4: int = proto_field(4) # 0 + f6: int = proto_field(6) # 48 + f7: str = proto_field(7) + operator_uid: str = proto_field(3) + + +class PBGroupBotAddedBody(ProtoStruct): + grp_id: int = proto_field(1) + bot_uid_1: Optional[str] = proto_field(2, default=None) + bot_uid_2: Optional[str] = proto_field(3, default=None) # f**k tx + flag: int = proto_field(4) + + +class PBGroupBotAdded(ProtoStruct): + # f1: 39 + grp_id: int = proto_field(4) + # f13: 38 + body: PBGroupBotAddedBody = proto_field(47) + + +class PBGroupGrayTipBody(ProtoStruct): + message: str = proto_field(2) + flag: int = proto_field(3) + + +class PBBotGrayTip(ProtoStruct): + # f1: 1 + grp_id: int = proto_field(4) + body: PBGroupGrayTipBody = proto_field(5) From 5dfbfd9765f2e4a0140f1b172b95dc6ab89c498c Mon Sep 17 00:00:00 2001 From: Decrabbit Date: Sun, 13 Oct 2024 23:10:03 +0800 Subject: [PATCH 2/5] feat:maybe decode markdown --- lagrange/client/message/decoder.py | 8 +++- lagrange/client/message/elems.py | 61 ++++++++++++++++++++++++-- lagrange/client/message/types.py | 16 ++----- lagrange/pb/message/rich_text/elems.py | 51 +++++++++++++++++++++ 4 files changed, 119 insertions(+), 17 deletions(-) diff --git a/lagrange/client/message/decoder.py b/lagrange/client/message/decoder.py index 4b9051c..43f44d6 100644 --- a/lagrange/client/message/decoder.py +++ b/lagrange/client/message/decoder.py @@ -11,7 +11,7 @@ from .types import Element from lagrange.utils.binary.reader import Reader from lagrange.utils.binary.protobuf import proto_encode -from lagrange.pb.message.rich_text.elems import GroupFileExtra, FileExtra +from lagrange.pb.message.rich_text.elems import GroupFileExtra, FileExtra, PBKeyboard from lagrange.pb.highway.comm import MsgInfo if TYPE_CHECKING: @@ -158,6 +158,12 @@ async def parse_msg_new( f8=common.pb_elem[8], ) ) + if common.service_type == 45: + md_c: bytes = common.pb_elem[1] + msg_chain.append(elems.Markdown(text=f"[markdown:{md_c.decode()}]", content=md_c.decode())) + if common.service_type == 46: + kb = PBKeyboard.decode(proto_encode(common.pb_elem)).keyboard + msg_chain.append(elems.Keyboard(text="[button]", content=kb.content, bot_appid=kb.bot_appid)) if common.bus_type in [10, 20]: # 10: friend, 20: group extra = MsgInfo.decode(proto_encode(raw.common_elem.pb_elem)) index = extra.body[0].index diff --git a/lagrange/client/message/elems.py b/lagrange/client/message/elems.py index e11567a..9e404ce 100644 --- a/lagrange/client/message/elems.py +++ b/lagrange/client/message/elems.py @@ -157,9 +157,15 @@ class File(Text): file_hash: Optional[str] @classmethod - def _paste_build(cls, file_size: int, file_name: str, - file_md5: bytes, file_id: Optional[str] = None, - file_uuid: Optional[str] = None, file_hash: Optional[str] = None) -> "File": + def _paste_build( + cls, + file_size: int, + file_name: str, + file_md5: bytes, + file_id: Optional[str] = None, + file_uuid: Optional[str] = None, + file_hash: Optional[str] = None, + ) -> "File": return cls( text=f"[file:{file_name}]", file_size=file_size, @@ -178,3 +184,52 @@ def grp_paste_build(cls, file_size: int, file_name: str, file_md5: bytes, file_i @classmethod def pri_paste_build(cls, file_size: int, file_name: str, file_md5: bytes, file_uuid: str, file_hash: str) -> "File": return cls._paste_build(file_size, file_name, file_md5, file_uuid=file_uuid, file_hash=file_hash) + + +@dataclass +class Markdown(Text): + content: str + + +class Permission: + type: int + specify_role_ids: Optional[list[str]] + specify_user_ids: Optional[list[str]] + + +class RenderData: + label: Optional[str] + visited_label: Optional[str] + style: int + + +class Action: + type: Optional[int] + permission: Optional[Permission] + data: str + reply: bool + enter: bool + anchor: Optional[int] + unsupport_tips: Optional[str] + click_limit: Optional[int] # deprecated + at_bot_show_channel_list: bool # deprecated + + +class Button: + id: Optional[str] + render_data: Optional[RenderData] + action: Optional[Action] + + +class InlineKeyboardRow: + buttons: Optional[list[Button]] + + +class InlineKeyboard: + rows: list[InlineKeyboardRow] + + +@dataclass +class Keyboard(Text): + content: Optional[list[InlineKeyboard]] + bot_appid: int diff --git a/lagrange/client/message/types.py b/lagrange/client/message/types.py index 8f6da59..fc64103 100644 --- a/lagrange/client/message/types.py +++ b/lagrange/client/message/types.py @@ -2,19 +2,7 @@ from typing_extensions import TypeAlias if TYPE_CHECKING: - from .elems import ( - Text, - At, - AtAll, - Image, - Emoji, - Json, - Quote, - Raw, - Audio, - Poke, - MarketFace, - ) + from .elems import Text, At, AtAll, Image, Emoji, Json, Quote, Raw, Audio, Poke, MarketFace, Markdown, Keyboard # T = TypeVar( # "T", @@ -42,4 +30,6 @@ "Audio", "Poke", "MarketFace", + "Markdown", + "Keyboard", ] diff --git a/lagrange/pb/message/rich_text/elems.py b/lagrange/pb/message/rich_text/elems.py index 43b9b3a..64a3ffa 100644 --- a/lagrange/pb/message/rich_text/elems.py +++ b/lagrange/pb/message/rich_text/elems.py @@ -196,3 +196,54 @@ class GroupFileExtra(ProtoStruct): file_name: str = proto_field(2) display: str = proto_field(3) inner: GroupFileExtraInner = proto_field(7) + + +# class Markdown(ProtoStruct): +# content: str = proto_field(1) + + +class Permission(ProtoStruct): + type: int = proto_field(1, default=0) + specify_role_ids: Optional[list[str]] = proto_field(2, default=None) + specify_user_ids: Optional[list[str]] = proto_field(3, default=None) + + +class RenderData(ProtoStruct): + label: Optional[str] = proto_field(1, default=None) + visited_label: Optional[str] = proto_field(2, default=None) + style: int = proto_field(3, default=0) + + +class Action(ProtoStruct): + type: Optional[int] = proto_field(1, default=None) + permission: Optional[Permission] = proto_field(2, default=None) + data: str = proto_field(5) + reply: bool = proto_field(7, default=False) + enter: bool = proto_field(8, default=False) + anchor: Optional[int] = proto_field(9, default=None) + unsupport_tips: Optional[str] = proto_field(4, default=None) + click_limit: Optional[int] = proto_field(3) # deprecated + at_bot_show_channel_list: bool = proto_field(6, default=False) # deprecated + + +class Button(ProtoStruct): + id: Optional[str] = proto_field(1, default=None) + render_data: Optional[RenderData] = proto_field(2, default=None) + action: Optional[Action] = proto_field(3, default=None) + + +class InlineKeyboardRow(ProtoStruct): + buttons: Optional[list[Button]] = proto_field(1, default=None) + + +class InlineKeyboard(ProtoStruct): + rows: list[InlineKeyboardRow] = proto_field(1) + + +class Keyboard(ProtoStruct): + content: Optional[list[InlineKeyboard]] = proto_field(1, default=None) + bot_appid: int = proto_field(2) + + +class PBKeyboard(ProtoStruct): + keyboard: Keyboard = proto_field(1) From 5bbf5ca925bee3401a80f7df4e11fe95f9c44c45 Mon Sep 17 00:00:00 2001 From: nullcat Date: Mon, 14 Oct 2024 19:26:32 +0800 Subject: [PATCH 3/5] Update decoder.py --- lagrange/client/message/decoder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lagrange/client/message/decoder.py b/lagrange/client/message/decoder.py index 4be838c..0a47cc6 100644 --- a/lagrange/client/message/decoder.py +++ b/lagrange/client/message/decoder.py @@ -165,10 +165,10 @@ async def parse_msg_new( ) if common.service_type == 45: md_c: bytes = common.pb_elem[1] - msg_chain.append(elems.Markdown(text=f"[markdown:{md_c.decode()}]", content=md_c.decode())) + msg_chain.append(elems.Markdown(content=md_c.decode())) if common.service_type == 46: kb = PBKeyboard.decode(proto_encode(common.pb_elem)).keyboard - msg_chain.append(elems.Keyboard(text="[button]", content=kb.content, bot_appid=kb.bot_appid)) + msg_chain.append(elems.Keyboard(content=kb.content, bot_appid=kb.bot_appid)) if common.bus_type in [10, 20]: # 10: friend, 20: group extra = MsgInfo.decode(proto_encode(raw.common_elem.pb_elem)) index = extra.body[0].index From ca8466164019fcd2b05ea87f1c2a135c87699410 Mon Sep 17 00:00:00 2001 From: nullcat Date: Mon, 14 Oct 2024 19:29:38 +0800 Subject: [PATCH 4/5] Update elems.py --- lagrange/client/message/elems.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lagrange/client/message/elems.py b/lagrange/client/message/elems.py index f70539f..bac68ff 100644 --- a/lagrange/client/message/elems.py +++ b/lagrange/client/message/elems.py @@ -261,9 +261,13 @@ def display(self) -> str: @dataclass -class Markdown(Text): +class Markdown(BaseElem): content: str + @property + def display(self) -> str: + return f"[markdown:{self.md_c.decode()}]" + class Permission: type: int @@ -307,3 +311,7 @@ class InlineKeyboard: class Keyboard(Text): content: Optional[list[InlineKeyboard]] bot_appid: int + + @property + def display(self) -> str: + return f"[keyboard:{self.bot_appid}]" From 2d82b8ea1ceb1df574d796f6be30026500dedc19 Mon Sep 17 00:00:00 2001 From: nullcat Date: Mon, 14 Oct 2024 19:30:28 +0800 Subject: [PATCH 5/5] Update elems.py --- lagrange/client/message/elems.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lagrange/client/message/elems.py b/lagrange/client/message/elems.py index bac68ff..f1ee905 100644 --- a/lagrange/client/message/elems.py +++ b/lagrange/client/message/elems.py @@ -308,7 +308,7 @@ class InlineKeyboard: @dataclass -class Keyboard(Text): +class Keyboard(BaseElem): content: Optional[list[InlineKeyboard]] bot_appid: int