Skip to content

Commit 564b3ea

Browse files
authored
feat(event): Implement the file event parsing section (#27)
* feat(client): supplement the tip for when group_msg receives 0x210 * feat(client): initial implementation of parsing file
1 parent ec0f70e commit 564b3ea

File tree

4 files changed

+114
-3
lines changed

4 files changed

+114
-3
lines changed

lagrange/client/message/decoder.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88

99
from . import elems
1010
from .types import Element
11+
from lagrange.utils.binary.reader import Reader
1112
from lagrange.utils.binary.protobuf import proto_encode
13+
from lagrange.pb.message.rich_text.elems import GroupFileExtra, FileExtra
1214
from lagrange.pb.highway.comm import MsgInfo
1315

1416
if TYPE_CHECKING:
@@ -36,6 +38,18 @@ def parse_friend_info(pkg: MsgPushBody) -> Tuple[int, str, int, str]:
3638

3739

3840
async def parse_msg_new(client: "Client", pkg: MsgPushBody) -> Sequence[Element]:
41+
if not pkg.message or not pkg.message.body:
42+
if pkg.content_head.sub_type == 4:
43+
data = FileExtra.decode(pkg.message.buf2)
44+
return [
45+
elems.File.pri_paste_build(
46+
file_size=data.file.file_size,
47+
file_name=data.file.file_name,
48+
file_md5=data.file.file_md5,
49+
file_uuid=data.file.file_uuid,
50+
file_hash=data.file.file_hash,
51+
)
52+
]
3953
rich: RichText = pkg.message.body
4054
if rich.ptt:
4155
ptt = rich.ptt
@@ -166,6 +180,21 @@ async def parse_msg_new(client: "Client", pkg: MsgPushBody) -> Sequence[Element]
166180
qmsg=None,
167181
)
168182
)
183+
elif raw.trans_elem:
184+
elem_type, trans = raw.trans_elem.elem_type, raw.trans_elem.elem_value
185+
if elem_type == 24:
186+
reader = Reader(trans)
187+
reader.read_bytes(1)
188+
data = reader.read_bytes_with_length("u16", False)
189+
file_extra = GroupFileExtra.decode(data)
190+
msg_chain.append(
191+
elems.File.grp_paste_build(
192+
file_size=file_extra.inner.info.file_size,
193+
file_name=file_extra.inner.info.file_name,
194+
file_md5=file_extra.inner.info.file_md5,
195+
file_id=file_extra.inner.info.file_id
196+
)
197+
)
169198
elif raw.rich_msg:
170199
service = raw.rich_msg
171200
if service.template:

lagrange/client/message/elems.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,37 @@ class MarketFace(Text):
144144
def url(self) -> str:
145145
pic_id = self.face_id.hex()
146146
return f"https://i.gtimg.cn/club/item/parcel/item/{pic_id[:2]}/{pic_id}/{self.width}x{self.height}.png"
147+
148+
149+
@dataclass
150+
class File(Text):
151+
file_size: int
152+
file_name: str
153+
file_md5: bytes
154+
file_url: Optional[str]
155+
file_id: Optional[str] # only in group
156+
file_uuid: Optional[str] # only in private
157+
file_hash: Optional[str]
158+
159+
@classmethod
160+
def _paste_build(cls, file_size: int, file_name: str,
161+
file_md5: bytes, file_id: Optional[str] = None,
162+
file_uuid: Optional[str] = None, file_hash: Optional[str] = None) -> "File":
163+
return cls(
164+
text=f"[file:{file_name}]",
165+
file_size=file_size,
166+
file_name=file_name,
167+
file_md5=file_md5,
168+
file_url=None,
169+
file_id=file_id,
170+
file_uuid=file_uuid,
171+
file_hash=file_hash,
172+
)
173+
174+
@classmethod
175+
def grp_paste_build(cls, file_size: int, file_name: str, file_md5: bytes, file_id: str) -> "File":
176+
return cls._paste_build(file_size, file_name, file_md5, file_id=file_id)
177+
178+
@classmethod
179+
def pri_paste_build(cls, file_size: int, file_name: str, file_md5: bytes, file_uuid: str, file_hash: str) -> "File":
180+
return cls._paste_build(file_size, file_name, file_md5, file_uuid=file_uuid, file_hash=file_hash)

