Merge branch 'master' into master

This commit is contained in:
SiYuan 2023-12-07 18:57:55 +08:00 committed by GitHub
commit 050920d2c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 7813 additions and 4790 deletions

View File

@ -63,6 +63,37 @@ class MicroMsg:
lock.release()
return result
def get_contact_by_username(self, username):
if not self.open_flag:
return None
try:
lock.acquire(True)
sql = '''SELECT UserName, Alias, Type, Remark, NickName, PYInitial, RemarkPYInitial, ContactHeadImgUrl.smallHeadImgUrl, ContactHeadImgUrl.bigHeadImgUrl
FROM Contact
INNER JOIN ContactHeadImgUrl ON Contact.UserName = ContactHeadImgUrl.usrName
WHERE UserName = ?
'''
self.cursor.execute(sql, [username])
result = self.cursor.fetchone()
finally:
lock.release()
return result
def get_chatroom_info(self, chatroomname):
'''
获取群聊信息
'''
if not self.open_flag:
return None
try:
lock.acquire(True)
sql = '''SELECT ChatRoomName, RoomData FROM ChatRoom WHERE ChatRoomName = ?'''
self.cursor.execute(sql, [chatroomname])
result = self.cursor.fetchone()
finally:
lock.release()
return result
def close(self):
if self.open_flag:
try:

View File

@ -72,7 +72,7 @@ class Msg:
def get_messages_all(self):
sql = '''
select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID
select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID,BytesExtra,StrTalker,Reserved1
from MSG
order by CreateTime
'''
@ -126,19 +126,19 @@ class Msg:
lock.release()
return result
def get_messages_by_keyword(self, username_, keyword, num=5):
def get_messages_by_keyword(self, username_, keyword, num=5, max_len=10):
if not self.open_flag:
return None
sql = '''
select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID
from MSG
where StrTalker=? and Type=1 and StrContent like ?
where StrTalker=? and Type=1 and LENGTH(StrContent)<? and StrContent like ?
order by CreateTime desc
'''
temp = []
try:
lock.acquire(True)
self.cursor.execute(sql, [username_, f'%{keyword}%'])
self.cursor.execute(sql, [username_, max_len, f'%{keyword}%'])
messages = self.cursor.fetchall()
finally:
lock.release()
@ -213,4 +213,4 @@ if __name__ == '__main__':
pprint(msg.get_message_by_num('wxid_0o18ef858vnu22', local_id))
print(msg.get_messages_by_keyword(wxid, '干嘛'))
pprint(msg.get_messages_by_keyword(wxid, '干嘛')[0])
print(msg.get_first_time_of_message('wxid_0o18ef858vnu22'))
print(msg.get_first_time_of_message('wxid_fervbwign7m822'))

View File

