WeChatMsg/app/DataBase/output_pc.py

735 lines
22 KiB
Python
Raw Normal View History

2023-11-15 23:53:39 +08:00
import os
import pandas as pd
2023-11-16 22:39:59 +08:00
from PyQt5.QtCore import pyqtSignal, QThread
from . import msg
from ..log import log
2023-11-21 22:23:23 +08:00
from ..person import MePC
2023-11-15 23:53:39 +08:00
2023-11-20 22:30:31 +08:00
if not os.path.exists('./data/聊天记录'):
os.mkdir('./data/聊天记录')
2023-11-15 23:53:39 +08:00
class Output(QThread):
"""
发送信息线程
"""
progressSignal = pyqtSignal(int)
rangeSignal = pyqtSignal(int)
okSignal = pyqtSignal(int)
i = 1
CSV = 0
DOCX = 1
HTML = 2
2023-11-16 22:39:59 +08:00
def __init__(self, contact, parent=None, type_=DOCX):
2023-11-15 23:53:39 +08:00
super().__init__(parent)
2023-11-21 22:23:23 +08:00
self.last_timestamp = 0
2023-11-15 23:53:39 +08:00
self.sec = 2 # 默认1000秒
2023-11-16 22:39:59 +08:00
self.contact = contact
self.ta_username = contact.wxid
2023-11-15 23:53:39 +08:00
self.msg_id = 0
self.output_type = type_
self.total_num = 0
2023-11-16 22:39:59 +08:00
self.num = 0
2023-11-15 23:53:39 +08:00
@log
def to_csv(self, conRemark, path):
2023-11-16 22:39:59 +08:00
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')
2023-11-15 23:53:39 +08:00
2023-11-22 00:22:50 +08:00
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
2023-11-21 22:23:23 +08:00
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 = '''
2023-11-22 00:22:50 +08:00
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
2023-11-21 22:23:23 +08:00
2023-11-22 00:22:50 +08:00
*{
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;
}
2023-11-21 22:23:23 +08:00
2023-11-22 00:22:50 +08:00
.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;
}
2023-11-21 22:23:23 +08:00
2023-11-22 00:22:50 +08:00
/* 设置滚动条的样式 */
::-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">
'''
2023-11-21 22:23:23 +08:00
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'))
2023-11-22 00:22:50 +08:00
self.rangeSignal.emit(len(messages))
for index, message in enumerate(messages):
2023-11-21 22:23:23 +08:00
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]
2023-11-22 00:22:50 +08:00
self.progressSignal.emit(index)
2023-11-21 22:23:23 +08:00
if type_ == 1:
if self.is_5_min(timestamp):
f.write(
f'''
2023-11-22 00:22:50 +08:00
<div class="item item-center"><span>{str_time}</span></div>
'''
2023-11-21 22:23:23 +08:00
)
if is_send:
f.write(
f'''
2023-11-22 00:22:50 +08:00
<div class="item item-right">
<div class="bubble bubble-right">{str_content}</div>
<div class="avatar">
<img src="myhead.png" />
</div>
</div>
'''
2023-11-21 22:23:23 +08:00
)
else:
f.write(
f'''
2023-11-22 00:22:50 +08:00
<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">
2023-11-21 22:23:23 +08:00
<div class="avatar">
2023-11-22 00:22:50 +08:00
<img src="https://dss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=25668084,2889217189&fm=26&gp=0.jpg" />
2023-11-21 22:23:23 +08:00
</div>
2023-11-22 00:22:50 +08:00
<div class="bubble bubble-left">您好,我在武汉你可以直接送过来吗我有时间的话可以自己过去拿<br/><br/>123
2023-11-21 22:23:23 +08:00
</div>
</div>
2023-11-22 00:22:50 +08:00
<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>
2023-11-21 22:23:23 +08:00
</div>
2023-11-22 00:22:50 +08:00
</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>
2023-11-21 22:23:23 +08:00
</div>
2023-11-22 00:22:50 +08:00
<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>
2023-11-21 22:23:23 +08:00
</body>
</html>
2023-11-22 00:22:50 +08:00
'''
2023-11-21 22:23:23 +08:00
f.write(html_end)
f.close()
2023-11-22 00:22:50 +08:00
self.okSignal.emit(1)
2023-11-21 22:23:23 +08:00
2023-11-15 23:53:39 +08:00
def run(self):
if self.output_type == self.DOCX:
return
elif self.output_type == self.CSV:
2023-11-22 00:22:50 +08:00
return
2023-11-21 22:23:23 +08:00
elif self.output_type == self.HTML:
2023-11-22 00:22:50 +08:00
self.to_html_()