lagrange/client/server_push/msg.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ async def msg_push_handler(client: "Client", sso: SSOPacket):
5757
logger.debug("msg_push received, type: {}.{}".format(typ, sub_typ))
5858
if typ == 82: # grp msg
5959
return await parse_grp_msg(client, pkg)
60-
elif typ == 166: # frd msg
60+
elif typ in [166, 529]: # frd msg
6161
return await parse_friend_msg(client, pkg)
6262
elif typ == 33: # member joined
6363
pb = MemberChanged.decode(pkg.message.buf2)
@@ -88,8 +88,8 @@ async def msg_push_handler(client: "Client", sso: SSOPacket):
8888
return GroupMemberJoinRequest(
8989
grp_id=inn.grp_id, uid=inn.uid, invitor_uid=inn.invitor_uid
9090
)
91-
elif typ == 0x210: # frd event
92-
logger.debug("unhandled friend event: %s" % pkg)
91+
elif typ == 0x210: # friend event / group file upload notice event
92+
logger.debug("unhandled friend event / group file upload notice event: %s" % pkg) # TODO: paste
9393
elif typ == 0x2DC: # grp event, 732
9494
if sub_typ == 20: # nudge and group_sign(群打卡)
9595
if pkg.message:

lagrange/pb/message/rich_text/elems.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,51 @@ class VideoFile(ProtoStruct):
148148
thumb_width: int = proto_field(16)
149149
thumb_height: int = proto_field(17)
150150
# reserve on field 24?
151+
152+
153+
class NotOnlineFile(ProtoStruct):
154+
file_type: Optional[int] = proto_field(1)
155+
# sig: Optional[bytes] = proto_field(2)
156+
file_uuid: Optional[str] = proto_field(3)
157+
file_md5: Optional[bytes] = proto_field(4)
158+
file_name: Optional[str] = proto_field(5)
159+
file_size: Optional[int] = proto_field(6)
160+
# note: Optional[bytes] = proto_field(7)
161+
# reserved: Optional[int] = proto_field(8)
162+
subcmd: Optional[int] = proto_field(9)
163+
# micro_cloud: Optional[int] = proto_field(10)
164+
# bytes_file_urls: Optional[list[bytes]] = proto_field(11)
165+
# download_flag: Optional[int] = proto_field(12)
166+
danger_evel: Optional[int] = proto_field(50)
167+
# life_time: Optional[int] = proto_field(51)
168+
# upload_time: Optional[int] = proto_field(52)
169+
# abs_file_type: Optional[int] = proto_field(53)
170+
# client_type: Optional[int] = proto_field(54)
171+
expire_time: Optional[int] = proto_field(55)
172+
pb_reserve: bytes = proto_field(56)
173+
file_hash: Optional[str] = proto_field(57)
174+
175+
176+
class FileExtra(ProtoStruct):
177+
file: NotOnlineFile = proto_field(1)
178+
179+
180+
class GroupFileExtraInfo(ProtoStruct):
181+
bus_id: int = proto_field(1)
182+
file_id: str = proto_field(2)
183+
file_size: int = proto_field(3)
184+
file_name: str = proto_field(4)
185+
f5: int = proto_field(5)
186+
f7: str = proto_field(7)
187+
file_md5: bytes = proto_field(8)
188+
189+
190+
class GroupFileExtraInner(ProtoStruct):
191+
info: GroupFileExtraInfo = proto_field(2)
192+
193+
194+
class GroupFileExtra(ProtoStruct):
195+
f1: int = proto_field(1)
196+
file_name: str = proto_field(2)
197+
display: str = proto_field(3)
198+
inner: GroupFileExtraInner = proto_field(7)

0 commit comments

Comments
 (0)