Merge branch 'dev'

This commit is contained in:
KnugiHK
2026-01-06 21:19:22 +08:00
6 changed files with 114 additions and 51 deletions

View File

@@ -13,15 +13,19 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-latest, windows-latest, macos-latest] os: [ubuntu-latest]
python-version: ["3.13", "3.14"] python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
include: include:
- os: ubuntu-latest - os: windows-latest
python-version: "3.10" python-version: "3.13"
- os: ubuntu-latest - os: macos-latest
python-version: "3.11" python-version: "3.13"
- os: ubuntu-latest - os: windows-11-arm
python-version: "3.12" python-version: "3.13"
- os: macos-15-intel
python-version: "3.13"
- os: windows-latest
python-version: "3.14"
steps: steps:
- name: Checkout code - name: Checkout code

View File

@@ -10,7 +10,6 @@ permissions:
id-token: write id-token: write
attestations: write attestations: write
jobs: jobs:
linux: linux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -37,11 +36,10 @@ jobs:
subject-path: ./wtsexporter_linux_x64 subject-path: ./wtsexporter_linux_x64
- uses: actions/upload-artifact@v6 - uses: actions/upload-artifact@v6
with: with:
name: binary-linux name: binary-linux-x64
path: | path: ./wtsexporter_linux_x64
./wtsexporter_linux_x64
windows: windows-x64:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
@@ -57,19 +55,45 @@ jobs:
- name: Build binary with Nuitka - name: Build binary with Nuitka
run: | run: |
python -m nuitka --onefile --include-data-file=./Whatsapp_Chat_Exporter/whatsapp.html=./Whatsapp_Chat_Exporter/whatsapp.html --assume-yes-for-downloads Whatsapp_Chat_Exporter --output-filename=wtsexporter python -m nuitka --onefile --include-data-file=./Whatsapp_Chat_Exporter/whatsapp.html=./Whatsapp_Chat_Exporter/whatsapp.html --assume-yes-for-downloads Whatsapp_Chat_Exporter --output-filename=wtsexporter
Rename-Item -Path "wtsexporter.exe" -NewName "wtsexporter_x64.exe" Rename-Item -Path "wtsexporter.exe" -NewName "wtsexporter_win_x64.exe"
Get-FileHash wtsexporter_x64.exe Get-FileHash wtsexporter_win_x64.exe
- name: Generate artifact attestation - name: Generate artifact attestation
uses: actions/attest-build-provenance@v3 uses: actions/attest-build-provenance@v3
with: with:
subject-path: .\wtsexporter_x64.exe subject-path: .\wtsexporter_win_x64.exe
- uses: actions/upload-artifact@v6 - uses: actions/upload-artifact@v6
with: with:
name: binary-windows name: binary-windows-x64
path: | path: .\wtsexporter_win_x64.exe
.\wtsexporter_x64.exe
macos: windows-arm:
runs-on: windows-11-arm
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.13'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pycryptodome javaobj-py3 ordered-set zstandard nuitka==2.8.9
pip install .
- name: Build binary with Nuitka
run: |
python -m nuitka --onefile --include-data-file=./Whatsapp_Chat_Exporter/whatsapp.html=./Whatsapp_Chat_Exporter/whatsapp.html --assume-yes-for-downloads Whatsapp_Chat_Exporter --output-filename=wtsexporter
Rename-Item -Path "wtsexporter.exe" -NewName "wtsexporter_win_arm64.exe"
Get-FileHash wtsexporter_win_arm64.exe
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v3
with:
subject-path: .\wtsexporter_win_arm64.exe
- uses: actions/upload-artifact@v6
with:
name: binary-windows-arm64
path: .\wtsexporter_win_arm64.exe
macos-arm:
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
@@ -86,7 +110,8 @@ jobs:
run: | run: |
python -m nuitka --onefile \ python -m nuitka --onefile \
--include-data-file=./Whatsapp_Chat_Exporter/whatsapp.html=./Whatsapp_Chat_Exporter/whatsapp.html \ --include-data-file=./Whatsapp_Chat_Exporter/whatsapp.html=./Whatsapp_Chat_Exporter/whatsapp.html \
--assume-yes-for-downloads Whatsapp_Chat_Exporter --output-filename=wtsexporter_macos_arm64 --assume-yes-for-downloads Whatsapp_Chat_Exporter --output-filename=wtsexporter
mv wtsexporter wtsexporter_macos_arm64
shasum -a 256 wtsexporter_macos_arm64 shasum -a 256 wtsexporter_macos_arm64
- name: Generate artifact attestation - name: Generate artifact attestation
uses: actions/attest-build-provenance@v3 uses: actions/attest-build-provenance@v3
@@ -94,7 +119,34 @@ jobs:
subject-path: ./wtsexporter_macos_arm64 subject-path: ./wtsexporter_macos_arm64
- uses: actions/upload-artifact@v6 - uses: actions/upload-artifact@v6
with: with:
name: binary-macos name: binary-macos-arm64
path: | path: ./wtsexporter_macos_arm64
./wtsexporter_macos_arm64
macos-intel:
runs-on: macos-15-intel
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.13'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pycryptodome javaobj-py3 ordered-set zstandard nuitka==2.8.9
pip install .
- name: Build binary with Nuitka
run: |
python -m nuitka --onefile \
--include-data-file=./Whatsapp_Chat_Exporter/whatsapp.html=./Whatsapp_Chat_Exporter/whatsapp.html \
--assume-yes-for-downloads Whatsapp_Chat_Exporter --output-filename=wtsexporter
mv wtsexporter wtsexporter_macos_x64
shasum -a 256 wtsexporter_macos_x64
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v3
with:
subject-path: ./wtsexporter_macos_x64
- uses: actions/upload-artifact@v6
with:
name: binary-macos-x64
path: ./wtsexporter_macos_x64

