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
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: Set up Python ${{ matrix.python-version }} on ${{ matrix.os }}
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
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v7
# Add any setup steps before running the `github/codeql-action/init` action.
# This includes steps like installing compilers or runtimes (`actions/setup-node`
+7 -7
View File
@@ -14,7 +14,7 @@ jobs:
linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v6
with:
@@ -31,7 +31,7 @@ jobs:
--assume-yes-for-downloads Whatsapp_Chat_Exporter --output-filename=wtsexporter_linux_x64
sha256sum wtsexporter_linux_x64
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v3
uses: actions/attest-build-provenance@v4
with:
subject-path: ./wtsexporter_linux_x64
- uses: actions/upload-artifact@v6
@@ -42,7 +42,7 @@ jobs:
windows-x64:
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v6
with:
@@ -69,7 +69,7 @@ jobs:
windows-arm:
runs-on: windows-11-arm
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v6
with:
@@ -96,7 +96,7 @@ jobs:
macos-arm:
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v6
with:
@@ -125,7 +125,7 @@ jobs:
macos-intel:
runs-on: macos-15-intel
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v6
with:
@@ -143,7 +143,7 @@ jobs:
mv wtsexporter wtsexporter_macos_x64
shasum -a 256 wtsexporter_macos_x64
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v3
uses: actions/attest-build-provenance@v4
with:
subject-path: ./wtsexporter_macos_x64
- uses: actions/upload-artifact@v6
+2 -2
View File
@@ -19,7 +19,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Set up Node.js
uses: actions/setup-node@v6
@@ -36,7 +36,7 @@ jobs:
- name: Deploy to gh-pages
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:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v5
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)
[![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]
> 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]
> 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
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)
```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)
+4 -67
View File
@@ -932,26 +932,14 @@ def calls(db, data, timezone_offset, filter_chat):
c = db.cursor()
# Check if there are any calls that match the filter
# The order matters here, modern query should be attempted first,
# 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)
total_row_number = _get_calls_count(c, filter_chat)
if total_row_number == 0:
return
logging.info(f"Processing calls...({total_row_number})", extra={"clear": True})
# Fetch call data
# Again, we try modern query first and fallback to legacy if it fails,
# 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)
calls_data = _fetch_calls_data(c, filter_chat)
# Create a chat store for all calls
chat = ChatStore(Device.ANDROID, "WhatsApp Calls")
@@ -967,29 +955,7 @@ def calls(db, data, timezone_offset, filter_chat):
data.add_chat("000000000000000", chat)
logging.info(f"Processed {total_row_number} calls in {convert_time_unit(total_time)}")
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):
def _get_calls_count(c, filter_chat):
"""Get the count of call records that match the filter."""
# Build the filter conditions
@@ -1014,36 +980,7 @@ def _get_calls_count_modern(c, filter_chat):
return c.fetchone()[0]
def _fetch_calls_data_legacy(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):
def _fetch_calls_data(c, filter_chat):
"""Fetch call data from the database."""
# Build the filter conditions