diff --git a/lagrange/client/client.py b/lagrange/client/client.py index 3b94454..c92e121 100644 --- a/lagrange/client/client.py +++ b/lagrange/client/client.py @@ -17,6 +17,12 @@ 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, + GetFriendListUin, + PBGetFriendListRequest, + propertys, +) from lagrange.pb.service.group import ( FetchGroupResponse, GetGrpMsgRsp, @@ -58,7 +64,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 @@ -337,6 +343,58 @@ async def get_grp_msg( return [*filter(lambda msg: msg.rand != -1, rsp)] return rsp + async def get_friend_list(self): + nextuin_cache: List[GetFriendListUin] = [] + rsp: List[BotFriend] = [] + frist_send = GetFriendListRsp.decode( + (await self.send_oidb_svc(0xFD4, 1, PBGetFriendListRequest().encode())).data + ) + 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: + 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( + ( + await self.send_oidb_svc( + 0xFD4, 1, PBGetFriendListRequest().encode() + ) + ).data + ) + for raw in next.friend_list: + for j in raw.additional: + 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) + + 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..b84d8f2 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 + personal_sign: 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..6ea3541 --- /dev/null +++ b/lagrange/pb/service/friend.py @@ -0,0 +1,76 @@ +from typing import List, Optional, Union + +from lagrange.utils.binary.protobuf import ProtoStruct, proto_field + + +class FriendProperty(ProtoStruct): + code: int = proto_field(1) + value: Optional[str] = proto_field(2, default=None) + + +class FriendLayer1(ProtoStruct): + properties: List[FriendProperty] = proto_field(2, default=None) + + +class FriendAdditional(ProtoStruct): + type: int = proto_field(1) + layer1: FriendLayer1 = proto_field(2) + + +class FriendInfo(ProtoStruct): + uid: str = proto_field(1) + custom_group: Optional[int] = proto_field(2, default=None) + 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) + self_uin: int = proto_field(7) + friend_list: List[FriendInfo] = proto_field(101) + + +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