Compare commits

..

6 Commits

Author SHA1 Message Date
Knugi 9537b4c050 Update README.md 2026-07-27 15:10:59 +00:00
Knugi 2cc07b8b7c Update README.md 2026-07-27 13:24:33 +00:00
Knugi 776a887b47 Update pull_request_template.md 2026-07-20 16:15:05 +00:00
KnugiHK 68089f5ae4 Update actions 2026-07-18 13:41:38 +08:00
Knugi cac2a2464d Update outdated script reference in README (#213) 2026-05-18 15:03:06 +00:00
KnugiHK a0719bc2bf Update README.md 2026-01-24 23:05:00 +08:00
8 changed files with 26 additions and 84 deletions
+7 -2
View File
@@ -1,6 +1,11 @@
# Important Note # Pre-flight Check
**All PRs (except for changes unrelated to source files) should target and start from the `dev` branch.** **All pull requests (excluding those with changes unrelated to source files) must target and branch off from the `dev` branch.**
Please select the applicable option below:
- [ ] This PR does not modify any source files (e.g., only updates the README).
- [ ] This PR modifies one or more source files and targets the `dev` branch.
## Related Issue ## Related Issue
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v7
- name: Set up Python ${{ matrix.python-version }} on ${{ matrix.os }} - name: Set up Python ${{ matrix.python-version }} on ${{ matrix.os }}
uses: actions/setup-python@v5 uses: actions/setup-python@v5
+1 -1
View File
@@ -57,7 +57,7 @@ jobs:
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v7
# Add any setup steps before running the `github/codeql-action/init` action. # Add any setup steps before running the `github/codeql-action/init` action.
# This includes steps like installing compilers or runtimes (`actions/setup-node` # This includes steps like installing compilers or runtimes (`actions/setup-node`
+7 -7
View File
@@ -14,7 +14,7 @@ jobs:
linux: linux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
@@ -31,7 +31,7 @@ jobs:
--assume-yes-for-downloads Whatsapp_Chat_Exporter --output-filename=wtsexporter_linux_x64 --assume-yes-for-downloads Whatsapp_Chat_Exporter --output-filename=wtsexporter_linux_x64
sha256sum wtsexporter_linux_x64 sha256sum wtsexporter_linux_x64
- name: Generate artifact attestation - name: Generate artifact attestation
uses: actions/attest-build-provenance@v3 uses: actions/attest-build-provenance@v4
with: with:
subject-path: ./wtsexporter_linux_x64 subject-path: ./wtsexporter_linux_x64
- uses: actions/upload-artifact@v6 - uses: actions/upload-artifact@v6
@@ -42,7 +42,7 @@ jobs:
windows-x64: windows-x64:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
@@ -69,7 +69,7 @@ jobs:
windows-arm: windows-arm:
runs-on: windows-11-arm runs-on: windows-11-arm
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
@@ -96,7 +96,7 @@ jobs:
macos-arm: macos-arm:
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
@@ -125,7 +125,7 @@ jobs:
macos-intel: macos-intel:
runs-on: macos-15-intel runs-on: macos-15-intel
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
@@ -143,7 +143,7 @@ jobs:
mv wtsexporter wtsexporter_macos_x64 mv wtsexporter wtsexporter_macos_x64
shasum -a 256 wtsexporter_macos_x64 shasum -a 256 wtsexporter_macos_x64
- name: Generate artifact attestation - name: Generate artifact attestation
uses: actions/attest-build-provenance@v3 uses: actions/attest-build-provenance@v4
with: with:
subject-path: ./wtsexporter_macos_x64 subject-path: ./wtsexporter_macos_x64
- uses: actions/upload-artifact@v6 - uses: actions/upload-artifact@v6
+2 -2
View File
@@ -19,7 +19,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v7
- name: Set up Node.js - name: Set up Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v6
@@ -36,7 +36,7 @@ jobs:
- name: Deploy to gh-pages - name: Deploy to gh-pages
if: github.ref == 'refs/heads/main' # Ensure deployment only happens from main if: github.ref == 'refs/heads/main' # Ensure deployment only happens from main
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e uses: peaceiris/actions-gh-pages@84c30a85c19949d7eee79c4ff27748b70285e453 # v4.1.0
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs publish_dir: ./docs
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
+3 -3
View File
@@ -5,7 +5,7 @@
[![Matrix Chat Room](https://img.shields.io/matrix/wtsexporter:matrix.org.svg?label=Matrix%20Chat%20Room)](https://matrix.to/#/#wtsexporter:matrix.org) [![Matrix Chat Room](https://img.shields.io/matrix/wtsexporter:matrix.org.svg?label=Matrix%20Chat%20Room)](https://matrix.to/#/#wtsexporter:matrix.org)
[![Since 2021](https://img.shields.io/github/created-at/knugihk/WhatsApp-Chat-Exporter?label=Since&color=purple)](https://wts.knugi.dev) [![Since 2021](https://img.shields.io/github/created-at/knugihk/WhatsApp-Chat-Exporter?label=Since&color=purple)](https://wts.knugi.dev)
A customizable Android and iPhone Whatsapp database parser that will give you the history of your Whatsapp conversations in HTML and JSON. Inspired by [Telegram Chat Export Tool](https://telegram.org/blog/export-and-more). A customizable Android and iOS/iPadOS Whatsapp database parser that will give you the history of your Whatsapp conversations in HTML and JSON. Inspired by [Telegram Chat Export Tool](https://telegram.org/blog/export-and-more).
> [!TIP] > [!TIP]
> If you plan to uninstall WhatsApp or delete your WhatsApp account, please make a backup of your WhatsApp database. You may want to use this exporter again on the same database in the future as the exporter develops. > If you plan to uninstall WhatsApp or delete your WhatsApp account, please make a backup of your WhatsApp database. You may want to use this exporter again on the same database in the future as the exporter develops.
@@ -113,7 +113,7 @@ Do an iPhone/iPad Backup with iTunes/Finder first.
> [!NOTE] > [!NOTE]
> If you are working on unencrypted iOS/iPadOS backup, skip this. > If you are working on unencrypted iOS/iPadOS backup, skip this.
If you want to work on an encrypted iOS/iPadOS Backup, you should install iphone_backup_decrypt from [KnugiHK/iphone_backup_decrypt](https://github.com/KnugiHK/iphone_backup_decrypt) before you run the extract_iphone_media.py. If you want to work on an encrypted iOS/iPadOS Backup, you should install `iphone_backup_decrypt` from [KnugiHK/iphone_backup_decrypt](https://github.com/KnugiHK/iphone_backup_decrypt) before you run the `ios_media_handler.py`.
```sh ```sh
pip install git+https://github.com/KnugiHK/iphone_backup_decrypt pip install git+https://github.com/KnugiHK/iphone_backup_decrypt
``` ```
@@ -289,7 +289,7 @@ To ensure that the binaries provided in the releases were built directly from th
### Using Bash (Linux/WSL/macOS) ### Using Bash (Linux/WSL/macOS)
```bash ```bash
for file in wtsexporter*; do ; gh attestation verify "$file" -R KnugiHK/WhatsApp-Chat-Exporter; done for file in wtsexporter*; do gh attestation verify "$file" -R KnugiHK/WhatsApp-Chat-Exporter; done
``` ```
### Using PowerShell (Windows) ### Using PowerShell (Windows)
+4 -67
View File
@@ -932,26 +932,14 @@ def calls(db, data, timezone_offset, filter_chat):
c = db.cursor() c = db.cursor()
# Check if there are any calls that match the filter # Check if there are any calls that match the filter
# The order matters here, modern query should be attempted first, total_row_number = _get_calls_count(c, filter_chat)
# if it fails, we can be pretty sure that legacy one will work,
# but not the other way around. This is because legacy query is
# more simple and less likely to have issues with missing tables/columns.
try:
total_row_number = _get_calls_count_modern(c, filter_chat)
except sqlite3.OperationalError as e:
total_row_number = _get_calls_count_legacy(c, filter_chat)
if total_row_number == 0: if total_row_number == 0:
return return
logging.info(f"Processing calls...({total_row_number})", extra={"clear": True}) logging.info(f"Processing calls...({total_row_number})", extra={"clear": True})
# Fetch call data # Fetch call data
# Again, we try modern query first and fallback to legacy if it fails, calls_data = _fetch_calls_data(c, filter_chat)
# for the same reasons as above.
try:
calls_data = _fetch_calls_data_modern(c, filter_chat)
except sqlite3.OperationalError as e:
calls_data = _fetch_calls_data_legacy(c, filter_chat)
# Create a chat store for all calls # Create a chat store for all calls
chat = ChatStore(Device.ANDROID, "WhatsApp Calls") chat = ChatStore(Device.ANDROID, "WhatsApp Calls")
@@ -967,29 +955,7 @@ def calls(db, data, timezone_offset, filter_chat):
data.add_chat("000000000000000", chat) data.add_chat("000000000000000", chat)
logging.info(f"Processed {total_row_number} calls in {convert_time_unit(total_time)}") logging.info(f"Processed {total_row_number} calls in {convert_time_unit(total_time)}")
def _get_calls_count(c, filter_chat):
def _get_calls_count_legacy(c, filter_chat):
"""Get the count of call records that match the filter."""
# Build the filter conditions
include_filter = get_chat_condition(filter_chat[0], True, ["key_remote_jid"])
exclude_filter = get_chat_condition(filter_chat[1], False, ["key_remote_jid"])
query = f"""SELECT count(),
jid.raw_string as key_remote_jid
FROM call_log
INNER JOIN jid
ON call_log.jid_row_id = jid._id
LEFT JOIN chat
ON call_log.jid_row_id = chat.jid_row_id
WHERE 1=1
{include_filter}
{exclude_filter}"""
c.execute(query)
return c.fetchone()[0]
def _get_calls_count_modern(c, filter_chat):
"""Get the count of call records that match the filter.""" """Get the count of call records that match the filter."""
# Build the filter conditions # Build the filter conditions
@@ -1014,36 +980,7 @@ def _get_calls_count_modern(c, filter_chat):
return c.fetchone()[0] return c.fetchone()[0]
def _fetch_calls_data_legacy(c, filter_chat): def _fetch_calls_data(c, filter_chat):
"""Fetch call data from the database."""
# Build the filter conditions
include_filter = get_chat_condition(filter_chat[0], True, ["key_remote_jid"])
exclude_filter = get_chat_condition(filter_chat[1], False, ["key_remote_jid"])
query = f"""SELECT call_log._id,
jid.raw_string as key_remote_jid,
from_me,
call_id,
timestamp,
video_call,
duration,
call_result,
bytes_transferred,
chat.subject as chat_subject
FROM call_log
INNER JOIN jid
ON call_log.jid_row_id = jid._id
LEFT JOIN chat
ON call_log.jid_row_id = chat.jid_row_id
WHERE 1=1
{include_filter}
{exclude_filter}"""
c.execute(query)
return c
def _fetch_calls_data_modern(c, filter_chat):
"""Fetch call data from the database.""" """Fetch call data from the database."""
# Build the filter conditions # Build the filter conditions