View File

@@ -145,22 +145,24 @@ After extracting, you will get this:
Invoke the wtsexporter with --help option will show you all options available. Invoke the wtsexporter with --help option will show you all options available.
```sh ```sh
> wtsexporter --help > wtsexporter --help
usage: wtsexporter [-h] [-a] [-i] [-e EXPORTED] [-w WA] [-m MEDIA] [-b BACKUP] [-d DB] [-k [KEY]] usage: wtsexporter [-h] [--debug] [-a] [-i] [-e EXPORTED] [-w WA] [-m MEDIA] [-b BACKUP] [-d DB] [-k [KEY]]
[--call-db [CALL_DB_IOS]] [--wab WAB] [-o OUTPUT] [-j [JSON]] [--txt [TEXT_FORMAT]] [--no-html] [--call-db [CALL_DB_IOS]] [--wab WAB] [-o OUTPUT] [-j [JSON]] [--txt [TEXT_FORMAT]] [--no-html]
[--size [SIZE]] [--avoid-encoding-json] [--pretty-print-json [PRETTY_PRINT_JSON]] [--per-chat] [--size [SIZE]] [--no-reply] [--avoid-encoding-json] [--pretty-print-json [PRETTY_PRINT_JSON]]
[--import] [-t TEMPLATE] [--offline OFFLINE] [--no-avatar] [--experimental-new-theme] [--tg] [--per-chat] [--import] [-t TEMPLATE] [--offline OFFLINE] [--no-avatar] [--old-theme]
[--headline HEADLINE] [-c] [--create-separated-media] [--time-offset {-12 to 14}] [--date DATE] [--headline HEADLINE] [-c] [--create-separated-media] [--time-offset {-12 to 14}] [--date DATE]
[--date-format FORMAT] [--include [phone number ...]] [--exclude [phone number ...]] [--date-format FORMAT] [--include [phone number ...]] [--exclude [phone number ...]]
[--dont-filter-empty] [--enrich-from-vcards ENRICH_FROM_VCARDS] [--dont-filter-empty] [--enrich-from-vcards ENRICH_FROM_VCARDS]
[--default-country-code DEFAULT_COUNTRY_CODE] [-s] [--check-update] [--assume-first-as-me] [--default-country-code DEFAULT_COUNTRY_CODE] [--incremental-merge] [--source-dir SOURCE_DIR]
[--business] [--decrypt-chunk-size DECRYPT_CHUNK_SIZE] [--target-dir TARGET_DIR] [-s] [--check-update] [--assume-first-as-me] [--business]
[--max-bruteforce-worker MAX_BRUTEFORCE_WORKER] [--decrypt-chunk-size DECRYPT_CHUNK_SIZE] [--max-bruteforce-worker MAX_BRUTEFORCE_WORKER]
[--no-banner]
A customizable Android and iOS/iPadOS WhatsApp database parser that will give you the history of your WhatsApp A customizable Android and iOS/iPadOS WhatsApp database parser that will give you the history of your WhatsApp
conversations in HTML and JSON. Android Backup Crypt12, Crypt14 and Crypt15 supported. conversations in HTML and JSON. Android Backup Crypt12, Crypt14 and Crypt15 supported.
options: options:
-h, --help show this help message and exit -h, --help show this help message and exit
--debug Enable debug mode
Device Type: Device Type:
-a, --android Define the target as Android -a, --android Define the target as Android
@@ -188,12 +190,14 @@ Output Options:
--no-html Do not output html files --no-html Do not output html files
--size, --output-size, --split [SIZE] --size, --output-size, --split [SIZE]
Maximum (rough) size of a single output file in bytes, 0 for auto Maximum (rough) size of a single output file in bytes, 0 for auto
--no-reply Do not process replies (iOS only) (default: handle replies)
JSON Options: JSON Options:
--avoid-encoding-json --avoid-encoding-json
Don't encode non-ascii characters in the output JSON files Don't encode non-ascii characters in the output JSON files
--pretty-print-json [PRETTY_PRINT_JSON] --pretty-print-json [PRETTY_PRINT_JSON]
Pretty print the output JSON. Pretty print the output JSON.
--tg, --telegram Output the JSON in a format compatible with Telegram export (implies json-per-chat)
--per-chat Output the JSON file per chat --per-chat Output the JSON file per chat
--import Import JSON file and convert to HTML output --import Import JSON file and convert to HTML output
@@ -202,8 +206,7 @@ HTML Options:
Path to custom HTML template Path to custom HTML template
--offline OFFLINE Relative path to offline static files --offline OFFLINE Relative path to offline static files
--no-avatar Do not render avatar in HTML output --no-avatar Do not render avatar in HTML output
--experimental-new-theme --old-theme Use the old Telegram-alike theme
Use the newly designed WhatsApp-alike theme
--headline HEADLINE The custom headline for the HTML output. Use '??' as a placeholder for the chat name --headline HEADLINE The custom headline for the HTML output. Use '??' as a placeholder for the chat name
Media Handling: Media Handling:
@@ -232,12 +235,11 @@ Contact Enrichment:
will be used. 1 is for US, 66 for Thailand etc. Most likely use the number of your own country will be used. 1 is for US, 66 for Thailand etc. Most likely use the number of your own country
Incremental Merging: Incremental Merging:
--incremental-merge Performs an incremental merge of two exports. Requires setting both --source- --incremental-merge Performs an incremental merge of two exports. Requires setting both --source-dir and --target-
dir and --target-dir. The chats (JSON files only) and media from the source dir. The chats (JSON files only) and media from the source directory will be merged into the
directory will be merged into the target directory. No chat messages or media target directory. No chat messages or media will be deleted from the target directory; only
will be deleted from the target directory; only new chat messages and media new chat messages and media will be added to it. This enables chat messages and media to be
will be added to it. This enables chat messages and media to be deleted from deleted from the device to free up space, while ensuring they are preserved in the exported
the device to free up space, while ensuring they are preserved in the exported
backups. backups.
--source-dir SOURCE_DIR --source-dir SOURCE_DIR
Sets the source directory. Used for performing incremental merges. Sets the source directory. Used for performing incremental merges.
@@ -253,8 +255,9 @@ Miscellaneous:
Specify the chunk size for decrypting iOS backup, which may affect the decryption speed. Specify the chunk size for decrypting iOS backup, which may affect the decryption speed.
--max-bruteforce-worker MAX_BRUTEFORCE_WORKER --max-bruteforce-worker MAX_BRUTEFORCE_WORKER
Specify the maximum number of worker for bruteforce decryption. Specify the maximum number of worker for bruteforce decryption.
--no-banner Do not show the banner
WhatsApp Chat Exporter: 0.13.0rc1 Licensed with MIT. See https://wts.knugi.dev/docs?dest=osl for all open source WhatsApp Chat Exporter: 0.13.0rc2 Licensed with MIT. See https://wts.knugi.dev/docs?dest=osl for all open source
licenses. licenses.
``` ```

