mirror of
https://github.com/LC044/WeChatMsg
synced 2025-02-22 19:02:17 +08:00
聊天统计
This commit is contained in:
parent
3cf9d5b475
commit
d848a80b32
136
app/Ui/contact/analysis/analysis.py
Normal file
136
app/Ui/contact/analysis/analysis.py
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
from PyQt5.QtWidgets import *
|
||||||
|
from PyQt5.QtCore import *
|
||||||
|
from PyQt5.QtGui import *
|
||||||
|
from app.DataBase import data
|
||||||
|
|
||||||
|
# class AnalysisController(QMainWindow, Ui_Dialog):
|
||||||
|
# exitSignal = pyqtSignal()
|
||||||
|
#
|
||||||
|
# # username = ''
|
||||||
|
# def __init__(self, username, parent=None):
|
||||||
|
# super(AnalysisController, self).__init__(parent)
|
||||||
|
# self.setupUi(self)
|
||||||
|
# self.setWindowTitle('WeChat')
|
||||||
|
# self.setWindowIcon(QIcon('./app/data/icon.png'))
|
||||||
|
# self.Me = data.get_myinfo()
|
||||||
|
import sys
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
from PyQt5.QtWidgets import (QWidget, QHBoxLayout, QSplitter, QApplication)
|
||||||
|
from PyQt5.QtCore import (Qt, QUrl)
|
||||||
|
from PyQt5.QtWebEngineWidgets import *
|
||||||
|
from . import charts
|
||||||
|
|
||||||
|
|
||||||
|
class AnalysisController(QWidget):
|
||||||
|
def __init__(self, username):
|
||||||
|
super().__init__()
|
||||||
|
self.ta_username = username
|
||||||
|
self.load_data()
|
||||||
|
self.initUI()
|
||||||
|
self.setWindowTitle('WeChat')
|
||||||
|
self.setWindowIcon(QIcon('./app/data/icon.png'))
|
||||||
|
# self.setBackground()
|
||||||
|
|
||||||
|
def load_data(self):
|
||||||
|
charts.send_recv_rate(self.ta_username)
|
||||||
|
charts.message_word_cloud(self.ta_username)
|
||||||
|
charts.msg_type_rate(self.ta_username)
|
||||||
|
charts.calendar_chart(self.ta_username)
|
||||||
|
charts.month_num(self.ta_username)
|
||||||
|
|
||||||
|
def initUI(self):
|
||||||
|
main_box = QHBoxLayout(self)
|
||||||
|
self.browser1 = QWebEngineView()
|
||||||
|
self.browser1.load(QUrl('http://www.baidu.com'))
|
||||||
|
# self.browser1 = QFrame(self)
|
||||||
|
# self.browser1.setFrameShape(QFrame.StyledPanel)
|
||||||
|
# self.layoutWidget = QtWidgets.QWidget(self.browser1)
|
||||||
|
# # self.layoutWidget.setGeometry(QtCore.QRect(71, 63, 227, 155))
|
||||||
|
# self.verticalLayout = QtWidgets.QVBoxLayout(self.layoutWidget)
|
||||||
|
# self.verticalLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
# self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
|
||||||
|
# self.label_2 = QtWidgets.QLabel(self.layoutWidget)
|
||||||
|
# _translate = QtCore.QCoreApplication.translate
|
||||||
|
# conRemark = data.get_conRemark(self.ta_username)
|
||||||
|
# self.label_2.setText(_translate("Dialog", f"{conRemark}"))
|
||||||
|
# self.horizontalLayout_2.addWidget(self.label_2)
|
||||||
|
# self.browser1.setLayout(self.horizontalLayout_2)
|
||||||
|
|
||||||
|
self.browser2 = QWebEngineView()
|
||||||
|
self.browser2.load(QUrl('file:///data/聊天统计/wordcloud.html'))
|
||||||
|
self.browser3 = QWebEngineView()
|
||||||
|
self.browser3.load(QUrl('http://www.baidu.com'))
|
||||||
|
self.browser4 = QWebEngineView()
|
||||||
|
self.browser4.load(QUrl('http://www.baidu.com'))
|
||||||
|
self.browser5 = QWebEngineView()
|
||||||
|
self.browser5.load(QUrl('http://www.baidu.com'))
|
||||||
|
self.browser6 = QWebEngineView()
|
||||||
|
self.browser6.load(QUrl('http://www.baidu.com'))
|
||||||
|
self.browser7 = QWebEngineView()
|
||||||
|
self.browser7.load(QUrl('file:///data/聊天统计/month_num.html'))
|
||||||
|
self.browser8 = QWebEngineView()
|
||||||
|
self.browser8.load(QUrl('file:///data/聊天统计/calendar.html'))
|
||||||
|
self.browser9 = QWebEngineView()
|
||||||
|
self.browser9.load(QUrl('file:///data/聊天统计/msg_type_rate.html'))
|
||||||
|
self.browser10 = QWebEngineView()
|
||||||
|
self.browser10.load(QUrl('file:///data/聊天统计/send_recv_rate.html'))
|
||||||
|
|
||||||
|
splitter1 = QSplitter(Qt.Vertical)
|
||||||
|
splitter2 = QSplitter(Qt.Horizontal)
|
||||||
|
splitter3 = QSplitter(Qt.Horizontal)
|
||||||
|
splitter4 = QSplitter(Qt.Vertical)
|
||||||
|
splitter5 = QSplitter(Qt.Horizontal)
|
||||||
|
splitter6 = QSplitter(Qt.Vertical)
|
||||||
|
splitter7 = QSplitter(Qt.Vertical)
|
||||||
|
splitter8 = QSplitter(Qt.Vertical)
|
||||||
|
splitter9 = QSplitter(Qt.Vertical)
|
||||||
|
|
||||||
|
splitter1.addWidget(self.browser1)
|
||||||
|
splitter1.addWidget(splitter2)
|
||||||
|
splitter1.setSizes([1, 5])
|
||||||
|
|
||||||
|
splitter2.addWidget(splitter6)
|
||||||
|
splitter2.addWidget(splitter3)
|
||||||
|
splitter2.setSizes([1, 3])
|
||||||
|
|
||||||
|
splitter3.addWidget(splitter4)
|
||||||
|
splitter3.addWidget(splitter8)
|
||||||
|
splitter3.setSizes([2, 1])
|
||||||
|
|
||||||
|
splitter4.addWidget(splitter5)
|
||||||
|
splitter4.addWidget(self.browser2)
|
||||||
|
splitter4.setSizes([1, 3])
|
||||||
|
|
||||||
|
splitter5.addWidget(self.browser3)
|
||||||
|
splitter5.addWidget(self.browser4)
|
||||||
|
|
||||||
|
splitter6.addWidget(self.browser5)
|
||||||
|
splitter6.addWidget(splitter7)
|
||||||
|
splitter6.setSizes([1, 2])
|
||||||
|
|
||||||
|
splitter7.addWidget(self.browser6)
|
||||||
|
splitter7.addWidget(self.browser7)
|
||||||
|
|
||||||
|
splitter8.addWidget(self.browser8)
|
||||||
|
splitter8.addWidget(splitter9)
|
||||||
|
splitter8.setSizes([1, 2])
|
||||||
|
|
||||||
|
splitter9.addWidget(self.browser9)
|
||||||
|
splitter9.addWidget(self.browser10)
|
||||||
|
|
||||||
|
main_box.addWidget(splitter1)
|
||||||
|
self.setLayout(main_box)
|
||||||
|
self.setGeometry(300, 300, 600, 500)
|
||||||
|
|
||||||
|
def setBackground(self):
|
||||||
|
palette = QPalette()
|
||||||
|
pix = QPixmap("./app/data/bg.png")
|
||||||
|
pix = pix.scaled(self.width(), self.height(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation) # 自适应图片大小
|
||||||
|
palette.setBrush(self.backgroundRole(), QBrush(pix)) # 设置背景图片
|
||||||
|
# palette.setColor(self.backgroundRole(), QColor(192, 253, 123)) # 设置背景颜色
|
||||||
|
self.setPalette(palette)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
ex = MainWindow()
|
||||||
|
sys.exit(app.exec_())
|
174
app/Ui/contact/analysis/charts.py
Normal file
174
app/Ui/contact/analysis/charts.py
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
import datetime
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
|
||||||
|
import jieba
|
||||||
|
from pyecharts import options as opts
|
||||||
|
from pyecharts.charts import Pie, WordCloud, Calendar, Bar
|
||||||
|
from pyecharts.faker import Faker
|
||||||
|
from ....DataBase import data
|
||||||
|
|
||||||
|
data.mkdir(os.path.abspath('.') + '/data/聊天统计')
|
||||||
|
|
||||||
|
Type = {
|
||||||
|
'1': '文字',
|
||||||
|
'3': '图片',
|
||||||
|
'43': '视频',
|
||||||
|
'-1879048185': '微信运动排行榜',
|
||||||
|
'5': '',
|
||||||
|
'47': '表情包',
|
||||||
|
'268445456': '撤回消息',
|
||||||
|
'34': '语音',
|
||||||
|
'419430449': '转账',
|
||||||
|
'50': '语音电话',
|
||||||
|
'100001': '领取红包',
|
||||||
|
'10000': '消息已发出,但被对方拒收了。',
|
||||||
|
'822083633': '回复消息',
|
||||||
|
'922746929': '拍一拍',
|
||||||
|
'1090519089': '文件',
|
||||||
|
'318767153': '付款成功',
|
||||||
|
'436207665': '发红包',
|
||||||
|
'49': '分享链接'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def send_recv_rate(username):
|
||||||
|
send_num = data.send_nums(username)
|
||||||
|
recv_num = data.recv_nums(username)
|
||||||
|
total_num = send_num + recv_num
|
||||||
|
print(send_num, recv_num)
|
||||||
|
c = (
|
||||||
|
Pie(init_opts=opts.InitOpts(width="460px", height="240px"))
|
||||||
|
.add(
|
||||||
|
"",
|
||||||
|
[
|
||||||
|
('发送', send_num), ('接收', recv_num)
|
||||||
|
],
|
||||||
|
center=["40%", "50%"],
|
||||||
|
)
|
||||||
|
.set_global_opts(
|
||||||
|
title_opts=opts.TitleOpts(title=f"信息发送接收",subtitle=f"总计:{total_num}条消息", pos_bottom="0%"),
|
||||||
|
legend_opts=opts.LegendOpts(type_="scroll", pos_left="80%", orient="vertical"),
|
||||||
|
)
|
||||||
|
.set_series_opts(
|
||||||
|
label_opts=opts.LabelOpts(formatter="{b}:{d}%"),
|
||||||
|
)
|
||||||
|
.render("./data/聊天统计/send_recv_rate.html")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def msg_type_rate(username):
|
||||||
|
type_data = data.msg_type_num(username)
|
||||||
|
type_data = sorted(type_data, key=lambda x: x[1], reverse=True)
|
||||||
|
data1 = type_data[:4]
|
||||||
|
data2 = sum(map(lambda x: x[1], type_data[4:]))
|
||||||
|
print(type_data)
|
||||||
|
new_data = []
|
||||||
|
for t in data1:
|
||||||
|
try:
|
||||||
|
new_data.append((Type[str(t[0])], t[1]))
|
||||||
|
except:
|
||||||
|
new_data.append(('未知类型', t[1]))
|
||||||
|
new_data.append(('其他', data2))
|
||||||
|
|
||||||
|
c = (
|
||||||
|
Pie(init_opts=opts.InitOpts(width="460px", height="240px"))
|
||||||
|
.add(
|
||||||
|
"",
|
||||||
|
new_data
|
||||||
|
,
|
||||||
|
center=["40%", "50%"],
|
||||||
|
)
|
||||||
|
.set_global_opts(
|
||||||
|
title_opts=opts.TitleOpts(title=f"消息类型占比", pos_bottom="0%"),
|
||||||
|
legend_opts=opts.LegendOpts(type_="scroll", pos_left="80%", orient="vertical"),
|
||||||
|
)
|
||||||
|
.set_series_opts(
|
||||||
|
label_opts=opts.LabelOpts(formatter="{b}:{d}%"),
|
||||||
|
)
|
||||||
|
.render("./data/聊天统计/msg_type_rate.html")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def message_word_cloud(username):
|
||||||
|
text = data.get_text(username)
|
||||||
|
total_msg_len = len(text)
|
||||||
|
word_list = jieba.cut(text)
|
||||||
|
# word = " ".join(word_list)
|
||||||
|
# print(word)
|
||||||
|
stopwords = set()
|
||||||
|
content = [line.strip() for line in open('./app/data/stopwords.txt', 'r', encoding='utf-8').readlines()]
|
||||||
|
stopwords.update(content)
|
||||||
|
wordcount = {}
|
||||||
|
for word in jieba.cut(text):
|
||||||
|
if len(word) > 1 and word not in stopwords:
|
||||||
|
wordcount[word] = wordcount.get(word, 0) + 1
|
||||||
|
text_data = sorted(wordcount.items(), key=lambda x: x[1], reverse=True)
|
||||||
|
if len(text_data) > 100:
|
||||||
|
text_data = text_data[:100]
|
||||||
|
print(text_data)
|
||||||
|
(
|
||||||
|
WordCloud(init_opts=opts.InitOpts(width="900px", height="550px"))
|
||||||
|
.add(series_name="聊天文字", data_pair=text_data, word_size_range=[20, 100])
|
||||||
|
.set_global_opts(
|
||||||
|
title_opts=opts.TitleOpts(
|
||||||
|
title=f"词云图", subtitle=f"总计{total_msg_len}字",
|
||||||
|
title_textstyle_opts=opts.TextStyleOpts(font_size=23)
|
||||||
|
),
|
||||||
|
tooltip_opts=opts.TooltipOpts(is_show=True),
|
||||||
|
)
|
||||||
|
.render("./data/聊天统计/wordcloud.html")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def calendar_chart(username):
|
||||||
|
msg_data = data.get_msg_by_days(username)
|
||||||
|
min_ = min(map(lambda x: x[1], msg_data))
|
||||||
|
max_ = max(map(lambda x: x[1], msg_data))
|
||||||
|
c = (
|
||||||
|
Calendar(init_opts=opts.InitOpts(width="460px", height="255px"))
|
||||||
|
.add(
|
||||||
|
"",
|
||||||
|
msg_data,
|
||||||
|
calendar_opts=opts.CalendarOpts(range_="2022")
|
||||||
|
)
|
||||||
|
.set_global_opts(
|
||||||
|
title_opts=opts.TitleOpts(title="2022年聊天情况"),
|
||||||
|
visualmap_opts=opts.VisualMapOpts(
|
||||||
|
max_=max_,
|
||||||
|
min_=min_,
|
||||||
|
orient="horizontal",
|
||||||
|
# is_piecewise=True,
|
||||||
|
# pos_top="200px",
|
||||||
|
pos_bottom="0px",
|
||||||
|
pos_left="0px",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.render("./data/聊天统计/calendar.html")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def month_num(username):
|
||||||
|
"""
|
||||||
|
每月聊天条数
|
||||||
|
"""
|
||||||
|
msg_data = data.get_msg_by_month(username, year='2022')
|
||||||
|
y_data = list(map(lambda x: x[1], msg_data))
|
||||||
|
x_axis = list(map(lambda x: x[0], msg_data))
|
||||||
|
c = (
|
||||||
|
Bar(init_opts=opts.InitOpts(width="440px", height="245px"))
|
||||||
|
.add_xaxis(x_axis)
|
||||||
|
.add_yaxis("消息数量", y_data)
|
||||||
|
# .add_yaxis("商家B", Faker.values())
|
||||||
|
.set_global_opts(
|
||||||
|
title_opts=opts.TitleOpts(title="逐月聊天统计", subtitle=None),
|
||||||
|
datazoom_opts=opts.DataZoomOpts(),
|
||||||
|
toolbox_opts=opts.ToolboxOpts(),
|
||||||
|
)
|
||||||
|
.render("./data/聊天统计/month_num.html")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
send_recv_rate('wxid_wt2vsktnu4z022')
|
174
app/Ui/contact/analysis/pie_scroll_legend.html
Normal file
174
app/Ui/contact/analysis/pie_scroll_legend.html
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Awesome-pyecharts</title>
|
||||||
|
<script type="text/javascript" src="https://assets.pyecharts.org/assets/v5/echarts.min.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body >
|
||||||
|
<div id="ec5cb8f7096941f5ba51d65454d9c631" class="chart-container" style="width:470px; height:270px; "></div>
|
||||||
|
<script>
|
||||||
|
var chart_ec5cb8f7096941f5ba51d65454d9c631 = echarts.init(
|
||||||
|
document.getElementById('ec5cb8f7096941f5ba51d65454d9c631'), 'white', {renderer: 'canvas'});
|
||||||
|
var option_ec5cb8f7096941f5ba51d65454d9c631 = {
|
||||||
|
"animation": true,
|
||||||
|
"animationThreshold": 2000,
|
||||||
|
"animationDuration": 1000,
|
||||||
|
"animationEasing": "cubicOut",
|
||||||
|
"animationDelay": 0,
|
||||||
|
"animationDurationUpdate": 300,
|
||||||
|
"animationEasingUpdate": "cubicOut",
|
||||||
|
"animationDelayUpdate": 0,
|
||||||
|
"aria": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"color": [
|
||||||
|
"#5470c6",
|
||||||
|
"#91cc75",
|
||||||
|
"#fac858",
|
||||||
|
"#ee6666",
|
||||||
|
"#73c0de",
|
||||||
|
"#3ba272",
|
||||||
|
"#fc8452",
|
||||||
|
"#9a60b4",
|
||||||
|
"#ea7ccc"
|
||||||
|
],
|
||||||
|
"series": [
|
||||||
|
{
|
||||||
|
"type": "pie",
|
||||||
|
"colorBy": "data",
|
||||||
|
"legendHoverLink": true,
|
||||||
|
"selectedMode": false,
|
||||||
|
"selectedOffset": 10,
|
||||||
|
"clockwise": true,
|
||||||
|
"startAngle": 90,
|
||||||
|
"minAngle": 0,
|
||||||
|
"minShowLabelAngle": 0,
|
||||||
|
"avoidLabelOverlap": true,
|
||||||
|
"stillShowZeroSum": true,
|
||||||
|
"percentPrecision": 2,
|
||||||
|
"showEmptyCircle": true,
|
||||||
|
"emptyCircleStyle": {
|
||||||
|
"color": "lightgray",
|
||||||
|
"borderColor": "#000",
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderType": "solid",
|
||||||
|
"borderDashOffset": 0,
|
||||||
|
"borderCap": "butt",
|
||||||
|
"borderJoin": "bevel",
|
||||||
|
"borderMiterLimit": 10,
|
||||||
|
"opacity": 1
|
||||||
|
},
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"name": "\u53d1\u9001",
|
||||||
|
"value": 0.6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "\u63a5\u6536",
|
||||||
|
"value": 0.4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"radius": [
|
||||||
|
"0%",
|
||||||
|
"75%"
|
||||||
|
],
|
||||||
|
"center": [
|
||||||
|
"40%",
|
||||||
|
"50%"
|
||||||
|
],
|
||||||
|
"label": {
|
||||||
|
"show": true,
|
||||||
|
"margin": 8,
|
||||||
|
"formatter": "{b}: {c}"
|
||||||
|
},
|
||||||
|
"labelLine": {
|
||||||
|
"show": true,
|
||||||
|
"showAbove": false,
|
||||||
|
"length": 15,
|
||||||
|
"length2": 15,
|
||||||
|
"smooth": false,
|
||||||
|
"minTurnAngle": 90,
|
||||||
|
"maxSurfaceAngle": 90
|
||||||
|
},
|
||||||
|
"rippleEffect": {
|
||||||
|
"show": true,
|
||||||
|
"brushType": "stroke",
|
||||||
|
"scale": 2.5,
|
||||||
|
"period": 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"legend": [
|
||||||
|
{
|
||||||
|
"data": [
|
||||||
|
"\u53d1\u9001",
|
||||||
|
"\u63a5\u6536"
|
||||||
|
],
|
||||||
|
"selected": {},
|
||||||
|
"type": "scroll",
|
||||||
|
"show": true,
|
||||||
|
"left": "60%",
|
||||||
|
"orient": "vertical",
|
||||||
|
"padding": 5,
|
||||||
|
"itemGap": 10,
|
||||||
|
"itemWidth": 25,
|
||||||
|
"itemHeight": 14,
|
||||||
|
"backgroundColor": "transparent",
|
||||||
|
"borderColor": "#ccc",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"borderRadius": 0,
|
||||||
|
"pageButtonItemGap": 5,
|
||||||
|
"pageButtonPosition": "end",
|
||||||
|
"pageFormatter": "{current}/{total}",
|
||||||
|
"pageIconColor": "#2f4554",
|
||||||
|
"pageIconInactiveColor": "#aaa",
|
||||||
|
"pageIconSize": 15,
|
||||||
|
"animationDurationUpdate": 800,
|
||||||
|
"selector": false,
|
||||||
|
"selectorPosition": "auto",
|
||||||
|
"selectorItemGap": 7,
|
||||||
|
"selectorButtonGap": 10
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tooltip": {
|
||||||
|
"show": true,
|
||||||
|
"trigger": "item",
|
||||||
|
"triggerOn": "mousemove|click",
|
||||||
|
"axisPointer": {
|
||||||
|
"type": "line"
|
||||||
|
},
|
||||||
|
"showContent": true,
|
||||||
|
"alwaysShowContent": false,
|
||||||
|
"showDelay": 0,
|
||||||
|
"hideDelay": 100,
|
||||||
|
"enterable": false,
|
||||||
|
"confine": false,
|
||||||
|
"appendToBody": false,
|
||||||
|
"transitionDuration": 0.4,
|
||||||
|
"textStyle": {
|
||||||
|
"fontSize": 14
|
||||||
|
},
|
||||||
|
"borderWidth": 0,
|
||||||
|
"padding": 5,
|
||||||
|
"order": "seriesAsc"
|
||||||
|
},
|
||||||
|
"title": [
|
||||||
|
{
|
||||||
|
"show": true,
|
||||||
|
"text": "\u53d1\u9001\u63a5\u6536\u5360\u6bd4",
|
||||||
|
"target": "blank",
|
||||||
|
"subtarget": "blank",
|
||||||
|
"padding": 5,
|
||||||
|
"itemGap": 10,
|
||||||
|
"textAlign": "auto",
|
||||||
|
"textVerticalAlign": "auto",
|
||||||
|
"triggerEvent": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
chart_ec5cb8f7096941f5ba51d65454d9c631.setOption(option_ec5cb8f7096941f5ba51d65454d9c631);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user