Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

muiltmsg support #48

Draft
wants to merge 4 commits into
base: broken
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 46 additions & 12 deletions lagrange/client/client.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import gzip
import os
import struct
import asyncio
Expand All @@ -15,6 +16,7 @@
from collections.abc import Coroutine

from lagrange.info import AppInfo, DeviceInfo, SigInfo
from lagrange.pb.message.longmsg import LongMsgResult, RecvLongMsgReq, RecvLongMsgRsp
from lagrange.pb.message.msg_push import MsgPushBody
from lagrange.pb.message.send import SendMsgRsp
from lagrange.pb.service.comm import (
Expand Down Expand Up @@ -70,9 +72,9 @@
from .events.group import GroupMessage
from .events.service import ClientOnline, ClientOffline
from .highway import HighWaySession
from .message.decoder import parse_grp_msg
from .message.elems import Audio, Image
from .message.encoder import build_message
from .message.decoder import parse_grp_msg, parse_msg_new
from .message.elems import Audio, Image, MulitMsg
from .message.encoder import _get_mulitmsg_resid, build_message
from .message.types import Element
from .models import UserInfo, BotFriend
from .server_push import PushDeliver, bind_services
Expand Down Expand Up @@ -210,13 +212,27 @@ async def _send_msg_raw(self, pb: dict, *, grp_id=0, uid="") -> SendMsgRsp:
return SendMsgRsp.decode(packet.data)

async def send_grp_msg(self, msg_chain: list[Element], grp_id: int) -> int:
result = await self._send_msg_raw({1: build_message(msg_chain).encode()}, grp_id=grp_id)
result = await self._send_msg_raw({1: (await build_message(msg_chain)).encode()}, grp_id=grp_id)
if result.ret_code:
raise AssertionError(result.ret_code, result.err_msg)
return result.seq

async def send_friend_msg(self, msg_chain: list[Element], uid: str) -> int:
result = await self._send_msg_raw({1: build_message(msg_chain).encode()}, uid=uid)
result = await self._send_msg_raw({1: (await build_message(msg_chain)).encode()}, uid=uid)
if result.ret_code:
raise AssertionError(result.ret_code, result.err_msg)
return result.seq

async def send_grp_forward_msg(self, forward_msg: MulitMsg, grp_id: int):
forward_msg.resid = await _get_mulitmsg_resid(self, forward_msg, grp_id=grp_id)
result = await self._send_msg_raw({1: (await build_message([forward_msg])).encode()}, grp_id=grp_id)
if result.ret_code:
raise AssertionError(result.ret_code, result.err_msg)
return result.seq

async def send_friend_forward_msg(self, forward_msg: MulitMsg, uid: str):
forward_msg.resid = await _get_mulitmsg_resid(self, forward_msg, target=uid)
result = await self._send_msg_raw({1: (await build_message([forward_msg])).encode()}, uid=uid)
if result.ret_code:
raise AssertionError(result.ret_code, result.err_msg)
return result.seq
Expand Down Expand Up @@ -493,17 +509,13 @@ async def set_grp_request(self, grp_id: int, grp_req_seq: int, ev_type: int, act
raise AssertionError(rsp.ret_code, rsp.err_msg)

@overload
async def get_user_info(self, uid_or_uin: Union[str, int], /) -> UserInfo:
...
async def get_user_info(self, uid_or_uin: Union[str, int], /) -> UserInfo: ...

@overload
async def get_user_info(self, uid_or_uin: Union[list[str], list[int]], /) -> list[UserInfo]:
...
async def get_user_info(self, uid_or_uin: Union[list[str], list[int]], /) -> list[UserInfo]: ...

async def get_user_info(
self,
uid_or_uin: Union[str, int, list[str], list[int]],
/
self, uid_or_uin: Union[str, int, list[str], list[int]], /
) -> Union[UserInfo, list[UserInfo]]:
if isinstance(uid_or_uin, list):
assert uid_or_uin, "empty uid or uin"
Expand Down Expand Up @@ -613,3 +625,25 @@ async def get_rkey(self) -> tuple[str, str]:
rsp = await self.send_oidb_svc(0x9067, 202, proto_encode(body), True)
temp = proto_decode(rsp.data).into((4, 1), dict[int, list[bytes]])
return temp[0][1].decode(), temp[1][1].decode()

async def get_forward_msg(self, res_id: str) -> list[list[Element]]:
"""
res_id: from MultiMsg
"""
ret: list[list[Element]] = []
rsp = RecvLongMsgRsp.decode(
(
await self.send_uni_packet(
"trpc.group.long_msg_interface.MsgService.SsoRecvLongMsg",
RecvLongMsgReq.build(self.uid, res_id).encode(),
)
).data
)
payload = gzip.decompress(rsp.result.payload)
awa = LongMsgResult.decode(payload)
for msg in awa.action:
for elem in msg.action_data.action_list:
ret.append(list(await parse_msg_new(self, elem)))
# 对,但是不是很对的解析
# 顺序对了,但是顺序不对
return ret # TODO: retrun MulitMsg
16 changes: 14 additions & 2 deletions lagrange/client/message/decoder.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
from xml.dom import minidom
import zlib
from typing import TYPE_CHECKING, cast, Literal, Union
from collections.abc import Sequence
Expand Down Expand Up @@ -218,7 +219,18 @@ async def parse_msg_new(
content = zlib.decompress(jr[1:])
else:
content = jr[1:]
msg_chain.append(elems.Service(id=sid, raw=content))
if sid == 35:
# msg_chain.append(elems.MultiMsg(res_id=service))
root: minidom.Document = minidom.parseString(content)
msg_elem: minidom.Element = root.getElementsByTagName("msg")[0]
return [
elems.MulitMsg(
msg_elem.getAttribute("m_fileName"),
resid=msg_elem.getAttribute("m_resid"),
)
]
else:
msg_chain.append(elems.Service(id=sid, raw=content))
ignore_next = True
elif raw.open_data:
msg_chain.append(elems.Raw(data=raw.open_data.data))
Expand Down Expand Up @@ -299,7 +311,7 @@ async def parse_friend_msg(client: "Client", pkg: MsgPushBody) -> FriendMessage:
from_uin, from_uid, to_uin, to_uid = parse_friend_info(pkg)

seq = pkg.content_head.seq
msg_id = pkg.content_head.msg_id
msg_id = pkg.content_head.random
timestamp = pkg.content_head.timestamp
parsed_msg = await parse_msg_new(client, pkg, fri_id=from_uid, grp_id=None)
msg_text = "".join([getattr(msg, "display", "") for msg in parsed_msg])
Expand Down
75 changes: 74 additions & 1 deletion lagrange/client/message/elems.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import json
from dataclasses import dataclass, field
from typing import Optional
import time
from typing import TYPE_CHECKING, Optional

from lagrange.client.events.group import GroupMessage
from lagrange.info.serialize import JsonSerializer

if TYPE_CHECKING:
from .types import Element


@dataclass
class BaseElem(JsonSerializer):
Expand All @@ -16,6 +20,10 @@ def display(self) -> str:
def type(self) -> str:
return self.__class__.__name__.lower()

@property
def raw_text(self) -> str:
return ""


@dataclass
class CompatibleText(BaseElem):
Expand Down Expand Up @@ -48,6 +56,10 @@ class Text(BaseElem):
def display(self) -> str:
return self.text

@property
def raw_text(self) -> str:
return self.text


@dataclass
class Quote(CompatibleText):
Expand Down Expand Up @@ -101,6 +113,10 @@ class AtAll(BaseElem):
def display(self) -> str:
return self.text

@property
def raw_text(self) -> str:
return self.text


@dataclass
class At(BaseElem):
Expand All @@ -112,6 +128,14 @@ class At(BaseElem):
def build(cls, ev: GroupMessage) -> "At":
return cls(uin=ev.uin, uid=ev.uid, text=f"@{ev.nickname or ev.uin}")

@property
def display(self) -> str:
return self.text

@property
def raw_text(self) -> str:
return self.text


@dataclass
class Image(CompatibleText, MediaInfo):
Expand All @@ -120,6 +144,10 @@ class Image(CompatibleText, MediaInfo):
is_emoji: bool
display_name: str

@property
def raw_text(self) -> str:
return "[图片]"

@property
def display(self) -> str:
return self.display_name
Expand All @@ -132,6 +160,10 @@ class Video(CompatibleText, MediaInfo):
time: int
file_key: str = field(repr=True)

@property
def raw_text(self) -> str:
return "[视频]"

@property
def display(self) -> str:
return f"[video:{self.width}x{self.height},{self.time}s]"
Expand All @@ -142,6 +174,10 @@ class Audio(CompatibleText, MediaInfo):
time: int
file_key: str = field(repr=True)

@property
def raw_text(self) -> str:
return "[语音]"

@property
def display(self) -> str:
return f"[audio:{self.time}]"
Expand Down Expand Up @@ -201,6 +237,10 @@ def url(self) -> str:
def display(self) -> str:
return f"[marketface:{self.name}]"

@property
def raw_text(self) -> str:
return "[动画表情]"


@dataclass
class File(CompatibleText):
Expand All @@ -216,6 +256,10 @@ class File(CompatibleText):
def display(self) -> str:
return f"[file:{self.file_name}]"

@property
def raw_text(self) -> str:
return "[文件]"

@classmethod
def _paste_build(
cls,
Expand Down Expand Up @@ -268,6 +312,10 @@ class Markdown(BaseElem):
def display(self) -> str:
return f"[markdown:{self.content}]"

@property
def raw_text(self) -> str:
return "[Markdown]"


class Permission:
type: int
Expand Down Expand Up @@ -315,3 +363,28 @@ class Keyboard(BaseElem):
@property
def display(self) -> str:
return f"[keyboard:{self.bot_appid}]"


@dataclass
class ForwardNode(BaseElem):
content: list["Element"]

sender_uin: int
sender_nick: str = ""
sender_avatar_url: str = ""

timestamp: int = field(default_factory=lambda: int(time.time()))


@dataclass
class MulitMsg(BaseElem):
messages: list[ForwardNode]
resid: Optional[str] = None

@property
def display(self) -> str:
return f"[forward:{self.resid}]"

@property
def raw_text(self) -> str:
return "[聊天记录]"
Loading