View File

@@ -42,7 +42,7 @@ WTSEXPORTER_BANNER = f"""=======================================================
╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
WhatsApp Chat Exporter: A customizable Android and iOS/iPadOS WhatsApp database parser WhatsApp Chat Exporter: A customizable Android and iOS/iPadOS WhatsApp database parser
Version: {__version__} {f"Version: {__version__}".center(104)}
========================================================================================================""" ========================================================================================================"""

View File

@@ -178,6 +178,17 @@ def messages(db, data, media_folder, timezone_offset, filter_date, filter_chat,
""" """
c.execute(messages_query) c.execute(messages_query)
reply_query = """SELECT ZSTANZAID,
ZTEXT,
ZTITLE
FROM ZWAMESSAGE
LEFT JOIN ZWAMEDIAITEM
ON ZWAMESSAGE.Z_PK = ZWAMEDIAITEM.ZMESSAGE
WHERE ZTEXT IS NOT NULL
OR ZTITLE IS NOT NULL;"""
cursor2.execute(reply_query)
message_map = {row[0][:17]: row[1] or row[2] for row in cursor2.fetchall() if row[0]}
# Process each message # Process each message
i = 0 i = 0
content = c.fetchone() content = c.fetchone()
@@ -207,7 +218,7 @@ def messages(db, data, media_folder, timezone_offset, filter_date, filter_chat,
) )
# Process message data # Process message data
invalid = process_message_data(message, content, is_group_message, data, cursor2, no_reply) invalid = process_message_data(message, content, is_group_message, data, message_map, no_reply)
# Add valid messages to chat # Add valid messages to chat
if not invalid: if not invalid:
@@ -221,7 +232,7 @@ def messages(db, data, media_folder, timezone_offset, filter_date, filter_chat,
logger.info(f"Processed {total_row_number} messages{CLEAR_LINE}") logger.info(f"Processed {total_row_number} messages{CLEAR_LINE}")
def process_message_data(message, content, is_group_message, data, cursor2, no_reply): def process_message_data(message, content, is_group_message, data, message_map, no_reply):
"""Process and set message data from content row.""" """Process and set message data from content row."""
# Handle group sender info # Handle group sender info
if is_group_message and content["ZISFROMME"] == 0: if is_group_message and content["ZISFROMME"] == 0:
@@ -247,14 +258,7 @@ def process_message_data(message, content, is_group_message, data, cursor2, no_r
if content["ZMETADATA"] is not None and content["ZMETADATA"].startswith(b"\x2a\x14") and not no_reply: if content["ZMETADATA"] is not None and content["ZMETADATA"].startswith(b"\x2a\x14") and not no_reply:
quoted = content["ZMETADATA"][2:19] quoted = content["ZMETADATA"][2:19]
message.reply = quoted.decode() message.reply = quoted.decode()
cursor2.execute(f"""SELECT ZTEXT message.quoted_data = message_map.get(message.reply)
FROM ZWAMESSAGE
WHERE ZSTANZAID LIKE '{message.reply}%'""")
quoted_content = cursor2.fetchone()
if quoted_content and "ZTEXT" in quoted_content:
message.quoted_data = quoted_content["ZTEXT"]
else:
message.quoted_data = None
# Handle stickers # Handle stickers
if content["ZMESSAGETYPE"] == 15: if content["ZMESSAGETYPE"] == 15:

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "whatsapp-chat-exporter" name = "whatsapp-chat-exporter"
version = "0.13.0rc1" version = "0.13.0rc2"
description = "A Whatsapp database parser that provides history of your Whatsapp conversations in HTML and JSON. Android, iOS, iPadOS, Crypt12, Crypt14, Crypt15 supported." description = "A Whatsapp database parser that provides history of your Whatsapp conversations in HTML and JSON. Android, iOS, iPadOS, Crypt12, Crypt14, Crypt15 supported."
readme = "README.md" readme = "README.md"
authors = [ authors = [