├── app
├── ui_pc
│ ├── __init__.py
│ ├── tool
│ │ ├── __init__.py
│ │ ├── pc_decrypt
│ │ │ └── __init__.py
│ │ ├── tool_window.py
│ │ └── toolUI.py
│ ├── chat
│ │ ├── __init__.py
│ │ ├── chatInfoUi.py
│ │ ├── chatUi.py
│ │ └── chatInfoUi.ui
│ ├── contact
│ │ ├── __init__.py
│ │ ├── userinfo
│ │ │ ├── __init__.py
│ │ │ └── userinfo.py
│ │ ├── contactUi.py
│ │ ├── contactInfoUi.py
│ │ └── contactInfo.py
│ └── Icon.py
├── web_ui
│ ├── __init__.py
│ └── web.py
├── ImageBox
│ ├── __init__.py
│ ├── icons
│ │ ├── zoom_in.jpg
│ │ └── zoom_out.jpg
│ ├── images
│ │ ├── wallhaven-748705.jpg
│ │ ├── wallhaven-753155.jpg
│ │ └── wallhaven-vml6em.jpg
│ ├── run.py
│ ├── config.py
│ └── ui.py
├── resources
│ ├── __init__.py
│ ├── icons
│ │ ├── __init__.py
│ │ ├── 404.png
│ │ ├── resources.qrc
│ │ ├── output.svg
│ │ ├── emotion.svg
│ │ ├── analysis.svg
│ │ ├── chat.svg
│ │ ├── myinfo.svg
│ │ ├── back.svg
│ │ ├── search.svg
│ │ ├── word.svg
│ │ ├── html.svg
│ │ ├── contact (1).svg
│ │ ├── logo.svg
│ │ ├── contact (2).svg
│ │ ├── csv.svg
│ │ ├── 404.svg
│ │ ├── contact.svg
│ │ ├── annual_report.svg
│ │ └── annual_report1.svg
│ └── resource.qrc
├── Ui
│ ├── contact
│ │ ├── report
│ │ │ ├── __init__.py
│ │ │ ├── annual_report.py
│ │ │ └── report.py
│ │ ├── emotion
│ │ │ ├── __init__.py
│ │ │ ├── emotionUi.ui
│ │ │ └── emotionUi.py
│ │ ├── analysis
│ │ │ └── charts - 副本.zip
│ │ ├── __init__.py
│ │ ├── userinfo
│ │ │ ├── __init__.py
│ │ │ └── userinfo.py
│ │ ├── test.ui
│ │ ├── contactUi.py
│ │ ├── contact.py
│ │ └── contactInfoUi.py
│ ├── chat
│ │ ├── myinfo.zip
│ │ └── __init__.py
│ ├── __init__.py
│ ├── Icon.py
│ ├── userinfo
│ │ ├── userinfo.py
│ │ ├── userinfoUi.py
│ │ └── userinfoUi.ui
│ └── decrypt
│ │ ├── decryptUi.ui
│ │ ├── decryptUi.py
│ │ └── decrypt.py
├── util
│ ├── __init__.py
│ ├── search.py
│ ├── path.py
│ ├── dat2pic.py
│ └── emoji.py
├── bg.png
├── components
│ ├── __init__.py
│ ├── prompt_bar.py
│ ├── contact_info_ui.py
│ └── Button_Contact.py
├── data
│ ├── bg.gif
│ ├── bg.png
│ ├── icon.png
│ ├── icon60x60.png
│ └── __init__.py
├── log
│ ├── __init__.py
│ └── logger.py
├── __init__.py
├── DataBase
│ ├── config.txt
│ ├── __init__.py
│ ├── misc.py
│ ├── micro_msg.py
│ ├── merge.py
│ ├── msg.py
│ └── hard_link.py
├── main
│ └── __init__.py
├── config.py
├── person_pc.py
├── person.py
└── decrypt
│ └── decrypt.py
├── doc
├── 数据库介绍.md
├── images
│ ├── MT
│ ├── qq.jpg
│ ├── chat_.png
│ ├── dirs.png
│ ├── logo.png
│ ├── cv-opration
│ ├── cv_process
│ ├── setting.png
│ ├── pc_contact.png
│ ├── html_message.png
│ ├── path_select.png
│ ├── messages_demo.png
│ ├── pc_decrypt_info.png
│ ├── image-20230520235113261.png
│ ├── image-20230520235220104.png
│ ├── image-20230520235338305.png
│ ├── image-20230520235351749.png
│ ├── image-20230520235400772.png
│ ├── image-20230520235409112.png
│ ├── image-20230520235422128.png
│ ├── image-20230520235431091.png
│ ├── image-20230521001305274.png
│ ├── image-20230521001547481.png
│ └── image-20230521001726799.png
├── 获取个人文件
│ └── 使用说明.md
└── 电脑端使用教程.md
├── resource
├── render
│ ├── __init__.py
│ ├── templates
│ │ ├── nb_components.html
│ │ ├── components.html
│ │ ├── nb_nteract.html
│ │ ├── simple_chart.html
│ │ ├── nb_jupyter_lab.html
│ │ ├── nb_jupyter_lab_tab.html
│ │ ├── nb_jupyter_notebook.html
│ │ ├── nb_jupyter_notebook_tab.html
│ │ ├── simple_tab.html
│ │ ├── simple_page.html
│ │ ├── simple_globe.html
│ │ └── nb_jupyter_globe.html
│ ├── display.py
│ ├── snapshot.py
│ └── engine.py
└── datasets
│ ├── countries_regions_db.json
│ └── __init__.py
├── logo.ico
├── logo16x16.ico
├── requirements.txt
├── sqlcipher-3.0.1
└── bin
│ ├── bat使用说明.txt
│ ├── sqlcipher.bat
│ ├── sqlcipher - 副本.txt
│ ├── sqlcipher-shell32.exe
│ ├── sqlcipher-shell64.exe
│ └── adb.txt
├── requirements_decrypt.txt
├── .idea
├── copyright
│ └── copyright.xml
├── vcs.xml
├── inspectionProfiles
│ └── profiles_settings.xml
├── misc.xml
└── WeChatMsg.iml
├── requirements_pc.txt
├── .github
└── ISSUE_TEMPLATE
│ ├── custom.md
│ ├── feature_request.md
│ └── bug_report.md
├── .gitignore
├── hook-pyecharts.py
├── auth_info_key_prefs.xml
├── decrypt_window.py
├── main_pc.py
├── main.py
└── main.spec
/app/ui_pc/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/web_ui/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/ImageBox/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/resources/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/Ui/contact/report/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/resources/icons/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/ui_pc/tool/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/Ui/contact/emotion/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/util/__init__.py:
--------------------------------------------------------------------------------
1 | from .path import get_abs_path
2 |
--------------------------------------------------------------------------------
/doc/数据库介绍.md:
--------------------------------------------------------------------------------
1 | # 微信数据库介绍
2 |
3 | **这个人比较懒,还什么都没写**
4 |
5 |
--------------------------------------------------------------------------------
/app/ui_pc/chat/__init__.py:
--------------------------------------------------------------------------------
1 | from .chat_window import ChatWindow
2 |
--------------------------------------------------------------------------------
/resource/render/__init__.py:
--------------------------------------------------------------------------------
1 | from .snapshot import make_snapshot
2 |
--------------------------------------------------------------------------------
/app/ui_pc/contact/__init__.py:
--------------------------------------------------------------------------------
1 | from .contact_window import ContactWindow
2 |
--------------------------------------------------------------------------------
/app/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/app/bg.png
--------------------------------------------------------------------------------
/app/components/__init__.py:
--------------------------------------------------------------------------------
1 | from .contact_info_ui import ContactQListWidgetItem
2 |
--------------------------------------------------------------------------------
/logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/logo.ico
--------------------------------------------------------------------------------
/app/data/bg.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/app/data/bg.gif
--------------------------------------------------------------------------------
/app/data/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/app/data/bg.png
--------------------------------------------------------------------------------
/app/log/__init__.py:
--------------------------------------------------------------------------------
1 | from .logger import log, logger
2 |
3 | __all__ = ["logger", "log"]
4 |
--------------------------------------------------------------------------------
/doc/images/MT:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/MT
--------------------------------------------------------------------------------
/logo16x16.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/logo16x16.ico
--------------------------------------------------------------------------------
/app/data/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/app/data/icon.png
--------------------------------------------------------------------------------
/doc/images/qq.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/qq.jpg
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/requirements.txt
--------------------------------------------------------------------------------
/doc/images/chat_.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/chat_.png
--------------------------------------------------------------------------------
/doc/images/dirs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/dirs.png
--------------------------------------------------------------------------------
/doc/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/logo.png
--------------------------------------------------------------------------------
/app/Ui/chat/myinfo.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/app/Ui/chat/myinfo.zip
--------------------------------------------------------------------------------
/app/data/icon60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/app/data/icon60x60.png
--------------------------------------------------------------------------------
/doc/images/cv-opration:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/cv-opration
--------------------------------------------------------------------------------
/doc/images/cv_process:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/cv_process
--------------------------------------------------------------------------------
/doc/images/setting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/setting.png
--------------------------------------------------------------------------------
/doc/images/pc_contact.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/pc_contact.png
--------------------------------------------------------------------------------
/app/resources/icons/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/app/resources/icons/404.png
--------------------------------------------------------------------------------
/app/ui_pc/tool/pc_decrypt/__init__.py:
--------------------------------------------------------------------------------
1 | from .pc_decrypt import DecryptControl
2 |
3 | __all__ = ['DecryptControl']
4 |
--------------------------------------------------------------------------------
/doc/images/html_message.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/html_message.png
--------------------------------------------------------------------------------
/doc/images/path_select.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/path_select.png
--------------------------------------------------------------------------------
/app/ImageBox/icons/zoom_in.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/app/ImageBox/icons/zoom_in.jpg
--------------------------------------------------------------------------------
/doc/images/messages_demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/messages_demo.png
--------------------------------------------------------------------------------
/doc/images/pc_decrypt_info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/pc_decrypt_info.png
--------------------------------------------------------------------------------
/app/ImageBox/icons/zoom_out.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/app/ImageBox/icons/zoom_out.jpg
--------------------------------------------------------------------------------
/sqlcipher-3.0.1/bin/bat使用说明.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/sqlcipher-3.0.1/bin/bat使用说明.txt
--------------------------------------------------------------------------------
/sqlcipher-3.0.1/bin/sqlcipher.bat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/sqlcipher-3.0.1/bin/sqlcipher.bat
--------------------------------------------------------------------------------
/app/ImageBox/images/wallhaven-748705.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/app/ImageBox/images/wallhaven-748705.jpg
--------------------------------------------------------------------------------
/app/ImageBox/images/wallhaven-753155.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/app/ImageBox/images/wallhaven-753155.jpg
--------------------------------------------------------------------------------
/app/ImageBox/images/wallhaven-vml6em.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/app/ImageBox/images/wallhaven-vml6em.jpg
--------------------------------------------------------------------------------
/app/Ui/contact/analysis/charts - 副本.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/app/Ui/contact/analysis/charts - 副本.zip
--------------------------------------------------------------------------------
/doc/images/image-20230520235113261.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/image-20230520235113261.png
--------------------------------------------------------------------------------
/doc/images/image-20230520235220104.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/image-20230520235220104.png
--------------------------------------------------------------------------------
/doc/images/image-20230520235338305.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/image-20230520235338305.png
--------------------------------------------------------------------------------
/doc/images/image-20230520235351749.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/image-20230520235351749.png
--------------------------------------------------------------------------------
/doc/images/image-20230520235400772.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/image-20230520235400772.png
--------------------------------------------------------------------------------
/doc/images/image-20230520235409112.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/image-20230520235409112.png
--------------------------------------------------------------------------------
/doc/images/image-20230520235422128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/image-20230520235422128.png
--------------------------------------------------------------------------------
/doc/images/image-20230520235431091.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/image-20230520235431091.png
--------------------------------------------------------------------------------
/doc/images/image-20230521001305274.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/image-20230521001305274.png
--------------------------------------------------------------------------------
/doc/images/image-20230521001547481.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/image-20230521001547481.png
--------------------------------------------------------------------------------
/doc/images/image-20230521001726799.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/doc/images/image-20230521001726799.png
--------------------------------------------------------------------------------
/sqlcipher-3.0.1/bin/sqlcipher - 副本.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/sqlcipher-3.0.1/bin/sqlcipher - 副本.txt
--------------------------------------------------------------------------------
/app/resources/icons/resources.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | logo.svg
4 |
5 |
6 |
--------------------------------------------------------------------------------
/requirements_decrypt.txt:
--------------------------------------------------------------------------------
1 | PyQt5
2 | psutil
3 | pycryptodomex
4 | pywin32
5 | pymem
6 | silk-python
7 | pyaudio
8 | fuzzywuzzy
9 | python-Levenshtein
--------------------------------------------------------------------------------
/sqlcipher-3.0.1/bin/sqlcipher-shell32.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/sqlcipher-3.0.1/bin/sqlcipher-shell32.exe
--------------------------------------------------------------------------------
/sqlcipher-3.0.1/bin/sqlcipher-shell64.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wo1261931780/fork-WeChatMsg/HEAD/sqlcipher-3.0.1/bin/sqlcipher-shell64.exe
--------------------------------------------------------------------------------
/.idea/copyright/copyright.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/resource/render/templates/nb_components.html:
--------------------------------------------------------------------------------
1 | {% import 'macro' as macro %}
2 |
3 | {% for chart in charts %}
4 | {{ macro.gen_components_content(chart) }}
5 | {% endfor %}
6 |
--------------------------------------------------------------------------------
/requirements_pc.txt:
--------------------------------------------------------------------------------
1 | PyQt5
2 | psutil
3 | pycryptodomex
4 | pywin32
5 | pymem
6 | silk-python
7 | pyaudio
8 | fuzzywuzzy
9 | python-Levenshtein
10 | pillow
11 | requests
12 |
--------------------------------------------------------------------------------
/sqlcipher-3.0.1/bin/adb.txt:
--------------------------------------------------------------------------------
1 | PRAGMA key = '10f35f1';
2 | PRAGMA cipher_migrate;
3 | ATTACH DATABASE 'plaintext.db' AS plaintext KEY '';
4 | SELECT sqlcipher_export('plaintext');
5 | DETACH DATABASE plaintext;
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/custom.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Custom issue template
3 | about: Describe this issue template's purpose here.
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/ImageBox/run.py:
--------------------------------------------------------------------------------
1 | from ui import MainDemo
2 | from config import *
3 |
4 |
5 | if __name__ == '__main__':
6 | app = QApplication(sys.argv)
7 | box = MainDemo()
8 | box.show()
9 | app.exec_()
10 |
--------------------------------------------------------------------------------
/app/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | @File : __init__.py.py
4 | @Author : Shuaikang Zhou
5 | @Time : 2023/1/5 17:43
6 | @IDE : Pycharm
7 | @Version : Python3.10
8 | @comment : ···
9 | """
10 |
--------------------------------------------------------------------------------
/app/DataBase/config.txt:
--------------------------------------------------------------------------------
1 |
2 | PRAGMA key = '10f35f1';
3 | PRAGMA cipher_migrate;
4 | ATTACH DATABASE './app/DataBase/Msg.db' AS Msg KEY '';
5 | SELECT sqlcipher_export('Msg');
6 | DETACH DATABASE Msg;
7 |
--------------------------------------------------------------------------------
/app/main/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | @File : __init__.py.py
4 | @Author : Shuaikang Zhou
5 | @Time : 2023/1/5 18:11
6 | @IDE : Pycharm
7 | @Version : Python3.10
8 | @comment : ···
9 | """
10 |
--------------------------------------------------------------------------------
/app/Ui/chat/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | @File : __init__.py.py
4 | @Author : Shuaikang Zhou
5 | @Time : 2022/12/13 20:33
6 | @IDE : Pycharm
7 | @Version : Python3.10
8 | @comment : ···
9 | """
10 |
--------------------------------------------------------------------------------
/app/data/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | @File : __init__.py.py
4 | @Author : Shuaikang Zhou
5 | @Time : 2022/12/13 14:19
6 | @IDE : Pycharm
7 | @Version : Python3.10
8 | @comment : ···
9 | """
10 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/Ui/contact/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | @File : __init__.py.py
4 | @Author : Shuaikang Zhou
5 | @Time : 2022/12/13 20:33
6 | @IDE : Pycharm
7 | @Version : Python3.10
8 | @comment : ···
9 | """
10 |
--------------------------------------------------------------------------------
/app/Ui/contact/userinfo/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | @File : __init__.py.py
4 | @Author : Shuaikang Zhou
5 | @Time : 2022/12/24 10:34
6 | @IDE : Pycharm
7 | @Version : Python3.10
8 | @comment : ···
9 | """
10 |
--------------------------------------------------------------------------------
/app/ui_pc/contact/userinfo/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | @File : __init__.py.py
4 | @Author : Shuaikang Zhou
5 | @Time : 2022/12/24 10:34
6 | @IDE : Pycharm
7 | @Version : Python3.10
8 | @comment : ···
9 | """
10 |
--------------------------------------------------------------------------------
/app/config.py:
--------------------------------------------------------------------------------
1 | version = '0.2.5'
2 | contact = '474379264'
3 | description = [
4 | '1. 支持获取个人信息
',
5 | '2. 支持显示聊天界面
',
6 | '3. 支持导出聊天记录
* csv
* html
',
7 | '4. 查找联系人
',
8 | ]
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | .idea
3 | build
4 | data
5 | sqlcipher-3.0.1
6 | dist
7 | venv
8 | venv_decrypt
9 | venv_main_pc
10 | TEST
11 | app/data/avatar
12 | app/data/image2
13 | app/data/emoji
14 | app/DataBase/Msg/*
15 | *.db
16 | *.pyc
17 | *.log
18 | *.spec
19 | test*
--------------------------------------------------------------------------------
/resource/render/templates/components.html:
--------------------------------------------------------------------------------
1 | {% import 'macro' as macro %}
2 |
3 |
4 |
5 |
6 | {{ chart.page_title }}
7 |
8 |
9 |
10 | {{ macro.gen_components_content(chart) }}
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/DataBase/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | @File : __init__.py.py
4 | @Author : Shuaikang Zhou
5 | @Time : 2023/1/5 0:10
6 | @IDE : Pycharm
7 | @Version : Python3.10
8 | @comment : ···
9 | """
10 | # from . import data
11 | # from . import output
12 |
13 | __all__ = ["data", 'output']
14 |
--------------------------------------------------------------------------------
/app/ImageBox/config.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QFileDialog
3 | from PyQt5.Qt import QPixmap, QPoint, Qt, QPainter, QIcon
4 | from PyQt5.QtCore import QSize
5 | from PyQt5 import QtCore, QtGui, QtWidgets
6 | from PyQt5.QtGui import QImageReader
7 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/resource/render/templates/nb_nteract.html:
--------------------------------------------------------------------------------
1 | {% import 'macro' as macro %}
2 |
3 |
4 |
5 |
6 | {{ macro.render_chart_dependencies(chart) }}
7 |
8 |
9 | {% for c in chart %}
10 | {{ macro.render_chart_content(c) }}
11 | {% endfor %}
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/WeChatMsg.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/resource/render/templates/simple_chart.html:
--------------------------------------------------------------------------------
1 | {% import 'macro' as macro %}
2 |
3 |
4 |
5 |
6 | {{ chart.page_title }}
7 | {{ macro.render_chart_dependencies(chart) }}
8 |
9 |
10 | {{ macro.render_chart_content(chart) }}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/resource/render/templates/nb_jupyter_lab.html:
--------------------------------------------------------------------------------
1 | {% import 'macro' as macro %}
2 |
3 |
4 |
5 |
6 |
7 |
8 | {% for chart in charts %}
9 | {% if chart._component_type in ("table", "image") %}
10 | {{ macro.gen_components_content(chart) }}
11 | {% else %}
12 | {{ macro.render_chart_content(chart) }}
13 | {% endif %}
14 | {% endfor %}
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/util/search.py:
--------------------------------------------------------------------------------
1 | from typing import List
2 |
3 | from fuzzywuzzy import process
4 |
5 |
6 | def search_by_content(key, choices: List[List]):
7 | result = []
8 | for i, choice in enumerate(choices):
9 | res = process.extractOne(key, choice)
10 | result.append((res, i))
11 | result.sort(key=lambda x: x[0][1], reverse=True)
12 | k = result[0][1]
13 | item = result[0][0][0]
14 | return choices[k].index(item)
15 |
--------------------------------------------------------------------------------
/app/Ui/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | @File : __init__.py.py
4 | @Author : Shuaikang Zhou
5 | @Time : 2022/12/13 14:19
6 | @IDE : Pycharm
7 | @Version : Python3.10
8 | @comment : ···
9 | """
10 | # from .ICON import Icon
11 | # from .chat import chat
12 | from app.Ui import mainview
13 | # 文件__init__.py
14 | # from login import login
15 | from app.Ui.decrypt import decrypt
16 |
17 | __all__ = ["decrypt", 'mainview', 'chat']
18 |
--------------------------------------------------------------------------------
/doc/获取个人文件/使用说明.md:
--------------------------------------------------------------------------------
1 | 教程参考
2 | [导出聊天记录](https://blog.csdn.net/m0_59452630/article/details/124222235?spm=1001.2014.3001.5501 "一文教会你导出微信聊天记录")
3 |
4 | 对于模拟器中信息提取,如果使用自带的Amaze复制到共享文件夹出现错误,无法导入可以使用MT管理器
5 |
6 | 
7 |
8 | MT管理器打开界面如下图所示,右边打开微信文件存储位置,找到MicroMsg,左边打开共享文件位置,可以通过右边的电脑图案查看路径。
9 |
10 | 如图,长按MicroMsg文件夹,弹出弹窗,点击复制->确认。
11 |
12 | 
13 |
14 | 开始复制,稍等一会即可完成复制。
15 |
16 | 
17 |
--------------------------------------------------------------------------------
/resource/render/templates/nb_jupyter_lab_tab.html:
--------------------------------------------------------------------------------
1 | {% import 'macro' as macro %}
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{ macro.generate_tab_css() }}
9 | {{ macro.display_tablinks(charts) }}
10 |
11 | {% for chart in charts %}
12 | {% if chart._component_type in ("table", "image") %}
13 | {{ macro.gen_components_content(chart) }}
14 | {% else %}
15 | {{ macro.render_chart_content(chart) }}
16 | {% endif %}
17 | {% endfor %}
18 | {{ macro.switch_tabs() }}
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/Ui/contact/emotion/emotionUi.ui:
--------------------------------------------------------------------------------
1 |
2 | Dialog
3 |
4 |
5 |
6 | 0
7 | 0
8 | 400
9 | 300
10 |
11 |
12 |
13 | Dialog
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/resource/render/templates/nb_jupyter_notebook.html:
--------------------------------------------------------------------------------
1 | {% import 'macro' as macro %}
2 |
3 |
12 |
13 | {% for chart in charts %}
14 | {% if chart._component_type in ("table", "image") %}
15 | {{ macro.gen_components_content(chart) }}
16 | {% else %}
17 |
18 | {% endif %}
19 | {% endfor %}
20 |
21 | {{ macro.render_notebook_charts(charts, libraries) }}
22 |
--------------------------------------------------------------------------------
/app/ui_pc/contact/userinfo/userinfo.py:
--------------------------------------------------------------------------------
1 | from PyQt5.QtWidgets import *
2 |
3 | from .userinfoUi import Ui_Frame
4 |
5 |
6 | class UserinfoController(QWidget, Ui_Frame):
7 | def __init__(self, contact, parent=None):
8 | super().__init__(parent)
9 | self.setupUi(self)
10 | self.l_remark.setText(contact.remark)
11 | self.l_avatar.setPixmap(contact.avatar)
12 | self.l_nickname.setText(f'昵称:{contact.nickName}')
13 | self.l_username.setText(f'微信号:{contact.alias}')
14 | self.lineEdit.setText(contact.remark)
15 | self.progressBar.setVisible(False)
16 |
--------------------------------------------------------------------------------
/app/Ui/contact/userinfo/userinfo.py:
--------------------------------------------------------------------------------
1 | from PyQt5.QtWidgets import *
2 |
3 | from .userinfoUi import Ui_Frame
4 |
5 |
6 | class UserinfoController(QWidget, Ui_Frame):
7 | def __init__(self, contact, parent=None):
8 | super().__init__(parent)
9 | self.setupUi(self)
10 | self.l_remark.setText(contact.conRemark)
11 | self.l_avatar.setPixmap(contact.avatar)
12 | self.l_nickname.setText(f'昵称:{contact.nickname}')
13 | self.l_username.setText(f'微信号:{contact.alias}')
14 | self.lineEdit.setText(contact.conRemark)
15 | self.progressBar.setVisible(False)
16 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/hook-pyecharts.py:
--------------------------------------------------------------------------------
1 | #-----------------------------------------------------------------------------
2 | # Copyright (c) 2017-2020, PyInstaller Development Team.
3 | #
4 | # Distributed under the terms of the GNU General Public License (version 2
5 | # or later) with exception for distributing the bootloader.
6 | #
7 | # The full license is in the file COPYING.txt, distributed with this software.
8 | #
9 | # SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
10 | #-----------------------------------------------------------------------------
11 | # Hook for nanite: https://pypi.python.org/pypi/nanite
12 | from PyInstaller.utils.hooks import collect_data_files
13 | datas = collect_data_files('pyecharts')
--------------------------------------------------------------------------------
/app/resources/icons/output.svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/resource/render/templates/nb_jupyter_notebook_tab.html:
--------------------------------------------------------------------------------
1 | {% import 'macro' as macro %}
2 |
3 |
12 |
13 | {{ macro.generate_tab_css() }}
14 | {{ macro.display_tablinks(charts) }}
15 |
16 | {% for chart in charts %}
17 | {% if chart._component_type in ("table", "image") %}
18 | {{ macro.gen_components_content(chart) }}
19 | {% else %}
20 |
22 | {% endif %}
23 | {% endfor %}
24 |
25 | {{ macro.render_notebook_charts(charts, libraries) }}
26 | {{ macro.switch_tabs() }}
27 |
--------------------------------------------------------------------------------
/app/Ui/contact/emotion/emotionUi.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'emotionUi.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.7
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore
12 |
13 |
14 | class Ui_Dialog(object):
15 | def setupUi(self, Dialog):
16 | Dialog.setObjectName("Dialog")
17 | Dialog.resize(400, 300)
18 |
19 | self.retranslateUi(Dialog)
20 | QtCore.QMetaObject.connectSlotsByName(Dialog)
21 |
22 | def retranslateUi(self, Dialog):
23 | _translate = QtCore.QCoreApplication.translate
24 | Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
25 |
--------------------------------------------------------------------------------
/app/resources/icons/emotion.svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/app/Ui/contact/test.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | MainWindow
6 |
7 |
8 |
9 | 0
10 | 0
11 | 800
12 | 600
13 |
14 |
15 |
16 | MainWindow
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/Ui/Icon.py:
--------------------------------------------------------------------------------
1 | from PyQt5.QtGui import QIcon
2 |
3 |
4 | class Icon:
5 | Default_avatar_path = './app/data/icons/default_avatar.svg'
6 | MainWindow_Icon = QIcon('./app/data/icons/logo.svg')
7 | Default_avatar = QIcon(Default_avatar_path)
8 | Output = QIcon('./app/data/icons/output.svg')
9 | Back = QIcon('./app/data/icons/back.svg')
10 | ToDocx = QIcon('app/data/icons/word.svg')
11 | ToCSV = QIcon('app/data/icons/csv.svg')
12 | ToHTML = QIcon('app/data/icons/html.svg')
13 | Chat_Icon = QIcon('./app/data/icons/chat.svg')
14 | Contact_Icon = QIcon('./app/data/icons/contact.svg')
15 | MyInfo_Icon = QIcon('./app/data/icons/myinfo.svg')
16 | Annual_Report_Icon = QIcon('./app/data/icons/annual_report.svg')
17 | Analysis_Icon = QIcon('./app/data/icons/analysis.svg')
18 | Emotion_Icon = QIcon('./app/data/icons/emotion.svg')
19 |
--------------------------------------------------------------------------------
/auth_info_key_prefs.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
--------------------------------------------------------------------------------
/app/resources/icons/analysis.svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/resource/render/templates/simple_tab.html:
--------------------------------------------------------------------------------
1 | {% import 'macro' as macro %}
2 |
3 |
4 |
5 |
6 | {{ chart.page_title }}
7 | {{ macro.render_chart_dependencies(chart) }}
8 | {{ macro.render_chart_css(chart) }}
9 |
10 |
11 | {% if chart.use_custom_tab_css is not true %}
12 | {{ macro.generate_tab_css() }}
13 | {% else %}
14 |
15 | {% endif %}
16 | {{ macro.display_tablinks(chart) }}
17 |
18 |
19 | {% for c in chart %}
20 | {% if c._component_type in ("table", "image") %}
21 | {{ macro.gen_components_content(c) }}
22 | {% else %}
23 | {{ macro.render_chart_content(c) }}
24 | {% endif %}
25 | {% endfor %}
26 |
27 |
28 |
34 | {{ macro.switch_tabs() }}
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/resources/resource.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | icons/404.png
4 | icons/analysis.svg
5 | icons/annual_report.svg
6 | icons/back.svg
7 | icons/chat.svg
8 | icons/contact.svg
9 | icons/csv.svg
10 | icons/default_avatar.svg
11 | icons/emotion.svg
12 | icons/html.svg
13 | icons/loading.svg
14 | icons/logo.svg
15 | icons/myinfo.svg
16 | icons/output.svg
17 | icons/search.svg
18 | icons/word.svg
19 | version_list.json
20 | icons/logo.ico
21 | icons/logo.png
22 | icons/tool.svg
23 | icons/home.svg
24 | icons/help.svg
25 |
26 |
27 | version_list.json
28 |
29 |
30 |
--------------------------------------------------------------------------------
/resource/render/templates/simple_page.html:
--------------------------------------------------------------------------------
1 | {% import 'macro' as macro %}
2 |
3 |
4 |
5 |
6 | {{ chart.page_title }}
7 | {{ macro.render_chart_dependencies(chart) }}
8 | {{ macro.render_chart_css(chart) }}
9 |
10 |
11 |
12 | {% if chart.download_button %}
13 |
14 | {% endif %}
15 |
16 | {% for c in chart %}
17 | {% if c._component_type in ("table", "image") %}
18 | {{ macro.gen_components_content(c) }}
19 | {% else %}
20 | {{ macro.render_chart_content(c) }}
21 | {% endif %}
22 | {% for _ in range(chart.page_interval) %}
23 | {% if chart.remove_br is false %}
{% endif %}
24 | {% endfor %}
25 | {% endfor %}
26 |
27 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/doc/电脑端使用教程.md:
--------------------------------------------------------------------------------
1 | # 一、解密微信数据库
2 |
3 | ## 主要功能
4 |
5 | 1. 解密微信数据库
6 | 2. 查看聊天记录
7 | 3. 导出聊天记录
8 | * CSV
9 | * docx(待实现)
10 | * HTML(待实现)
11 |
12 | ## 安装
13 |
14 | ```shell
15 | git clone https://github.com/LC044/WeChatMsg
16 | cd WeChatMsg
17 | pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
18 | ```
19 |
20 | ## 解密
21 |
22 |
23 |
24 | 解密步骤:
25 |
26 | 1. 登录微信
27 |
28 | 2. 运行程序
29 |
30 | ```shell
31 | python decrypt_window.py
32 | ```
33 |
34 | 3. 点击获取信息
35 |
36 | 
37 |
38 | 4. 设置微信安装路径
39 | 可以到微信->设置->文件管理查看
40 |
41 | 
42 |
43 | 点击**设置微信路径**按钮,选择该文件夹路径下的带有wxid_xxx的路径
44 | 
45 |
46 | 5. 获取到密钥和微信路径之后点击开始解密
47 |
48 | 6. 解密后的数据库文件保存在./app/DataBase/Msg路径下
49 |
50 |
51 |
52 | ## 查看聊天记录
53 |
54 |
55 |
56 | 1. 运行程序
57 |
58 | ```shell
59 | python main_pc.py
60 | ```
61 |
62 | 2. 选择联系人
63 |
64 |
65 |
66 | 3. 导出聊天记录
67 |
68 | 聊天记录保存在 **/data/聊天记录/** 文件夹下
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/app/Ui/contact/report/annual_report.py:
--------------------------------------------------------------------------------
1 | from bs4 import BeautifulSoup
2 |
3 |
4 | def create_title_page(nickname, time, avatar_path):
5 | with open('D:\\Project\\Python\\WeChatMsg\\app\\data\\html\\0.html', 'r+', encoding='utf-8') as f:
6 | html_document = f.read()
7 | # 创建Beautiful Soup对象
8 | soup = BeautifulSoup(html_document, 'html.parser')
9 | # 找到需要替换的图片元素
10 | target_image = soup.find(id='avatar')
11 | # 替换图片元素的src属性
12 | if target_image:
13 | target_image['src'] = avatar_path
14 | # 找到需要替换的元素
15 | target_element = soup.find(id='nickname')
16 | # 替换元素的文本内容
17 | if target_element:
18 | target_element.string = nickname
19 | target_element = soup.find(id='first_time')
20 | # 替换元素的文本内容
21 | if target_element:
22 | target_element.string = time
23 | with open('./data/AnnualReport/0.html', 'w', encoding='utf-8') as f1:
24 | f1.write(soup.prettify())
25 |
26 |
27 | if __name__ == '__main__':
28 | create_title_page('小学生', '2023-09-18 20:39:08', 'D:\Project\Python\WeChatMsg\\app\data\icons\default_avatar.svg')
29 |
--------------------------------------------------------------------------------
/app/resources/icons/chat.svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/app/resources/icons/myinfo.svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/app/ui_pc/Icon.py:
--------------------------------------------------------------------------------
1 | from PyQt5.QtGui import QIcon
2 |
3 | from app.resources import resource_rc
4 |
5 | var = resource_rc.qt_resource_name
6 |
7 |
8 | class Icon:
9 | Default_avatar_path = ':/icons/icons/default_avatar.svg'
10 | Default_image_path = ':/icons/icons/404.png'
11 | MainWindow_Icon = QIcon(':/icons/icons/logo.svg')
12 | Default_avatar = QIcon(Default_avatar_path)
13 | Output = QIcon(':/icons/icons/output.svg')
14 | Back = QIcon(':/icons/icons/back.svg')
15 | ToDocx = QIcon(':/icons/icons/word.svg')
16 | ToCSV = QIcon(':/icons/icons/csv.svg')
17 | ToHTML = QIcon(':/icons/icons/html.svg')
18 | Chat_Icon = QIcon(':/icons/icons/chat.svg')
19 | Contact_Icon = QIcon(':/icons/icons/contact.svg')
20 | MyInfo_Icon = QIcon(':/icons/icons/myinfo.svg')
21 | Annual_Report_Icon = QIcon(':/icons/icons/annual_report.svg')
22 | Analysis_Icon = QIcon(':/icons/icons/analysis.svg')
23 | Emotion_Icon = QIcon(':/icons/icons/emotion.svg')
24 | Search_Icon = QIcon(':/icons/icons/search.svg')
25 | Tool_Icon = QIcon(':/icons/icons/tool.svg')
26 | Home_Icon = QIcon(':/icons/icons/home.svg')
27 | Help_Icon = QIcon(':/icons/icons/help.svg')
28 |
--------------------------------------------------------------------------------
/decrypt_window.py:
--------------------------------------------------------------------------------
1 | import ctypes
2 | import sys
3 |
4 | from PyQt5.QtGui import QIcon
5 | from PyQt5.QtWidgets import QApplication, QMessageBox, QWidget
6 |
7 | from app.resources import resource_rc
8 | from app.ui_pc.tool.pc_decrypt import pc_decrypt
9 |
10 | var = resource_rc.qt_resource_name
11 | ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("WeChatReport")
12 |
13 |
14 | class ViewController(QWidget):
15 | def __init__(self):
16 | super().__init__()
17 | self.setWindowTitle('解密')
18 | self.setWindowIcon(QIcon(':/icons/icons/logo.svg'))
19 | self.viewMainWIn = None
20 | self.viewDecrypt = None
21 |
22 | def loadPCDecryptView(self):
23 | """
24 | 登录界面
25 | :return:
26 | """
27 | self.viewDecrypt = pc_decrypt.DecryptControl(self)
28 | self.viewDecrypt.DecryptSignal.connect(self.show_success)
29 | # self.viewDecrypt.show()
30 |
31 | def show_success(self):
32 | QMessageBox.about(self, "解密成功", "数据库文件存储在\napp/DataBase/Msg\n文件夹下")
33 | self.close()
34 |
35 |
36 | if __name__ == '__main__':
37 | app = QApplication(sys.argv)
38 | view = ViewController()
39 | view.loadPCDecryptView()
40 | view.show()
41 | sys.exit(app.exec_())
42 |
--------------------------------------------------------------------------------
/app/log/logger.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import os
3 | import time
4 | import traceback
5 | from functools import wraps
6 |
7 | filename = time.strftime("%Y-%m-%d", time.localtime(time.time()))
8 | logger = logging.getLogger('test')
9 | logger.setLevel(level=logging.DEBUG)
10 | formatter = logging.Formatter('%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
11 | try:
12 | if not os.path.exists('./app/log/logs'):
13 | os.mkdir('./app/log/logs')
14 | file_handler = logging.FileHandler(f'./app/log/logs/{filename}-log.log')
15 | except:
16 | file_handler = logging.FileHandler(f'{filename}-log.log')
17 |
18 | file_handler.setLevel(level=logging.INFO)
19 | file_handler.setFormatter(formatter)
20 | stream_handler = logging.StreamHandler()
21 | stream_handler.setLevel(logging.DEBUG)
22 | stream_handler.setFormatter(formatter)
23 | logger.addHandler(file_handler)
24 | logger.addHandler(stream_handler)
25 |
26 |
27 | def log(func):
28 | @wraps(func)
29 | def log_(*args, **kwargs):
30 | try:
31 | return func(*args, **kwargs)
32 | except Exception as e:
33 | logger.error(
34 | f"\n{func.__qualname__} is error,params:{(args, kwargs)},here are details:\n{traceback.format_exc()}")
35 |
36 | return log_
37 |
--------------------------------------------------------------------------------
/app/Ui/userinfo/userinfo.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | @File : contact.py
4 | @Author : Shuaikang Zhou
5 | @Time : 2022/12/13 15:07
6 | @IDE : Pycharm
7 | @Version : Python3.10
8 | @comment : ···
9 | """
10 | from PyQt5.QtCore import *
11 | from PyQt5.QtGui import *
12 | from PyQt5.QtWidgets import *
13 |
14 | from .userinfoUi import *
15 | from ...DataBase import data
16 |
17 |
18 | class MyinfoController(QWidget, Ui_Dialog):
19 | exitSignal = pyqtSignal()
20 | urlSignal = pyqtSignal(QUrl)
21 |
22 | # username = ''
23 |
24 | def __init__(self, Me, parent=None):
25 | super(MyinfoController, self).__init__(parent)
26 | self.setupUi(self)
27 | self.setWindowTitle('WeChat')
28 | self.setWindowIcon(QIcon('./app/data/icon.png'))
29 | self.Me = Me
30 | self.initui()
31 |
32 | def initui(self):
33 | self.myinfo = data.get_myInfo()
34 | avatar = self.Me.my_avatar
35 | pixmap = QPixmap(avatar).scaled(80, 80) # 按指定路径找到图片
36 | self.label_avatar.setPixmap(pixmap) # 在label上显示图片
37 | self.label_name.setText(self.myinfo['name'])
38 | self.label_wxid.setText('微信号:' + self.myinfo['username'])
39 | city = f"地区:{self.myinfo['province']}{self.myinfo['city']}"
40 | self.label_city.setText(city)
41 |
--------------------------------------------------------------------------------
/app/web_ui/web.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, render_template
2 | from pyecharts import options as opts
3 | from pyecharts.charts import Bar
4 | from pyecharts.globals import ThemeType
5 |
6 | app = Flask(__name__)
7 |
8 |
9 | @app.route("/")
10 | def index():
11 | # 创建一个简单的柱状图
12 | bar = (
13 | Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT))
14 | .add_xaxis(["A", "B", "C", "D", "E"])
15 | .add_yaxis("Series", [5, 20, 36, 10, 75])
16 | .set_global_opts(title_opts=opts.TitleOpts(title="Flask and Pyecharts Interaction"))
17 | )
18 |
19 | # 将图表转换成 HTML
20 | chart_html = bar.render_embed()
21 |
22 | # 渲染模板,并传递图表的 HTML 到模板中
23 | return render_template("index.html", chart_html=chart_html)
24 |
25 |
26 | @app.route("/index")
27 | def index0():
28 | return render_template("index.html")
29 |
30 |
31 | @app.route('/home')
32 | def home():
33 | data = {
34 | 'sub_title': '二零二三年度报告',
35 | 'avatar_path': "static/my_resource/avatar.png",
36 | 'nickname': '司小远',
37 | 'first_time': '2023-09-18 20:39:08',
38 | }
39 | return render_template('home.html', **data)
40 |
41 |
42 | @app.route('/message_num')
43 | def one():
44 | return "1hello world"
45 |
46 |
47 | if __name__ == "__main__":
48 | app.run(debug=True, host='0.0.0.0')
49 |
--------------------------------------------------------------------------------
/app/resources/icons/back.svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/app/resources/icons/search.svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/app/DataBase/misc.py:
--------------------------------------------------------------------------------
1 | import os.path
2 | import sqlite3
3 | import threading
4 | import time
5 |
6 | lock = threading.Lock()
7 | DB = None
8 | cursor = None
9 | db_path = "./app/Database/Msg/Misc.db"
10 | # misc_path = './Msg/Misc.db'
11 | if os.path.exists(db_path):
12 | DB = sqlite3.connect(db_path, check_same_thread=False)
13 | # '''创建游标'''
14 | cursor = DB.cursor()
15 |
16 |
17 | def init_database():
18 | global DB
19 | global cursor
20 | if not DB:
21 | if os.path.exists(db_path):
22 | DB = sqlite3.connect(db_path, check_same_thread=False)
23 | # '''创建游标'''
24 | cursor = DB.cursor()
25 |
26 |
27 | def get_avatar_buffer(userName):
28 | sql = '''
29 | select smallHeadBuf
30 | from ContactHeadImg1
31 | where usrName=?;
32 | '''
33 | try:
34 | lock.acquire(True)
35 | try:
36 | cursor.execute(sql, [userName])
37 | except:
38 | time.sleep(0.5)
39 | init_database()
40 | finally:
41 | cursor.execute(sql, [userName])
42 | result = cursor.fetchall()
43 | # print(result[0][0])
44 | if result:
45 | return result[0][0]
46 | finally:
47 | lock.release()
48 | return None
49 |
50 |
51 | def close():
52 | global DB
53 | if DB:
54 | DB.close()
55 |
56 |
57 | if __name__ == '__main__':
58 | get_avatar_buffer('wxid_al2oan01b6fn11')
59 |
--------------------------------------------------------------------------------
/app/util/path.py:
--------------------------------------------------------------------------------
1 | import os
2 | import winreg
3 |
4 | from app.person_pc import MePC
5 | from app.util import dat2pic
6 |
7 | if not os.path.exists('./data/'):
8 | os.mkdir('./data/')
9 | if not os.path.exists('./data/image'):
10 | os.mkdir('./data/image')
11 |
12 |
13 | def get_abs_path(path):
14 | # return os.path.join(os.getcwd(), 'app/data/icons/404.png')
15 | if path:
16 | base_path = os.getcwd() + "/data/image"
17 | output_path = dat2pic.decode_dat(os.path.join(MePC().wx_dir, path), base_path) # './data/image')
18 | return output_path if output_path else ':/icons/icons/404.png'
19 | else:
20 | return ':/icons/icons/404.png'
21 |
22 |
23 | def wx_path():
24 | ## 获取当前用户名
25 | users = os.path.expandvars('$HOMEPATH')
26 | ## 找到3ebffe94.ini配置文件
27 | f = open(r'C:' + users + '\\AppData\\Roaming\\Tencent\\WeChat\\All Users\\config\\3ebffe94.ini')
28 | txt = f.read()
29 | f.close()
30 | # 打开Windows注册表
31 | reg_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
32 | "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
33 | # 获取“我的文档”路径的注册表键值
34 | documents_path_value = winreg.QueryValueEx(reg_key, "Personal")
35 | # 输出路径
36 | ##读取文件将路径放到wx_location变量里
37 | if txt == 'MyDocument:':
38 | wx_location = documents_path_value[0] + '\WeChat Files'
39 | else:
40 | wx_location = txt + "\WeChat Files"
41 | return wx_location
42 |
--------------------------------------------------------------------------------
/app/DataBase/micro_msg.py:
--------------------------------------------------------------------------------
1 | import os.path
2 | import sqlite3
3 | import threading
4 |
5 | lock = threading.Lock()
6 | DB = None
7 | cursor = None
8 | micromsg_path = "./app/Database/Msg/MicroMsg.db"
9 | if os.path.exists(micromsg_path):
10 | DB = sqlite3.connect(micromsg_path, check_same_thread=False)
11 | # '''创建游标'''
12 | cursor = DB.cursor()
13 |
14 |
15 | def init_database():
16 | global DB
17 | global cursor
18 | if not DB:
19 | if os.path.exists(micromsg_path):
20 | DB = sqlite3.connect(micromsg_path, check_same_thread=False)
21 | # '''创建游标'''
22 | cursor = DB.cursor()
23 |
24 |
25 | def is_database_exist():
26 | return os.path.exists(micromsg_path)
27 |
28 |
29 | def get_contact():
30 | try:
31 | lock.acquire(True)
32 | sql = '''select UserName,Alias,Type,Remark,NickName,PYInitial,RemarkPYInitial,ContactHeadImgUrl.smallHeadImgUrl,ContactHeadImgUrl.bigHeadImgUrl
33 | from Contact inner join ContactHeadImgUrl on Contact.UserName = ContactHeadImgUrl.usrName
34 | where Type%2=1 and Alias is not null
35 | order by PYInitial
36 | '''
37 | cursor.execute(sql)
38 | result = cursor.fetchall()
39 | finally:
40 | lock.release()
41 | # DB.commit()
42 | return result
43 |
44 |
45 | def close():
46 | global DB
47 | if DB:
48 | DB.close()
49 |
50 |
51 | if __name__ == '__main__':
52 | get_contact()
53 |
--------------------------------------------------------------------------------
/app/components/prompt_bar.py:
--------------------------------------------------------------------------------
1 | from PyQt5 import QtGui
2 | from PyQt5.QtCore import *
3 | from PyQt5.QtGui import *
4 | from PyQt5.QtWidgets import *
5 |
6 |
7 | class PromptBar(QLabel):
8 | def __init__(self, parent=None):
9 | super().__init__(parent)
10 |
11 | def paintEvent(self, e): # 绘图事件
12 | qp = QPainter()
13 | qp.begin(self)
14 | self.drawRectangles1(qp) # 绘制线条矩形
15 | self.drawRectangles2(qp) # 绘制填充矩形
16 | self.drawRectangles3(qp) # 绘制线条+填充矩形
17 | self.drawRectangles4(qp) # 绘制线条矩形2
18 | qp.end()
19 |
20 | def drawRectangles1(self, qp): # 绘制填充矩形
21 | qp.setPen(QPen(Qt.black, 2, Qt.SolidLine)) # 颜色、线宽、线性
22 | qp.drawRect(*self.data)
23 |
24 | def drawRectangles2(self, qp): # 绘制填充矩形
25 | qp.setPen(QPen(Qt.black, 2, Qt.NoPen))
26 | qp.setBrush(QColor(200, 0, 0))
27 | qp.drawRect(220, 15, 200, 100)
28 |
29 | def drawRectangles3(self, qp): # 绘制线条+填充矩形
30 | qp.setPen(QPen(Qt.black, 2, Qt.SolidLine))
31 | qp.setBrush(QColor(200, 0, 0))
32 | qp.drawRect(430, 15, 200, 100)
33 |
34 | def drawRectangles4(self, qp): # 绘制线条矩形2
35 | path = QtGui.QPainterPath()
36 | qp.setPen(QPen(Qt.blue, 2, Qt.SolidLine))
37 | qp.setBrush(QColor(0, 0, 0, 0)) # 设置画刷颜色透明
38 | path.addRect(100, 200, 200, 100)
39 | qp.drawPath(path)
40 |
--------------------------------------------------------------------------------
/resource/render/templates/simple_globe.html:
--------------------------------------------------------------------------------
1 | {% import 'macro' as macro %}
2 |
3 |
4 |
5 |
6 | {{ chart.page_title }}
7 | {{ macro.render_chart_dependencies(chart) }}
8 |
9 |
10 |
11 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/resource/render/templates/nb_jupyter_globe.html:
--------------------------------------------------------------------------------
1 |
10 |
11 | {% for chart in charts %}
12 |
13 | {% endfor %}
14 |
15 |
16 |
50 |
--------------------------------------------------------------------------------
/resource/render/display.py:
--------------------------------------------------------------------------------
1 | from ..types import Optional, Sequence, Union
2 |
3 |
4 | class HTML:
5 | def __init__(self, data: Optional[str] = None):
6 | self.data = data
7 |
8 | def _repr_html_(self):
9 | return self.data
10 |
11 | def __html__(self):
12 | return self._repr_html_()
13 |
14 |
15 | _lib_t1 = """new Promise(function(resolve, reject) {
16 | var script = document.createElement("script");
17 | script.onload = resolve;
18 | script.onerror = reject;
19 | script.src = "%s";
20 | document.head.appendChild(script);
21 | }).then(() => {
22 | """
23 |
24 | _lib_t2 = """
25 | });"""
26 |
27 | _css_t = """var link = document.createElement("link");
28 | link.ref = "stylesheet";
29 | link.type = "text/css";
30 | link.href = "%s";
31 | document.head.appendChild(link);
32 | """
33 |
34 |
35 | class Javascript:
36 | def __init__(
37 | self,
38 | data: Optional[str] = None,
39 | lib: Optional[Union[str, Sequence]] = None,
40 | css: Optional[Union[str, Sequence]] = None,
41 | ):
42 | if isinstance(lib, str):
43 | lib = [lib]
44 | elif lib is None:
45 | lib = []
46 | if isinstance(css, str):
47 | css = [css]
48 | elif css is None:
49 | css = []
50 | self.lib = lib
51 | self.css = css
52 | self.data = data or ""
53 |
54 | def _repr_javascript_(self):
55 | r = ""
56 | for c in self.css:
57 | r += _css_t % c
58 | for d in self.lib:
59 | r += _lib_t1 % d
60 | r += self.data
61 | r += _lib_t2 * len(self.lib)
62 | return r
63 |
--------------------------------------------------------------------------------
/main_pc.py:
--------------------------------------------------------------------------------
1 | import ctypes
2 | import sys
3 | import time
4 |
5 | from PyQt5.QtGui import QIcon
6 | from PyQt5.QtWidgets import *
7 |
8 | from app.ui_pc import mainview
9 | from app.ui_pc.tool.pc_decrypt import pc_decrypt
10 |
11 | ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("WeChatReport")
12 |
13 |
14 | class ViewController(QWidget):
15 | def __init__(self):
16 | super().__init__()
17 | self.setWindowTitle('解密')
18 | self.setWindowIcon(QIcon(':/icons/icons/logo.png'))
19 | self.viewMainWIndow = None
20 | self.viewDecrypt = None
21 |
22 | def loadPCDecryptView(self):
23 | """
24 | 登录界面
25 | :return:
26 | """
27 | self.viewDecrypt = pc_decrypt.DecryptControl()
28 | self.viewDecrypt.DecryptSignal.connect(self.show_success)
29 | self.viewDecrypt.show()
30 |
31 | def loadMainWinView(self, username=None):
32 | """
33 | 聊天界面
34 | :param username: 账号
35 | :return:
36 | """
37 | username = ''
38 | start = time.time()
39 | self.viewMainWIndow = mainview.MainWinController(username=username)
40 | self.viewMainWIndow.setWindowTitle("Chat")
41 | self.viewMainWIndow.show()
42 | end = time.time()
43 | print('ok', end - start)
44 | self.viewMainWIndow.init_ui()
45 |
46 | def show_success(self):
47 | QMessageBox.about(self, "解密成功", "数据库文件存储在\napp/DataBase/Msg\n文件夹下")
48 |
49 |
50 | if __name__ == '__main__':
51 | app = QApplication(sys.argv)
52 | view = ViewController()
53 | # view.loadPCDecryptView()
54 | view.loadMainWinView()
55 | # view.show()
56 | # view.show_success()
57 | sys.exit(app.exec_())
58 |
--------------------------------------------------------------------------------
/app/resources/icons/word.svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/app/DataBase/merge.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sqlite3
3 |
4 |
5 | def merge_databases(source_paths, target_path):
6 | # 创建目标数据库连接
7 | target_conn = sqlite3.connect(target_path)
8 | target_cursor = target_conn.cursor()
9 | try:
10 | # 开始事务
11 | target_conn.execute("BEGIN;")
12 | for i, source_path in enumerate(source_paths):
13 | if not os.path.exists(source_path):
14 | break
15 | db = sqlite3.connect(source_path)
16 | cursor = db.cursor()
17 | sql = '''
18 | SELECT TalkerId,MsgsvrID,Type,SubType,IsSender,CreateTime,Sequence,StrTalker,StrContent,DisplayContent,BytesExtra
19 | FROM MSG;
20 | '''
21 | cursor.execute(sql)
22 | result = cursor.fetchall()
23 | # 附加源数据库
24 | target_cursor.executemany(
25 | "INSERT INTO MSG "
26 | "(TalkerId,MsgsvrID,Type,SubType,IsSender,CreateTime,Sequence,StrTalker,StrContent,DisplayContent,"
27 | "BytesExtra)"
28 | "VALUES(?,?,?,?,?,?,?,?,?,?,?)",
29 | result)
30 | cursor.close()
31 | db.close()
32 | # 提交事务
33 | target_conn.execute("COMMIT;")
34 |
35 | except Exception as e:
36 | # 发生异常时回滚事务
37 | target_conn.execute("ROLLBACK;")
38 | raise e
39 |
40 | finally:
41 | # 关闭目标数据库连接
42 | target_conn.close()
43 |
44 |
45 | if __name__ == "__main__":
46 | # 源数据库文件列表
47 | source_databases = ["Msg/MSG1.db", "Msg/MSG2.db", "Msg/MSG3.db"]
48 |
49 | # 目标数据库文件
50 | target_database = "Msg/MSG.db"
51 | import shutil
52 |
53 | shutil.copy('Msg/MSG0.db', target_database) # 使用一个数据库文件作为模板
54 | # 合并数据库
55 | merge_databases(source_databases, target_database)
56 |
--------------------------------------------------------------------------------
/app/components/contact_info_ui.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | from PyQt5.Qt import *
4 | from PyQt5.QtCore import *
5 | from PyQt5.QtWidgets import *
6 |
7 | from .CAvatar import CAvatar
8 |
9 |
10 | # 自定义的item 继承自QListWidgetItem
11 | class ContactQListWidgetItem(QListWidgetItem):
12 | def __init__(self, name, url, img_bytes=None):
13 | super().__init__()
14 | # 自定义item中的widget 用来显示自定义的内容
15 | self.widget = QWidget()
16 | # 用来显示name
17 | self.nameLabel = QLabel()
18 | self.nameLabel.setText(name)
19 | # 用来显示avator(图像)
20 | self.avatorLabel = CAvatar(None, shape=CAvatar.Rectangle, size=QSize(60, 60),
21 | url=url, img_bytes=img_bytes)
22 | # 设置布局用来对nameLabel和avatorLabel进行布局
23 | self.hbox = QHBoxLayout()
24 | self.hbox.addWidget(self.avatorLabel)
25 | self.hbox.addWidget(self.nameLabel)
26 | self.hbox.addStretch(1)
27 | # 设置widget的布局
28 | self.widget.setLayout(self.hbox)
29 | # 设置自定义的QListWidgetItem的sizeHint,不然无法显示
30 | self.setSizeHint(self.widget.sizeHint())
31 |
32 |
33 | if __name__ == "__main__":
34 | app = QApplication(sys.argv)
35 |
36 | # 主窗口
37 | w = QWidget()
38 | w.setWindowTitle("QListWindow")
39 | # 新建QListWidget
40 | listWidget = QListWidget(w)
41 | listWidget.resize(300, 300)
42 |
43 | # 新建两个自定义的QListWidgetItem(customQListWidgetItem)
44 | item1 = ContactQListWidgetItem("鲤鱼王", "liyuwang.jpg")
45 | item2 = ContactQListWidgetItem("可达鸭", "kedaya.jpg")
46 |
47 | # 在listWidget中加入两个自定义的item
48 | listWidget.addItem(item1)
49 | listWidget.setItemWidget(item1, item1.widget)
50 | listWidget.addItem(item2)
51 | listWidget.setItemWidget(item2, item2.widget)
52 |
53 | # 绑定点击槽函数 点击显示对应item中的name
54 | listWidget.itemClicked.connect(lambda item: print(item.nameLabel.text()))
55 |
56 | w.show()
57 | sys.exit(app.exec_())
58 |
--------------------------------------------------------------------------------
/app/resources/icons/html.svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import ctypes
2 | import sys
3 | import time
4 |
5 | from PyQt5.QtWidgets import *
6 |
7 | import app.DataBase.data as DB
8 | from app.Ui import decrypt, mainview
9 |
10 | ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("WeChatReport")
11 |
12 |
13 | class ViewController:
14 | def __init__(self):
15 | self.viewMainWIn = None
16 | self.viewDecrypt = None
17 |
18 | def loadDecryptView(self):
19 | """
20 | 登录界面
21 | :return:
22 | """
23 | if DB.is_db_exist():
24 | self.loadMainWinView()
25 | else:
26 | self.viewDecrypt = decrypt.DecryptControl() # 需要将view login设为成员变量
27 | self.viewDecrypt.DecryptSignal.connect(self.loadMainWinView)
28 | self.viewDecrypt.show()
29 | self.viewDecrypt.db_exist()
30 |
31 | def loadPCDecryptView(self):
32 | """
33 | 登录界面
34 | :return:
35 | """
36 | self.viewDecrypt = pc_decrypt.DecryptControl()
37 | self.viewDecrypt.DecryptSignal.connect(self.loadMainWinView)
38 | self.viewDecrypt.show()
39 |
40 | def loadMainWinView(self, username=None):
41 | """
42 | 聊天界面
43 | :param username: 账号
44 | :return:
45 | """
46 | username = ''
47 | start = time.time()
48 | self.viewMainWIn = mainview.MainWinController(username=username)
49 | self.viewMainWIn.setWindowTitle("Chat")
50 | # print(username)
51 | self.viewMainWIn.username = username
52 | # self.viewMainWIn.exitSignal.connect(self.loadDecryptView) # 不需要回到登录界面可以省略
53 | self.viewMainWIn.show()
54 | end = time.time()
55 | print('ok', end - start)
56 | # self.viewMainWIn.signUp()
57 |
58 |
59 | if __name__ == '__main__':
60 | app = QApplication(sys.argv)
61 | view = ViewController()
62 | # view.loadPCDecryptView()
63 | view.loadDecryptView() # 进入登录界面,如果view login不是成员变量,则离开作用域后失效。
64 | # view.loadMainWinView('102')
65 | sys.exit(app.exec_())
66 |
--------------------------------------------------------------------------------
/app/ui_pc/chat/chatInfoUi.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'chatInfoUi.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.7
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtWidgets
12 |
13 |
14 | class Ui_Form(object):
15 | def setupUi(self, Form):
16 | Form.setObjectName("Form")
17 | self.verticalLayout = QtWidgets.QVBoxLayout(Form)
18 | self.verticalLayout.setContentsMargins(0, 0, 0, 0)
19 | self.verticalLayout.setSpacing(0)
20 | self.verticalLayout.setObjectName("verticalLayout")
21 | self.frame = QtWidgets.QFrame(Form)
22 | self.frame.setFrameShape(QtWidgets.QFrame.NoFrame)
23 | self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
24 | self.frame.setObjectName("frame")
25 | self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.frame)
26 | self.verticalLayout_2.setObjectName("verticalLayout_2")
27 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
28 | self.horizontalLayout_2.setObjectName("horizontalLayout_2")
29 | self.label_reamrk = QtWidgets.QLabel(self.frame)
30 | self.label_reamrk.setObjectName("label_reamrk")
31 | self.horizontalLayout_2.addWidget(self.label_reamrk)
32 | spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
33 | self.horizontalLayout_2.addItem(spacerItem)
34 | self.toolButton = QtWidgets.QToolButton(self.frame)
35 | self.toolButton.setObjectName("toolButton")
36 | self.horizontalLayout_2.addWidget(self.toolButton)
37 | self.verticalLayout_2.addLayout(self.horizontalLayout_2)
38 |
39 | self.verticalLayout.addWidget(self.frame)
40 |
41 | self.retranslateUi(Form)
42 | QtCore.QMetaObject.connectSlotsByName(Form)
43 |
44 | def retranslateUi(self, Form):
45 | _translate = QtCore.QCoreApplication.translate
46 | Form.setWindowTitle(_translate("Form", "Form"))
47 | self.label_reamrk.setText(_translate("Form", "TextLabel"))
48 | self.toolButton.setText(_translate("Form", "..."))
49 |
--------------------------------------------------------------------------------
/app/person_pc.py:
--------------------------------------------------------------------------------
1 | from typing import Dict
2 |
3 | from PyQt5.QtCore import Qt
4 | from PyQt5.QtGui import QPixmap
5 |
6 | from app.ui_pc.Icon import Icon
7 |
8 |
9 | def singleton(cls):
10 | _instance = {}
11 |
12 | def inner():
13 | if cls not in _instance:
14 | _instance[cls] = cls()
15 | return _instance[cls]
16 |
17 | return inner
18 |
19 |
20 | @singleton
21 | class MePC:
22 | def __init__(self):
23 | self.avatar = QPixmap(Icon.Default_avatar_path)
24 | self.avatar_path = ':/icons/icons/default_avatar.svg'
25 | self.wxid = ''
26 | self.wx_dir = ''
27 | self.name = ''
28 | self.mobile = ''
29 |
30 | def set_avatar(self, img_bytes):
31 | if not img_bytes:
32 | self.avatar.load(Icon.Default_avatar_path)
33 | return
34 | if img_bytes[:4] == b'\x89PNG':
35 | self.avatar.loadFromData(img_bytes, format='PNG')
36 | else:
37 | self.avatar.loadFromData(img_bytes, format='jfif')
38 |
39 |
40 | class ContactPC:
41 | def __init__(self, contact_info: Dict):
42 | self.wxid = contact_info.get('UserName')
43 | self.remark = contact_info.get('Remark')
44 | # Alias,Type,Remark,NickName,PYInitial,RemarkPYInitial,ContactHeadImgUrl.smallHeadImgUrl,ContactHeadImgUrl,bigHeadImgUrl
45 | self.alias = contact_info.get('Alias')
46 | self.nickName = contact_info.get('NickName')
47 | if not self.remark:
48 | self.remark = self.nickName
49 | self.smallHeadImgUrl = contact_info.get('smallHeadImgUrl')
50 | self.smallHeadImgBLOG = b''
51 | self.avatar = QPixmap()
52 | self.avatar_path = ':/icons/icons/default_avatar.svg'
53 |
54 | def set_avatar(self, img_bytes):
55 | if not img_bytes:
56 | self.avatar.load(Icon.Default_avatar_path)
57 | return
58 | if img_bytes[:4] == b'\x89PNG':
59 | self.avatar.loadFromData(img_bytes, format='PNG')
60 | else:
61 | self.avatar.loadFromData(img_bytes, format='jfif')
62 |
63 | self.avatar.scaled(60, 60, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
64 |
65 |
66 | if __name__ == '__main__':
67 | p1 = MePC()
68 | p2 = MePC()
69 | print(p1 == p2)
70 |
--------------------------------------------------------------------------------
/app/resources/icons/contact (1).svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/app/resources/icons/logo.svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/app/resources/icons/contact (2).svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/app/resources/icons/csv.svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/app/resources/icons/404.svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/app/Ui/contact/report/report.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | from PyQt5.QtCore import *
4 | from PyQt5.QtGui import *
5 | from PyQt5.QtWebEngineWidgets import QWebEngineView
6 | from PyQt5.QtWidgets import *
7 |
8 | from app import person
9 | from app.DataBase import data
10 | from . import annual_report
11 |
12 |
13 | class ReportController(QWidget):
14 | def __init__(self, contact: person.Contact, me: person.Me = None, parent=None):
15 | super().__init__(parent)
16 | self.ta_username = contact.wxid
17 | self.contact = contact
18 | self.Me = me
19 | # self.setStyleSheet('''QWidget{background-color:rgb(240, 240, 240);}''')
20 | # 加载动画
21 | self.center()
22 | self.label_01()
23 |
24 | def center(self): # 定义一个函数使得窗口居中显示
25 | # 获取屏幕坐标系
26 | screen = QDesktopWidget().screenGeometry()
27 | # 获取窗口坐标系
28 | size = self.geometry()
29 | newLeft = (screen.width() - size.width()) / 2
30 | newTop = (screen.height() - size.height()) / 2
31 | self.move(int(newLeft), int(newTop))
32 |
33 | def label_01(self):
34 | w = self.size().width()
35 | h = self.size().height()
36 | self.label = QLabel(self)
37 | self.label.setGeometry(w // 2, h // 2, 100, 100)
38 | self.label.setToolTip("这是一个标签")
39 | # self.m_movie()
40 | self.initUI()
41 |
42 | def m_movie(self):
43 | movie = QMovie("./app/data/bg.gif")
44 | self.label.setMovie(movie)
45 | movie.start()
46 |
47 | def initUI(self):
48 | start_time = data.get_msg_start_time(self.contact.wxid)
49 | annual_report.create_title_page(self.contact.nickname, start_time, self.contact.avatar_path)
50 | self.label.setVisible(False)
51 | # self.setStyleSheet('''QWidget{background-color:rgb(244, 244, 244);}''')
52 | main_box = QHBoxLayout(self)
53 | self.browser1 = QWebEngineView()
54 | self.browser1.load(QUrl('file:///data/AnnualReport/index.html'))
55 |
56 | splitter1 = QSplitter(Qt.Vertical)
57 | splitter1.addWidget(self.browser1)
58 | main_box.addWidget(splitter1)
59 | self.setLayout(main_box)
60 |
61 | def setBackground(self):
62 | palette = QPalette()
63 | pix = QPixmap("./app/data/bg.png")
64 | pix = pix.scaled(self.width(), self.height(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation) # 自适应图片大小
65 | palette.setBrush(self.backgroundRole(), QBrush(pix)) # 设置背景图片
66 | # palette.setColor(self.backgroundRole(), QColor(192, 253, 123)) # 设置背景颜色
67 | self.setPalette(palette)
68 |
69 |
70 | if __name__ == '__main__':
71 | app = QApplication(sys.argv)
72 | ex = ReportController(1)
73 | ex.show()
74 | sys.exit(app.exec_())
75 |
--------------------------------------------------------------------------------
/app/util/dat2pic.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | # 图片字节头信息,
4 | # [0][1]为jpg头信息,
5 | # [2][3]为png头信息,
6 | # [4][5]为gif头信息
7 | pic_head = [0xff, 0xd8, 0x89, 0x50, 0x47, 0x49]
8 | # 解密码
9 | decode_code = 0
10 |
11 |
12 | def get_code(file_path):
13 | """
14 | 自动判断文件类型,并获取dat文件解密码
15 | :param file_path: dat文件路径
16 | :return: 如果文件为jpg/png/gif格式,则返回解密码,否则返回-1
17 | """
18 | if os.path.isdir(file_path):
19 | return -1, -1
20 | # if file_path[-4:] != ".dat":
21 | # return -1, -1
22 | dat_file = open(file_path, "rb")
23 | dat_read = dat_file.read(2)
24 | # print(dat_read)
25 | head_index = 0
26 | while head_index < len(pic_head):
27 | # 使用第一个头信息字节来计算加密码
28 | # 第二个字节来验证解密码是否正确
29 | code = dat_read[0] ^ pic_head[head_index]
30 | idf_code = dat_read[1] ^ code
31 | head_index = head_index + 1
32 | # if idf_code == pic_head[head_index]:
33 | # dat_file.close()
34 | return head_index, code
35 | head_index = head_index + 1
36 | dat_file.close()
37 | print("not jpg, png, gif")
38 | return -1, -1
39 |
40 |
41 | def decode_dat(file_path, out_path):
42 | """
43 | 解密文件,并生成图片
44 | :param file_path: dat文件路径
45 | :return: 无
46 | """
47 | if not os.path.exists(file_path):
48 | return None
49 | file_type, decode_code = get_code(file_path)
50 |
51 | if decode_code == -1:
52 | return
53 | if file_type == 1:
54 | pic_name = os.path.basename(file_path)[:-4] + ".jpg"
55 | elif file_type == 3:
56 | pic_name = file_path[:-4] + ".png"
57 | elif file_type == 5:
58 | pic_name = file_path[:-4] + ".gif"
59 | else:
60 | pic_name = file_path[:-4] + ".jpg"
61 | file_outpath = os.path.join(out_path, pic_name)
62 | if os.path.exists(file_outpath):
63 | return file_outpath
64 | with open(file_path, 'rb') as file_in:
65 | data = file_in.read()
66 | # 对数据进行异或加密/解密
67 | encrypted_data = bytes([byte ^ decode_code for byte in data])
68 | with open(file_outpath, 'wb') as file_out:
69 | file_out.write(encrypted_data)
70 | print(file_path, '->', file_outpath)
71 | return file_outpath
72 |
73 |
74 | def find_datfile(dir_path, out_path):
75 | """
76 | 获取dat文件目录下所有的文件
77 | :param dir_path: dat文件目录
78 | :return: 无
79 | """
80 | files_list = os.listdir(dir_path)
81 | for file_name in files_list:
82 | file_path = dir_path + "\\" + file_name
83 | decode_dat(file_path, out_path)
84 |
85 |
86 | if __name__ == "__main__":
87 | path = "E:\86390\Documents\WeChat Files\wxid_27hqbq7vx5hf22\FileStorage\CustomEmotion\\71\\"
88 | outpath = "D:\\test"
89 | if not os.path.exists(outpath):
90 | os.mkdir(outpath)
91 | find_datfile(path, outpath)
92 |
--------------------------------------------------------------------------------
/app/DataBase/msg.py:
--------------------------------------------------------------------------------
1 | import os.path
2 | import sqlite3
3 | import threading
4 | from pprint import pprint
5 |
6 | DB = None
7 | cursor = None
8 | db_path = "./app/Database/Msg/MSG.db"
9 | lock = threading.Lock()
10 |
11 | # misc_path = './Msg/Misc.db'
12 | if os.path.exists(db_path):
13 | DB = sqlite3.connect(db_path, check_same_thread=False)
14 | # '''创建游标'''
15 | cursor = DB.cursor()
16 |
17 |
18 | def is_database_exist():
19 | return os.path.exists(db_path)
20 |
21 |
22 | def init_database():
23 | global DB
24 | global cursor
25 | if not DB:
26 | if os.path.exists(db_path):
27 | DB = sqlite3.connect(db_path, check_same_thread=False)
28 | # '''创建游标'''
29 | cursor = DB.cursor()
30 |
31 |
32 | def get_messages(username_):
33 | sql = '''
34 | select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime
35 | from MSG
36 | where StrTalker=?
37 | order by CreateTime
38 | '''
39 | try:
40 | lock.acquire(True)
41 | cursor.execute(sql, [username_])
42 | result = cursor.fetchall()
43 | finally:
44 | lock.release()
45 | result.sort(key=lambda x: x[5])
46 | return result
47 |
48 |
49 | def get_messages_all():
50 | sql = '''
51 | select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime
52 | from MSG
53 | order by CreateTime
54 | '''
55 | try:
56 | lock.acquire(True)
57 | cursor.execute(sql)
58 | result = cursor.fetchall()
59 | finally:
60 | lock.release()
61 | result.sort(key=lambda x: x[5])
62 | return result
63 |
64 |
65 | def get_message_by_num(username_, local_id):
66 | sql = '''
67 | select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime
68 | from MSG
69 | where StrTalker = ? and localId < ?
70 | order by CreateTime desc
71 | limit 10
72 | '''
73 | try:
74 | lock.acquire(True)
75 | cursor.execute(sql, [username_, local_id])
76 | result = cursor.fetchall()
77 | finally:
78 | lock.release()
79 | # result.sort(key=lambda x: x[5])
80 | return result
81 |
82 |
83 | def close():
84 | global DB
85 | if DB:
86 | DB.close()
87 |
88 |
89 | if __name__ == '__main__':
90 | msg_root_path = './Msg/'
91 | init_database()
92 | result = get_message_by_num('wxid_0o18ef858vnu22', 9999999)
93 | print(result)
94 | print(result[-1][0])
95 | local_id = result[-1][0]
96 | pprint(get_message_by_num('wxid_0o18ef858vnu22', local_id))
97 |
--------------------------------------------------------------------------------
/app/ui_pc/tool/tool_window.py:
--------------------------------------------------------------------------------
1 | from PyQt5.QtCore import Qt, pyqtSignal
2 | from PyQt5.QtGui import QFont
3 | from PyQt5.QtWidgets import QWidget, QListWidgetItem, QLabel
4 |
5 | from app.ui_pc.Icon import Icon
6 | from .pc_decrypt import DecryptControl
7 | from .toolUI import Ui_Dialog
8 |
9 | # 美化样式表
10 | Stylesheet = """
11 |
12 | /*去掉item虚线边框*/
13 | QListWidget, QListView, QTreeWidget, QTreeView {
14 | outline: 0px;
15 | border:none;
16 | background-color:rgb(240,240,240)
17 | }
18 | /*设置左侧选项的最小最大宽度,文字颜色和背景颜色*/
19 | QListWidget {
20 | min-width: 400px;
21 | max-width: 400px;
22 | min-height: 80px;
23 | max-height: 80px;
24 | color: black;
25 | border:none;
26 | }
27 | QListWidget::item{
28 | height:80px;
29 | width:80px;
30 | }
31 | /*被选中时的背景颜色和左边框颜色*/
32 | QListWidget::item:selected {
33 | background: rgb(204, 204, 204);
34 | border-bottom: 4px solid rgb(9, 187, 7);
35 | border-left:none;
36 | color: black;
37 | font-weight: bold;
38 | }
39 | /*鼠标悬停颜色*/
40 | HistoryPanel::item:hover {
41 | background: rgb(52, 52, 52);
42 | }
43 | """
44 |
45 |
46 | class ToolWindow(QWidget, Ui_Dialog):
47 | get_info_signal = pyqtSignal(str)
48 | decrypt_success_signal = pyqtSignal(bool)
49 | load_finish_signal = pyqtSignal(bool)
50 |
51 | def __init__(self, parent=None):
52 | super().__init__(parent)
53 | self.setupUi(self)
54 | self.setStyleSheet(Stylesheet)
55 | self.init_ui()
56 | self.load_finish_signal.emit(True)
57 |
58 | def init_ui(self):
59 | self.listWidget.clear()
60 | self.listWidget.currentRowChanged.connect(self.setCurrentIndex)
61 | chat_item = QListWidgetItem(Icon.Chat_Icon, '解密', self.listWidget)
62 | contact_item = QListWidgetItem(Icon.Contact_Icon, '别点', self.listWidget)
63 | myinfo_item = QListWidgetItem(Icon.MyInfo_Icon, '别点', self.listWidget)
64 | tool_item = QListWidgetItem(Icon.MyInfo_Icon, '别点', self.listWidget)
65 | decrypt_window = DecryptControl()
66 | decrypt_window.get_wxidSignal.connect(self.get_info_signal)
67 | decrypt_window.DecryptSignal.connect(self.decrypt_success_signal)
68 | self.stackedWidget.addWidget(decrypt_window)
69 | label = QLabel('都说了不让你点', self)
70 | label.setFont(QFont("微软雅黑", 50))
71 | label.setAlignment(Qt.AlignCenter)
72 | # 设置label的背景颜色(这里随机)
73 | # 这里加了一个margin边距(方便区分QStackedWidget和QLabel的颜色)
74 | # label.setStyleSheet('background: rgb(%d, %d, %d);margin: 50px;' % (
75 | # randint(0, 255), randint(0, 255), randint(0, 255)))
76 | self.stackedWidget.addWidget(label)
77 | self.stackedWidget.addWidget(label)
78 | self.stackedWidget.addWidget(label)
79 | self.listWidget.setCurrentRow(0)
80 | self.stackedWidget.setCurrentIndex(0)
81 |
82 | def setCurrentIndex(self, row):
83 | print(row)
84 | self.stackedWidget.setCurrentIndex(row)
85 |
--------------------------------------------------------------------------------
/app/resources/icons/contact.svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/main.spec:
--------------------------------------------------------------------------------
1 | # -*- mode: python ; coding: utf-8 -*-
2 |
3 | add_files = [
4 | ("D:\\Project\\Python\\WeChatMsg\\app\\data\\icon.png",'.\\app\\data'),
5 | ("D:\\Project\\Python\\WeChatMsg\\app\\data\\stopwords.txt",'.\\app\\data'),
6 | ("D:\\Project\\Python\\WeChatMsg\\app\\data\\bg.gif",'.\\app\\data'),
7 | ("D:\\Project\\Python\\WeChatMsg\\app\\data\\icons",'.\\app\\data\\icons'),
8 | ("D:\\Project\\Python\\WeChatMsg\\app\\ImageBox",'.\\app\\ImageBox'),
9 | ("D:\\Project\\Python\\WeChatMsg\\app\\DataBase",'.\\app\\DataBase'),
10 | #("D:\\Project\\Python\\WeChatMsg\\app\\Ui",'.\\app\\Ui'),
11 | ("D:\\Project\\Python\\WeChatMsg\\sqlcipher-3.0.1",'.\\sqlcipher-3.0.1'),
12 | ('.\\resource\\datasets', 'pyecharts\\datasets\\.'),
13 | ('.\\resource\\render\\templates', 'pyecharts\\render\\templates\\.'),
14 | ('.\\data\\AnnualReport', 'data\\AnnualReport'),
15 | ("D:\\Program Files\\Python310\\Lib\\site-packages\\snownlp",'snownlp')
16 |
17 | ]
18 | block_cipher = None
19 |
20 | #("D:\\Project\\Python\\WeChatMsg\\sqlcipher-3.0.1",'.\\sqlcipher-3.0.1')
21 |
22 | a = Analysis(
23 | ['main.py',
24 | './app/DataBase/data.py','./app/DataBase/output.py',
25 | './app/Ui/mainview.py','./app/Ui/mainwindow.py',
26 | './app/Ui/__init__.py',
27 | './app/Ui/chat/chat.py','./app/Ui/chat/chatUi.py',
28 | './app/Ui/contact/contact.py','./app/Ui/contact/contactUi.py','./app/Ui/contact/analysis/analysis.py','./app/Ui/contact/analysis/charts.py','./app/Ui/contact/report/report.py',
29 | './app/Ui/contact/emotion/emotion.py','./app/Ui/contact/emotion/emotionUi.py',
30 | './app/Ui/contact/contactInfo.py','./app/Ui/contact/contactInfoUi.py',
31 | './app/Ui/contact/userinfo/userinfoUi.py','./app/Ui/contact/userinfo/userinfo.py',
32 | './app/Ui/decrypt/decrypt.py','./app/Ui/decrypt/decryptUi.py',
33 | './app/Ui/userinfo/userinfo.py','./app/Ui/userinfo/userinfoUi.py',
34 | './app/person.py',
35 | './app/Ui/ICON.py',
36 | './app/Ui/MyComponents/Button_Contact.py'
37 | ],
38 | pathex=[],
39 | binaries=[],
40 | datas=add_files,
41 | hiddenimports=[],
42 | hookspath=[],
43 | hooksconfig={},
44 | runtime_hooks=[],
45 | excludes=[],
46 | win_no_prefer_redirects=False,
47 | win_private_assemblies=False,
48 | cipher=block_cipher,
49 | noarchive=False,
50 | )
51 | pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
52 |
53 | exe = EXE(
54 | pyz,
55 | a.scripts,
56 | [],
57 | exclude_binaries=True,
58 | name='main',
59 | debug=False,
60 | bootloader_ignore_signals=False,
61 | strip=False,
62 | upx=True,
63 | console=True,
64 | disable_windowed_traceback=True,
65 | argv_emulation=False,
66 | target_arch=None,
67 | codesign_identity=None,
68 | entitlements_file=None,
69 | icon='./app/data/icon.png'
70 | )
71 | coll = COLLECT(
72 | exe,
73 | a.binaries,
74 | a.zipfiles,
75 | a.datas,
76 | strip=False,
77 | upx=True,
78 | upx_exclude=[],
79 | name='main',
80 | )
81 |
--------------------------------------------------------------------------------
/app/Ui/userinfo/userinfoUi.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'userinfoUi.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.7
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtGui, QtWidgets
12 |
13 |
14 | class Ui_Dialog(object):
15 | def setupUi(self, Dialog):
16 | Dialog.setObjectName("Dialog")
17 | Dialog.resize(1120, 720)
18 | Dialog.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
19 | Dialog.setAutoFillBackground(False)
20 | self.frame_2 = QtWidgets.QFrame(Dialog)
21 | self.frame_2.setGeometry(QtCore.QRect(0, 0, 1120, 720))
22 | self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel)
23 | self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised)
24 | self.frame_2.setObjectName("frame_2")
25 | self.horizontalLayoutWidget = QtWidgets.QWidget(self.frame_2)
26 | self.horizontalLayoutWidget.setGeometry(QtCore.QRect(340, 60, 291, 82))
27 | self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
28 | self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
29 | self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
30 | self.horizontalLayout.setObjectName("horizontalLayout")
31 | self.label_avatar = QtWidgets.QLabel(self.horizontalLayoutWidget)
32 | self.label_avatar.setMinimumSize(QtCore.QSize(80, 80))
33 | self.label_avatar.setObjectName("label_avatar")
34 | self.horizontalLayout.addWidget(self.label_avatar)
35 | self.verticalLayout = QtWidgets.QVBoxLayout()
36 | self.verticalLayout.setObjectName("verticalLayout")
37 | self.label_name = QtWidgets.QLabel(self.horizontalLayoutWidget)
38 | self.label_name.setObjectName("label_name")
39 | self.verticalLayout.addWidget(self.label_name)
40 | self.label_wxid = QtWidgets.QLabel(self.horizontalLayoutWidget)
41 | self.label_wxid.setObjectName("label_wxid")
42 | self.verticalLayout.addWidget(self.label_wxid)
43 | self.label_city = QtWidgets.QLabel(self.horizontalLayoutWidget)
44 | self.label_city.setObjectName("label_city")
45 | self.verticalLayout.addWidget(self.label_city)
46 | self.horizontalLayout.addLayout(self.verticalLayout)
47 | self.horizontalLayout.setStretch(0, 1)
48 | self.horizontalLayout.setStretch(1, 3)
49 |
50 | self.retranslateUi(Dialog)
51 | QtCore.QMetaObject.connectSlotsByName(Dialog)
52 |
53 | def retranslateUi(self, Dialog):
54 | _translate = QtCore.QCoreApplication.translate
55 | Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
56 | self.label_avatar.setText(_translate("Dialog", "TextLabel"))
57 | self.label_name.setText(_translate("Dialog", "TextLabel"))
58 | self.label_wxid.setText(_translate("Dialog", "TextLabel"))
59 | self.label_city.setText(_translate("Dialog", "TextLabel"))
60 |
--------------------------------------------------------------------------------
/resource/render/snapshot.py:
--------------------------------------------------------------------------------
1 | import base64
2 | import codecs
3 | import logging
4 | import os
5 | from io import BytesIO
6 |
7 | from ..types import Any
8 |
9 | logger = logging.getLogger(__name__)
10 |
11 | PNG_FORMAT = "png"
12 | JPG_FORMAT = "jpeg"
13 | GIF_FORMAT = "gif"
14 | PDF_FORMAT = "pdf"
15 | SVG_FORMAT = "svg"
16 | EPS_FORMAT = "eps"
17 | B64_FORMAT = "base64"
18 |
19 |
20 | def make_snapshot(
21 | engine: Any,
22 | file_name: str,
23 | output_name: str,
24 | delay: float = 2,
25 | pixel_ratio: int = 2,
26 | is_remove_html: bool = False,
27 | **kwargs,
28 | ):
29 | logger.info("Generating file ...")
30 | file_type = output_name.split(".")[-1]
31 |
32 | content = engine.make_snapshot(
33 | html_path=file_name,
34 | file_type=file_type,
35 | delay=delay,
36 | pixel_ratio=pixel_ratio,
37 | **kwargs,
38 | )
39 | if file_type in [SVG_FORMAT, B64_FORMAT]:
40 | save_as_text(content, output_name)
41 | else:
42 | # pdf, gif, png, jpeg
43 | content_array = content.split(",")
44 | if len(content_array) != 2:
45 | raise OSError(content_array)
46 |
47 | image_data = decode_base64(content_array[1])
48 |
49 | if file_type in [PDF_FORMAT, GIF_FORMAT, EPS_FORMAT]:
50 | save_as(image_data, output_name, file_type)
51 | elif file_type in [PNG_FORMAT, JPG_FORMAT]:
52 | save_as_png(image_data, output_name)
53 | else:
54 | raise TypeError(f"Not supported file type '{file_type}'")
55 |
56 | if "/" not in output_name:
57 | output_name = os.path.join(os.getcwd(), output_name)
58 |
59 | if is_remove_html and not file_name.startswith("http"):
60 | os.unlink(file_name)
61 | logger.info(f"File saved in {output_name}")
62 |
63 |
64 | def decode_base64(data: str) -> bytes:
65 | """Decode base64, padding being optional.
66 |
67 | :param data: Base64 data as an ASCII byte string
68 | :returns: The decoded byte string.
69 | """
70 | missing_padding = len(data) % 4
71 | if missing_padding != 0:
72 | data += "=" * (4 - missing_padding)
73 | return base64.decodebytes(data.encode("utf-8"))
74 |
75 |
76 | def save_as_png(image_data: bytes, output_name: str):
77 | with open(output_name, "wb") as f:
78 | f.write(image_data)
79 |
80 |
81 | def save_as_text(image_data: str, output_name: str):
82 | with codecs.open(output_name, "w", encoding="utf-8") as f:
83 | f.write(image_data)
84 |
85 |
86 | def save_as(image_data: bytes, output_name: str, file_type: str):
87 | try:
88 | from PIL import Image
89 |
90 | m = Image.open(BytesIO(image_data))
91 | m.load()
92 | color = (255, 255, 255)
93 | b = Image.new("RGB", m.size, color)
94 | # BUG for Mac:
95 | # b.paste(m, mask=m.split()[3])
96 | b.paste(m)
97 | b.save(output_name, file_type, quality=100)
98 | except ModuleNotFoundError:
99 | raise Exception(f"Please install PIL for {file_type} image type")
100 |
--------------------------------------------------------------------------------
/app/Ui/decrypt/decryptUi.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Dialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 400
10 | 300
11 |
12 |
13 |
14 | Dialog
15 |
16 |
17 |
18 |
19 | 110
20 | 20
21 | 221
22 | 51
23 |
24 |
25 |
26 |
27 | 一纸情书
28 | 20
29 |
30 |
31 |
32 | 解密数据库
33 |
34 |
35 |
36 |
37 |
38 | 90
39 | 260
40 | 271
41 | 23
42 |
43 |
44 |
45 | 50
46 |
47 |
48 |
49 |
50 |
51 | 80
52 | 230
53 | 241
54 | 20
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | 80
65 | 80
66 | 245
67 | 134
68 |
69 |
70 |
71 | -
72 |
73 |
-
74 |
75 |
76 | 点击加载xml文件
77 |
78 |
79 |
80 | -
81 |
82 |
83 | xml未就绪
84 |
85 |
86 |
87 |
88 |
89 | -
90 |
91 |
-
92 |
93 |
94 | 点击加载数据库文件
95 |
96 |
97 |
98 | -
99 |
100 |
101 | 数据库未就绪
102 |
103 |
104 |
105 |
106 |
107 | -
108 |
109 |
110 | 开始解密数据库
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/app/resources/icons/annual_report.svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/app/DataBase/hard_link.py:
--------------------------------------------------------------------------------
1 | import binascii
2 | import os.path
3 | import sqlite3
4 | import threading
5 | import xml.etree.ElementTree as ET
6 |
7 | from app.log import log
8 |
9 | lock = threading.Lock()
10 | DB = None
11 | cursor = None
12 | db_path = "./app/Database/Msg/HardLinkImage.db"
13 | root_path = 'FileStorage/MsgAttach/'
14 | if os.path.exists(db_path):
15 | DB = sqlite3.connect(db_path, check_same_thread=False)
16 | # '''创建游标'''
17 | cursor = DB.cursor()
18 |
19 |
20 | def init_database():
21 | global DB
22 | global cursor
23 | if not DB:
24 | if os.path.exists(db_path):
25 | DB = sqlite3.connect(db_path, check_same_thread=False)
26 | # '''创建游标'''
27 | cursor = DB.cursor()
28 |
29 |
30 | def get_image_by_md5(md5: bytes):
31 | sql = '''
32 | select Md5Hash,MD5,FileName,HardLinkImageID.Dir as DirName1,HardLinkImageID2.Dir as DirName2
33 | from HardLinkImageAttribute
34 | join HardLinkImageID on HardLinkImageAttribute.DirID1 = HardLinkImageID.DirID
35 | join HardLinkImageID as HardLinkImageID2 on HardLinkImageAttribute.DirID2 = HardLinkImageID2.DirID
36 | where MD5 = ?;
37 | '''
38 | try:
39 | lock.acquire(True)
40 | try:
41 | cursor.execute(sql, [md5])
42 | except AttributeError:
43 | init_database()
44 | finally:
45 | cursor.execute(sql, [md5])
46 | result = cursor.fetchone()
47 | return result
48 | finally:
49 | lock.release()
50 |
51 |
52 | def get_md5_from_xml(content):
53 | # 解析XML
54 | root = ET.fromstring(content)
55 | # 提取md5的值
56 | md5_value = root.find(".//img").get("md5")
57 | # print(md5_value)
58 | return md5_value
59 |
60 |
61 | @log
62 | def get_image(content, thumb=False):
63 | md5 = get_md5_from_xml(content)
64 | result = get_image_by_md5(binascii.unhexlify(md5))
65 | if result:
66 | # print(result)
67 | dir1 = result[3]
68 | dir2 = result[4]
69 | data_image = result[2]
70 | dir0 = 'Thumb' if thumb else 'Image'
71 | dat_image = os.path.join(root_path, dir1, dir0, dir2, data_image)
72 | return dat_image
73 |
74 |
75 | def close():
76 | if DB:
77 | DB.close()
78 |
79 |
80 | # 6b02292eecea118f06be3a5b20075afc_t
81 |
82 | if __name__ == '__main__':
83 | msg_root_path = './Msg/'
84 | db_path = "./Msg/HardLinkImage.db"
85 | init_database()
86 | content = '''\n\t
\n\t\n\t\n\n'''
87 | print(get_image(content))
88 | print(get_image(content, thumb=False))
89 | result = get_md5_from_xml(content)
90 | print(result)
91 |
--------------------------------------------------------------------------------
/app/Ui/decrypt/decryptUi.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'decryptUi.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.7
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtGui, QtWidgets
12 |
13 |
14 | class Ui_Dialog(object):
15 | def setupUi(self, Dialog):
16 | Dialog.setObjectName("Dialog")
17 | Dialog.resize(400, 300)
18 | self.label_3 = QtWidgets.QLabel(Dialog)
19 | self.label_3.setGeometry(QtCore.QRect(110, 20, 221, 51))
20 | font = QtGui.QFont()
21 | font.setFamily("一纸情书")
22 | font.setPointSize(20)
23 | self.label_3.setFont(font)
24 | self.label_3.setObjectName("label_3")
25 | self.progressBar = QtWidgets.QProgressBar(Dialog)
26 | self.progressBar.setGeometry(QtCore.QRect(90, 260, 271, 23))
27 | self.progressBar.setProperty("value", 50)
28 | self.progressBar.setObjectName("progressBar")
29 | self.label_key = QtWidgets.QLabel(Dialog)
30 | self.label_key.setGeometry(QtCore.QRect(80, 230, 241, 20))
31 | self.label_key.setText("")
32 | self.label_key.setObjectName("label_key")
33 | self.widget = QtWidgets.QWidget(Dialog)
34 | self.widget.setGeometry(QtCore.QRect(80, 80, 245, 134))
35 | self.widget.setObjectName("widget")
36 | self.verticalLayout = QtWidgets.QVBoxLayout(self.widget)
37 | self.verticalLayout.setContentsMargins(0, 0, 0, 0)
38 | self.verticalLayout.setObjectName("verticalLayout")
39 | self.horizontalLayout = QtWidgets.QHBoxLayout()
40 | self.horizontalLayout.setObjectName("horizontalLayout")
41 | self.btn_xml = QtWidgets.QPushButton(self.widget)
42 | self.btn_xml.setObjectName("btn_xml")
43 | self.horizontalLayout.addWidget(self.btn_xml)
44 | self.label_xml = QtWidgets.QLabel(self.widget)
45 | self.label_xml.setObjectName("label_xml")
46 | self.horizontalLayout.addWidget(self.label_xml)
47 | self.verticalLayout.addLayout(self.horizontalLayout)
48 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
49 | self.horizontalLayout_2.setObjectName("horizontalLayout_2")
50 | self.btn_db = QtWidgets.QPushButton(self.widget)
51 | self.btn_db.setObjectName("btn_db")
52 | self.horizontalLayout_2.addWidget(self.btn_db)
53 | self.label_db = QtWidgets.QLabel(self.widget)
54 | self.label_db.setObjectName("label_db")
55 | self.horizontalLayout_2.addWidget(self.label_db)
56 | self.verticalLayout.addLayout(self.horizontalLayout_2)
57 | self.pushButton_3 = QtWidgets.QPushButton(self.widget)
58 | self.pushButton_3.setObjectName("pushButton_3")
59 | self.verticalLayout.addWidget(self.pushButton_3)
60 |
61 | self.retranslateUi(Dialog)
62 | QtCore.QMetaObject.connectSlotsByName(Dialog)
63 |
64 | def retranslateUi(self, Dialog):
65 | _translate = QtCore.QCoreApplication.translate
66 | Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
67 | self.label_3.setText(_translate("Dialog", "解密数据库"))
68 | self.btn_xml.setText(_translate("Dialog", "点击加载xml文件"))
69 | self.label_xml.setText(_translate("Dialog", "xml未就绪"))
70 | self.btn_db.setText(_translate("Dialog", "点击加载数据库文件"))
71 | self.label_db.setText(_translate("Dialog", "数据库未就绪"))
72 | self.pushButton_3.setText(_translate("Dialog", "开始解密数据库"))
73 |
--------------------------------------------------------------------------------
/app/person.py:
--------------------------------------------------------------------------------
1 | import os.path
2 | from typing import Dict
3 |
4 | from PyQt5.QtCore import Qt
5 | from PyQt5.QtGui import QPixmap
6 |
7 | from app.DataBase import data
8 | from app.ui_pc.Icon import Icon
9 |
10 |
11 | # from app.Ui.Icon import Icon
12 |
13 |
14 | class Person:
15 | def __init__(self, wxid: str):
16 |
17 | self.wxid = wxid
18 | self.conRemark = data.get_conRemark(wxid)
19 | self.nickname, self.alias = data.get_nickname(wxid)
20 | self.avatar_path = data.get_avator(wxid)
21 | if os.path.exists(self.avatar_path):
22 | self.avatar = QPixmap(self.avatar_path).scaled(60, 60)
23 | else:
24 | self.avatar_path = './app/data/icons/default_avatar.svg'
25 | # self.avatar_path = Icon.Default_avatar_path
26 | self.avatar = QPixmap(self.avatar_path).scaled(60, 60)
27 |
28 |
29 | class Me(Person):
30 | def __init__(self, wxid: str):
31 | super(Me, self).__init__(wxid)
32 | self.city = None
33 | self.province = None
34 |
35 |
36 | class Contact(Person):
37 | def __init__(self, wxid: str):
38 | super(Contact, self).__init__(wxid)
39 | self.smallHeadImgUrl = ''
40 | self.bigHeadImgUrl = ''
41 |
42 |
43 | def singleton(cls):
44 | _instance = {}
45 |
46 | def inner():
47 | if cls not in _instance:
48 | _instance[cls] = cls()
49 | return _instance[cls]
50 |
51 | return inner
52 |
53 |
54 | @singleton
55 | class MePC:
56 | def __init__(self):
57 | self.avatar = QPixmap(Icon.Default_avatar_path)
58 | self.avatar_path = 'D:\Project\Python\WeChatMsg\\app\data\icons\default_avatar.svg'
59 | self.wxid = ''
60 | self.wx_dir = ''
61 | self.name = ''
62 | self.mobile = ''
63 |
64 | def set_avatar(self, img_bytes):
65 | if not img_bytes:
66 | self.avatar.load(Icon.Default_avatar_path)
67 | return
68 | if img_bytes[:4] == b'\x89PNG':
69 | self.avatar.loadFromData(img_bytes, format='PNG')
70 | else:
71 | self.avatar.loadFromData(img_bytes, format='jfif')
72 |
73 |
74 | class ContactPC:
75 | def __init__(self, contact_info: Dict):
76 | self.wxid = contact_info.get('UserName')
77 | self.remark = contact_info.get('Remark')
78 | # Alias,Type,Remark,NickName,PYInitial,RemarkPYInitial,ContactHeadImgUrl.smallHeadImgUrl,ContactHeadImgUrl,bigHeadImgUrl
79 | self.alias = contact_info.get('Alias')
80 | self.nickName = contact_info.get('NickName')
81 | if not self.remark:
82 | self.remark = self.nickName
83 | self.smallHeadImgUrl = contact_info.get('smallHeadImgUrl')
84 | self.smallHeadImgBLOG = b''
85 | self.avatar = QPixmap()
86 | self.avatar_path = 'D:\Project\Python\WeChatMsg\\app\data\icons\default_avatar.svg'
87 |
88 | def set_avatar(self, img_bytes):
89 | if not img_bytes:
90 | self.avatar.load(Icon.Default_avatar_path)
91 | return
92 | if img_bytes[:4] == b'\x89PNG':
93 | self.avatar.loadFromData(img_bytes, format='PNG')
94 | else:
95 | self.avatar.loadFromData(img_bytes, format='jfif')
96 |
97 | self.avatar.scaled(60, 60, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
98 |
99 |
100 | class Group(Person):
101 | def __init__(self, wxid: str):
102 | super(Group, self).__init__(wxid)
103 |
104 |
105 | if __name__ == '__main__':
106 | p1 = MePC()
107 | p2 = MePC()
108 | print(p1 == p2)
109 |
--------------------------------------------------------------------------------
/app/ui_pc/chat/chatUi.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'chatUi.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.7
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtWidgets
12 |
13 |
14 | class Ui_Form(object):
15 | def setupUi(self, Form):
16 | Form.setObjectName("Form")
17 | Form.resize(840, 752)
18 | Form.setStyleSheet("background: rgb(240, 240, 240);")
19 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout(Form)
20 | self.horizontalLayout_2.setSpacing(6)
21 | self.horizontalLayout_2.setObjectName("horizontalLayout_2")
22 | self.verticalLayout_2 = QtWidgets.QVBoxLayout()
23 | self.verticalLayout_2.setSpacing(6)
24 | self.verticalLayout_2.setObjectName("verticalLayout_2")
25 | self.verticalLayout = QtWidgets.QVBoxLayout()
26 | self.verticalLayout.setObjectName("verticalLayout")
27 | self.horizontalLayout = QtWidgets.QHBoxLayout()
28 | self.horizontalLayout.setObjectName("horizontalLayout")
29 | self.label = QtWidgets.QLabel(Form)
30 | self.label.setText("")
31 | self.label.setObjectName("label")
32 | self.horizontalLayout.addWidget(self.label)
33 | self.lineEdit = QtWidgets.QLineEdit(Form)
34 | self.lineEdit.setMinimumSize(QtCore.QSize(200, 30))
35 | self.lineEdit.setMaximumSize(QtCore.QSize(200, 16777215))
36 | self.lineEdit.setStyleSheet("background:transparent;\n"
37 | "border-radius:5px;\n"
38 | "border-top: 0px solid #b2e281;\n"
39 | "border-bottom: 0px solid #b2e281;\n"
40 | "border-right: 0px solid #b2e281;\n"
41 | "border-left: 0px solid #b2e281;\n"
42 | "border-style:outset;\n"
43 | "background-color:rgb(226,226,226);\n"
44 | " ")
45 | self.lineEdit.setCursorMoveStyle(QtCore.Qt.VisualMoveStyle)
46 | self.lineEdit.setObjectName("lineEdit")
47 | self.horizontalLayout.addWidget(self.lineEdit)
48 | self.label_2 = QtWidgets.QLabel(Form)
49 | self.label_2.setMinimumSize(QtCore.QSize(30, 0))
50 | self.label_2.setText("")
51 | self.label_2.setObjectName("label_2")
52 | self.horizontalLayout.addWidget(self.label_2)
53 | self.verticalLayout.addLayout(self.horizontalLayout)
54 | self.verticalLayout_2.addLayout(self.verticalLayout)
55 | self.listWidget = QtWidgets.QListWidget(Form)
56 | self.listWidget.setMinimumSize(QtCore.QSize(250, 0))
57 | self.listWidget.setMaximumSize(QtCore.QSize(250, 16777215))
58 | self.listWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
59 | self.listWidget.setObjectName("listWidget")
60 | self.verticalLayout_2.addWidget(self.listWidget)
61 | self.verticalLayout_2.setStretch(1, 1)
62 | self.horizontalLayout_2.addLayout(self.verticalLayout_2)
63 | self.stackedWidget = QtWidgets.QStackedWidget(Form)
64 | self.stackedWidget.setObjectName("stackedWidget")
65 | self.horizontalLayout_2.addWidget(self.stackedWidget)
66 | self.horizontalLayout_2.setStretch(1, 1)
67 |
68 | self.retranslateUi(Form)
69 | self.stackedWidget.setCurrentIndex(-1)
70 | QtCore.QMetaObject.connectSlotsByName(Form)
71 |
72 | def retranslateUi(self, Form):
73 | _translate = QtCore.QCoreApplication.translate
74 | Form.setWindowTitle(_translate("Form", "Form"))
75 |
--------------------------------------------------------------------------------
/app/ui_pc/contact/contactUi.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'contactUi.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.7
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtWidgets
12 |
13 |
14 | class Ui_Form(object):
15 | def setupUi(self, Form):
16 | Form.setObjectName("Form")
17 | Form.resize(840, 752)
18 | Form.setStyleSheet("background: rgb(240, 240, 240);")
19 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout(Form)
20 | self.horizontalLayout_2.setObjectName("horizontalLayout_2")
21 | self.verticalLayout_2 = QtWidgets.QVBoxLayout()
22 | self.verticalLayout_2.setSpacing(6)
23 | self.verticalLayout_2.setObjectName("verticalLayout_2")
24 | self.verticalLayout = QtWidgets.QVBoxLayout()
25 | self.verticalLayout.setObjectName("verticalLayout")
26 | self.horizontalLayout = QtWidgets.QHBoxLayout()
27 | self.horizontalLayout.setObjectName("horizontalLayout")
28 | self.label = QtWidgets.QLabel(Form)
29 | self.label.setText("")
30 | self.label.setObjectName("label")
31 | self.horizontalLayout.addWidget(self.label)
32 | self.lineEdit = QtWidgets.QLineEdit(Form)
33 | self.lineEdit.setMinimumSize(QtCore.QSize(200, 30))
34 | self.lineEdit.setMaximumSize(QtCore.QSize(200, 16777215))
35 | self.lineEdit.setStyleSheet("background:transparent;\n"
36 | "border-radius:5px;\n"
37 | "border-top: 0px solid #b2e281;\n"
38 | "border-bottom: 0px solid #b2e281;\n"
39 | "border-right: 0px solid #b2e281;\n"
40 | "border-left: 0px solid #b2e281;\n"
41 | "border-style:outset;\n"
42 | "background-color:rgb(226,226,226);\n"
43 | " ")
44 | self.lineEdit.setCursorMoveStyle(QtCore.Qt.VisualMoveStyle)
45 | self.lineEdit.setObjectName("lineEdit")
46 | self.horizontalLayout.addWidget(self.lineEdit)
47 | self.label_2 = QtWidgets.QLabel(Form)
48 | self.label_2.setMinimumSize(QtCore.QSize(30, 0))
49 | self.label_2.setMaximumSize(QtCore.QSize(30, 16777215))
50 | self.label_2.setText("")
51 | self.label_2.setObjectName("label_2")
52 | self.horizontalLayout.addWidget(self.label_2)
53 | self.verticalLayout.addLayout(self.horizontalLayout)
54 | self.verticalLayout_2.addLayout(self.verticalLayout)
55 | self.listWidget = QtWidgets.QListWidget(Form)
56 | self.listWidget.setMinimumSize(QtCore.QSize(250, 0))
57 | self.listWidget.setMaximumSize(QtCore.QSize(250, 16777215))
58 | self.listWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
59 | self.listWidget.setObjectName("listWidget")
60 | self.verticalLayout_2.addWidget(self.listWidget)
61 | self.verticalLayout_2.setStretch(1, 1)
62 | self.horizontalLayout_2.addLayout(self.verticalLayout_2)
63 | self.stackedWidget = QtWidgets.QStackedWidget(Form)
64 | self.stackedWidget.setObjectName("stackedWidget")
65 | self.horizontalLayout_2.addWidget(self.stackedWidget)
66 | self.horizontalLayout_2.setStretch(1, 1)
67 |
68 | self.retranslateUi(Form)
69 | self.stackedWidget.setCurrentIndex(-1)
70 | QtCore.QMetaObject.connectSlotsByName(Form)
71 |
72 | def retranslateUi(self, Form):
73 | _translate = QtCore.QCoreApplication.translate
74 | Form.setWindowTitle(_translate("Form", "Form"))
75 |
--------------------------------------------------------------------------------
/app/Ui/userinfo/userinfoUi.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Dialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 1120
10 | 720
11 |
12 |
13 |
14 | ArrowCursor
15 |
16 |
17 | Dialog
18 |
19 |
20 | false
21 |
22 |
23 |
24 |
25 | 0
26 | 0
27 | 1120
28 | 720
29 |
30 |
31 |
32 | QFrame::StyledPanel
33 |
34 |
35 | QFrame::Raised
36 |
37 |
38 |
39 |
40 | 340
41 | 60
42 | 291
43 | 82
44 |
45 |
46 |
47 | -
48 |
49 |
50 |
51 | 80
52 | 80
53 |
54 |
55 |
56 | TextLabel
57 |
58 |
59 |
60 | -
61 |
62 |
-
63 |
64 |
65 | TextLabel
66 |
67 |
68 |
69 | -
70 |
71 |
72 | TextLabel
73 |
74 |
75 |
76 | -
77 |
78 |
79 | TextLabel
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/app/ui_pc/tool/toolUI.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'toolUI.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.7
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtGui, QtWidgets
12 |
13 |
14 | class Ui_Dialog(object):
15 | def setupUi(self, Dialog):
16 | Dialog.setObjectName("Dialog")
17 | Dialog.resize(630, 547)
18 | font = QtGui.QFont()
19 | font.setFamily("微软雅黑")
20 | Dialog.setFont(font)
21 | self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
22 | self.verticalLayout.setObjectName("verticalLayout")
23 | self.horizontalLayout = QtWidgets.QHBoxLayout()
24 | self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
25 | self.horizontalLayout.setSpacing(0)
26 | self.horizontalLayout.setObjectName("horizontalLayout")
27 | self.label = QtWidgets.QLabel(Dialog)
28 | self.label.setMaximumSize(QtCore.QSize(80, 80))
29 | self.label.setText("")
30 | self.label.setObjectName("label")
31 | self.horizontalLayout.addWidget(self.label)
32 | self.listWidget = QtWidgets.QListWidget(Dialog)
33 | self.listWidget.setMinimumSize(QtCore.QSize(500, 80))
34 | self.listWidget.setMaximumSize(QtCore.QSize(500, 80))
35 | self.listWidget.setFrameShape(QtWidgets.QFrame.NoFrame)
36 | self.listWidget.setFrameShadow(QtWidgets.QFrame.Plain)
37 | self.listWidget.setLineWidth(0)
38 | self.listWidget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
39 | self.listWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
40 | self.listWidget.setFlow(QtWidgets.QListView.LeftToRight)
41 | self.listWidget.setObjectName("listWidget")
42 | item = QtWidgets.QListWidgetItem()
43 | self.listWidget.addItem(item)
44 | item = QtWidgets.QListWidgetItem()
45 | self.listWidget.addItem(item)
46 | item = QtWidgets.QListWidgetItem()
47 | self.listWidget.addItem(item)
48 | item = QtWidgets.QListWidgetItem()
49 | self.listWidget.addItem(item)
50 | item = QtWidgets.QListWidgetItem()
51 | self.listWidget.addItem(item)
52 | self.horizontalLayout.addWidget(self.listWidget)
53 | self.label_2 = QtWidgets.QLabel(Dialog)
54 | self.label_2.setMaximumSize(QtCore.QSize(80, 80))
55 | self.label_2.setText("")
56 | self.label_2.setObjectName("label_2")
57 | self.horizontalLayout.addWidget(self.label_2)
58 | self.verticalLayout.addLayout(self.horizontalLayout)
59 | self.stackedWidget = QtWidgets.QStackedWidget(Dialog)
60 | self.stackedWidget.setObjectName("stackedWidget")
61 | self.verticalLayout.addWidget(self.stackedWidget)
62 | self.verticalLayout.setStretch(1, 1)
63 |
64 | self.retranslateUi(Dialog)
65 | self.stackedWidget.setCurrentIndex(-1)
66 | QtCore.QMetaObject.connectSlotsByName(Dialog)
67 |
68 | def retranslateUi(self, Dialog):
69 | _translate = QtCore.QCoreApplication.translate
70 | Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
71 | __sortingEnabled = self.listWidget.isSortingEnabled()
72 | self.listWidget.setSortingEnabled(False)
73 | item = self.listWidget.item(0)
74 | item.setText(_translate("Dialog", "新建项目"))
75 | item = self.listWidget.item(1)
76 | item.setText(_translate("Dialog", "新建项目"))
77 | item = self.listWidget.item(2)
78 | item.setText(_translate("Dialog", "新建项目"))
79 | item = self.listWidget.item(3)
80 | item.setText(_translate("Dialog", "新建项目"))
81 | item = self.listWidget.item(4)
82 | item.setText(_translate("Dialog", "新建项目"))
83 | self.listWidget.setSortingEnabled(__sortingEnabled)
84 |
--------------------------------------------------------------------------------
/app/Ui/contact/contactUi.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'contactUi.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.7
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtGui, QtWidgets
12 |
13 |
14 | class Ui_Dialog(object):
15 | def setupUi(self, Dialog):
16 | Dialog.setObjectName("Dialog")
17 | Dialog.resize(1141, 740)
18 | Dialog.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
19 | Dialog.setAutoFillBackground(False)
20 | self.horizontalLayout = QtWidgets.QHBoxLayout(Dialog)
21 | self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
22 | self.horizontalLayout.setSpacing(0)
23 | self.horizontalLayout.setObjectName("horizontalLayout")
24 | self.frame_2 = QtWidgets.QFrame(Dialog)
25 | self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel)
26 | self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised)
27 | self.frame_2.setObjectName("frame_2")
28 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.frame_2)
29 | self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
30 | self.horizontalLayout_2.setSpacing(0)
31 | self.horizontalLayout_2.setObjectName("horizontalLayout_2")
32 | self.scrollArea = QtWidgets.QScrollArea(self.frame_2)
33 | self.scrollArea.setEnabled(True)
34 | self.scrollArea.setMinimumSize(QtCore.QSize(325, 0))
35 | self.scrollArea.setMaximumSize(QtCore.QSize(325, 150000))
36 | self.scrollArea.setAutoFillBackground(False)
37 | self.scrollArea.setFrameShape(QtWidgets.QFrame.NoFrame)
38 | self.scrollArea.setFrameShadow(QtWidgets.QFrame.Raised)
39 | self.scrollArea.setMidLineWidth(0)
40 | self.scrollArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
41 | self.scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
42 | self.scrollArea.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContentsOnFirstShow)
43 | self.scrollArea.setWidgetResizable(False)
44 | self.scrollArea.setObjectName("scrollArea")
45 | self.scrollAreaWidgetContents = QtWidgets.QWidget()
46 | self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 300, 12000))
47 | self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
48 | self.pushButton_2 = QtWidgets.QPushButton(self.scrollAreaWidgetContents)
49 | self.pushButton_2.setGeometry(QtCore.QRect(0, 0, 300, 80))
50 | self.pushButton_2.setLayoutDirection(QtCore.Qt.LeftToRight)
51 | self.pushButton_2.setAutoFillBackground(False)
52 | self.pushButton_2.setText("")
53 | self.pushButton_2.setIconSize(QtCore.QSize(80, 80))
54 | self.pushButton_2.setObjectName("pushButton_2")
55 | self.label = QtWidgets.QLabel(self.scrollAreaWidgetContents)
56 | self.label.setGeometry(QtCore.QRect(220, 10, 72, 15))
57 | self.label.setObjectName("label")
58 | self.scrollArea.setWidget(self.scrollAreaWidgetContents)
59 | self.horizontalLayout_2.addWidget(self.scrollArea)
60 | self.stackedWidget = QtWidgets.QStackedWidget(self.frame_2)
61 | self.stackedWidget.setObjectName("stackedWidget")
62 | self.page = QtWidgets.QWidget()
63 | self.page.setObjectName("page")
64 | self.stackedWidget.addWidget(self.page)
65 | self.page_2 = QtWidgets.QWidget()
66 | self.page_2.setObjectName("page_2")
67 | self.stackedWidget.addWidget(self.page_2)
68 | self.horizontalLayout_2.addWidget(self.stackedWidget)
69 | self.horizontalLayout.addWidget(self.frame_2)
70 |
71 | self.retranslateUi(Dialog)
72 | QtCore.QMetaObject.connectSlotsByName(Dialog)
73 |
74 | def retranslateUi(self, Dialog):
75 | _translate = QtCore.QCoreApplication.translate
76 | Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
77 | self.label.setText(_translate("Dialog", "TextLabel"))
78 |
--------------------------------------------------------------------------------
/app/Ui/contact/contact.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | @File : contact.py
4 | @Author : Shuaikang Zhou
5 | @Time : 2022/12/13 15:07
6 | @IDE : Pycharm
7 | @Version : Python3.10
8 | @comment : ···
9 | """
10 | from typing import Dict
11 |
12 | from PyQt5 import QtCore
13 | from PyQt5.QtCore import *
14 | from PyQt5.QtWidgets import *
15 |
16 | import app.components.Button_Contact as MyLabel
17 | from app import person
18 | from app.DataBase import data
19 | from app.Ui.contact.contactInfo import ContactInfo
20 | from app.Ui.contact.contactUi import Ui_Dialog
21 |
22 | EMOTION = 1
23 | ANALYSIS = 2
24 |
25 |
26 | class StackedWidget():
27 | def __init__(self):
28 | pass
29 |
30 |
31 | class ContactController(QWidget, Ui_Dialog):
32 | exitSignal = pyqtSignal()
33 | urlSignal = pyqtSignal(QUrl)
34 |
35 | # username = ''
36 |
37 | def __init__(self, Me: person.Me, parent=None):
38 | super(ContactController, self).__init__(parent)
39 | self.chatroomFlag = None
40 | self.ta_avatar = None
41 | self.setupUi(self)
42 | self.Me = Me
43 | self.contacts: Dict[str, MyLabel.ContactUi] = {}
44 | self.contactInfo: Dict[str, ContactInfo] = {}
45 | self.show_flag = False
46 | self.last_talkerId = None
47 | self.now_talkerId = None
48 | # self.showContact()
49 | self.show_thread = ShowContactThread()
50 | self.show_thread.showSingal.connect(self.showContact)
51 | self.show_thread.heightSingal.connect(self.setScreenAreaHeight)
52 | self.show_thread.start()
53 |
54 | def showContact(self, data_):
55 | """
56 | data:Tuple[rconversation,index:int]
57 | 显示联系人
58 | :return:
59 | """
60 | rconversation, i = data_
61 | username = rconversation[1]
62 | # print(username)
63 | pushButton_2 = MyLabel.ContactUi(self.scrollAreaWidgetContents, i, rconversation)
64 | pushButton_2.setGeometry(QtCore.QRect(0, 80 * i, 300, 80))
65 | pushButton_2.setLayoutDirection(QtCore.Qt.LeftToRight)
66 | pushButton_2.clicked.connect(pushButton_2.show_msg)
67 | pushButton_2.usernameSingal.connect(self.Contact)
68 | self.contacts[username] = pushButton_2
69 | self.contactInfo[username] = ContactInfo(username, self.Me)
70 | self.stackedWidget.addWidget(self.contactInfo[username])
71 |
72 | def setScreenAreaHeight(self, height: int):
73 | self.scrollAreaWidgetContents.setGeometry(
74 | QtCore.QRect(0, 0, 300, height))
75 |
76 | def Contact(self, talkerId):
77 | """
78 | 聊天界面 点击联系人头像时候显示聊天数据
79 | :param talkerId:
80 | :return:
81 | """
82 | self.now_talkerId = talkerId
83 | # 把当前按钮设置为灰色
84 | if self.last_talkerId and self.last_talkerId != talkerId:
85 | print('对方账号:', self.last_talkerId)
86 | self.contacts[self.last_talkerId].setStyleSheet(
87 | "QPushButton {background-color: rgb(220,220,220);}"
88 | "QPushButton:hover{background-color: rgb(208,208,208);}\n"
89 | )
90 | self.last_talkerId = talkerId
91 | self.contacts[talkerId].setStyleSheet(
92 | "QPushButton {background-color: rgb(198,198,198);}"
93 | "QPushButton:hover{background-color: rgb(209,209,209);}\n"
94 | )
95 | self.stackedWidget.setCurrentWidget(self.contactInfo[talkerId])
96 |
97 | if '@chatroom' in talkerId:
98 | self.chatroomFlag = True
99 | else:
100 | self.chatroomFlag = False
101 |
102 |
103 | class ShowContactThread(QThread):
104 | showSingal = pyqtSignal(tuple)
105 | heightSingal = pyqtSignal(int)
106 |
107 | def __init__(self):
108 | super().__init__()
109 |
110 | def run(self) -> None:
111 | rconversations = data.get_rconversation()
112 | max_height = max(len(rconversations) * 80, 680)
113 | # 设置滚动区域的高度
114 | self.heightSingal.emit(max_height)
115 | for i in range(len(rconversations)):
116 | self.showSingal.emit((rconversations[i], i))
117 |
--------------------------------------------------------------------------------
/app/ui_pc/chat/chatInfoUi.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Form
4 |
5 |
6 |
7 | 0
8 | 0
9 | 817
10 | 748
11 |
12 |
13 |
14 | Form
15 |
16 |
17 |
18 | 0
19 |
20 |
21 | 0
22 |
23 |
24 | 0
25 |
26 |
27 | 0
28 |
29 |
30 | 0
31 |
32 | -
33 |
34 |
35 | QFrame::NoFrame
36 |
37 |
38 | QFrame::Raised
39 |
40 |
41 |
-
42 |
43 |
-
44 |
45 |
46 | TextLabel
47 |
48 |
49 |
50 | -
51 |
52 |
53 | Qt::Horizontal
54 |
55 |
56 |
57 | 40
58 | 20
59 |
60 |
61 |
62 |
63 | -
64 |
65 |
66 | ...
67 |
68 |
69 |
70 |
71 |
72 | -
73 |
74 |
75 | true
76 |
77 |
78 |
79 |
80 | 0
81 | 0
82 | 797
83 | 700
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/resource/datasets/countries_regions_db.json:
--------------------------------------------------------------------------------
1 | {
2 | "蒲隆地": "BI",
3 | "圣卢西亚": "LC",
4 | "巴巴多斯": "BB",
5 | "安道尔": "AD",
6 | "利比里亚": "LR",
7 | "吉尔吉斯斯坦": "KG",
8 | "阿曼": "OM",
9 | "利比亚": "LY",
10 | "根西": "GG",
11 | "南非": "ZA",
12 | "阿塞拜疆": "AZ",
13 | "巴拉圭": "PY",
14 | "匈牙利": "HU",
15 | "巴勒斯坦": "PS",
16 | "泰国": "TH",
17 | "黎巴嫩": "LB",
18 | "英属维尔京群岛": "VG",
19 | "特立尼达和多巴哥": "TT",
20 | "巴基斯坦": "PK",
21 | "加拿大": "CA",
22 | "保加利亚": "BG",
23 | "马来西亚": "MY",
24 | "叙利亚": "SY",
25 | "毛里塔尼亚": "MR",
26 | "白俄罗斯": "BY",
27 | "秘鲁": "PE",
28 | "摩洛哥": "MA",
29 | "莱索托": "LS",
30 | "法罗群岛": "FO",
31 | "圣基茨和尼维斯": "KN",
32 | "阿根廷": "AR",
33 | "委内瑞拉": "VE",
34 | "塞舌尔": "SC",
35 | "英属印度洋领地": "IO",
36 | "瓦努阿图": "VU",
37 | "纳米比亚": "NA",
38 | "马拉维": "MW",
39 | "克罗地亚": "HR",
40 | "玻利维亚": "BO",
41 | "塞内加尔": "SN",
42 | "希腊": "GR",
43 | "蒙古": "MN",
44 | "东帝汶": "TL",
45 | "新加坡": "SG",
46 | "意大利": "IT",
47 | "皮特凱恩群島": "PN",
48 | "芬兰": "FI",
49 | "圭亚那": "GY",
50 | "福克兰群岛": "FK",
51 | "毛里求斯": "MU",
52 | "马其顿": "MK",
53 | "新西兰": "NZ",
54 | "波斯尼亚-黑塞哥维那": "BA",
55 | "吐瓦鲁": "TV",
56 | "以色列": "IL",
57 | "塞尔维亚": "RS",
58 | "密克罗尼西亚联邦": "FM",
59 | "斯洛文尼亚": "SI",
60 | "阿富汗": "AF",
61 | "科威特": "KW",
62 | "英国": "GB",
63 | "汤加": "TO",
64 | "贝宁": "BJ",
65 | "撒拉威阿拉伯民主共和国": "",
66 | "海地": "HT",
67 | "格鲁吉亚": "GE",
68 | "安提瓜和巴布达": "AG",
69 | "刚果金": "CD",
70 | "澳大利亚": "AU",
71 | "史瓦济兰": "SZ",
72 | "马达加斯加": "MG",
73 | "哥伦比亚": "CO",
74 | "多米尼加共和国": "DO",
75 | "爱沙尼亚": "EE",
76 | "哥斯达黎加": "CR",
77 | "塞浦路斯": "CY",
78 | "沙特阿拉伯": "SA",
79 | "南苏丹": "SS",
80 | "荷兰": "NL",
81 | "圣马力诺": "SM",
82 | "罗马尼亚": "RO",
83 | "智利": "CL",
84 | "几内亚比索": "GW",
85 | "尼加拉瓜": "NI",
86 | "特克斯和凯科斯群岛": "TC",
87 | "珊瑚海群岛领地": "",
88 | "泽西": "JE",
89 | "奥地利": "AT",
90 | "蒙特塞拉特": "MS",
91 | "纽埃": "NU",
92 | "圣文森特和格林纳丁斯": "VC",
93 | "伯利兹": "BZ",
94 | "斯洛伐克": "SK",
95 | "哈萨克斯坦": "KZ",
96 | "直布罗陀": "GI",
97 | "柬埔寨": "KH",
98 | "立陶宛": "LT",
99 | "中非共和国": "CF",
100 | "瑞典": "SE",
101 | "爱尔兰": "IE",
102 | "赤道几内亚": "GQ",
103 | "亚美尼亚": "AM",
104 | "捷克": "CZ",
105 | "冈比亚": "GM",
106 | "格陵兰": "GL",
107 | "科索沃": "",
108 | "印度": "IN",
109 | "多哥": "TG",
110 | "挪威": "NO",
111 | "土耳其": "TR",
112 | "拉脱维亚": "LV",
113 | "佛得角": "CV",
114 | "乌兹别克斯坦": "UZ",
115 | "肯尼亚": "KE",
116 | "博茨瓦纳": "BW",
117 | "加蓬": "GA",
118 | "塔吉克斯坦": "TJ",
119 | "尼日尔": "NE",
120 | "布基纳法索": "BF",
121 | "安圭拉": "AI",
122 | "老挝": "LA",
123 | "阿尔巴尼亚": "AL",
124 | "厄瓜多尔": "EC",
125 | "葡萄牙": "PT",
126 | "乍得": "TD",
127 | "南乔治亚和南桑威奇群岛": "GS",
128 | "几内亚": "GN",
129 | "冰岛": "IS",
130 | "托克劳": "TK",
131 | "摩纳哥": "MC",
132 | "埃及": "EG",
133 | "马绍尔群岛": "MH",
134 | "巴拿马": "PA",
135 | "巴林": "BH",
136 | "苏里南": "SR",
137 | "帕劳": "PW",
138 | "尼日利亚": "NG",
139 | "加纳": "GH",
140 | "牙买加": "JM",
141 | "巴哈马": "BS",
142 | "波兰": "PL",
143 | "土库曼": "TM",
144 | "古巴": "CU",
145 | "吉布提": "DJ",
146 | "马恩岛": "IM",
147 | "巴布亚新几内亚": "PG",
148 | "文莱": "BN",
149 | "摩尔多瓦": "MD",
150 | "开曼群岛": "KY",
151 | "安哥拉": "AO",
152 | "乌克兰": "UA",
153 | "喀麦隆": "CM",
154 | "萨尔瓦多": "SV",
155 | "萨摩亚": "WS",
156 | "黑山": "ME",
157 | "孟加拉国": "BD",
158 | "韩国": "KR",
159 | "苏丹": "SD",
160 | "厄立特里亚": "ER",
161 | "库克群岛": "CK",
162 | "马里": "ML",
163 | "伊朗": "IR",
164 | "伊拉克": "IQ",
165 | "日本": "JP",
166 | "突尼斯": "TN",
167 | "津巴布韦": "ZW",
168 | "菲律宾": "PH",
169 | "约旦": "JO",
170 | "埃塞俄比亚": "ET",
171 | "德国": "DE",
172 | "巴西": "BR",
173 | "卢旺达": "RW",
174 | "洪都拉斯": "HN",
175 | "梵蒂冈": "VA",
176 | "基里巴斯": "KI",
177 | "阿拉伯联合酋长国": "AE",
178 | "墨西哥": "MX",
179 | "塞拉利昂": "SL",
180 | "所罗门群岛": "SB",
181 | "斯里兰卡": "LK",
182 | "俄罗斯": "RU",
183 | "法国": "FR",
184 | "危地马拉": "GT",
185 | "越南": "VN",
186 | "坦桑尼亚": "TZ",
187 | "比利时": "BE",
188 | "丹麦": "DK",
189 | "索马里": "SO",
190 | "美国": "US",
191 | "马尔代夫": "MV",
192 | "列支敦士登": "LI",
193 | "科摩罗": "KM",
194 | "百慕达群岛": "BM",
195 | "不丹": "BT",
196 | "朝鲜": "KP",
197 | "莫桑比克": "MZ",
198 | "印度尼西亚, 印尼": "ID",
199 | "也门": "YE",
200 | "刚果-布拉柴维尔": "CG",
201 | "乌拉圭": "UY",
202 | "圣赫勒拿-阿森松和特里斯坦-达库尼亚": "SH",
203 | "尼泊尔": "NP",
204 | "多米尼克": "DM",
205 | "马耳他": "MT",
206 | "阿尔及利亚": "DZ",
207 | "卡塔尔": "QA",
208 | "赞比亚": "ZM",
209 | "缅甸": "MM",
210 | "卢森堡": "LU",
211 | "斐济": "FJ",
212 | "西班牙": "ES",
213 | "乌干达": "UG",
214 | "中国": "CN",
215 | "中国香港": "HK",
216 | "中国台湾": "TW",
217 | "china": "CN"
218 | }
219 |
--------------------------------------------------------------------------------
/app/resources/icons/annual_report1.svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/resource/render/engine.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | try:
4 | from collections.abc import Iterable
5 | except ImportError:
6 | from collections import Iterable
7 |
8 | from jinja2 import Environment
9 |
10 | from ..commons import utils
11 | from ..datasets import EXTRA, FILENAMES
12 | from ..globals import CurrentConfig, NotebookType
13 | from ..types import Any, Optional
14 | from .display import HTML, Javascript
15 |
16 |
17 | def write_utf8_html_file(file_name: str, html_content: str):
18 | with open(file_name, "w+", encoding="utf-8") as html_file:
19 | html_file.write(html_content)
20 |
21 |
22 | class RenderEngine:
23 | def __init__(self, env: Optional[Environment] = None):
24 | self.env = env or CurrentConfig.GLOBAL_ENV
25 |
26 | @staticmethod
27 | def generate_js_link(chart: Any) -> Any:
28 | if not chart.js_host:
29 | chart.js_host = CurrentConfig.ONLINE_HOST
30 | links = []
31 | for dep in chart.js_dependencies.items:
32 | # TODO: if?
33 | if dep.startswith("https://api.map.baidu.com"):
34 | links.append(dep)
35 | if dep in FILENAMES:
36 | f, ext = FILENAMES[dep]
37 | links.append("{}{}.{}".format(chart.js_host, f, ext))
38 | else:
39 | for url, files in EXTRA.items():
40 | if dep in files:
41 | f, ext = files[dep]
42 | links.append("{}{}.{}".format(url, f, ext))
43 | break
44 | chart.dependencies = links
45 | return chart
46 |
47 | def render_chart_to_file(self, template_name: str, chart: Any, path: str, **kwargs):
48 | """
49 | Render a chart or page to local html files.
50 |
51 | :param chart: A Chart or Page object
52 | :param path: The destination file which the html code write to
53 | :param template_name: The name of template file.
54 | """
55 | tpl = self.env.get_template(template_name)
56 | html = utils.replace_placeholder(
57 | tpl.render(chart=self.generate_js_link(chart), **kwargs)
58 | )
59 | write_utf8_html_file(path, html)
60 |
61 | def render_chart_to_template(self, template_name: str, chart: Any, **kwargs) -> str:
62 | tpl = self.env.get_template(template_name)
63 | return utils.replace_placeholder(
64 | tpl.render(chart=self.generate_js_link(chart), **kwargs)
65 | )
66 |
67 | def render_chart_to_notebook(self, template_name: str, **kwargs) -> str:
68 | tpl = self.env.get_template(template_name)
69 | return utils.replace_placeholder(tpl.render(**kwargs))
70 |
71 |
72 | def render(
73 | chart, path: str, template_name: str, env: Optional[Environment], **kwargs
74 | ) -> str:
75 | RenderEngine(env).render_chart_to_file(
76 | template_name=template_name, chart=chart, path=path, **kwargs
77 | )
78 | return os.path.abspath(path)
79 |
80 |
81 | def render_embed(
82 | chart, template_name: str, env: Optional[Environment], **kwargs
83 | ) -> str:
84 | return RenderEngine(env).render_chart_to_template(
85 | template_name=template_name, chart=chart, **kwargs
86 | )
87 |
88 |
89 | def render_notebook(self, notebook_template, lab_template):
90 | instance = self if isinstance(self, Iterable) else (self,)
91 | if CurrentConfig.NOTEBOOK_TYPE == NotebookType.JUPYTER_NOTEBOOK:
92 | require_config = utils.produce_require_dict(self.js_dependencies, self.js_host)
93 | return HTML(
94 | RenderEngine().render_chart_to_notebook(
95 | template_name=notebook_template,
96 | charts=instance,
97 | config_items=require_config["config_items"],
98 | libraries=require_config["libraries"],
99 | )
100 | )
101 |
102 | if CurrentConfig.NOTEBOOK_TYPE == NotebookType.JUPYTER_LAB:
103 | return HTML(
104 | RenderEngine().render_chart_to_notebook(
105 | template_name=lab_template, charts=instance
106 | )
107 | )
108 |
109 | if CurrentConfig.NOTEBOOK_TYPE == NotebookType.NTERACT:
110 | return HTML(self.render_embed())
111 |
112 | if CurrentConfig.NOTEBOOK_TYPE == NotebookType.ZEPPELIN:
113 | print("%html " + self.render_embed())
114 |
115 |
116 | def load_javascript(chart):
117 | scripts = []
118 | for dep in chart.js_dependencies.items:
119 | f, ext = FILENAMES[dep]
120 | scripts.append("{}{}.{}".format(CurrentConfig.ONLINE_HOST, f, ext))
121 | return Javascript(lib=scripts)
122 |
--------------------------------------------------------------------------------
/app/Ui/contact/contactInfoUi.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'contactInfoUi.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.7
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtGui, QtWidgets
12 |
13 |
14 | class Ui_Form(object):
15 | def setupUi(self, Form):
16 | Form.setObjectName("Form")
17 | Form.resize(817, 748)
18 | self.horizontalLayout = QtWidgets.QHBoxLayout(Form)
19 | self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
20 | self.horizontalLayout.setSpacing(0)
21 | self.horizontalLayout.setObjectName("horizontalLayout")
22 | self.frame = QtWidgets.QFrame(Form)
23 | self.frame.setFrameShape(QtWidgets.QFrame.NoFrame)
24 | self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
25 | self.frame.setObjectName("frame")
26 | self.verticalLayout = QtWidgets.QVBoxLayout(self.frame)
27 | self.verticalLayout.setContentsMargins(0, 0, 0, 0)
28 | self.verticalLayout.setSpacing(0)
29 | self.verticalLayout.setObjectName("verticalLayout")
30 | self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
31 | self.horizontalLayout_3.setSpacing(0)
32 | self.horizontalLayout_3.setObjectName("horizontalLayout_3")
33 | self.label_remark = QtWidgets.QLabel(self.frame)
34 | self.label_remark.setMaximumSize(QtCore.QSize(16777215, 100))
35 | font = QtGui.QFont()
36 | font.setPointSize(12)
37 | self.label_remark.setFont(font)
38 | self.label_remark.setText("")
39 | self.label_remark.setObjectName("label_remark")
40 | self.horizontalLayout_3.addWidget(self.label_remark)
41 | self.btn_analysis = QtWidgets.QPushButton(self.frame)
42 | self.btn_analysis.setStyleSheet("")
43 | self.btn_analysis.setFlat(True)
44 | self.btn_analysis.setObjectName("btn_analysis")
45 | self.horizontalLayout_3.addWidget(self.btn_analysis)
46 | self.btn_emotion = QtWidgets.QPushButton(self.frame)
47 | self.btn_emotion.setFlat(True)
48 | self.btn_emotion.setObjectName("btn_emotion")
49 | self.horizontalLayout_3.addWidget(self.btn_emotion)
50 | self.btn_report = QtWidgets.QPushButton(self.frame)
51 | self.btn_report.setFlat(True)
52 | self.btn_report.setObjectName("btn_report")
53 | self.horizontalLayout_3.addWidget(self.btn_report)
54 | self.btn_back = QtWidgets.QPushButton(self.frame)
55 | self.btn_back.setFlat(True)
56 | self.btn_back.setObjectName("btn_back")
57 | self.horizontalLayout_3.addWidget(self.btn_back)
58 | self.toolButton_output = QtWidgets.QToolButton(self.frame)
59 | icon = QtGui.QIcon()
60 | icon.addPixmap(QtGui.QPixmap("../../data/icons/output.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
61 | self.toolButton_output.setIcon(icon)
62 | self.toolButton_output.setCheckable(False)
63 | self.toolButton_output.setPopupMode(QtWidgets.QToolButton.MenuButtonPopup)
64 | self.toolButton_output.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
65 | self.toolButton_output.setAutoRaise(True)
66 | self.toolButton_output.setArrowType(QtCore.Qt.NoArrow)
67 | self.toolButton_output.setObjectName("toolButton_output")
68 | self.horizontalLayout_3.addWidget(self.toolButton_output)
69 | self.verticalLayout.addLayout(self.horizontalLayout_3)
70 | self.stackedWidget = QtWidgets.QStackedWidget(self.frame)
71 | self.stackedWidget.setObjectName("stackedWidget")
72 | self.page_3 = QtWidgets.QWidget()
73 | self.page_3.setObjectName("page_3")
74 | self.stackedWidget.addWidget(self.page_3)
75 | self.page_4 = QtWidgets.QWidget()
76 | self.page_4.setObjectName("page_4")
77 | self.stackedWidget.addWidget(self.page_4)
78 | self.verticalLayout.addWidget(self.stackedWidget)
79 | self.horizontalLayout.addWidget(self.frame)
80 |
81 | self.retranslateUi(Form)
82 | self.stackedWidget.setCurrentIndex(1)
83 | QtCore.QMetaObject.connectSlotsByName(Form)
84 |
85 | def retranslateUi(self, Form):
86 | _translate = QtCore.QCoreApplication.translate
87 | Form.setWindowTitle(_translate("Form", "Form"))
88 | self.btn_analysis.setText(_translate("Form", "统计信息"))
89 | self.btn_emotion.setText(_translate("Form", "情感分析"))
90 | self.btn_report.setText(_translate("Form", "年度报告"))
91 | self.btn_back.setText(_translate("Form", "退出"))
92 | self.toolButton_output.setText(_translate("Form", "导出聊天记录"))
93 |
--------------------------------------------------------------------------------
/app/ui_pc/contact/contactInfoUi.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'contactInfoUi.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.7
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtGui, QtWidgets
12 |
13 |
14 | class Ui_Form(object):
15 | def setupUi(self, Form):
16 | Form.setObjectName("Form")
17 | Form.resize(817, 748)
18 | self.horizontalLayout = QtWidgets.QHBoxLayout(Form)
19 | self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
20 | self.horizontalLayout.setSpacing(0)
21 | self.horizontalLayout.setObjectName("horizontalLayout")
22 | self.frame = QtWidgets.QFrame(Form)
23 | self.frame.setFrameShape(QtWidgets.QFrame.NoFrame)
24 | self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
25 | self.frame.setObjectName("frame")
26 | self.verticalLayout = QtWidgets.QVBoxLayout(self.frame)
27 | self.verticalLayout.setContentsMargins(0, 0, 0, 0)
28 | self.verticalLayout.setSpacing(0)
29 | self.verticalLayout.setObjectName("verticalLayout")
30 | self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
31 | self.horizontalLayout_3.setSpacing(0)
32 | self.horizontalLayout_3.setObjectName("horizontalLayout_3")
33 | self.label_remark = QtWidgets.QLabel(self.frame)
34 | self.label_remark.setMaximumSize(QtCore.QSize(16777215, 100))
35 | font = QtGui.QFont()
36 | font.setPointSize(12)
37 | self.label_remark.setFont(font)
38 | self.label_remark.setText("")
39 | self.label_remark.setObjectName("label_remark")
40 | self.horizontalLayout_3.addWidget(self.label_remark)
41 | self.btn_analysis = QtWidgets.QPushButton(self.frame)
42 | self.btn_analysis.setStyleSheet("")
43 | self.btn_analysis.setFlat(True)
44 | self.btn_analysis.setObjectName("btn_analysis")
45 | self.horizontalLayout_3.addWidget(self.btn_analysis)
46 | self.btn_emotion = QtWidgets.QPushButton(self.frame)
47 | self.btn_emotion.setFlat(True)
48 | self.btn_emotion.setObjectName("btn_emotion")
49 | self.horizontalLayout_3.addWidget(self.btn_emotion)
50 | self.btn_report = QtWidgets.QPushButton(self.frame)
51 | self.btn_report.setFlat(True)
52 | self.btn_report.setObjectName("btn_report")
53 | self.horizontalLayout_3.addWidget(self.btn_report)
54 | self.btn_back = QtWidgets.QPushButton(self.frame)
55 | self.btn_back.setFlat(True)
56 | self.btn_back.setObjectName("btn_back")
57 | self.horizontalLayout_3.addWidget(self.btn_back)
58 | self.toolButton_output = QtWidgets.QToolButton(self.frame)
59 | icon = QtGui.QIcon()
60 | icon.addPixmap(QtGui.QPixmap("../../data/icons/output.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
61 | self.toolButton_output.setIcon(icon)
62 | self.toolButton_output.setCheckable(False)
63 | self.toolButton_output.setPopupMode(QtWidgets.QToolButton.MenuButtonPopup)
64 | self.toolButton_output.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
65 | self.toolButton_output.setAutoRaise(True)
66 | self.toolButton_output.setArrowType(QtCore.Qt.NoArrow)
67 | self.toolButton_output.setObjectName("toolButton_output")
68 | self.horizontalLayout_3.addWidget(self.toolButton_output)
69 | self.verticalLayout.addLayout(self.horizontalLayout_3)
70 | self.stackedWidget = QtWidgets.QStackedWidget(self.frame)
71 | self.stackedWidget.setObjectName("stackedWidget")
72 | self.page_3 = QtWidgets.QWidget()
73 | self.page_3.setObjectName("page_3")
74 | self.stackedWidget.addWidget(self.page_3)
75 | self.page_4 = QtWidgets.QWidget()
76 | self.page_4.setObjectName("page_4")
77 | self.stackedWidget.addWidget(self.page_4)
78 | self.verticalLayout.addWidget(self.stackedWidget)
79 | self.horizontalLayout.addWidget(self.frame)
80 |
81 | self.retranslateUi(Form)
82 | self.stackedWidget.setCurrentIndex(1)
83 | QtCore.QMetaObject.connectSlotsByName(Form)
84 |
85 | def retranslateUi(self, Form):
86 | _translate = QtCore.QCoreApplication.translate
87 | Form.setWindowTitle(_translate("Form", "Form"))
88 | self.btn_analysis.setText(_translate("Form", "统计信息"))
89 | self.btn_emotion.setText(_translate("Form", "情感分析"))
90 | self.btn_report.setText(_translate("Form", "年度报告"))
91 | self.btn_back.setText(_translate("Form", "退出"))
92 | self.toolButton_output.setText(_translate("Form", "导出聊天记录"))
93 |
--------------------------------------------------------------------------------
/app/components/Button_Contact.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 |
3 | from PyQt5 import QtWidgets, QtGui, QtCore
4 | from PyQt5.QtCore import *
5 |
6 | import app.DataBase.data as data
7 | from app import person
8 |
9 |
10 | class ContactUi(QtWidgets.QPushButton):
11 | """
12 | 联系人类,继承自pyqt的按钮,里面封装了联系人头像等标签
13 | """
14 | usernameSingal = pyqtSignal(str)
15 |
16 | def __init__(self, Ui, id=None, rconversation=None):
17 | super(ContactUi, self).__init__(Ui)
18 | self.contact: person.Contact = person.Contact(rconversation[1])
19 | self.init_ui(Ui)
20 | self.msgCount = rconversation[0]
21 | self.username = rconversation[1]
22 | self.conversationTime = rconversation[6]
23 | self.msgType = rconversation[7]
24 | self.digest = rconversation[8]
25 | hasTrunc = rconversation[10]
26 | attrflag = rconversation[11]
27 | if hasTrunc == 0:
28 | if attrflag == 0:
29 | self.digest = '[动画表情]'
30 | elif attrflag == 67108864:
31 | try:
32 | remark = data.get_conRemark(rconversation[9])
33 | msg = self.digest.split(':')[1].strip('\n').strip()
34 | self.digest = f'{remark}:{msg}'
35 | except Exception as e:
36 | pass
37 | else:
38 | pass
39 | self.show_info(id)
40 |
41 | def init_ui(self, Ui):
42 | self.layoutWidget = QtWidgets.QWidget(Ui)
43 | self.layoutWidget.setObjectName("layoutWidget")
44 | self.gridLayout1 = QtWidgets.QGridLayout(self.layoutWidget)
45 | self.gridLayout1.setSizeConstraint(QtWidgets.QLayout.SetMaximumSize)
46 | self.gridLayout1.setContentsMargins(10, 10, 10, 10)
47 | self.gridLayout1.setSpacing(10)
48 | self.gridLayout1.setObjectName("gridLayout1")
49 | self.label_time = QtWidgets.QLabel(self.layoutWidget)
50 | font = QtGui.QFont()
51 | font.setFamily("微软雅黑")
52 | font.setPointSize(8)
53 | self.label_time.setFont(font)
54 | self.label_time.setLayoutDirection(QtCore.Qt.RightToLeft)
55 | self.label_time.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
56 | self.label_time.setObjectName("label_time")
57 | self.gridLayout1.addWidget(self.label_time, 0, 2, 1, 1)
58 | self.label_remark = QtWidgets.QLabel(self.layoutWidget)
59 | font = QtGui.QFont()
60 | font.setFamily("黑体")
61 | font.setPointSize(10)
62 | # font.setBold(True)
63 | self.label_remark.setFont(font)
64 | self.label_remark.setObjectName("label_remark")
65 | self.gridLayout1.addWidget(self.label_remark, 0, 1, 1, 1)
66 | self.label_msg = QtWidgets.QLabel(self.layoutWidget)
67 | font = QtGui.QFont()
68 | font.setFamily("微软雅黑")
69 | font.setPointSize(8)
70 | self.label_msg.setFont(font)
71 | self.label_msg.setObjectName("label_msg")
72 | self.gridLayout1.addWidget(self.label_msg, 1, 1, 1, 2)
73 | self.label_avatar = QtWidgets.QLabel(self.layoutWidget)
74 | self.label_avatar.setMinimumSize(QtCore.QSize(60, 60))
75 | self.label_avatar.setMaximumSize(QtCore.QSize(60, 60))
76 | self.label_avatar.setLayoutDirection(QtCore.Qt.RightToLeft)
77 | self.label_avatar.setAutoFillBackground(False)
78 | self.label_avatar.setStyleSheet("background-color: #ffffff;")
79 | self.label_avatar.setInputMethodHints(QtCore.Qt.ImhNone)
80 | self.label_avatar.setFrameShape(QtWidgets.QFrame.NoFrame)
81 | self.label_avatar.setFrameShadow(QtWidgets.QFrame.Plain)
82 | self.label_avatar.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
83 | self.label_avatar.setObjectName("label_avatar")
84 | self.gridLayout1.addWidget(self.label_avatar, 0, 0, 2, 1)
85 | self.gridLayout1.setColumnStretch(0, 1)
86 | self.gridLayout1.setColumnStretch(1, 6)
87 | self.gridLayout1.setRowStretch(0, 5)
88 | self.gridLayout1.setRowStretch(1, 3)
89 | self.setLayout(self.gridLayout1)
90 | self.setStyleSheet(
91 | "QPushButton {background-color: rgb(220,220,220);}"
92 | "QPushButton:hover{background-color: rgb(208,208,208);}\n"
93 | )
94 |
95 | def show_info(self, id):
96 | time = datetime.now().strftime("%m-%d %H:%M")
97 | msg = '还没说话'
98 | self.label_avatar.setPixmap(self.contact.avatar) # 在label上显示图片
99 | self.label_remark.setText(self.contact.conRemark)
100 | self.label_msg.setText(self.digest)
101 | self.label_time.setText(data.timestamp2str(self.conversationTime)[2:])
102 |
103 | def show_msg(self):
104 | self.usernameSingal.emit(self.username)
105 |
--------------------------------------------------------------------------------
/app/decrypt/decrypt.py:
--------------------------------------------------------------------------------
1 | import hashlib
2 | import hmac
3 | import os
4 | from typing import Union, List
5 |
6 | from Cryptodome.Cipher import AES
7 |
8 | from app.log import log
9 |
10 | # from Crypto.Cipher import AES # 如果上面的导入失败,可以尝试使用这个
11 |
12 | SQLITE_FILE_HEADER = "SQLite format 3\x00" # SQLite文件头
13 |
14 | KEY_SIZE = 32
15 | DEFAULT_PAGESIZE = 4096
16 | DEFAULT_ITER = 64000
17 |
18 |
19 | # 通过密钥解密数据库
20 | @log
21 | def decrypt(key: str, db_path, out_path):
22 | if not os.path.exists(db_path):
23 | return f"[-] db_path:'{db_path}' File not found!"
24 | if not os.path.exists(os.path.dirname(out_path)):
25 | return f"[-] out_path:'{out_path}' File not found!"
26 | if len(key) != 64:
27 | return f"[-] key:'{key}' Error!"
28 | password = bytes.fromhex(key.strip())
29 | with open(db_path, "rb") as file:
30 | blist = file.read()
31 |
32 | salt = blist[:16]
33 | byteKey = hashlib.pbkdf2_hmac("sha1", password, salt, DEFAULT_ITER, KEY_SIZE)
34 | first = blist[16:DEFAULT_PAGESIZE]
35 |
36 | mac_salt = bytes([(salt[i] ^ 58) for i in range(16)])
37 | mac_key = hashlib.pbkdf2_hmac("sha1", byteKey, mac_salt, 2, KEY_SIZE)
38 | hash_mac = hmac.new(mac_key, first[:-32], hashlib.sha1)
39 | hash_mac.update(b'\x01\x00\x00\x00')
40 |
41 | if hash_mac.digest() != first[-32:-12]:
42 | return f"[-] Password Error! (key:'{key}'; db_path:'{db_path}'; out_path:'{out_path}' )"
43 |
44 | newblist = [blist[i:i + DEFAULT_PAGESIZE] for i in range(DEFAULT_PAGESIZE, len(blist), DEFAULT_PAGESIZE)]
45 |
46 | with open(out_path, "wb") as deFile:
47 | deFile.write(SQLITE_FILE_HEADER.encode())
48 | t = AES.new(byteKey, AES.MODE_CBC, first[-48:-32])
49 | decrypted = t.decrypt(first[:-48])
50 | deFile.write(decrypted)
51 | deFile.write(first[-48:])
52 |
53 | for i in newblist:
54 | t = AES.new(byteKey, AES.MODE_CBC, i[-48:-32])
55 | decrypted = t.decrypt(i[:-48])
56 | deFile.write(decrypted)
57 | deFile.write(i[-48:])
58 | return [True, db_path, out_path, key]
59 |
60 |
61 | @log
62 | def batch_decrypt(key: str, db_path: Union[str, List[str]], out_path: str):
63 | if not isinstance(key, str) or not isinstance(out_path, str) or not os.path.exists(out_path) or len(key) != 64:
64 | return f"[-] (key:'{key}' or out_path:'{out_path}') Error!"
65 |
66 | process_list = []
67 |
68 | if isinstance(db_path, str):
69 | if not os.path.exists(db_path):
70 | return f"[-] db_path:'{db_path}' not found!"
71 |
72 | if os.path.isfile(db_path):
73 | inpath = db_path
74 | outpath = os.path.join(out_path, 'de_' + os.path.basename(db_path))
75 | process_list.append([key, inpath, outpath])
76 |
77 | elif os.path.isdir(db_path):
78 | for root, dirs, files in os.walk(db_path):
79 | for file in files:
80 | inpath = os.path.join(root, file)
81 | rel = os.path.relpath(root, db_path)
82 | outpath = os.path.join(out_path, rel, 'de_' + file)
83 |
84 | if not os.path.exists(os.path.dirname(outpath)):
85 | os.makedirs(os.path.dirname(outpath))
86 | process_list.append([key, inpath, outpath])
87 | else:
88 | return f"[-] db_path:'{db_path}' Error "
89 | elif isinstance(db_path, list):
90 | rt_path = os.path.commonprefix(db_path)
91 | if not os.path.exists(rt_path):
92 | rt_path = os.path.dirname(rt_path)
93 |
94 | for inpath in db_path:
95 | if not os.path.exists(inpath):
96 | return f"[-] db_path:'{db_path}' not found!"
97 |
98 | inpath = os.path.normpath(inpath)
99 | rel = os.path.relpath(os.path.dirname(inpath), rt_path)
100 | outpath = os.path.join(out_path, rel, 'de_' + os.path.basename(inpath))
101 | if not os.path.exists(os.path.dirname(outpath)):
102 | os.makedirs(os.path.dirname(outpath))
103 | process_list.append([key, inpath, outpath])
104 | else:
105 | return f"[-] db_path:'{db_path}' Error "
106 |
107 | result = []
108 | for i in process_list:
109 | result.append(decrypt(*i)) # 解密
110 |
111 | # 删除空文件夹
112 | for root, dirs, files in os.walk(out_path, topdown=False):
113 | for dir in dirs:
114 | if not os.listdir(os.path.join(root, dir)):
115 | os.rmdir(os.path.join(root, dir))
116 | return result
117 |
118 |
119 | if __name__ == '__main__':
120 | # 调用 decrypt 函数,并传入参数
121 | key = "2aafab10af7940328bb92ac9d2a8ab5fc07a685646b14f2e9ae6948a7060c0fc"
122 | db_path = "E:\86390\Documents\WeChat Files\wxid_27hqbq7vx5hf22\FileStorage\CustomEmotion\\71\\71CE49ED3CE9E57E43E07F802983BF45"
123 | out_path = "./test/1.png"
124 | print(decrypt(key, db_path, out_path))
125 |
--------------------------------------------------------------------------------
/app/ui_pc/contact/contactInfo.py:
--------------------------------------------------------------------------------
1 | from PyQt5.QtCore import *
2 | from PyQt5.QtWidgets import *
3 |
4 | from app.DataBase.output_pc import Output
5 | from app.ui_pc.Icon import Icon
6 | from .contactInfoUi import Ui_Form
7 | from .userinfo import userinfo
8 |
9 |
10 | class ContactInfo(QWidget, Ui_Form):
11 | exitSignal = pyqtSignal()
12 | urlSignal = pyqtSignal(QUrl)
13 |
14 | # username = ''
15 | def __init__(self, contact, parent=None):
16 | super(ContactInfo, self).__init__(parent)
17 | self.setupUi(self)
18 | self.contact = contact
19 | self.view_userinfo = userinfo.UserinfoController(self.contact)
20 | self.btn_back.clicked.connect(self.back)
21 | self.init_ui()
22 |
23 | def init_ui(self):
24 | self.btn_back.setIcon(Icon.Back)
25 | self.btn_report.setIcon(Icon.Annual_Report_Icon)
26 | self.btn_analysis.setIcon(Icon.Analysis_Icon)
27 | self.btn_emotion.setIcon(Icon.Emotion_Icon)
28 | self.label_remark.setText(self.contact.remark)
29 | self.stackedWidget.addWidget(self.view_userinfo)
30 | self.stackedWidget.setCurrentWidget(self.view_userinfo)
31 | menu = QMenu(self)
32 | self.toDocxAct = QAction(Icon.ToDocx, '导出Docx', self)
33 | self.toCSVAct = QAction(Icon.ToCSV, '导出CSV', self)
34 | self.toHtmlAct = QAction(Icon.ToHTML, '导出HTML', self)
35 | self.toolButton_output.setPopupMode(QToolButton.MenuButtonPopup)
36 | self.toolButton_output.clicked.connect(self.toolButton_show)
37 | menu.addAction(self.toDocxAct)
38 | menu.addAction(self.toCSVAct)
39 | menu.addAction(self.toHtmlAct)
40 | self.toolButton_output.setMenu(menu)
41 | self.toolButton_output.setIcon(Icon.Output)
42 | # self.toolButton_output.addSeparator()
43 | self.toHtmlAct.triggered.connect(self.output)
44 | self.toDocxAct.triggered.connect(self.output)
45 | self.toCSVAct.triggered.connect(self.output)
46 |
47 | def toolButton_show(self):
48 | self.toolButton_output.showMenu()
49 |
50 | def analysis(self):
51 | self.stackedWidget.setCurrentWidget(self.view_analysis)
52 | if 'room' in self.contact.wxid:
53 | QMessageBox.warning(
54 | self, '警告',
55 | '暂不支持群组'
56 | )
57 | return
58 | self.view_analysis.start()
59 |
60 | def annual_report(self):
61 | QMessageBox.warning(
62 | self,
63 | "提示",
64 | "敬请期待"
65 | )
66 | return
67 | # self.report = report.ReportController(self.contact)
68 | # self.report.show()
69 |
70 | def emotionale_Analysis(self):
71 | self.stackedWidget.setCurrentWidget(self.view_emotion)
72 | if 'room' in self.contact.wxid:
73 | QMessageBox.warning(
74 | self, '警告',
75 | '暂不支持群组'
76 | )
77 | return
78 | self.view_emotion.start()
79 |
80 | def back(self):
81 | """
82 | 将userinfo界面设置为可见,其他界面设置为不可见
83 | """
84 | self.stackedWidget.setCurrentWidget(self.view_userinfo)
85 |
86 | def output(self):
87 | """
88 | 导出聊天记录
89 | :return:
90 | """
91 | self.stackedWidget.setCurrentWidget(self.view_userinfo)
92 | if self.sender() == self.toDocxAct:
93 | print('功能暂未实现')
94 | QMessageBox.warning(self,
95 | "别急别急",
96 | "马上就实现该功能"
97 | )
98 | return
99 | self.outputThread = Output(self.Me, self.contact.wxid)
100 | elif self.sender() == self.toCSVAct:
101 | self.outputThread = Output(self.contact, type_=Output.CSV)
102 | elif self.sender() == self.toHtmlAct:
103 | self.outputThread = Output(self.contact, type_=Output.HTML)
104 |
105 | self.outputThread.progressSignal.connect(self.output_progress)
106 | self.outputThread.rangeSignal.connect(self.set_progressBar_range)
107 | self.outputThread.okSignal.connect(self.hide_progress_bar)
108 | self.outputThread.start()
109 |
110 | def hide_progress_bar(self, int):
111 | reply = QMessageBox(self)
112 | reply.setIcon(QMessageBox.Information)
113 | reply.setWindowTitle('OK')
114 | reply.setText(f"导出聊天记录成功\n在.\\data\\目录下")
115 | reply.addButton("确认", QMessageBox.AcceptRole)
116 | reply.addButton("取消", QMessageBox.RejectRole)
117 | api = reply.exec_()
118 | self.view_userinfo.progressBar.setVisible(False)
119 |
120 | def output_progress(self, value):
121 | self.view_userinfo.progressBar.setProperty('value', value)
122 |
123 | def set_progressBar_range(self, value):
124 | print('进度条范围', value)
125 | self.view_userinfo.progressBar.setVisible(True)
126 | self.view_userinfo.progressBar.setRange(0, value)
127 |
--------------------------------------------------------------------------------
/resource/datasets/__init__.py:
--------------------------------------------------------------------------------
1 | import difflib
2 | import os
3 | import typing
4 | import urllib.request
5 |
6 | import simplejson as json
7 |
8 |
9 | class FuzzyDict(dict):
10 | """Provides a dictionary that performs fuzzy lookup"""
11 |
12 | def __init__(self, cutoff: float = 0.6):
13 | """Construct a new FuzzyDict instance
14 |
15 | items is an dictionary to copy items from (optional)
16 | cutoff is the match ratio below which matches should not be considered
17 | cutoff needs to be a float between 0 and 1 (where zero is no match
18 | and 1 is a perfect match)"""
19 | super(FuzzyDict, self).__init__()
20 | self.cutoff = cutoff
21 |
22 | # short wrapper around some super (dict) methods
23 | self._dict_contains = lambda key: super(FuzzyDict, self).__contains__(key)
24 | self._dict_getitem = lambda key: super(FuzzyDict, self).__getitem__(key)
25 |
26 | def _search(self, lookfor: typing.Any, stop_on_first: bool = False):
27 | """Returns the value whose key best matches lookfor
28 |
29 | if stop_on_first is True then the method returns as soon
30 | as it finds the first item
31 | """
32 |
33 | # if the item is in the dictionary then just return it
34 | if self._dict_contains(lookfor):
35 | return True, lookfor, self._dict_getitem(lookfor), 1
36 |
37 | # set up the fuzzy matching tool
38 | ratio_calc = difflib.SequenceMatcher()
39 | ratio_calc.set_seq1(lookfor)
40 |
41 | # test each key in the dictionary
42 | best_ratio = 0
43 | best_match = None
44 | best_key = None
45 | for key in self:
46 | # if the current key is not a string
47 | # then we just skip it
48 | try:
49 | # set up the SequenceMatcher with other text
50 | ratio_calc.set_seq2(key)
51 | except TypeError:
52 | continue
53 |
54 | # we get an error here if the item to look for is not a
55 | # string - if it cannot be fuzzy matched and we are here
56 | # this it is definitely not in the dictionary
57 | try:
58 | # calculate the match value
59 | ratio = ratio_calc.ratio()
60 | except TypeError:
61 | break
62 |
63 | # if this is the best ratio so far - save it and the value
64 | if ratio > best_ratio:
65 | best_ratio = ratio
66 | best_key = key
67 | best_match = self._dict_getitem(key)
68 |
69 | if stop_on_first and ratio >= self.cutoff:
70 | break
71 |
72 | return best_ratio >= self.cutoff, best_key, best_match, best_ratio
73 |
74 | def __contains__(self, item: typing.Any):
75 | if self._search(item, True)[0]:
76 | return True
77 | else:
78 | return False
79 |
80 | def __getitem__(self, lookfor: typing.Any):
81 | matched, key, item, ratio = self._search(lookfor)
82 |
83 | if not matched:
84 | raise KeyError(
85 | "'%s'. closest match: '%s' with ratio %.3f"
86 | % (str(lookfor), str(key), ratio)
87 | )
88 |
89 | return item
90 |
91 |
92 | __HERE = os.path.abspath(os.path.dirname(__file__))
93 | with open(os.path.join(__HERE, "map_filename.json"), "r", encoding="utf8") as f:
94 | FILENAMES: FuzzyDict = FuzzyDict()
95 | for k, v in json.load(f).items():
96 | FILENAMES[k] = v
97 |
98 | with open(os.path.join(__HERE, "city_coordinates.json"), "r", encoding="utf8") as f:
99 | COORDINATES: FuzzyDict = FuzzyDict()
100 | for k, v in json.load(f).items():
101 | COORDINATES[k] = v
102 |
103 | EXTRA = {}
104 |
105 |
106 | def register_url(asset_url: str):
107 | if asset_url:
108 | registry = asset_url + "/registry.json"
109 | try:
110 | contents = urllib.request.urlopen(registry).read()
111 | contents = json.loads(contents)
112 | except Exception as e:
113 | raise e
114 | files = {}
115 | pinyin_names = set()
116 | for name, pinyin in contents["PINYIN_MAP"].items():
117 | file_name = contents["FILE_MAP"][pinyin]
118 | files[name] = [file_name, "js"]
119 | pinyin_names.add(pinyin)
120 |
121 | for key, file_name in contents["FILE_MAP"].items():
122 | if key not in pinyin_names:
123 | # English names
124 | files[key] = [file_name, "js"]
125 |
126 | js_folder_name = contents["JS_FOLDER"]
127 | if js_folder_name == "/":
128 | js_file_prefix = f"{asset_url}/"
129 | else:
130 | js_file_prefix = f"{asset_url}/{js_folder_name}/"
131 | EXTRA[js_file_prefix] = files
132 |
133 |
134 | def register_files(asset_files: dict):
135 | if asset_files:
136 | FILENAMES.update(asset_files)
137 |
138 |
139 | def register_coords(coords: dict):
140 | if coords:
141 | COORDINATES.update(coords)
142 |
--------------------------------------------------------------------------------
/app/util/emoji.py:
--------------------------------------------------------------------------------
1 | import os
2 | import xml.etree.ElementTree as ET
3 |
4 | import requests
5 |
6 | root_path = './data/emoji/'
7 | if not os.path.exists('./data'):
8 | os.mkdir('./data')
9 | if not os.path.exists(root_path):
10 | os.mkdir(root_path)
11 |
12 |
13 | def get_image_format(header):
14 | # 定义图片格式的 magic numbers
15 | image_formats = {
16 | b'\xFF\xD8\xFF': 'jpeg',
17 | b'\x89\x50\x4E\x47\x0D\x0A\x1A\x0A': 'png',
18 | b'\x47\x49\x46': 'gif',
19 | b'\x42\x4D': 'bmp',
20 | # 添加其他图片格式的 magic numbers
21 | }
22 | # 判断文件的图片格式
23 | for magic_number, image_format in image_formats.items():
24 | if header.startswith(magic_number):
25 | return image_format
26 | # 如果无法识别格式,返回 None
27 | return None
28 |
29 |
30 | def parser_xml(xml_string):
31 | # Parse the XML string
32 | root = ET.fromstring(xml_string)
33 | emoji = root.find('./emoji')
34 | # Accessing attributes of the 'emoji' element
35 | fromusername = emoji.get('fromusername')
36 | tousername = emoji.get('tousername')
37 | md5 = emoji.get('md5')
38 | cdnurl = emoji.get('cdnurl')
39 | encrypturl = emoji.get('encrypturl')
40 | thumburl = emoji.get('thumburl')
41 | externurl = emoji.get('externurl')
42 | androidmd5 = emoji.get('androidmd5')
43 | width = emoji.get('width')
44 | height = emoji.get('height')
45 | return {
46 | 'width': width,
47 | 'height': height,
48 | 'cdnurl': cdnurl,
49 | 'thumburl': thumburl if thumburl else cdnurl,
50 | 'md5': md5 if md5 else androidmd5,
51 | }
52 |
53 |
54 | def download(url, output_dir, name, thumb=False):
55 | if not url:
56 | return ':/icons/icons/404.png'
57 | resp = requests.get(url)
58 | byte = resp.content
59 | image_format = get_image_format(byte[:8])
60 | if image_format:
61 | if thumb:
62 | output_path = os.path.join(output_dir, 'th_' + name + '.' + image_format)
63 | else:
64 | output_path = os.path.join(output_dir, name + '.' + image_format)
65 | else:
66 | output_path = os.path.join(output_dir, name)
67 | with open(output_path, 'wb') as f:
68 | f.write(resp.content)
69 | return output_path
70 |
71 |
72 | def get_emoji(xml_string, thumb=True) -> str:
73 | emoji_info = parser_xml(xml_string)
74 | md5 = emoji_info['md5']
75 | image_format = ['.png', '.gif', '.jpeg']
76 | for f in image_format:
77 | prefix = 'th_' if thumb else ''
78 | file_path = os.path.join(root_path, prefix + md5 + f)
79 | if os.path.exists(file_path):
80 | return file_path
81 | url = emoji_info['thumburl'] if thumb else emoji_info['cdnurl']
82 | print("下载表情包ing:", url)
83 | return download(url, root_path, md5, thumb)
84 |
85 |
86 | if __name__ == '__main__':
87 | xml_string = ' '
88 | res1 = parser_xml(xml_string)
89 | print(res1, res1['md5'])
90 | # download(res1['cdnurl'], "./data/emoji/", res1['md5'])
91 | # download(res1['thumburl'], "./data/emoji/", res1['md5'], True)
92 | print(get_emoji(xml_string, True))
93 | print(get_emoji(xml_string, False))
94 | # http://vweixinf.tc.qq.com/110/20403/stodownload?m=3a4d439aba02dce4834b2c54e9f15597&filekey=3043020101042f302d02016e0402534804203361346434333961626130326463653438333462326335346539663135353937020213f0040d00000004627466730000000131&hy=SH&storeid=323032313037323030373236313130303039653236646365316535316534383236386234306230303030303036653033303034666233&ef=3&bizid=1022
95 |
--------------------------------------------------------------------------------
/app/ImageBox/ui.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from .config import *
3 |
4 |
5 | class ImageBox(QWidget):
6 | def __init__(self):
7 | super(ImageBox, self).__init__()
8 | self.img = None
9 | self.scaled_img = None
10 | self.point = QPoint(100, 100)
11 | self.start_pos = None
12 | self.end_pos = None
13 | self.left_click = False
14 | self.scale = 1
15 |
16 | def init_ui(self):
17 | self.setWindowTitle("ImageBox")
18 |
19 | def set_image(self, img_path):
20 | """
21 | open image file
22 | :param img_path: image file path
23 | :return:
24 | """
25 | # img = QImageReader(img_path)
26 | # img.setScaledSize(QSize(self.size().width(), self.size().height()))
27 | # img = img.read()
28 | self.img = QPixmap(img_path)
29 | # print(self.img.size(),self.img.size().width(),self.img.size().height())
30 | self.scaled_img = self.img
31 | # print(img_size)
32 | img_size = self.scaled_img.size()
33 | x = min(500, max((1000 - img_size.width()) // 2, 0))
34 | y = min(300, max((600 - img_size.height()) // 2 - 60, 0))
35 | # print(x,y)
36 | self.point = QPoint(x, y)
37 |
38 | def paintEvent(self, e):
39 | """
40 | receive paint events
41 | :param e: QPaintEvent
42 | :return:
43 | """
44 | if self.scaled_img:
45 | painter = QPainter()
46 | painter.begin(self)
47 | painter.scale(self.scale, self.scale)
48 | painter.drawPixmap(self.point, self.scaled_img)
49 | painter.end()
50 |
51 | def wheelEvent(self, event):
52 | angle = event.angleDelta() / 8 # 返回QPoint对象,为滚轮转过的数值,单位为1/8度
53 | angleY = angle.y()
54 | # 获取当前鼠标相对于view的位置
55 | if angleY > 0:
56 | self.scale *= 1.1
57 | else: # 滚轮下滚
58 | self.scale *= 0.9
59 | self.adjustSize()
60 | self.update()
61 |
62 | def mouseMoveEvent(self, e):
63 | """
64 | mouse move events for the widget
65 | :param e: QMouseEvent
66 | :return:
67 | """
68 | if self.left_click:
69 | self.end_pos = e.pos() - self.start_pos
70 | self.point = self.point + self.end_pos
71 | self.start_pos = e.pos()
72 | self.repaint()
73 |
74 | def mousePressEvent(self, e):
75 | """
76 | mouse press events for the widget
77 | :param e: QMouseEvent
78 | :return:
79 | """
80 | if e.button() == Qt.LeftButton:
81 | self.left_click = True
82 | self.start_pos = e.pos()
83 |
84 | def mouseReleaseEvent(self, e):
85 | """
86 | mouse release events for the widget
87 | :param e: QMouseEvent
88 | :return:
89 | """
90 | if e.button() == Qt.LeftButton:
91 | self.left_click = False
92 |
93 |
94 | class MainDemo(QWidget):
95 | def __init__(self):
96 | super(MainDemo, self).__init__()
97 |
98 | self.setWindowTitle("Image Viewer")
99 | self.setFixedSize(1000, 600)
100 | self.setWindowIcon(QIcon('./app/data/icons/logo.svg'))
101 | self.zoom_in = QPushButton("")
102 | self.zoom_in.clicked.connect(self.large_click)
103 | self.zoom_in.setFixedSize(30, 30)
104 | in_icon = QIcon("./app/ImageBox/icons/zoom_in.jpg")
105 | self.zoom_in.setIcon(in_icon)
106 | self.zoom_in.setIconSize(QSize(30, 30))
107 |
108 | self.zoom_out = QPushButton("")
109 | self.zoom_out.clicked.connect(self.small_click)
110 | self.zoom_out.setFixedSize(30, 30)
111 | out_icon = QIcon("./app/ImageBox/icons/zoom_out.jpg")
112 | self.zoom_out.setIcon(out_icon)
113 | self.zoom_out.setIconSize(QSize(30, 30))
114 |
115 | w = QWidget(self)
116 | layout = QHBoxLayout()
117 | layout.addWidget(self.zoom_in)
118 | layout.addWidget(self.zoom_out)
119 | layout.setAlignment(Qt.AlignLeft)
120 | w.setLayout(layout)
121 | w.setFixedSize(550, 50)
122 |
123 | self.box = ImageBox()
124 | self.box.resize(500, 300)
125 |
126 | layout = QVBoxLayout()
127 | layout.addWidget(w)
128 | layout.addWidget(self.box)
129 | self.setLayout(layout)
130 |
131 | def open_image(self):
132 | """
133 | select image file and open it
134 | :return:
135 | """
136 | img_name, _ = QFileDialog.getOpenFileName(self, "Open Image File", "*.jpg;;*.png;;*.jpeg")
137 | self.box.set_image(img_name)
138 |
139 | def large_click(self):
140 | """
141 | used to enlarge image
142 | :return:
143 | """
144 | if self.box.scale < 2:
145 | self.box.scale += 0.1
146 | self.box.adjustSize()
147 | self.update()
148 |
149 | def small_click(self):
150 | """
151 | used to reduce image
152 | :return:
153 | """
154 | if self.box.scale > 0.1:
155 | self.box.scale -= 0.2
156 | self.box.adjustSize()
157 | self.update()
158 |
--------------------------------------------------------------------------------
/app/Ui/decrypt/decrypt.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | @File : decrypt.py
4 | @Author : Shuaikang Zhou
5 | @Time : 2023/1/5 18:13
6 | @IDE : Pycharm
7 | @Version : Python3.10
8 | @comment : ··· 解密数据库,导出原始数据库文件
9 | """
10 | import hashlib
11 | import time
12 | import xml.etree.ElementTree as ET
13 |
14 | from PyQt5.QtCore import *
15 | from PyQt5.QtGui import *
16 | from PyQt5.QtWidgets import *
17 |
18 | from . import decryptUi
19 | from ...DataBase import data
20 |
21 |
22 | class DecryptControl(QWidget, decryptUi.Ui_Dialog):
23 | DecryptSignal = pyqtSignal(str)
24 | registerSignal = pyqtSignal(str)
25 |
26 | def __init__(self, parent=None):
27 | super(DecryptControl, self).__init__(parent)
28 | self.setupUi(self)
29 | self.setWindowTitle('解密')
30 | self.setWindowIcon(QIcon('./app/data/icons/logo.svg'))
31 | self.btn_db.clicked.connect(self.get_db)
32 | self.btn_xml.clicked.connect(self.get_xml)
33 | self.pushButton_3.clicked.connect(self.decrypt)
34 | self.xml_path: str = None
35 | self.db_path: str = None
36 |
37 | def db_exist(self):
38 | if data.is_db_exist():
39 | self.btnEnterClicked()
40 | self.close()
41 |
42 | def get_xml(self):
43 | self.xml_path, _ = QFileDialog.getOpenFileName(self, 'Open file', r'..', "Xml files (*.xml)")
44 | if self.xml_path:
45 | self.label_xml.setText('xml已就绪')
46 | key = self.parser_xml()
47 | self.label_key.setText(f'数据库密钥:{key}')
48 | return self.xml_path
49 | return False
50 |
51 | def get_db(self):
52 | self.db_path, _ = QFileDialog.getOpenFileName(self, 'Open file', r'..', "Database files (*.db)")
53 | if self.db_path:
54 | if ' ' in self.db_path:
55 | self.label_db.setText('数据库未就绪')
56 | QMessageBox.critical(self, "错误", "db文件路径请不要带有空格\n可以放在D:\\\\data 目录下")
57 | self.db_path = ''
58 | elif self.db_path.isascii():
59 | self.label_db.setText('数据库已就绪')
60 | return self.db_path
61 | else:
62 | self.label_db.setText('数据库未就绪')
63 | QMessageBox.critical(self, "错误", "db文件请不要带有中文路径\n可以放在D:\\\\data 目录下")
64 | self.db_path = ''
65 | return False
66 |
67 | def decrypt(self):
68 | if not (self.xml_path and self.db_path):
69 | QMessageBox.critical(self, "错误", "请把两个文件加载进来")
70 | return
71 | key = self.parser_xml()
72 | self.label_key.setText(f'数据库密钥:{key}')
73 | self.thread1 = MyThread()
74 | self.thread1.signal.connect(self.progressBar_view)
75 | self.thread1.start()
76 | self.thread2 = DecryptThread(self.db_path, key)
77 | self.thread2.signal.connect(self.progressBar_view)
78 | self.thread2.start()
79 |
80 | def parser_xml(self):
81 | if not self.xml_path:
82 | return False
83 | pid = self.pid(self.xml_path)
84 | if not pid:
85 | return False
86 | key = self.key(pid)
87 | return key
88 |
89 | def pid(self, xml_path):
90 | tree = ET.parse(xml_path)
91 | # 根节点
92 | root = tree.getroot()
93 | # 标签名
94 | for stu in root:
95 | if stu.attrib["name"] == '_auth_uin':
96 | return stu.attrib['value']
97 | return False
98 |
99 | def key(self, uin, IMEI='1234567890ABCDEF'):
100 | m = hashlib.md5()
101 | m.update(bytes((IMEI + uin).encode('utf-8')))
102 | psw = m.hexdigest()
103 | return psw[:7]
104 |
105 | def btnEnterClicked(self):
106 | # print("enter clicked")
107 | # 中间可以添加处理逻辑
108 | self.DecryptSignal.emit('ok')
109 | self.close()
110 |
111 | def progressBar_view(self, value):
112 | """
113 | 进度条显示
114 | :param value: 进度0-100
115 | :return: None
116 | """
117 | self.progressBar.setProperty('value', value)
118 | if value == '99':
119 | QMessageBox.information(self, "温馨提示", "我知道你很急\n但你先别急")
120 | if value == '100':
121 | QMessageBox.information(self, "解密成功", "请退出该界面",
122 | QMessageBox.Yes)
123 | self.btnExitClicked()
124 | data.init_database()
125 |
126 | def btnExitClicked(self):
127 | # print("Exit clicked")
128 | self.DecryptSignal.emit('ok')
129 | self.close()
130 |
131 |
132 | class DecryptThread(QThread):
133 | signal = pyqtSignal(str)
134 |
135 | def __init__(self, db_path, key):
136 | super(DecryptThread, self).__init__()
137 | self.db_path = db_path
138 | self.key = key
139 | self.textBrowser = None
140 |
141 | def __del__(self):
142 | pass
143 |
144 | def run(self):
145 | data.decrypt(self.db_path, self.key)
146 | self.signal.emit('100')
147 |
148 |
149 | class MyThread(QThread):
150 | signal = pyqtSignal(str)
151 |
152 | def __init__(self):
153 | super(MyThread, self).__init__()
154 |
155 | def __del__(self):
156 | pass
157 |
158 | def run(self):
159 | for i in range(100):
160 | self.signal.emit(str(i))
161 | time.sleep(0.1)
162 |
--------------------------------------------------------------------------------