@ -5,13 +5,23 @@ import os
from PyQt5.QtCore import pyqtSignal, QThread
from . import msg_db
from .package_msg import PackageMsg
from ..DataBase import hard_link_db
from ..person_pc import MePC
from ..util import get_abs_path
from ..util import path
os.makedirs('./data/聊天记录', exist_ok=True)
def makedirs(path):
os.makedirs(path,exist_ok=True)
os.makedirs(os.path.join(path, 'image'), exist_ok=True)
os.makedirs(os.path.join(path, 'emoji'), exist_ok=True)
os.makedirs(os.path.join(path, 'video'), exist_ok=True)
os.makedirs(os.path.join(path, 'voice'), exist_ok=True)
os.makedirs(os.path.join(path, 'file'), exist_ok=True)
def escape_js_and_html(input_str):
# 转义HTML特殊字符
html_escaped = html.escape(input_str, quote=False)
@ -65,8 +75,10 @@ class Output(QThread):
# columns = ["用户名", "消息内容", "发送时间", "发送状态", "消息类型", "isSend", "msgId"]
columns = ['localId', 'TalkerId', 'Type', 'SubType',
'IsSender', 'CreateTime', 'Status', 'StrContent',
'StrTime']
messages = msg_db.get_messages_all()
'StrTime', 'Remark', 'NickName', 'Sender']
# messages = msg_db.get_messages_all()
packagemsg = PackageMsg()
messages = packagemsg.get_package_message_all()
# 写入CSV文件
with open(filename, mode='w', newline='', encoding='utf-8') as file:
writer = csv.writer(file)
@ -165,8 +177,10 @@ class ChildThread(QThread):
# columns = ["用户名", "消息内容", "发送时间", "发送状态", "消息类型", "isSend", "msgId"]
columns = ['localId', 'TalkerId', 'Type', 'SubType',
'IsSender', 'CreateTime', 'Status', 'StrContent',
'StrTime']
messages = msg_db.get_messages_all()
'StrTime', 'Remark', 'NickName', 'Sender']
# messages = msg_db.get_messages_all()
packagemsg = PackageMsg()
messages = packagemsg.get_package_message_all()
# 写入CSV文件
with open(filename, mode='w', newline='', encoding='utf-8') as file:
writer = csv.writer(file)
@ -434,7 +448,7 @@ class ChildThread(QThread):
def to_html_(self):
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}"
os.makedirs(origin_docx_path, exist_ok=True)
makedirs(origin_docx_path)
messages = msg_db.get_messages(self.contact.wxid)
filename = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}/{self.contact.remark}.html"
f = open(filename, 'w', encoding='utf-8')
@ -671,6 +685,13 @@ input {
font-size: 14px;
}
.emoji_img {
width: 26px;
height: 26px;
position: relative;
bottom: -6px;
}
</style>
</head>
<body>
@ -723,6 +744,116 @@ const chatMessages = [
MePC().avatar.save(os.path.join(origin_docx_path, 'myhead.png'))
self.contact.avatar.save(os.path.join(origin_docx_path, 'tahead.png'))
self.rangeSignal.emit(len(messages))
emoji = {
'[微笑]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_1@2x.png" id="微笑" class="emoji_img">',
'[撇嘴]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_2@2x.png" id="撇嘴" class="emoji_img">',
'[色]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_3@2x.png" id="" class="emoji_img">',
'[发呆]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_4@2x.png" id="发呆" class="emoji_img">',
'[得意]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_5@2x.png" id="得意" class="emoji_img">',
'[流泪]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_6@2x.png" id="流泪" class="emoji_img">',
'[害羞]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_7@2x.png" id="害羞" class="emoji_img">',
'[闭嘴]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_8@2x.png" id="闭嘴" class="emoji_img">',
'[睡]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_9@2x.png" id="" class="emoji_img">',
'[大哭]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_10@2x.png" id="大哭" class="emoji_img">',
'[尴尬]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_11@2x.png" id="尴尬" class="emoji_img">',
'[发怒]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_12@2x.png" id="发怒" class="emoji_img">',
'[调皮]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_13@2x.png" id="调皮" class="emoji_img">',
'[呲牙]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_14@2x.png" id="呲牙" class="emoji_img">',
'[惊讶]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_15@2x.png" id="惊讶" class="emoji_img">',
'[难过]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_16@2x.png" id="难过" class="emoji_img">',
'[抓狂]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_19@2x.png" id="抓狂" class="emoji_img">',
'[吐]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_20@2x.png" id="" class="emoji_img">',
'[偷笑]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_21@2x.png" id="偷笑" class="emoji_img">',
'[愉快]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_22@2x.png" id="愉快" class="emoji_img">',
'[白眼]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_23@2x.png" id="白 眼" class="emoji_img">',
'[傲慢]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_24@2x.png" id="傲慢" class="emoji_img">',
'[困]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_26@2x.png" id="" class="emoji_img">',
'[惊恐]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_27@2x.png" id="惊恐" class="emoji_img">',
'[憨笑]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_29@2x.png" id="憨笑" class="emoji_img">',
'[悠闲]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_30@2x.png" id="悠闲" class="emoji_img">',
'[咒骂]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_32@2x.png" id="咒骂" class="emoji_img">',
'[疑问]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_33@2x.png" id="疑问" class="emoji_img">',
'[嘘]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_34@2x.png" id="" class="emoji_img">',
'[晕]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_35@2x.png" id="" class="emoji_img">',
'[衰]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_37@2x.png" id="" class="emoji_img">',
'[骷髅]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_38@2x.png" id="骷髅" class="emoji_img">',
'[敲打]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_39@2x.png" id="敲打" class="emoji_img">',
'[再见]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_40@2x.png" id="再见" class="emoji_img">',
'[擦汗]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_41@2x.png" id="擦汗" class="emoji_img">',
'[抠鼻]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_42@2x.png" id="抠鼻" class="emoji_img">',
'[鼓掌]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_43@2x.png" id="鼓掌" class="emoji_img">',
'[坏笑]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_45@2x.png" id="坏笑" class="emoji_img">',
'[右哼哼]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_47@2x.png" id="右哼哼" class="emoji_img">',
'[鄙视]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_49@2x.png" id="鄙视" class="emoji_img">',
'[委屈]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_50@2x.png" id="委屈" class="emoji_img">',
'[快哭了]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_51@2x.png" id="快哭了" class="emoji_img">',
'[阴险]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_52@2x.png" id="阴险" class="emoji_img">',
'[亲亲]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_53@2x.png" id="亲亲" class="emoji_img">',
'[可怜]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_55@2x.png" id="可怜" class="emoji_img">',
'[笑脸]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Happy.png" id="笑脸" class="emoji_img">',
'[生病]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Sick.png" id="生病" class="emoji_img">',
'[脸红]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Flushed.png" id="脸红" class="emoji_img">',
'[破涕为笑]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Lol.png" id="破涕为笑" class="emoji_img">',
'[恐惧]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Terror.png" id="恐惧" class="emoji_img">',
'[失望]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/LetDown.png" id="失望" class="emoji_img">',
'[无语]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Duh.png" id="无语" class="emoji_img">',
'[嘿哈]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/2_04.png" id="嘿哈" class="emoji_img">',
'[捂脸]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/2_05.png" id="捂脸" class="emoji_img">',
'[奸笑]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/2_02.png" id="奸笑" class="emoji_img">',
'[机智]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/2_06.png" id="机智" class="emoji_img">',
'[皱眉]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/2_12.png" id="皱眉" class="emoji_img">',
'[耶]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/2_11.png" id="" class="emoji_img">',
'[吃瓜]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Watermelon.png" id="吃瓜" class="emoji_img">',
'[加油]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Addoil.png" id="加油" class="emoji_img">',
'[汗]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Sweat.png" id="" class="emoji_img">',
'[天啊]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Shocked.png" id="天啊" class="emoji_img">',
'[Emm]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Cold.png" id="Emm" class="emoji_img">',
'[社会社会]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Social.png" id="社会社会" class="emoji_img">',
'[旺柴]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Yellowdog.png" id="旺柴" class="emoji_img">',
'[好的]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/NoProb.png" id="好的" class="emoji_img">',
'[打脸]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Slap.png" id="打脸" class="emoji_img">',
'[哇]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Wow.png" id="" class="emoji_img">',
'[翻白眼]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Boring.png" id="翻白眼" class="emoji_img">',
'[666]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/666.png" id="666" class="emoji_img">',
'[让我看看]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/LetMeSee.png" id="让我看看" class="emoji_img">',
'[叹气]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Sigh.png" id="叹气" class="emoji_img">',
'[苦涩]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Hurt.png" id="苦涩" class="emoji_img">',
'[裂开]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Broken.png" id="裂开" class="emoji_img">',
'[嘴唇]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_66@2x.png" id="嘴唇" class="emoji_img">',
'[爱心]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_67@2x.png" id="爱心" class="emoji_img">',
'[心碎]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_68@2x.png" id="心碎" class="emoji_img">',
'[拥抱]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_79@2x.png" id="拥抱" class="emoji_img">',
'[强]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_80@2x.png" id="" class="emoji_img">',
'[弱]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_81@2x.png" id="" class="emoji_img">',
'[握手]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_82@2x.png" id="握手" class="emoji_img">',
'[胜利]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_83@2x.png" id="胜利" class="emoji_img">',
'[抱拳]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_84@2x.png" id="抱拳" class="emoji_img">',
'[勾引]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_85@2x.png" id="勾引" class="emoji_img">',
'[拳头]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_86@2x.png" id="拳头" class="emoji_img">',
'[OK]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_90@2x.png" id="OK" class="emoji_img">',
'[合十]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Worship.png" id="合十" class="emoji_img">',
'[啤酒]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_58@2x.png" id="啤酒" class="emoji_img">',
'[咖啡]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_61@2x.png" id="咖啡" class="emoji_img">',
'[蛋糕]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_69@2x.png" id="蛋糕" class="emoji_img">',
'[玫瑰]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_64@2x.png" id="玫 瑰" class="emoji_img">',
'[凋谢]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_65@2x.png" id="凋谢" class="emoji_img">',
'[菜刀]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_56@2x.png" id="菜刀" class="emoji_img">',
'[炸弹]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_71@2x.png" id="炸弹" class="emoji_img">',
'[便便]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_75@2x.png" id="便便" class="emoji_img">',
'[月亮]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_76@2x.png" id="月亮" class="emoji_img">',
'[太阳]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_77@2x.png" id="太阳" class="emoji_img">',
'[庆 祝]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Party.png" id="庆祝" class="emoji_img">',
'[礼物]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_78@2x.png" id="礼物" class="emoji_img">',
'[红包]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/2_09.png" id="红包" class="emoji_img">',
'[發]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/2_16.png" id="" class="emoji_img">',
'[福]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/2_15.png" id="" class="emoji_img">',
'[烟花]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Fireworks.png" id="烟花" class="emoji_img">',
'[爆竹]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Firecracker.png" id="爆竹" class="emoji_img">',
'[猪头]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_63@2x.png" id="猪头" class="emoji_img">',
'[跳跳]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_93@2x.png" id="跳跳" class="emoji_img">',
'[发抖]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_94@2x.png" id="发抖" class="emoji_img">',
'[转圈]': '<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/Expression/Expression_96@2x.png" id="转圈" class="emoji_img">'}
from re import findall
for index, message in enumerate(messages):
type_ = message[2]
str_content = message[7]
@ -740,6 +871,11 @@ const chatMessages = [
f.write(
f'''{{ type:0, text: '{str_time}',is_send:0,avatar_path:''}},'''
)
emojiText = findall(r"(\[.+?\])", str_content)
for emoji_text in emojiText:
if emoji_text in emoji:
str_content = str_content.replace(emoji_text, emoji[emoji_text])
f.write(
f'''{{ type:{type_}, text: '{str_content}',is_send:{is_send},avatar_path:'{avatar}'}},'''
)
@ -753,9 +889,8 @@ const chatMessages = [
match = re.search(pattern2, str_content)
if match:
continue
image_path = hard_link_db.get_image(content=str_content, thumb=False)
image_path = get_abs_path(image_path)
image_path = path.get_relative_path(image_path, base_path=f'/data/聊天记录/{self.contact.remark}/image')
image_path = image_path.replace('\\', '/')
# print(f"tohtml:---{image_path}")
if self.is_5_min(timestamp):

103
app/DataBase/package_msg.py Normal file
View File

@ -0,0 +1,103 @@
import threading
from app.DataBase import msg_db, micro_msg_db
from app.util.protocbuf.msg_pb2 import MessageBytesExtra
from app.util.protocbuf.roomdata_pb2 import ChatRoomData
lock = threading.Lock()
def singleton(cls):
_instance = {}
def inner():
if cls not in _instance:
_instance[cls] = cls()
return _instance[cls]
return inner
@singleton
class PackageMsg:
def __init__(self):
self.ChatRoomMap = {}
def get_package_message_all(self):
'''
获取完整的聊天记录
'''
updated_messages = [] # 用于存储修改后的消息列表
messages = msg_db.get_messages_all()
for row in messages:
row_list = list(row)
# 删除不使用的几个字段
del row_list[12]
del row_list[11]
del row_list[10]
strtalker = row[11]
info = micro_msg_db.get_contact_by_username(strtalker)
if info is not None:
row_list.append(info[3])
row_list.append(info[4])
# 判断是否是群聊
if strtalker.__contains__('@chatroom'):
# 自己发送
if row[12] == 1:
row_list.append('')
else:
# 存在BytesExtra为空的情况此时消息类型应该为提示性消息。跳过不处理
if row[10] is None:
continue
# 解析BytesExtra
msgbytes = MessageBytesExtra()
msgbytes.ParseFromString(row[10])
wxid = ''
for tmp in msgbytes.message2:
if tmp.field1 != 1:
continue
wxid = tmp.field2
sender = ''
# 获取群聊成员列表
membersMap = self.get_chatroom_member_list(strtalker)
if membersMap is not None:
if wxid in membersMap:
sender = membersMap.get(wxid)
else:
senderinfo = micro_msg_db.get_contact_by_username(wxid)
if senderinfo is not None:
sender = senderinfo[4]
membersMap[wxid] = senderinfo[4]
if len(senderinfo[3]) > 0:
sender = senderinfo[3]
membersMap[wxid] = senderinfo[3]
row_list.append(sender)
updated_messages.append(tuple(row_list))
return updated_messages
def get_chatroom_member_list(self, strtalker):
membermap = {}
'''
获取群聊成员
'''
try:
lock.acquire(True)
if strtalker in self.ChatRoomMap:
membermap = self.ChatRoomMap.get(strtalker)
else:
chatroom = micro_msg_db.get_chatroom_info(strtalker)
if chatroom is None:
return None
# 解析RoomData数据
parsechatroom = ChatRoomData()
parsechatroom.ParseFromString(chatroom[1])
# 群成员数据放入字典存储
for mem in parsechatroom.members:
if mem.displayName is not None and len(mem.displayName) > 0:
membermap[mem.wxID] = mem.displayName
self.ChatRoomMap[strtalker] = membermap
finally:
lock.release()
return membermap

View File

@ -19,6 +19,13 @@ wordcloud_height = 720
def wordcloud(wxid):
import jieba
txt_messages = msg_db.get_messages_by_type(wxid, MsgType.TEXT)
if not txt_messages:
return {
'chart_data': None,
'keyword': "没有聊天你想分析啥",
'max_num': "0",
'dialogs': []
}
text = ''.join(map(lambda x: x[7], txt_messages))
total_msg_len = len(text)
# 使用jieba进行分词并加入停用词
@ -26,7 +33,7 @@ def wordcloud(wxid):
# 统计词频
word_count = Counter(words)
# 过滤停用词
stopwords_file = './app/data/stopwords.txt'
stopwords_file = './app000/data/stopwords.txt'
try:
with open(stopwords_file, "r", encoding="utf-8") as stopword_file:
stopwords = set(stopword_file.read().splitlines())
@ -65,7 +72,7 @@ def wordcloud(wxid):
'chart_data': w.dump_options_with_quotes(),
'keyword': keyword,
'max_num': str(max_num),
'dialogs': msg_db.get_messages_by_keyword(wxid, keyword, num=5)
'dialogs': msg_db.get_messages_by_keyword(wxid, keyword, num=5, max_len=12)
}

View File

@ -1,8 +1,9 @@
version = '0.2.6'
version = '0.2.7'
contact = '474379264'
description = [
'1. 支持获取个人信息<br>',
'2. 支持显示聊天界面<br>',
'3. 支持导出聊天记录<br>&nbsp;&nbsp;&nbsp;&nbsp;* csv<br>&nbsp;&nbsp;&nbsp;&nbsp;* html<br>',
'4. 查找联系人<br>',
'5. 生成年度报告<br>',
]

File diff suppressed because it is too large Load Diff

2561
app/resources/stopwords.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -50,14 +50,15 @@ def decode_dat(file_path, out_path):
if decode_code == -1:
return
filename = os.path.basename(file_path)
if file_type == 1:
pic_name = os.path.basename(file_path)[:-4] + ".jpg"
elif file_type == 3:
pic_name = file_path[:-4] + ".png"
pic_name = filename[:-4] + ".png"
elif file_type == 5:
pic_name = file_path[:-4] + ".gif"
pic_name = filename[:-4] + ".gif"
else:
pic_name = file_path[:-4] + ".jpg"
pic_name = filename[:-4] + ".jpg"
file_outpath = os.path.join(out_path, pic_name)
if os.path.exists(file_outpath):
return file_outpath

View File

@ -7,16 +7,26 @@ from app.util import dat2pic
os.makedirs('./data/image', exist_ok=True)
def get_abs_path(path):
def get_abs_path(path, base_path="/data/image"):
# return os.path.join(os.getcwd(), 'app/data/icons/404.png')
if path:
base_path = os.getcwd() + "/data/image"
output_path = dat2pic.decode_dat(os.path.join(MePC().wx_dir, path), base_path) # './data/image')
base_path = os.getcwd() + base_path
output_path = dat2pic.decode_dat(os.path.join(MePC().wx_dir, path), base_path)
return output_path if output_path else ':/icons/icons/404.png'
else:
return ':/icons/icons/404.png'
def get_relative_path(path, base_path):
if path:
base_path = os.getcwd() + base_path
output_path = dat2pic.decode_dat(os.path.join(MePC().wx_dir, path), base_path)
relative_path = './image/'+os.path.basename(output_path) if output_path else 'https://www.bing.com/images/search?view=detailV2&ccid=Zww6woP3&id=CCC91337C740656E800E51247E928ACD3052FECF&thid=OIP.Zww6woP3Em49TdSG_lnggAHaEK&mediaurl=https%3a%2f%2fmeekcitizen.files.wordpress.com%2f2018%2f09%2f404.jpg%3fw%3d656&exph=360&expw=640&q=404&simid=608040792714530493&FORM=IRPRST&ck=151E7337A86F1B9C5C5DB08B15B90809&selectedIndex=21&itb=0'
return relative_path
else:
return ':/icons/icons/404.png'
def mkdir(path):
if not os.path.exists(path):
os.mkdir(path)
@ -44,4 +54,3 @@ def wx_path():
return wx_location
except FileNotFoundError:
return '.'

View File

View File

@ -0,0 +1,18 @@
syntax = "proto3";
package app.protobuf;
option go_package=".;proto";
message SubMessage1 {
int32 field1 = 1;
int32 field2 = 2;
}
message SubMessage2 {
int32 field1 = 1;
string field2 = 2;
}
message MessageBytesExtra {
SubMessage1 message1 = 1;
repeated SubMessage2 message2 = 3;
}

View File

@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: msg.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tmsg.proto\x12\x0c\x61pp.protobuf\"-\n\x0bSubMessage1\x12\x0e\n\x06\x66ield1\x18\x01 \x01(\x05\x12\x0e\n\x06\x66ield2\x18\x02 \x01(\x05\"-\n\x0bSubMessage2\x12\x0e\n\x06\x66ield1\x18\x01 \x01(\x05\x12\x0e\n\x06\x66ield2\x18\x02 \x01(\t\"m\n\x11MessageBytesExtra\x12+\n\x08message1\x18\x01 \x01(\x0b\x32\x19.app.protobuf.SubMessage1\x12+\n\x08message2\x18\x03 \x03(\x0b\x32\x19.app.protobuf.SubMessage2b\x06proto3')
_SUBMESSAGE1 = DESCRIPTOR.message_types_by_name['SubMessage1']
_SUBMESSAGE2 = DESCRIPTOR.message_types_by_name['SubMessage2']
_MESSAGEBYTESEXTRA = DESCRIPTOR.message_types_by_name['MessageBytesExtra']
SubMessage1 = _reflection.GeneratedProtocolMessageType('SubMessage1', (_message.Message,), {
'DESCRIPTOR' : _SUBMESSAGE1,
'__module__' : 'msg_pb2'
# @@protoc_insertion_point(class_scope:app.protobuf.SubMessage1)
})
_sym_db.RegisterMessage(SubMessage1)
SubMessage2 = _reflection.GeneratedProtocolMessageType('SubMessage2', (_message.Message,), {
'DESCRIPTOR' : _SUBMESSAGE2,
'__module__' : 'msg_pb2'
# @@protoc_insertion_point(class_scope:app.protobuf.SubMessage2)
})
_sym_db.RegisterMessage(SubMessage2)
MessageBytesExtra = _reflection.GeneratedProtocolMessageType('MessageBytesExtra', (_message.Message,), {
'DESCRIPTOR' : _MESSAGEBYTESEXTRA,
'__module__' : 'msg_pb2'
# @@protoc_insertion_point(class_scope:app.protobuf.MessageBytesExtra)
})
_sym_db.RegisterMessage(MessageBytesExtra)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
_SUBMESSAGE1._serialized_start=27
_SUBMESSAGE1._serialized_end=72
_SUBMESSAGE2._serialized_start=74
_SUBMESSAGE2._serialized_end=119
_MESSAGEBYTESEXTRA._serialized_start=121
_MESSAGEBYTESEXTRA._serialized_end=230
# @@protoc_insertion_point(module_scope)

View File

@ -0,0 +1,34 @@
# 说明
## 解析
```shell
protoc --decode_raw < msg_data.txt
```
## 根据解析结果,设置.proto文件
```shell
1 {
1: 16
2: 0
}
3 {
1: 1
2: "wxid_4b1t09d63spw22"
}
3 {
1: 7
2: "<msgsource>\n\t<alnode>\n\t\t<fr>2</fr>\n\t</alnode>\n\t<sec_msg_node>\n\t\t<uuid>c6680ab2c57499a1a22e44a7eada76e8_</uuid>\n\t</sec_msg_node>\n\t<silence>1</silence>\n\t<membercount>198</membercount>\n\t<signature>v1_Gj7hfmi5</signature>\n\t<tmp_node>\n\t\t<publisher-id></publisher-id>\n\t</tmp_node>\n</msgsource>\n"
}
3 {
1: 2
2: "c13acbc95512d1a59bb686d684fd64d8"
}
3 {
1: 4
2: "yiluoAK_47\\FileStorage\\Cache\\2023-08\\2286b5852db82f6cbd9c2084ccd52358"
}
```
## 生成python文件
```shell
protoc --python_out=. msg.proto
```

View File

@ -0,0 +1,19 @@
syntax = "proto3";
package app.protobuf;
option go_package=".;proto";
message ChatRoomData {
message ChatRoomMember {
string wxID = 1;
string displayName = 2;
int32 state = 3;
}
repeated ChatRoomMember members = 1;
int32 field_2 = 2;
int32 field_3 = 3;
int32 field_4 = 4;
int32 room_capacity = 5;
int32 field_6 = 6;
int64 field_7 = 7;
int64 field_8 = 8;
}

View File

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: roomdata.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0eroomdata.proto\x12\x0c\x61pp.protobuf\"\x8b\x02\n\x0c\x43hatRoomData\x12:\n\x07members\x18\x01 \x03(\x0b\x32).app.protobuf.ChatRoomData.ChatRoomMember\x12\x0f\n\x07\x66ield_2\x18\x02 \x01(\x05\x12\x0f\n\x07\x66ield_3\x18\x03 \x01(\x05\x12\x0f\n\x07\x66ield_4\x18\x04 \x01(\x05\x12\x15\n\rroom_capacity\x18\x05 \x01(\x05\x12\x0f\n\x07\x66ield_6\x18\x06 \x01(\x05\x12\x0f\n\x07\x66ield_7\x18\x07 \x01(\x03\x12\x0f\n\x07\x66ield_8\x18\x08 \x01(\x03\x1a\x42\n\x0e\x43hatRoomMember\x12\x0c\n\x04wxID\x18\x01 \x01(\t\x12\x13\n\x0b\x64isplayName\x18\x02 \x01(\t\x12\r\n\x05state\x18\x03 \x01(\x05\x62\x06proto3')
_CHATROOMDATA = DESCRIPTOR.message_types_by_name['ChatRoomData']
_CHATROOMDATA_CHATROOMMEMBER = _CHATROOMDATA.nested_types_by_name['ChatRoomMember']
ChatRoomData = _reflection.GeneratedProtocolMessageType('ChatRoomData', (_message.Message,), {
'ChatRoomMember' : _reflection.GeneratedProtocolMessageType('ChatRoomMember', (_message.Message,), {
'DESCRIPTOR' : _CHATROOMDATA_CHATROOMMEMBER,
'__module__' : 'roomdata_pb2'
# @@protoc_insertion_point(class_scope:app.protobuf.ChatRoomData.ChatRoomMember)
})
,
'DESCRIPTOR' : _CHATROOMDATA,
'__module__' : 'roomdata_pb2'
# @@protoc_insertion_point(class_scope:app.protobuf.ChatRoomData)
})
_sym_db.RegisterMessage(ChatRoomData)
_sym_db.RegisterMessage(ChatRoomData.ChatRoomMember)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
_CHATROOMDATA._serialized_start=33
_CHATROOMDATA._serialized_end=300
_CHATROOMDATA_CHATROOMMEMBER._serialized_start=234
_CHATROOMDATA_CHATROOMMEMBER._serialized_end=300
# @@protoc_insertion_point(module_scope)

View File

@ -42,16 +42,16 @@ def index0():
@app.route('/home')
def home():
try:
data = {
'sub_title': '二零二三年度报告',
'avatar_path': contact.avatar_path,
'nickname': contact.remark,
'first_time': msg_db.get_first_time_of_message(contact.wxid)[1],
}
return render_template('home.html', **data)
except IndexError:
first_message, first_time = msg_db.get_first_time_of_message(contact.wxid)
except TypeError:
return set_text('咱就是说,一次都没聊过就别分析了')
data = {
'sub_title': '二零二三年度报告',
'avatar_path': contact.avatar_path,
'nickname': contact.remark,
'first_time': first_time,
}
return render_template('home.html', **data)
@app.route('/wordcloud')

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 KiB

View File

@ -9,7 +9,7 @@ from PyQt5.QtWidgets import *
from app.log import logger
from app.ui_pc import mainview
from app.ui_pc.tool.pc_decrypt import pc_decrypt
from app.config import version
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("WeChatReport")
@ -39,7 +39,7 @@ class ViewController(QWidget):
self.viewMainWindow = mainview.MainWinController(username=username)
self.viewMainWindow.exitSignal.connect(self.close)
try:
self.viewMainWindow.setWindowTitle("留痕")
self.viewMainWindow.setWindowTitle(f"留痕-{version}")
self.viewMainWindow.show()
end = time.time()
print('ok', '本次加载用了', end - start, 's')

View File

@ -294,7 +294,8 @@ python main.py
---
> 声明:该项目有且仅有一个目的:“留痕”——我的数据我做主,前提是“我的数据”其次才是“我做主”,禁止任何人以任何形式将其用于任何非法用途,对于使用该程序所造成的任何后果,所有创作者不承担任何责任🙄
> 声明:该项目有且仅有一个目的:“留痕”——我的数据我做主,前提是“我的数据”其次才是“我做主”,禁止任何人以任何形式将其用于任何非法用途,对于使用该程序所造成的任何后果,所有创作者不承担任何责任🙄<br>
> 该软件不会对您使用的微信造成任何影响,更不会对他人的微信造成任何影响,不能找回删除的聊天记录,任何企图篡改微信聊天数据的想法都是无稽之谈。本项目所有功能均建立在”前言“的基础之上,基于该项目的所有开发者均不能接受任何有悖于”前言“的功能需求,违者后果自负。
[![Star History Chart](https://api.star-history.com/svg?repos=LC044/WeChatMsg&type=Date)](https://star-history.com/?utm_source=bestxtools.com#LC044/WeChatMsg&Date)

Binary file not shown.

View File

@ -12,3 +12,6 @@ requests
flask==3.0.0
pyecharts==2.0.1
jieba==0.42.1
google==3.0.0
protobuf==4.25.1
soupsieve==2.5