支持导出表情包

This commit is contained in:
shuaikangzhou 2023-12-09 22:48:15 +08:00
parent f3499b307d
commit c5d4ee6e3f
9 changed files with 443 additions and 665 deletions

View File

@ -54,7 +54,6 @@ class Misc:
lock.acquire(True) lock.acquire(True)
self.cursor.execute(sql, [userName]) self.cursor.execute(sql, [userName])
result = self.cursor.fetchall() result = self.cursor.fetchall()
# print(result[0][0])
if result: if result:
return result[0][0] return result[0][0]
finally: finally:

View File

@ -184,7 +184,6 @@ class Msg:
(msg1[4], msg1[5], msg1[7].split(keyword), msg1[8]), (msg1[4], msg1[5], msg1[7].split(keyword), msg1[8]),
(msg2[4], msg2[5], msg2[7], msg2[8]) (msg2[4], msg2[5], msg2[7], msg2[8])
)) ))
return res return res
def get_messages_by_days(self, username_, year_='2023'): def get_messages_by_days(self, username_, year_='2023'):

View File

@ -10,6 +10,9 @@ from .package_msg import PackageMsg
from ..DataBase import hard_link_db from ..DataBase import hard_link_db
from ..person_pc import MePC from ..person_pc import MePC
from ..util import path from ..util import path
import shutil
from ..util.emoji import get_emoji
os.makedirs('./data/聊天记录', exist_ok=True) os.makedirs('./data/聊天记录', exist_ok=True)
@ -73,8 +76,9 @@ class Output(QThread):
def to_csv_all(self): def to_csv_all(self):
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/" origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/"
os.makedirs(origin_docx_path, exist_ok=True) os.makedirs(origin_docx_path, exist_ok=True)
filename = QFileDialog.getSaveFileName(None, "save file", os.path.join(os.getcwd(),'messages.csv'), "csv files (*.csv);;all files(*.*)") filename = QFileDialog.getSaveFileName(None, "save file", os.path.join(os.getcwd(), 'messages.csv'),
if not filename: "csv files (*.csv);;all files(*.*)")
if not filename[0]:
return return
filename = filename[0] filename = filename[0]
# columns = ["用户名", "消息内容", "发送时间", "发送状态", "消息类型", "isSend", "msgId"] # columns = ["用户名", "消息内容", "发送时间", "发送状态", "消息类型", "isSend", "msgId"]
@ -93,12 +97,14 @@ class Output(QThread):
self.okSignal.emit(1) self.okSignal.emit(1)
def contact_to_csv(self): def contact_to_csv(self):
filename = QFileDialog.getSaveFileName(None, "save file", os.path.join(os.getcwd(),'contacts.csv'), "csv files (*.csv);;all files(*.*)") filename = QFileDialog.getSaveFileName(None, "save file", os.path.join(os.getcwd(), 'contacts.csv'),
if not filename: "csv files (*.csv);;all files(*.*)")
if not filename[0]:
return return
filename = filename[0] filename = filename[0]
# columns = ["用户名", "消息内容", "发送时间", "发送状态", "消息类型", "isSend", "msgId"] # columns = ["用户名", "消息内容", "发送时间", "发送状态", "消息类型", "isSend", "msgId"]
columns = ['UserName','Alias', 'Type', 'Remark', 'NickName', 'PYInitial', 'RemarkPYInitial', 'smallHeadImgUrl', 'bigHeadImgUrl'] columns = ['UserName', 'Alias', 'Type', 'Remark', 'NickName', 'PYInitial', 'RemarkPYInitial', 'smallHeadImgUrl',
'bigHeadImgUrl']
contacts = micro_msg_db.get_contact() contacts = micro_msg_db.get_contact()
# 写入CSV文件 # 写入CSV文件
with open(filename, mode='w', newline='', encoding='utf-8') as file: with open(filename, mode='w', newline='', encoding='utf-8') as file:
@ -212,263 +218,6 @@ class ChildThread(QThread):
writer.writerows(messages) writer.writerows(messages)
self.okSignal.emit(1) self.okSignal.emit(1)
def to_html(self):
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}"
os.makedirs(origin_docx_path, exist_ok=True)
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')
html_head = '''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
*{
padding: 0;
margin: 0;
}
body{
height: 100vh;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.container{
height: 760px;
width: 900px;
border-radius: 4px;
border: 0.5px solid #e0e0e0;
background-color: #f5f5f5;
display: flex;
flex-flow: column;
overflow: hidden;
}
.content{
width: calc(100% - 40px);
padding: 20px;
overflow-y: scroll;
flex: 1;
}
.content:hover::-webkit-scrollbar-thumb{
background:rgba(0,0,0,0.1);
}
.bubble{
max-width: 400px;
padding: 10px;
border-radius: 5px;
position: relative;
color: #000;
word-wrap:break-word;
word-break:normal;
}
.item-left .bubble{
margin-left: 15px;
background-color: #fff;
}
.item-left .bubble:before{
content: "";
position: absolute;
width: 0;
height: 0;
border-left: 10px solid transparent;
border-top: 10px solid transparent;
border-right: 10px solid #fff;
border-bottom: 10px solid transparent;
left: -20px;
}
.item-right .bubble{
margin-right: 15px;
background-color: #9eea6a;
}
.item-right .bubble:before{
content: "";
position: absolute;
width: 0;
height: 0;
border-left: 10px solid #9eea6a;
border-top: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 10px solid transparent;
right: -20px;
}
.item{
margin-top: 15px;
display: flex;
width: 100%;
}
.item.item-right{
justify-content: flex-end;
}
.item.item-center{
justify-content: center;
}
.item.item-center span{
font-size: 12px;
padding: 2px 4px;
color: #fff;
background-color: #dadada;
border-radius: 3px;
-moz-user-select:none; /*火狐*/
-webkit-user-select:none; /*webkit浏览器*/
-ms-user-select:none; /*IE10*/
-khtml-user-select:none; /*早期浏览器*/
user-select:none;
}
.avatar img{
width: 42px;
height: 42px;
border-radius: 50%;
}
.input-area{
border-top:0.5px solid #e0e0e0;
height: 150px;
display: flex;
flex-flow: column;
background-color: #fff;
}
textarea{
flex: 1;
padding: 5px;
font-size: 14px;
border: none;
cursor: pointer;
overflow-y: auto;
overflow-x: hidden;
outline:none;
resize:none;
}
.button-area{
display: flex;
height: 40px;
margin-right: 10px;
line-height: 40px;
padding: 5px;
justify-content: flex-end;
}
.button-area button{
width: 80px;
border: none;
outline: none;
border-radius: 4px;
float: right;
cursor: pointer;
}
/* 设置滚动条的样式 */
::-webkit-scrollbar {
width:10px;
}
/* 滚动槽 */
::-webkit-scrollbar-track {
-webkit-box-shadow:inset006pxrgba(0,0,0,0.3);
border-radius:8px;
}
/* 滚动条滑块 */
::-webkit-scrollbar-thumb {
border-radius:10px;
background:rgba(0,0,0,0);
-webkit-box-shadow:inset006pxrgba(0,0,0,0.5);
}
</style>
</head>
<body>
<div class="container" id="container" onscroll="handleScroll()">
<div class="content">
'''
f.write(html_head)
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))
for index, message in enumerate(messages):
type_ = message[2]
str_content = message[7]
str_time = message[8]
# print(type_, type(type_))
is_send = message[4]
avatar = MePC().avatar_path if is_send else self.contact.avatar_path
timestamp = message[5]
self.progressSignal.emit(index)
if type_ == 1:
if self.is_5_min(timestamp):
f.write(
f'''
<div class="item item-center"><span>{str_time}</span></div>
'''
)
if is_send:
f.write(
f'''
<div class="item item-right">
<div class="bubble bubble-right">{str_content}</div>
<div class="avatar">
<img src="myhead.png" />
</div>
</div>
'''
)
else:
f.write(
f'''
<div class="item item-left">
<div class="avatar">
<img src="tahead.png" />
</div>
<div class="bubble bubble-left">{str_content}
</div>
</div>
'''
)
html_end = '''
</div>
</div>
<script>
const container = document.getElementById('container');
const content = document.getElementById('content');
const totalItems = 1000;
const itemsPerPage = 20;
const itemHeight = 50;
function updateContent() {
const scrollTop = container.scrollTop;
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(startIndex + itemsPerPage, totalItems);
// Remove existing items
content.innerHTML = '';
// Add new items
for (let i = startIndex; i < endIndex; i++) {
const item = document.createElement('div');
item.className = 'item';
item.textContent = `Item ${i}`;
content.appendChild(item);
}
// Update container height to show correct scrollbar
container.style.height = totalItems * itemHeight + 'px';
}
function handleScroll() {
updateContent();
}
// Initial content rendering
updateContent();
</script>
</body>
</html>
'''
f.write(html_end)
f.close()
self.okSignal.emit(1)
def to_html_(self): def to_html_(self):
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}" origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}"
makedirs(origin_docx_path) makedirs(origin_docx_path)
@ -921,7 +670,12 @@ const chatMessages = [
) )
elif type_ == 43: elif type_ == 43:
video_path = hard_link_db.get_video(content=str_content, thumb=False) video_path = hard_link_db.get_video(content=str_content, thumb=False)
video_path = f'file:///{path.wx_path()}/{MePC().wxid}/{video_path}' video_path = f'{MePC().wx_dir}/{video_path}'
if os.path.exists(video_path):
new_path = origin_docx_path + '/video/' + os.path.basename(video_path)
if not os.path.exists(new_path):
shutil.copy(video_path, os.path.join(origin_docx_path, 'video'))
video_path = f'./video/{os.path.basename(video_path)}'
video_path = video_path.replace('\\', '/') video_path = video_path.replace('\\', '/')
if self.is_5_min(timestamp): if self.is_5_min(timestamp):
f.write( f.write(
@ -930,6 +684,16 @@ const chatMessages = [
f.write( f.write(
f'''{{ type:{type_}, text: '{video_path}',is_send:{is_send},avatar_path:'{avatar}'}},''' f'''{{ type:{type_}, text: '{video_path}',is_send:{is_send},avatar_path:'{avatar}'}},'''
) )
elif type_ == 47:
emoji_path = get_emoji(str_content, thumb=True, output_path=origin_docx_path + '/emoji')
emoji_path = './emoji/'+os.path.basename(emoji_path)
if self.is_5_min(timestamp):
f.write(
f'''{{ type:0, text: '{str_time}',is_send:0,avatar_path:''}},'''
)
f.write(
f'''{{ type:{3}, text: '{emoji_path}',is_send:{is_send},avatar_path:'{avatar}'}},'''
)
elif type_ == 10000: elif type_ == 10000:
str_content = escape_js_and_html(str_content.lstrip('<revokemsg>').rstrip('</revokemsg>')) str_content = escape_js_and_html(str_content.lstrip('<revokemsg>').rstrip('</revokemsg>'))
f.write( f.write(

View File

@ -50,6 +50,10 @@ class Ui_Dialog(object):
self.label_3.setAlignment(QtCore.Qt.AlignCenter) self.label_3.setAlignment(QtCore.Qt.AlignCenter)
self.label_3.setObjectName("label_3") self.label_3.setObjectName("label_3")
self.verticalLayout.addWidget(self.label_3) self.verticalLayout.addWidget(self.label_3)
self.label_9 = QtWidgets.QLabel(Dialog)
self.label_9.setAlignment(QtCore.Qt.AlignCenter)
self.label_9.setObjectName("label_9")
self.verticalLayout.addWidget(self.label_9)
self.gridLayout_2 = QtWidgets.QGridLayout() self.gridLayout_2 = QtWidgets.QGridLayout()
self.gridLayout_2.setObjectName("gridLayout_2") self.gridLayout_2.setObjectName("gridLayout_2")
self.gridLayout = QtWidgets.QGridLayout() self.gridLayout = QtWidgets.QGridLayout()
@ -178,6 +182,7 @@ class Ui_Dialog(object):
Dialog.setWindowTitle(_translate("Dialog", "Dialog")) Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.btn_help.setText(_translate("Dialog", "使用说明")) self.btn_help.setText(_translate("Dialog", "使用说明"))
self.label_3.setText(_translate("Dialog", "解密数据库")) self.label_3.setText(_translate("Dialog", "解密数据库"))
self.label_9.setText(_translate("Dialog", "以下内容为自动获取,如获取失败请手动填写"))
self.label_7.setText(_translate("Dialog", "版本")) self.label_7.setText(_translate("Dialog", "版本"))
self.label_5.setText(_translate("Dialog", "微信昵称")) self.label_5.setText(_translate("Dialog", "微信昵称"))
self.label_6.setText(_translate("Dialog", "密钥")) self.label_6.setText(_translate("Dialog", "密钥"))

View File

@ -113,11 +113,20 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QLabel" name="label_9">
<property name="text">
<string>以下内容为自动获取,如获取失败请手动填写</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item> <item>
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0" rowspan="2"> <item row="0" column="0" rowspan="2">
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0,0,0" <layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0,0,0" columnstretch="1,10" columnminimumwidth="1,0">
columnstretch="1,10" columnminimumwidth="1,0">
<item row="2" column="1"> <item row="2" column="1">
<widget class="QLabel" name="label_phone"> <widget class="QLabel" name="label_phone">
<property name="text"> <property name="text">

View File

@ -125,7 +125,7 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog):
QMessageBox.critical(self, "错误", "文件夹选择错误\n一般以wxid_xxx结尾") QMessageBox.critical(self, "错误", "文件夹选择错误\n一般以wxid_xxx结尾")
return return
if self.info.get('key') == 'none': if self.info.get('key') == 'none':
QMessageBox.critical(self, "错误", "密钥错误\n请检查微信版本是否为最新") QMessageBox.critical(self, "错误", "密钥错误\n请检查微信版本是否为最新和微信路径是否正确")
self.label_tip.setVisible(True) self.label_tip.setVisible(True)
self.label_tip.setText('点我之后没有反应那就多等儿吧,不要再点了') self.label_tip.setText('点我之后没有反应那就多等儿吧,不要再点了')
self.thread2 = DecryptThread(db_dir, self.info['key']) self.thread2 = DecryptThread(db_dir, self.info['key'])
@ -133,7 +133,7 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog):
self.thread2.signal.connect(self.progressBar_view) self.thread2.signal.connect(self.progressBar_view)
self.thread2.okSignal.connect(self.btnExitClicked) self.thread2.okSignal.connect(self.btnExitClicked)
self.thread2.errorSignal.connect( self.thread2.errorSignal.connect(
lambda x: QMessageBox.critical(self, "错误", "密钥错误\n请检查微信版本是否为最新") lambda x: QMessageBox.critical(self, "错误", "密钥错误\n请检查微信版本是否为最新和微信路径是否正确")
) )
self.thread2.start() self.thread2.start()

View File

@ -79,18 +79,18 @@ def download(url, output_dir, name, thumb=False):
return output_path return output_path
def get_emoji(xml_string, thumb=True) -> str: def get_emoji(xml_string, thumb=True, output_path=root_path) -> str:
emoji_info = parser_xml(xml_string) emoji_info = parser_xml(xml_string)
md5 = emoji_info['md5'] md5 = emoji_info['md5']
image_format = ['.png', '.gif', '.jpeg'] image_format = ['.png', '.gif', '.jpeg']
for f in image_format: for f in image_format:
prefix = 'th_' if thumb else '' prefix = 'th_' if thumb else ''
file_path = os.path.join(root_path, prefix + md5 + f) file_path = os.path.join(output_path, prefix + md5 + f)
if os.path.exists(file_path): if os.path.exists(file_path):
return file_path return file_path
url = emoji_info['thumburl'] if thumb else emoji_info['cdnurl'] url = emoji_info['thumburl'] if thumb else emoji_info['cdnurl']
print("下载表情包ing:", url) print("下载表情包ing:", url)
return download(url, root_path, md5, thumb) return download(url, output_path, md5, thumb)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -17,11 +17,12 @@ def get_abs_path(path, base_path="/data/image"):
return ':/icons/icons/404.png' return ':/icons/icons/404.png'
def get_relative_path(path, base_path): def get_relative_path(path, base_path, type_='image'):
if path: if path:
base_path = os.getcwd() + base_path base_path = os.getcwd() + base_path
output_path = dat2pic.decode_dat(os.path.join(MePC().wx_dir, path), 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' 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 return relative_path
else: else:
return ':/icons/icons/404.png' return ':/icons/icons/404.png'

View File

@ -72,6 +72,7 @@ def set_text(text):
<div class="centered-text"> <div class="centered-text">
<!-- 这里是要显示的四个大字 --> <!-- 这里是要显示的四个大字 -->
%s %s
<img src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.2.8/assets/newemoji/Yellowdog.png" id="旺柴" class="emoji_img">
</div> </div>
</body> </body>
</html> </html>
@ -81,7 +82,7 @@ def set_text(text):
@app.route('/test') @app.route('/test')
def test(): def test():
return set_text('以下内容仅对VIP开放🐶') return set_text('以下内容仅对VIP开放')
def run(port=21314): def run(port=21314):