mirror of
https://github.com/KnugiHK/WhatsApp-Chat-Exporter.git
synced 2026-04-22 05:54:40 +00:00
Prepare for publishing in PyPi
This commit is contained in:
1
Whatsapp_Chat_Exporter/__init__.py
Normal file
1
Whatsapp_Chat_Exporter/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
__version__ = "0.5"
|
||||||
@@ -7,6 +7,7 @@ import os
|
|||||||
import requests
|
import requests
|
||||||
import shutil
|
import shutil
|
||||||
import re
|
import re
|
||||||
|
import pkgutil
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from mimetypes import MimeTypes
|
from mimetypes import MimeTypes
|
||||||
|
|
||||||
@@ -254,7 +255,7 @@ def vcard(db, data):
|
|||||||
|
|
||||||
|
|
||||||
def create_html(data, output_folder):
|
def create_html(data, output_folder):
|
||||||
templateLoader = jinja2.FileSystemLoader(searchpath="./")
|
templateLoader = jinja2.FileSystemLoader(searchpath=os.path.dirname(__file__))
|
||||||
templateEnv = jinja2.Environment(loader=templateLoader)
|
templateEnv = jinja2.Environment(loader=templateLoader)
|
||||||
templateEnv.globals.update(determine_day=determine_day)
|
templateEnv.globals.update(determine_day=determine_day)
|
||||||
TEMPLATE_FILE = "whatsapp.html"
|
TEMPLATE_FILE = "whatsapp.html"
|
||||||
@@ -6,6 +6,7 @@ import jinja2
|
|||||||
import os
|
import os
|
||||||
import requests
|
import requests
|
||||||
import shutil
|
import shutil
|
||||||
|
import pkgutil
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from mimetypes import MimeTypes
|
from mimetypes import MimeTypes
|
||||||
|
|
||||||
@@ -206,12 +207,12 @@ def vcard(db, data):
|
|||||||
|
|
||||||
|
|
||||||
def create_html(data, output_folder):
|
def create_html(data, output_folder):
|
||||||
templateLoader = jinja2.FileSystemLoader(searchpath="./")
|
templateLoader = jinja2.FileSystemLoader(searchpath=os.path.dirname(__file__))
|
||||||
templateEnv = jinja2.Environment(loader=templateLoader)
|
templateEnv = jinja2.Environment(loader=templateLoader)
|
||||||
templateEnv.globals.update(determine_day=determine_day)
|
templateEnv.globals.update(determine_day=determine_day)
|
||||||
TEMPLATE_FILE = "whatsapp.html"
|
TEMPLATE_FILE = "whatsapp.html"
|
||||||
template = templateEnv.get_template(TEMPLATE_FILE)
|
template = templateEnv.get_template(TEMPLATE_FILE)
|
||||||
|
|
||||||
total_row_number = len(data)
|
total_row_number = len(data)
|
||||||
print(f"\nCreating HTML...(0/{total_row_number})", end="\r")
|
print(f"\nCreating HTML...(0/{total_row_number})", end="\r")
|
||||||
|
|
||||||
@@ -73,11 +73,17 @@ def extract_media(base_dir):
|
|||||||
if not support_encrypted:
|
if not support_encrypted:
|
||||||
print("You don't have the dependencies to handle encrypted backup.")
|
print("You don't have the dependencies to handle encrypted backup.")
|
||||||
print("Read more about how to deal with encrypted backup:")
|
print("Read more about how to deal with encrypted backup:")
|
||||||
print("https://github.com/KnugiHK/Whatsapp-Chat-Exporter/blob/main/README.md#encrypted-iphone-backup")
|
print("https://github.com/KnugiHK/Whatsapp-Chat-Exporter/blob/main/README.md#usage")
|
||||||
return False
|
return False
|
||||||
password = getpass.getpass("Enter the password:")
|
password = getpass.getpass("Enter the password:")
|
||||||
extract_encrypted(base_dir, password)
|
extract_encrypted(base_dir, password)
|
||||||
else:
|
else:
|
||||||
|
wts_db = os.path.join(base_dir, "7c/7c7fba66680ef796b916b067077cc246adacf01d")
|
||||||
|
if not os.path.isfile(wts_db):
|
||||||
|
print("WhatsApp database not found.")
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
shutil.copyfile(wts_db, "7c7fba66680ef796b916b067077cc246adacf01d")
|
||||||
with sqlite3.connect(f"{base_dir}/Manifest.db") as manifest:
|
with sqlite3.connect(f"{base_dir}/Manifest.db") as manifest:
|
||||||
c = manifest.cursor()
|
c = manifest.cursor()
|
||||||
c.execute("""SELECT count()
|
c.execute("""SELECT count()
|
||||||
@@ -1,158 +1,158 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Whatsapp - {{ name }}</title>
|
<title>Whatsapp - {{ name }}</title>
|
||||||
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
|
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
|
||||||
<style>
|
<style>
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+HK:wght@300;400&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+HK:wght@300;400&display=swap');
|
||||||
html {
|
html {
|
||||||
font-family: 'Noto Sans HK', sans-serif;
|
font-family: 'Noto Sans HK', sans-serif;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
header {
|
header {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
border-bottom: 2px solid #e3e6e7;
|
border-bottom: 2px solid #e3e6e7;
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
font-weight: bolder;
|
font-weight: bolder;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
padding: 20px 0 20px 0;
|
padding: 20px 0 20px 0;
|
||||||
}
|
}
|
||||||
footer {
|
footer {
|
||||||
border-top: 2px solid #e3e6e7;
|
border-top: 2px solid #e3e6e7;
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
padding: 20px 0 20px 0;
|
padding: 20px 0 20px 0;
|
||||||
}
|
}
|
||||||
article {
|
article {
|
||||||
width:500px;
|
width:500px;
|
||||||
margin:100px auto;
|
margin:100px auto;
|
||||||
z-index:10;
|
z-index:10;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
img, video {
|
img, video {
|
||||||
max-width:100%;
|
max-width:100%;
|
||||||
}
|
}
|
||||||
a.anchor {
|
a.anchor {
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -100px;
|
top: -100px;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
div.reply{
|
div.reply{
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="w3-center w3-top">Chat history with {{ name }}</header>
|
<header class="w3-center w3-top">Chat history with {{ name }}</header>
|
||||||
<article class="w3-container">
|
<article class="w3-container">
|
||||||
<div class="table" style="width:100%">
|
<div class="table" style="width:100%">
|
||||||
{% set last = {'last': 946688461.001} %}
|
{% set last = {'last': 946688461.001} %}
|
||||||
{% for msg in msgs -%}
|
{% for msg in msgs -%}
|
||||||
<div class="w3-row" style="padding-bottom: 10px">
|
<div class="w3-row" style="padding-bottom: 10px">
|
||||||
<a class="anchor" id="{{ msg.key_id }}"></a>
|
<a class="anchor" id="{{ msg.key_id }}"></a>
|
||||||
{% if determine_day(last.last, msg.timestamp) is not none %}
|
{% if determine_day(last.last, msg.timestamp) is not none %}
|
||||||
<div class="w3-center" style="color:#70777c;padding: 10px 0 10px 0;">{{ determine_day(last.last, msg.timestamp) }}</div>
|
<div class="w3-center" style="color:#70777c;padding: 10px 0 10px 0;">{{ determine_day(last.last, msg.timestamp) }}</div>
|
||||||
{% if last.update({'last': msg.timestamp}) %}{% endif %}
|
{% if last.update({'last': msg.timestamp}) %}{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if msg.from_me == true %}
|
{% if msg.from_me == true %}
|
||||||
<div class="w3-row">
|
<div class="w3-row">
|
||||||
<div style="float: left; color:#70777c;">{{ msg.time }}</div>
|
<div style="float: left; color:#70777c;">{{ msg.time }}</div>
|
||||||
<div style="padding-left: 10px; text-align: right; color: #3892da;">You</div>
|
<div style="padding-left: 10px; text-align: right; color: #3892da;">You</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w3-row">
|
<div class="w3-row">
|
||||||
<div class="w3-col m10 l10">
|
<div class="w3-col m10 l10">
|
||||||
<div style="text-align: right;">
|
<div style="text-align: right;">
|
||||||
{% if msg.reply is not none %}
|
{% if msg.reply is not none %}
|
||||||
<div class="reply">
|
<div class="reply">
|
||||||
<span style="color: #70777a;">Replying to </span>
|
<span style="color: #70777a;">Replying to </span>
|
||||||
<a href="#{{msg.reply}}" style="color: #168acc;">"{{ msg.quoted_data or 'media' }}"</a>
|
<a href="#{{msg.reply}}" style="color: #168acc;">"{{ msg.quoted_data or 'media' }}"</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if msg.media == false %}
|
{% if msg.media == false %}
|
||||||
{% filter escape %}{{ msg.data or "{This message is not supported yet}" | replace('\n', '<br>') }}{% endfilter %}
|
{% filter escape %}{{ msg.data or "{This message is not supported yet}" | replace('\n', '<br>') }}{% endfilter %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if "image/" in msg.mime %}
|
{% if "image/" in msg.mime %}
|
||||||
<a href="{{ msg.data }}"><img src="{{ msg.data }}" /></a>
|
<a href="{{ msg.data }}"><img src="{{ msg.data }}" /></a>
|
||||||
{% elif "audio/" in msg.mime %}
|
{% elif "audio/" in msg.mime %}
|
||||||
<audio controls="controls" autobuffer="autobuffer">
|
<audio controls="controls" autobuffer="autobuffer">
|
||||||
<source src="{{ msg.data }}" />
|
<source src="{{ msg.data }}" />
|
||||||
</audio>
|
</audio>
|
||||||
{% elif "video/" in msg.mime %}
|
{% elif "video/" in msg.mime %}
|
||||||
<video controls="controls" autobuffer="autobuffer">
|
<video controls="controls" autobuffer="autobuffer">
|
||||||
<source src="{{ msg.data }}" />
|
<source src="{{ msg.data }}" />
|
||||||
</video>
|
</video>
|
||||||
{% elif "/" in msg.mime %}
|
{% elif "/" in msg.mime %}
|
||||||
{The file cannot be displayed here, however it should be located at {{ msg.data }}}
|
{The file cannot be displayed here, however it should be located at {{ msg.data }}}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% filter escape %}{{ msg.data }}{% endfilter %}
|
{% filter escape %}{{ msg.data }}{% endfilter %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if msg.caption is not none %}
|
{% if msg.caption is not none %}
|
||||||
<br>
|
<br>
|
||||||
{{ msg.caption }}
|
{{ msg.caption }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w3-col m2 l2" style="padding-left: 10px"><img src="{{ my_avatar }}" onerror="this.style.display='none'"></div>
|
<div class="w3-col m2 l2" style="padding-left: 10px"><img src="{{ my_avatar }}" onerror="this.style.display='none'"></div>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="w3-row">
|
<div class="w3-row">
|
||||||
<div style="padding-right: 10px; float: left; color: #3892da;">
|
<div style="padding-right: 10px; float: left; color: #3892da;">
|
||||||
{% if msg.sender is not none %}
|
{% if msg.sender is not none %}
|
||||||
{{ msg.sender }}
|
{{ msg.sender }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ name }}
|
{{ name }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div style="text-align: right; color:#70777c;">{{ msg.time }}</div>
|
<div style="text-align: right; color:#70777c;">{{ msg.time }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w3-row">
|
<div class="w3-row">
|
||||||
<div class="w3-col m2 l2"><img src="{{ their_avatar }}" onerror="this.style.display='none'"></div>
|
<div class="w3-col m2 l2"><img src="{{ their_avatar }}" onerror="this.style.display='none'"></div>
|
||||||
<div class="w3-col m10 l10">
|
<div class="w3-col m10 l10">
|
||||||
<div style="text-align: left;">
|
<div style="text-align: left;">
|
||||||
{% if msg.reply is not none %}
|
{% if msg.reply is not none %}
|
||||||
<div class="reply">
|
<div class="reply">
|
||||||
<span style="color: #70777a;">Replying to </span>
|
<span style="color: #70777a;">Replying to </span>
|
||||||
<a href="#{{msg.reply}}" style="color: #168acc;">"{{ msg.quoted_data or 'media' }}"</a>
|
<a href="#{{msg.reply}}" style="color: #168acc;">"{{ msg.quoted_data or 'media' }}"</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if msg.media == false %}
|
{% if msg.media == false %}
|
||||||
{% filter escape %}{{ msg.data or "{This message is not supported yet}" }}{% endfilter %}
|
{% filter escape %}{{ msg.data or "{This message is not supported yet}" }}{% endfilter %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if "image/" in msg.mime %}
|
{% if "image/" in msg.mime %}
|
||||||
<a href="{{ msg.data }}"><img src="{{ msg.data }}" /></a>
|
<a href="{{ msg.data }}"><img src="{{ msg.data }}" /></a>
|
||||||
{% elif "audio/" in msg.mime %}
|
{% elif "audio/" in msg.mime %}
|
||||||
<audio controls="controls" autobuffer="autobuffer">
|
<audio controls="controls" autobuffer="autobuffer">
|
||||||
<source src="{{ msg.data }}" />
|
<source src="{{ msg.data }}" />
|
||||||
</audio>
|
</audio>
|
||||||
{% elif "video/" in msg.mime %}
|
{% elif "video/" in msg.mime %}
|
||||||
<video controls="controls" autobuffer="autobuffer">
|
<video controls="controls" autobuffer="autobuffer">
|
||||||
<source src="{{ msg.data }}" />
|
<source src="{{ msg.data }}" />
|
||||||
</video>
|
</video>
|
||||||
{% elif "/" in msg.mime %}
|
{% elif "/" in msg.mime %}
|
||||||
{The file cannot be displayed here, however it should be located at {{ msg.data }}}
|
{The file cannot be displayed here, however it should be located at {{ msg.data }}}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% filter escape %}{{ msg.data }}{% endfilter %}
|
{% filter escape %}{{ msg.data }}{% endfilter %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if msg.caption is not none %}
|
{% if msg.caption is not none %}
|
||||||
<br>
|
<br>
|
||||||
{{ msg.caption }}
|
{{ msg.caption }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
<footer class="w3-center">
|
<footer class="w3-center">
|
||||||
End of history
|
End of history
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
46
setup.py
Normal file
46
setup.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import setuptools
|
||||||
|
from re import search
|
||||||
|
|
||||||
|
with open("README.md", "r") as fh:
|
||||||
|
long_description = fh.read()
|
||||||
|
|
||||||
|
with open("Whatsapp_Chat_Exporter/__init__.py", encoding="utf8") as f:
|
||||||
|
version = search(r'__version__ = "(.*?)"', f.read()).group(1)
|
||||||
|
|
||||||
|
setuptools.setup(
|
||||||
|
name="whatsapp-chat-exporter",
|
||||||
|
version=version,
|
||||||
|
author="KnugiHK",
|
||||||
|
author_email="info@knugi.com",
|
||||||
|
description="A Whatsapp database parser that will give you the history of your Whatsapp conversations in HTML and JSON.",
|
||||||
|
long_description=long_description,
|
||||||
|
long_description_content_type="text/markdown",
|
||||||
|
url="https://github.com/KnugiHK/Whatsapp-Chat-Exporter",
|
||||||
|
packages=setuptools.find_packages(),
|
||||||
|
package_data = {
|
||||||
|
'': ['whatsapp.html']
|
||||||
|
},
|
||||||
|
classifiers=[
|
||||||
|
"Programming Language :: Python :: 3 :: Only",
|
||||||
|
"Programming Language :: Python :: 3.7",
|
||||||
|
"Programming Language :: Python :: 3.8",
|
||||||
|
"Programming Language :: Python :: 3.8",
|
||||||
|
"License :: OSI Approved :: MIT License",
|
||||||
|
"Operating System :: OS Independent",
|
||||||
|
"Development Status :: Beta",
|
||||||
|
"Environment :: Console",
|
||||||
|
"Intended Audience :: End Users/Desktop"
|
||||||
|
"Topic :: Communications :: Chat",
|
||||||
|
"Topic :: Utilities",
|
||||||
|
"Topic :: Database"
|
||||||
|
],
|
||||||
|
python_requires='>=3.7',
|
||||||
|
install_requires=[
|
||||||
|
'jinja2'
|
||||||
|
],
|
||||||
|
entry_points={
|
||||||
|
"console_scripts": [
|
||||||
|
"wtsexporter = Whatsapp_Chat_Exporter.__main__:main"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user