diff --git a/app/DataBase/hard_link.py b/app/DataBase/hard_link.py index 22e93f82..cc6ab0d3 100644 --- a/app/DataBase/hard_link.py +++ b/app/DataBase/hard_link.py @@ -5,6 +5,7 @@ import xml.etree.ElementTree as ET from app.log import log +from app.util.protocbuf.msg_pb2 import MessageBytesExtra image_db_lock = threading.Lock() video_db_lock = threading.Lock() @@ -30,224 +31,47 @@ def get_md5_from_xml(content, type_="img"): return None -class tencent_struct: - def __setVals__(self, data, off): - if data: - self.__data = data - if self.__data: - self.__size = len(self.__data) - self.__off = off - - def __readString(self): - try: - length = self.__readUleb() - res = self.__data[self.__off: self.__off + length] - self.__add(length) - except: - raise - return res.decode("utf-8") - - def __readUleb(self): - try: - i = self.__data[self.__off] - self.__add() - if i & 0x80: - j = self.__data[self.__off] - i = i & 0x7F - i = i | (j << 7) - self.__add() - if i & 0x4000: - j = self.__data[self.__off] - i = i & 0x3FFF - i = i | (j << 14) - self.__add() - if i & 0x200000: - j = self.__data[self.__off] - i = i & 0x1FFFFF - i = i | (j << 21) - self.__add() - if i & 0x10000000: - j = self.__data[self.__off] - i = i & 0xFFFFFFF - i = i | (j << 28) - self.__add() - return i - except: - raise - - def __readData(self): - try: - length = self.__readUleb() - data = self.__data[self.__off: self.__off + length] - self.__add(length) - return data - except: - raise - - def __init__(self, data=None, off=0): - self.__data = data - self.__off = off - if self.__data: - self.__size = len(self.__data) - else: - self.__size = 0 - - def __add(self, value=1): - self.__off += value - if self.__off > self.__size: - raise "偏移量超出size" - - def readStruct(self, struct_type): - current_dict = None - if isinstance(struct_type, str): - current_dict = getattr(self, struct_type) - else: - current_dict = struct_type - res = {} - try: - while self.__off < self.__size: - key = self.__readUleb() - key = key >> 3 - if key == 0: - break - op = None - fieldName = "" - if key in current_dict: - op = current_dict[key][1] - fieldName = current_dict[key][0] - else: - break - if isinstance(op, dict): - if not key in res: - res[key] = [] - current_struct = self.__readData() - recursion = tencent_struct(current_struct) - res[key].append((fieldName, recursion.readStruct(op))) - elif op != "": - res[key] = (fieldName, self.__contenttype__[op](self)) - else: - break - except: - raise - return res - - __struct1__ = {1: ("", "I"), 2: ("", "I")} - - __msgInfo__ = {1: ("", "I"), 2: ("msg_info", "s")} - - __bytesExtra__ = { - 1: ("", __struct1__), - 3: ("msg_info_struct", __msgInfo__), - } - - __struct2__ = {1: ("", "s"), 2: ("", "s")} - - __extraBuf__ = { - 1: ("", __struct2__), - } - - def get_bytesExta_Content(self, data=None, off=0): - self.__setVals__(data, off) - try: - return self.readStruct("__bytesExtra__") - except: - raise - - def get_extraBuf_Content(self, data=None, off=0): - self.__setVals__(data, off) - try: - return self.readStruct("__extraBuf__") - except: - raise - - __contenttype__ = { - "s": __readString, - "I": __readUleb, - "P": __readData, - } - - -def parseBytes(content: bytes): - try: - bytesExtra = tencent_struct().get_bytesExta_Content(content) - return bytesExtra - except: - pass - - -def parseExtraBuf(content: bytes): - try: - extraBuf = tencent_struct().get_extraBuf_Content(content) - return extraBuf - except: - pass - - def decodeExtraBuf(extra_buf_content: bytes): - off = 0 - types = [b"\x04", b"\x18", b"\x17", b"\x02", b"\x05"] trunkName = { - "46CF10C4": "个性签名", - "A4D9024A": "国家", - "E2EAA8D1": "省份", - "1D025BBF": "市", - "81AE19B4": "朋友圈背景url", - "F917BCC0": "公司名称", - "4EB96D85": "企业微信属性", - "0E719F13": "备注图片", - "759378AD": "手机号", - "74752C06": "性别", + b"\x46\xCF\x10\xC4": "个性签名", + b"\xA4\xD9\x02\x4A": "国家", + b"\xE2\xEA\xA8\xD1": "省份", + b"\x1D\x02\x5B\xBF": "市", + # b"\x81\xAE\x19\xB4": "朋友圈背景url", + # b"\xF9\x17\xBC\xC0": "公司名称", + # b"\x4E\xB9\x6D\x85": "企业微信属性", + # b"\x0E\x71\x9F\x13": "备注图片", + b"\x75\x93\x78\xAD": "手机号", + b"\x74\x75\x2C\x06": "性别", } - res = {'手机号': {'18': ''}} - while off < len(extra_buf_content): - length = 4 # 块头 - trunk_head = extra_buf_content[off: off + length] - off += length - trunk_head = binascii.hexlify(trunk_head).decode().upper() - if trunk_head in trunkName: - trunk_head = trunkName[trunk_head] - res[trunk_head] = {} - char = extra_buf_content[off: off + 1] + res = {"手机号": ""} + off = 0 + for key in trunkName: + trunk_head = trunkName[key] + try: + off = extra_buf_content.index(key) + 4 + except: + pass + char = extra_buf_content[off : off + 1] off += 1 - field = binascii.hexlify(char).decode() if char == b"\x04": # 四个字节的int,小端序 - length = 4 - intContent = extra_buf_content[off: off + length] + intContent = extra_buf_content[off : off + 4] off += 4 intContent = int.from_bytes(intContent, "little") - res[trunk_head][field] = intContent + res[trunk_head] = intContent elif char == b"\x18": # utf-16字符串 - length = 4 - lengthContent = extra_buf_content[off: off + length] - off += 4 - lengthContent = int.from_bytes(lengthContent, "little") - strContent = extra_buf_content[off: off + lengthContent] - off += lengthContent - res[trunk_head][field] = strContent.decode("utf-16").rstrip("\x00") - elif char == b"\x17": # utf-8 protobuf - length = 4 - lengthContent = extra_buf_content[off: off + length] + lengthContent = extra_buf_content[off : off + 4] off += 4 lengthContent = int.from_bytes(lengthContent, "little") - strContent = extra_buf_content[off: off + lengthContent] + strContent = extra_buf_content[off : off + lengthContent] off += lengthContent - res[trunk_head][field] = parseExtraBuf(strContent) - elif char == b"\x02": # 一个字节的int - content = extra_buf_content[off: off + 1] - off += 1 - res[trunk_head][field] = int.from_bytes(content, "little") - elif char == b"\x05": # 暂时不知道有啥用,固定8个字节,先当int处理 - length = 8 - content = extra_buf_content[off: off + length] - off += length - res[trunk_head][field] = int.from_bytes(content, "little") - # print(res) + res[trunk_head] = strContent.decode("utf-16").rstrip("\x00") return { - 'region': (res['国家']['18'], res['省份']['18'], res['市']['18']), - 'signature': res['个性签名']['18'], - 'telephone': res['手机号']['18'], - 'gender': res['性别']['04'] + "region": (res["国家"], res["省份"], res["市"]), + "signature": res["个性签名"], + "telephone": res["手机号"], + "gender": res["性别"], } @@ -337,12 +161,14 @@ def get_video_by_md5(self, md5: bytes): video_db_lock.release() def get_image(self, content, bytesExtra, thumb=False): - bytesDict = parseBytes(bytesExtra) - for msginfo in bytesDict[3]: - if msginfo[1][1][1] == (3 if thumb else 4): - pathh = msginfo[1][2][1] # wxid\FileStorage\... - pathh = "\\".join(pathh.split("\\")[1:]) - return pathh + msg_bytes = MessageBytesExtra() + msg_bytes.ParseFromString(bytesExtra) + for tmp in msg_bytes.message2: + if tmp.field1 != (3 if thumb else 4): + continue + pathh = tmp.field2 # wxid\FileStorage\... + pathh = "\\".join(pathh.split("\\")[1:]) + return pathh md5 = get_md5_from_xml(content) if not md5: return None @@ -357,12 +183,14 @@ def get_image(self, content, bytesExtra, thumb=False): return dat_image def get_video(self, content, bytesExtra, thumb=False): - bytesDict = parseBytes(bytesExtra) - for msginfo in bytesDict[3]: - if msginfo[1][1][1] == (3 if thumb else 4): - pathh = msginfo[1][2][1] # wxid\FileStorage\... - pathh = "\\".join(pathh.split("\\")[1:]) - return pathh + msg_bytes = MessageBytesExtra() + msg_bytes.ParseFromString(bytesExtra) + for tmp in msg_bytes.message2: + if tmp.field1 != (3 if thumb else 4): + continue + pathh = tmp.field2 # wxid\FileStorage\... + pathh = "\\".join(pathh.split("\\")[1:]) + return pathh md5 = get_md5_from_xml(content, type_="video") if not md5: return None diff --git a/app/DataBase/msg.py b/app/DataBase/msg.py index d1a7c44f..4c67639d 100644 --- a/app/DataBase/msg.py +++ b/app/DataBase/msg.py @@ -4,7 +4,6 @@ import threading import traceback -from app.DataBase.hard_link import parseBytes from app.log import logger from app.util.compress_content import parser_reply from app.util.protocbuf.msg_pb2 import MessageBytesExtra @@ -651,12 +650,12 @@ def __del__(self): else: show_display_name = appinfo.find('appname').text print(title, des, url, show_display_name) - bytesDict = parseBytes(msg[10]) - for msginfo in bytesDict[3]: - print(msginfo) - if msginfo[1][1][1] == 3: - thumb = msginfo[1][2][1] + msg_bytes = MessageBytesExtra() + msg_bytes.ParseFromString(msg[10]) + for tmp in msg_bytes.message2: + if tmp.field1 == 3: + thumb = tmp.field2 print(thumb) - if msginfo[1][1][1] == 4: - app_logo = msginfo[1][2][1] + if tmp.field2 == 4: + app_logo = tmp.field2 print('logo',app_logo) \ No newline at end of file diff --git a/app/resources/data/template.html b/app/resources/data/template.html index 939bb7ac..83c48197 100644 --- a/app/resources/data/template.html +++ b/app/resources/data/template.html @@ -173,6 +173,7 @@ width: 42px; height: 42px; border-radius: 50%; + user-select: none; } .chat-video video{ margin-right: 18px; @@ -465,6 +466,12 @@ margin-left: 5px; } } + +.system-msg>.emoji_img { + width: 18px; + height: 18px; +} + .emoji_img { width: 22px; height: 22px; @@ -802,7 +809,7 @@ // 从数据列表中取出对应范围的元素并添加到容器中 for (let i = startIndex; i < endIndex && i < chatMessages.length; i++) { const message = chatMessages[i]; - if (i == startIndex) { // 判断一下在页面顶部多加一个时间 + if (i == startIndex && (reachedBottom ? !/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(chatMessages[i - 1].text) : 1)) { // 判断一下在页面顶部多加一个时间 if (!/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(message.text)) { // 时间戳转成时间 function timestampToTime(timestamp) { @@ -843,7 +850,7 @@ } else if (message.type == 0) { messageElement.className = "item item-center"; - messageElement.innerHTML = `${message.text}`; + messageElement.innerHTML = `${replaceEmoji(message.text)}`; } else if (message.type == 3) { // displayname 和 img diff --git a/app/util/compress_content.py b/app/util/compress_content.py index f8397e4f..bb6ab148 100644 --- a/app/util/compress_content.py +++ b/app/util/compress_content.py @@ -8,7 +8,7 @@ from urllib.parse import urlparse from bs4 import BeautifulSoup -from app.DataBase.hard_link import parseBytes +from app.util.protocbuf.msg_pb2 import MessageBytesExtra from ..util.file import get_file @@ -149,15 +149,16 @@ def share_card(bytesExtra, compress_content_): else: if appinfo is not None: show_display_name = appinfo.find('appname').text - bytesDict = parseBytes(bytesExtra) + msg_bytes = MessageBytesExtra() + msg_bytes.ParseFromString(bytesExtra) app_logo = '' thumbnail = '' - for msginfo in bytesDict[3]: - if msginfo[1][1][1] == 3: - thumbnail = msginfo[1][2][1] + for tmp in msg_bytes.message2: + if tmp.field1 == 3: + thumbnail = tmp.field2 thumbnail = "\\".join(thumbnail.split('\\')[1:]) - if msginfo[1][1][1] == 4: - app_logo = msginfo[1][2][1] + if tmp.field2 == 4: + app_logo = tmp.field2 app_logo = "\\".join(app_logo.split('\\')[1:]) if sourceusername is not None: from app.DataBase import micro_msg_db # 放上面会导致循环依赖