WeChatMsg/app/DataBase/output_pc.py
2023-11-23 22:58:27 +08:00

735 lines
22 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import pandas as pd
from PyQt5.QtCore import pyqtSignal, QThread
from . import msg
from ..log import log
from ..person_pc import MePC
if not os.path.exists('./data/聊天记录'):
os.mkdir('./data/聊天记录')
class Output(QThread):
"""
发送信息线程
"""
progressSignal = pyqtSignal(int)
rangeSignal = pyqtSignal(int)
okSignal = pyqtSignal(int)
i = 1
CSV = 0
DOCX = 1
HTML = 2
def __init__(self, contact, parent=None, type_=DOCX):
super().__init__(parent)
self.last_timestamp = 0
self.sec = 2 # 默认1000秒
self.contact = contact
self.ta_username = contact.wxid
self.msg_id = 0
self.output_type = type_
self.total_num = 0
self.num = 0
@log
def to_csv(self, conRemark, path):
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}"
if not os.path.exists(origin_docx_path):
os.mkdir(origin_docx_path)
filename = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}/{self.contact.remark}.csv"
# columns = ["用户名", "消息内容", "发送时间", "发送状态", "消息类型", "isSend", "msgId"]
columns = ['localId', 'TalkerId', 'Type', 'SubType',
'IsSender', 'CreateTime', 'Status', 'StrContent',
'StrTime']
messages = msg.get_messages(self.contact.wxid)
# print()
df = pd.DataFrame(
data=messages,
columns=columns,
)
df.to_csv(filename, encoding='utf-8')
self.okSignal.emit('ok')
def to_html(self):
self.okSignal.emit('ok')
def is_5_min(self, timestamp):
if abs(timestamp - self.last_timestamp) > 300:
self.last_timestamp = timestamp
return True
return False
def progress(self, value):
self.progressSignal.emit(value)
def run(self):
if self.output_type == self.DOCX:
return
elif self.output_type == self.CSV:
# print("线程导出csv")
self.to_csv(self.ta_username, "path")
elif self.output_type == self.HTML:
# self.to_html()
self.Child0 = ChildThread(self.contact, type_=ChildThread.HTML)
self.Child0.progressSignal.connect(self.progress)
self.Child0.rangeSignal.connect(self.rangeSignal)
self.Child0.okSignal.connect(self.okSignal)
self.Child0.run()
# self.okSignal.emit(1)
class ChildThread(QThread):
"""
子线程,用于导出部分聊天记录
"""
progressSignal = pyqtSignal(int)
rangeSignal = pyqtSignal(int)
okSignal = pyqtSignal(int)
i = 1
CSV = 0
DOCX = 1
HTML = 2
def __init__(self, contact, parent=None, type_=DOCX):
super().__init__(parent)
self.contact = contact
self.last_timestamp = 0
self.sec = 2 # 默认1000秒
self.msg_id = 0
self.output_type = type_
def is_5_min(self, timestamp):
if abs(timestamp - self.last_timestamp) > 300:
self.last_timestamp = timestamp
return True
return False
def text(self, doc, isSend, message, status):
return
def image(self, doc, isSend, Type, content, imgPath):
return
def emoji(self, doc, isSend, content, imgPath):
return
def wx_file(self, doc, isSend, content, status):
return
def retract_message(self, doc, isSend, content, status):
return
def reply(self, doc, isSend, content, status):
return
def pat_a_pat(self, doc, isSend, content, status):
return
def video(self, doc, isSend, content, status, img_path):
return
def to_html(self):
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}"
if not os.path.exists(origin_docx_path):
os.mkdir(origin_docx_path)
messages = msg.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):
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}"
if not os.path.exists(origin_docx_path):
os.mkdir(origin_docx_path)
messages = msg.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">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chat Records</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">
<div class="content" id="chat-container">
<div class="item item-center"><span>昨天 12:35</span></div>
<div class="item item-center"><span>你已添加了凡繁烦,现在可以开始聊天了。</span></div>
<div class="item item-left">
<div class="avatar">
<img src="https://dss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=25668084,2889217189&fm=26&gp=0.jpg" />
</div>
<div class="bubble bubble-left">您好,我在武汉,你可以直接送过来吗,我有时间的话,可以自己过去拿<br/><br/>123
</div>
</div>
<div class="item item-right">
<div class="bubble bubble-right">hello<br/>你好呀</div>
<div class="avatar">
<img src="https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3313909130,2406410525&fm=15&gp=0.jpg" />
</div>
</div>
<div class="item item-center">
<span>昨天 13:15</span>
</div>
</div>
</div>
<div></div>
<div>
<button onclick="prevPage()">上一页</button>
<div id = "paginationInfo"></div>
<button onclick="nextPage()">下一页</button>
<div></div>
<input type="number" id="gotoPageInput" placeholder="跳转到第几页">
<button onclick="gotoPage()">跳转</button>
</div>
<script>
const chatContainer = document.getElementById('chat-container');
// Sample chat messages (replace this with your actual data)
const chatMessages = [
'''
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
# avatar = avatar.replace('\\', '\\\\')
avatar = 'myhead.png' if is_send else 'tahead.png'
timestamp = message[5]
self.progressSignal.emit(index)
str_content = str_content.replace('"', '\\"').replace('{', '\\{').replace('}', '\\}').replace('\n',
'\\n').replace(
"'", "\\'")
if type_ == 1:
if self.is_5_min(timestamp):
f.write(
f'''{{ type:0, text: '{str_time}',is_send:0,avatar_path:''}},'''
)
f.write(
f'''{{ type:{type_}, text: '{str_content}',is_send:{is_send},avatar_path:'{avatar}'}},'''
)
html_end = '''
];
function renderMessages(messages) {
for (const message of messages) {
const messageElement = document.createElement('div');
if (message.type == 1){
if (message.is_send == 1){
messageElement.className = "item item-right";
messageElement.innerHTML = `<div class='bubble bubble-right'>${message.text}</div><div class='avatar'><img src="${message.avatar_path}" /></div>`
}
else if(message.is_send==0){
messageElement.className = "item item-left";
messageElement.innerHTML = `<div class='avatar'><img src="${message.avatar_path}" /></div><div class='bubble bubble-right'>${message.text}</div>`
}
}
else if(message.type == 0){
messageElement.className = "item item-center";
messageElement.innerHTML = `<span>${message.text}</span>`
}
chatContainer.appendChild(messageElement);
}
}
const itemsPerPage = 100; // 每页显示的元素个数
let currentPage = 1; // 当前页
function renderPage(page) {
const container = document.getElementById('chat-container');
container.innerHTML = ''; // 清空容器
// 计算当前页应该显示的元素范围
const startIndex = (page - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage;
console.log(page);
// 从数据列表中取出对应范围的元素并添加到容器中
for (let i = startIndex; i < endIndex && i < chatMessages.length; i++) {
const message = chatMessages[i];
const messageElement = document.createElement('div');
if (message.type == 1){
if (message.is_send == 1){
messageElement.className = "item item-right";
messageElement.innerHTML = `<div class='bubble bubble-right'>${message.text}</div><div class='avatar'><img src="${message.avatar_path}" /></div>`
}
else if(message.is_send==0){
messageElement.className = "item item-left";
messageElement.innerHTML = `<div class='avatar'><img src="${message.avatar_path}" /></div><div class='bubble bubble-right'>${message.text}</div>`
}
}
else if(message.type == 0){
messageElement.className = "item item-center";
messageElement.innerHTML = `<span>${message.text}</span>`
}
chatContainer.appendChild(messageElement);
}
updatePaginationInfo();
}
function prevPage() {
if (currentPage > 1) {
currentPage--;
renderPage(currentPage);
}
}
function nextPage() {
const totalPages = Math.ceil(chatMessages.length / itemsPerPage);
if (currentPage < totalPages) {
currentPage++;
renderPage(currentPage);
}
}
function updatePaginationInfo() {
const totalPages = Math.ceil(chatMessages.length / itemsPerPage);
const paginationInfo = document.getElementById('paginationInfo');
paginationInfo.textContent = `共 ${totalPages} 页,当前第 ${currentPage} 页`;
}
function gotoPage() {
const totalPages = Math.ceil(chatMessages.length / itemsPerPage);
const inputElement = document.getElementById('gotoPageInput');
const targetPage = parseInt(inputElement.value);
if (targetPage >= 1 && targetPage <= totalPages) {
currentPage = targetPage;
renderPage(currentPage);
} else {
alert('请输入有效的页码');
}
}
// 初始化页面
renderPage(currentPage);
</script>
</body>
</html>
'''
f.write(html_end)
f.close()
self.okSignal.emit(1)
def run(self):
if self.output_type == self.DOCX:
return
elif self.output_type == self.CSV:
return
elif self.output_type == self.HTML:
self.to_html_()