From 02f9aecfa319f8ef3cab4327f413516631c44549 Mon Sep 17 00:00:00 2001 From: Decrabbit Date: Wed, 14 Aug 2024 01:19:26 +0800 Subject: [PATCH 1/7] feat: support get_friend_list --- lagrange/client/client.py | 81 ++++++++++++++++++++++++++++++++--- lagrange/client/models.py | 11 +++++ lagrange/pb/service/friend.py | 74 ++++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 6 deletions(-) create mode 100644 lagrange/pb/service/friend.py diff --git a/lagrange/client/client.py b/lagrange/client/client.py index 30ff0e9..5a12759 100644 --- a/lagrange/client/client.py +++ b/lagrange/client/client.py @@ -2,12 +2,26 @@ import struct import asyncio from io import BytesIO -from typing import BinaryIO, Callable, Coroutine, List, Optional, Union, overload, Literal +from typing import ( + BinaryIO, + Callable, + Coroutine, + List, + Optional, + Union, + overload, + Literal, +) from lagrange.info import AppInfo, DeviceInfo, SigInfo from lagrange.pb.message.msg_push import MsgPushBody from lagrange.pb.message.send import SendMsgRsp from lagrange.pb.service.comm import SendNudge +from lagrange.pb.service.friend import ( + GetFriendListRsp, + PBGetFriendListRequest, + property, +) from lagrange.pb.service.group import ( FetchGroupResponse, GetGrpMsgRsp, @@ -49,7 +63,7 @@ from .message.elems import Audio, Image from .message.encoder import build_message from .message.types import Element -from .models import UserInfo +from .models import BotFriend, UserInfo from .server_push.binder import PushDeliver from .wtlogin.sso import SSOPacket @@ -61,7 +75,9 @@ def __init__( app_info: AppInfo, device_info: DeviceInfo, sig_info: SigInfo, - sign_provider: Optional[Callable[[str, int, bytes], Coroutine[None, None, dict]]] = None, + sign_provider: Optional[ + Callable[[str, int, bytes], Coroutine[None, None, dict]] + ] = None, use_ipv6=True, ): super().__init__(uin, app_info, device_info, sig_info, sign_provider, use_ipv6) @@ -100,7 +116,9 @@ async def easy_login(self) -> bool: else: raise AssertionError("siginfo not found, you must login first") - async def login(self, password: str = "", qrcode_path: Optional[str] = None) -> bool: + async def login( + self, password: str = "", qrcode_path: Optional[str] = None + ) -> bool: try: if self._sig.temp_pwd: rsp = await self.easy_login() @@ -235,7 +253,9 @@ async def down_grp_audio(self, audio: Audio, grp_id: int) -> BytesIO: async def down_friend_audio(self, audio: Audio) -> BytesIO: return await self._highway.download_audio(audio, uid=self.uid) - async def fetch_image_url(self, bus_type: Literal[10, 20], node: "IndexNode", uid=None, gid=None): + async def fetch_image_url( + self, bus_type: Literal[10, 20], node: "IndexNode", uid=None, gid=None + ): if bus_type == 10: return await self._get_pri_img_url(uid, node) elif bus_type == 20: @@ -313,12 +333,61 @@ async def get_grp_msg( ), "return args not matched" rsp = list( - await asyncio.gather(*[parse_grp_msg(self, MsgPushBody.decode(i)) for i in payload.elems]) + await asyncio.gather( + *[parse_grp_msg(self, MsgPushBody.decode(i)) for i in payload.elems] + ) ) if filter_deleted_msg: return [*filter(lambda msg: msg.rand != -1, rsp)] return rsp + async def get_Friend_list(self): + nextuin_cache = list() + rsp: List[BotFriend] = list() + frist_send = GetFriendListRsp.decode( + (await self.send_oidb_svc(0xFD4, 1, PBGetFriendListRequest().encode())).data + ) + nextuin_cache.append(frist_send.next) + for raw in frist_send.friend_list: + for j in raw.additional: + properties = property(j.layer1.properties) + rsp.append( + BotFriend( + raw.uin, + raw.uid, + properties[20002], + properties[103], + properties[102], + properties[27394], + ) + ) + + while nextuin_cache: + next = GetFriendListRsp.decode( + ( + await self.send_oidb_svc( + 0xFD4, 1, PBGetFriendListRequest().encode() + ) + ).data + ) + for raw in next.friend_list: + for j in raw.additional: + properties = property(j.layer1.properties) + rsp.append( + BotFriend( + raw.uin, + raw.uid, + properties[20002], + properties[103], + properties[102], + properties[27394], + ) + ) + if next.next: + nextuin_cache.append(next) + + return rsp + async def recall_grp_msg(self, grp_id: int, seq: int): payload = await self.send_uni_packet( "trpc.msg.msg_svc.MsgService.SsoGroupRecallMsg", diff --git a/lagrange/client/models.py b/lagrange/client/models.py index b5b9d62..14eaef0 100644 --- a/lagrange/client/models.py +++ b/lagrange/client/models.py @@ -2,6 +2,7 @@ from dataclasses import dataclass from datetime import datetime from enum import IntEnum +from typing import Optional from lagrange.pb.service.group import GetInfoRspBody @@ -65,3 +66,13 @@ def from_pb(cls, pb: GetInfoRspBody) -> "UserInfo": else: pass return rsp + + +@dataclass +class BotFriend: + Uin: int + Uid: Optional[str] = None + Nickname: Optional[str] = None + Remarks: Optional[str] = None + PersonalSign: Optional[str] = None + Qid: Optional[str] = None diff --git a/lagrange/pb/service/friend.py b/lagrange/pb/service/friend.py new file mode 100644 index 0000000..9edcba8 --- /dev/null +++ b/lagrange/pb/service/friend.py @@ -0,0 +1,74 @@ +from typing import List, Optional + +from lagrange.utils.binary.protobuf import ProtoStruct, proto_field + + +class FriendProperty(ProtoStruct): + code: int = proto_field(1) + value: str = proto_field(2) + + +class FriendLayer1(ProtoStruct): + properties: List[FriendProperty] = proto_field(2) + + +class FriendAdditional(ProtoStruct): + Type: int = proto_field(1) + layer1: FriendLayer1 = proto_field(2) + + +class FriendInfo(ProtoStruct): + uid: str = proto_field(1) + custom_group: int = proto_field(2) + uin: int = proto_field(3) + additional: List[FriendAdditional] = proto_field(10001) + + +class GetFriendNumbers(ProtoStruct): + f1: List[int] = proto_field(1) + + +class GetFriendBody(ProtoStruct): + Type: int = proto_field(1) + f2: GetFriendNumbers = proto_field(2) + + +class GetFriendListUin(ProtoStruct): + Uin: int = proto_field(1) + + +class PBGetFriendListRequest(ProtoStruct): + friend_count: int = proto_field(2, default=300) # paging get num + f4: int = proto_field(4, default=0) + next_uin: Optional[GetFriendListUin] = proto_field(5, default=None) + f6: int = proto_field(6, default=1) + f7: int = proto_field(7, default=2147483647) # MaxValue + body: List[GetFriendBody] = proto_field( + 10001, + default=[ + GetFriendBody(Type=1, f2=GetFriendNumbers(f1=[103, 102, 20002, 27394])), + GetFriendBody(Type=4, f2=GetFriendNumbers(f1=[100, 101, 102])), + ], + ) + f10002: List[int] = proto_field(10002, default=[13578, 13579, 13573, 13572, 13568]) + f10003: int = proto_field(10003, default=4051) + """ + * GetFriendNumbers里是要拿到的东西 + * 102:个性签名 + * 103:备注 + * 20002:昵称 + * 27394:QID + """ + + +class GetFriendListRsp(ProtoStruct): + next: Optional[GetFriendListUin] = proto_field(2, default=None) + display_friend_count: int = proto_field(3) + timestamp: int = proto_field(6) + selfuin: int = proto_field(7) + friend_list: List[FriendInfo] = proto_field(101) + + +def property(properties: List[FriendProperty]): + prop_dict = dict((prop.code, prop.value) for prop in properties) + return prop_dict From 8e519c0093e6b2fc69ab97111a574af39a9c0534 Mon Sep 17 00:00:00 2001 From: Decrabbit Date: Wed, 14 Aug 2024 02:45:37 +0800 Subject: [PATCH 2/7] fix null data --- lagrange/client/client.py | 49 +++++++++++++++++++---------------- lagrange/pb/service/friend.py | 12 +++++---- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/lagrange/client/client.py b/lagrange/client/client.py index 5a12759..2cc1ed9 100644 --- a/lagrange/client/client.py +++ b/lagrange/client/client.py @@ -20,7 +20,7 @@ from lagrange.pb.service.friend import ( GetFriendListRsp, PBGetFriendListRequest, - property, + propertys, ) from lagrange.pb.service.group import ( FetchGroupResponse, @@ -347,20 +347,24 @@ async def get_Friend_list(self): frist_send = GetFriendListRsp.decode( (await self.send_oidb_svc(0xFD4, 1, PBGetFriendListRequest().encode())).data ) - nextuin_cache.append(frist_send.next) + properties: Optional[dict] = None + if frist_send.next: + nextuin_cache.append(frist_send.next) for raw in frist_send.friend_list: for j in raw.additional: - properties = property(j.layer1.properties) - rsp.append( - BotFriend( - raw.uin, - raw.uid, - properties[20002], - properties[103], - properties[102], - properties[27394], + if j.Type == 1: + properties = propertys(j.layer1.properties) + if properties is not None: + rsp.append( + BotFriend( + raw.uin, + raw.uid, + properties.get(20002), + properties.get(103), + properties.get(102), + properties.get(27394), + ) ) - ) while nextuin_cache: next = GetFriendListRsp.decode( @@ -372,17 +376,18 @@ async def get_Friend_list(self): ) for raw in next.friend_list: for j in raw.additional: - properties = property(j.layer1.properties) - rsp.append( - BotFriend( - raw.uin, - raw.uid, - properties[20002], - properties[103], - properties[102], - properties[27394], + properties = propertys(j.layer1.properties) + if properties is not None: + rsp.append( + BotFriend( + raw.uin, + raw.uid, + properties.get(20002), + properties.get(103), + properties.get(102), + properties.get(27394), + ) ) - ) if next.next: nextuin_cache.append(next) diff --git a/lagrange/pb/service/friend.py b/lagrange/pb/service/friend.py index 9edcba8..0b45124 100644 --- a/lagrange/pb/service/friend.py +++ b/lagrange/pb/service/friend.py @@ -1,15 +1,15 @@ -from typing import List, Optional +from typing import List, Optional, Union from lagrange.utils.binary.protobuf import ProtoStruct, proto_field class FriendProperty(ProtoStruct): code: int = proto_field(1) - value: str = proto_field(2) + value: Optional[str] = proto_field(2, default=None) class FriendLayer1(ProtoStruct): - properties: List[FriendProperty] = proto_field(2) + properties: List[FriendProperty] = proto_field(2, default=None) class FriendAdditional(ProtoStruct): @@ -19,7 +19,7 @@ class FriendAdditional(ProtoStruct): class FriendInfo(ProtoStruct): uid: str = proto_field(1) - custom_group: int = proto_field(2) + custom_group: Optional[int] = proto_field(2, default=None) uin: int = proto_field(3) additional: List[FriendAdditional] = proto_field(10001) @@ -69,6 +69,8 @@ class GetFriendListRsp(ProtoStruct): friend_list: List[FriendInfo] = proto_field(101) -def property(properties: List[FriendProperty]): +def propertys(properties: Union[List[FriendProperty], None]): + if properties is None: + return {} prop_dict = dict((prop.code, prop.value) for prop in properties) return prop_dict From 837af263c581104c8a8cd4b8a28432466b406813 Mon Sep 17 00:00:00 2001 From: Decrabbit Date: Wed, 14 Aug 2024 03:01:44 +0800 Subject: [PATCH 3/7] Resolved conflicts --- lagrange/client/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lagrange/client/client.py b/lagrange/client/client.py index 2cc1ed9..ecd1da9 100644 --- a/lagrange/client/client.py +++ b/lagrange/client/client.py @@ -63,8 +63,8 @@ from .message.elems import Audio, Image from .message.encoder import build_message from .message.types import Element -from .models import BotFriend, UserInfo -from .server_push.binder import PushDeliver +from .models import UserInfo +from .server_push import PushDeliver, bind_services from .wtlogin.sso import SSOPacket From 5dd6a0d54cdb8aab0f46c65d03d8f0e2f38109db Mon Sep 17 00:00:00 2001 From: Decrabbit Date: Wed, 14 Aug 2024 03:03:26 +0800 Subject: [PATCH 4/7] add BotFriend --- lagrange/client/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lagrange/client/client.py b/lagrange/client/client.py index 497e34f..d09f265 100644 --- a/lagrange/client/client.py +++ b/lagrange/client/client.py @@ -63,7 +63,7 @@ from .message.elems import Audio, Image from .message.encoder import build_message from .message.types import Element -from .models import UserInfo +from .models import UserInfo, BotFriend from .server_push import PushDeliver, bind_services from .wtlogin.sso import SSOPacket From 022abed94bc3b16d691fae67aa34849af27eff35 Mon Sep 17 00:00:00 2001 From: Decrabbit Date: Thu, 15 Aug 2024 11:16:10 +0800 Subject: [PATCH 5/7] fix typo --- lagrange/client/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lagrange/client/client.py b/lagrange/client/client.py index d09f265..11abd56 100644 --- a/lagrange/client/client.py +++ b/lagrange/client/client.py @@ -342,7 +342,7 @@ async def get_grp_msg( return [*filter(lambda msg: msg.rand != -1, rsp)] return rsp - async def get_Friend_list(self): + async def get_friend_list(self): nextuin_cache = list() rsp: List[BotFriend] = list() frist_send = GetFriendListRsp.decode( From b842f1946105ab540c2cc4b3b46a47a5fd92db05 Mon Sep 17 00:00:00 2001 From: Decrabbit Date: Thu, 15 Aug 2024 11:26:11 +0800 Subject: [PATCH 6/7] standardized naming --- lagrange/pb/service/friend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lagrange/pb/service/friend.py b/lagrange/pb/service/friend.py index 0b45124..b2b2f6c 100644 --- a/lagrange/pb/service/friend.py +++ b/lagrange/pb/service/friend.py @@ -65,7 +65,7 @@ class GetFriendListRsp(ProtoStruct): next: Optional[GetFriendListUin] = proto_field(2, default=None) display_friend_count: int = proto_field(3) timestamp: int = proto_field(6) - selfuin: int = proto_field(7) + self_uin: int = proto_field(7) friend_list: List[FriendInfo] = proto_field(101) From 3ada3eae7a39f95b2e331a0d408061e79a4ff40b Mon Sep 17 00:00:00 2001 From: Decrabbit Date: Thu, 15 Aug 2024 11:34:32 +0800 Subject: [PATCH 7/7] standardized naming (again) --- lagrange/client/client.py | 7 ++++--- lagrange/client/models.py | 12 ++++++------ lagrange/pb/service/friend.py | 10 +++++----- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/lagrange/client/client.py b/lagrange/client/client.py index 11abd56..c92e121 100644 --- a/lagrange/client/client.py +++ b/lagrange/client/client.py @@ -19,6 +19,7 @@ from lagrange.pb.service.comm import SendNudge from lagrange.pb.service.friend import ( GetFriendListRsp, + GetFriendListUin, PBGetFriendListRequest, propertys, ) @@ -343,8 +344,8 @@ async def get_grp_msg( return rsp async def get_friend_list(self): - nextuin_cache = list() - rsp: List[BotFriend] = list() + nextuin_cache: List[GetFriendListUin] = [] + rsp: List[BotFriend] = [] frist_send = GetFriendListRsp.decode( (await self.send_oidb_svc(0xFD4, 1, PBGetFriendListRequest().encode())).data ) @@ -353,7 +354,7 @@ async def get_friend_list(self): nextuin_cache.append(frist_send.next) for raw in frist_send.friend_list: for j in raw.additional: - if j.Type == 1: + if j.type == 1: properties = propertys(j.layer1.properties) if properties is not None: rsp.append( diff --git a/lagrange/client/models.py b/lagrange/client/models.py index 14eaef0..b84d8f2 100644 --- a/lagrange/client/models.py +++ b/lagrange/client/models.py @@ -70,9 +70,9 @@ def from_pb(cls, pb: GetInfoRspBody) -> "UserInfo": @dataclass class BotFriend: - Uin: int - Uid: Optional[str] = None - Nickname: Optional[str] = None - Remarks: Optional[str] = None - PersonalSign: Optional[str] = None - Qid: Optional[str] = None + uin: int + uid: Optional[str] = None + nickname: Optional[str] = None + remarks: Optional[str] = None + personal_sign: Optional[str] = None + qid: Optional[str] = None diff --git a/lagrange/pb/service/friend.py b/lagrange/pb/service/friend.py index b2b2f6c..6ea3541 100644 --- a/lagrange/pb/service/friend.py +++ b/lagrange/pb/service/friend.py @@ -13,7 +13,7 @@ class FriendLayer1(ProtoStruct): class FriendAdditional(ProtoStruct): - Type: int = proto_field(1) + type: int = proto_field(1) layer1: FriendLayer1 = proto_field(2) @@ -29,12 +29,12 @@ class GetFriendNumbers(ProtoStruct): class GetFriendBody(ProtoStruct): - Type: int = proto_field(1) + type: int = proto_field(1) f2: GetFriendNumbers = proto_field(2) class GetFriendListUin(ProtoStruct): - Uin: int = proto_field(1) + uin: int = proto_field(1) class PBGetFriendListRequest(ProtoStruct): @@ -46,8 +46,8 @@ class PBGetFriendListRequest(ProtoStruct): body: List[GetFriendBody] = proto_field( 10001, default=[ - GetFriendBody(Type=1, f2=GetFriendNumbers(f1=[103, 102, 20002, 27394])), - GetFriendBody(Type=4, f2=GetFriendNumbers(f1=[100, 101, 102])), + GetFriendBody(type=1, f2=GetFriendNumbers(f1=[103, 102, 20002, 27394])), + GetFriendBody(type=4, f2=GetFriendNumbers(f1=[100, 101, 102])), ], ) f10002: List[int] = proto_field(10002, default=[13578, 13579, 13573, 13572, 13568])