Skip to content

Commit be99b15

Browse files
authored
Merge pull request #16 from de-nets/WhatsAppDB
Added WhatsApp DB import
2 parents 7af27e7 + b2ff6f1 commit be99b15

17 files changed

Lines changed: 629 additions & 34 deletions

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## 1.2.0
4+
5+
- Added WhatsApp import from DB
6+
- Added import reactions to signal
7+
- Updates docs
8+
39
## 1.1.0
410

511
- Added Telegram messages import

README.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ Import messages from other apps like Telegram or WhatsApp to Signal.
55
## Caveats
66

77
- Only tested on Android
8-
- No group text support
98
- The commands were run on macOS
109

1110
## Prerequisites
@@ -21,6 +20,7 @@ See: [Signal](docs/Signal.md)
2120
Import messages from:
2221

2322
- [Telegram](docs/Telegram.md)
23+
- [WhatApp DB](docs/WhatApp_DB.md)
2424
- [WhatApp export](docs/WhatApp_Export.md)
2525

2626
### Available commands
@@ -37,15 +37,34 @@ bin/move_to_signal.dart \
3737
-o build/move_to_signal_$(uname -s)_$(uname -m)
3838
```
3939

40+
## Feature Map
41+
42+
| Name | Telegram | WhatApp DB | WhatApp export |
43+
| :------------------------- | :------: | :--------: | :------------: |
44+
| All 1-on-1 text messages ||||
45+
| Group chats ||||
46+
| Original timestamps ||||
47+
| Reactions (emoji) ||||
48+
| Media (images/audio/links) ||||
49+
4050
## Known issues
4151

42-
### Language based date time format in Whatsapp exports
52+
### Language based date time format in WhatsApp exports
53+
54+
NOTE: This can be avoided by using the [WhatApp DB](docs/WhatApp_DB.md) import.
4355

44-
Whatsapp exports have language based time format in export file and for Android without seconds. In my case 01/12/2023, 23:59
56+
WhatsApp exports have language based time format in export file and for Android without seconds. In my case 01/12/2023, 23:59
4557
The new WhatsApp macOS App has a more usable format [01.12.23, 23:59:42] for the same message as Android, but in my case only loads the last 3 years of chat history.
4658

4759
Please open an issue with a small anonymized sample export and your device language. Or better open a PR with a fix. :)
4860

61+
### Missing messages form WhatsApp exports
62+
63+
NOTE: This can be avoided by using the [WhatApp DB](docs/WhatApp_DB.md) import.
64+
65+
If a message was part of a sent image the message won't be in the export.
66+
I opened an issue with WhatsApp, but don't know if this will ever be fixed.
67+
4968
## Sponsor this project
5069

5170
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://paypal.me/movetosignal/5)

bin/move_to_signal.dart

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import 'package:move_to_signal/import/signal.dart';
22
import 'package:move_to_signal/source/telegram.dart';
3-
import 'package:move_to_signal/source/whats_app.dart';
3+
import 'package:move_to_signal/source/whats_app_db.dart';
4+
import 'package:move_to_signal/source/whats_app_export.dart';
45

56
void main(List<String> arguments) {
6-
String command = 'ImportWhatsApp';
7+
String command = 'ImportWhatsAppExports';
78
bool verbose = false;
89

910
// Read all arguments
@@ -23,8 +24,14 @@ void main(List<String> arguments) {
2324
telegramImport.run(arguments);
2425

2526
break;
26-
case 'ImportWhatsApp':
27-
final whatsAppImport = WhatsApp();
27+
case 'ImportWhatsAppDb':
28+
final whatsAppImportDb = WhatsAppDb();
29+
whatsAppImportDb.verbose = verbose;
30+
whatsAppImportDb.run(arguments);
31+
32+
break;
33+
case 'ImportWhatsAppExports':
34+
final whatsAppImport = WhatsAppExport();
2835
whatsAppImport.verbose = verbose;
2936
whatsAppImport.run(arguments);
3037

docs/Commands.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
77
--command=
88
[ImportTelegram] For Telegram exports
9-
[ImportWhatsApp] For WhatsApp exports (default)
9+
[ImportWhatsAppDb] For WhatsApp db import (needs decrypted db with wa-crypt-tools)
10+
[ImportWhatsAppExports] For WhatsApp exports (default)
1011
[SignalDecrypt] Just to decrypt Signal backup file
1112
[SignalEncrypt] Just to encrypt Signal backup file
1213
@@ -35,6 +36,14 @@
3536
[Prepare] Prepare the import by extracting all conversations from telegramJson into separate files to review. (default)
3637
[Import] Imports the files from step [Prepare] into the Signal database.
3738
38-
--whatsappExports=
39-
Path to the WhatsApp export .txt file
39+
--whatsAppDb=
40+
Path to the WhatsApp msgstore.db file
41+
42+
--whatsAppExports=
43+
[ImportWhatsAppDb] Path where the WhatsApp .txt file are written
44+
[ImportWhatsAppExports] Path to the WhatsApp export .txt files
45+
46+
--whatsAppMode=
47+
[Prepare] Prepare the import by extracting all conversations from msgstore.db into separate files to review. (default)
48+
[Import] Imports the files from step [Prepare] into the Signal database.
4049
```

docs/Install.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
- Dependencies
44
- [Dart](https://dart.dev/) to run from source
55
- Download/Clone this git or download arm macOS binary
6-
- [signalbackup-tools](https://github.com/bepaald/signalbackup-tools) De and Encrypt the Signal backup
6+
- [signalbackup-tools](https://github.com/bepaald/signalbackup-tools) De- and Encrypt the Signal backup
7+
- [wa-crypt-tools](https://github.com/ElDavoo/wa-crypt-tools) Decrypt WhatsApp backup (Only needed to import from db)

docs/WhatApp_DB.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# WhatApp DB
2+
3+
Always start by creating a [Signal](docs/Signal.md) backup.
4+
5+
1. Create WhatsApp backup, decrypt the msgstore.db.crypt15 with [wa-crypt-tools](https://github.com/ElDavoo/wa-crypt-tools) and copy the msgstore.db to the working folder.
6+
7+
2. Run MoveToSignal in terminal for prepare the import
8+
9+
Mac arm64 binary
10+
11+
```bash
12+
cd path/to/working/folder/
13+
14+
path/to/MoveToSignal/move_to_signal_Darwin_arm64 \
15+
--command=ImportWhatsAppDb \
16+
--signalBackup=./signal-YYYY-MM-DD-HH-mm-ss.backup \
17+
--signalBackupKey=123451234512345123451234512345 \
18+
--signalPhoneNumber=+49123456789 \
19+
--whatsAppDb="path/to/msgstore.db" \
20+
--whatsAppExports=. \
21+
--whatsAppMode=Prepare \
22+
--verbose
23+
```
24+
25+
From source
26+
27+
```bash
28+
cd path/to/working/folder/
29+
30+
dart run path/to/MoveToSignal/bin/move_to_signal.dart \
31+
--command=ImportWhatsAppDb \
32+
--signalBackup=./signal-YYYY-MM-DD-HH-mm-ss.backup \
33+
--signalBackupKey=123451234512345123451234512345 \
34+
--signalPhoneNumber=+49123456789 \
35+
--whatsAppDb="path/to/msgstore.db" \
36+
--whatsAppExports=. \
37+
--whatsAppMode=Prepare \
38+
--verbose
39+
```
40+
41+
A new folder named WhatsAppExportsFolder will be created for the export files.
42+
WhatsApp exports will be named eg: +4912345678-(Screen name if found).txt
43+
44+
3. Rename exports
45+
46+
Please review the all .txt files and make sure to file names start with the contact phone number the user uses with Signal.
47+
At this point you can also merge files into one, if a user had multiple WhatsApp identities.
48+
Please delete all files you don't want to import.
49+
50+
All WhatsApp export files must be renamed like:
51+
contactPhoneNumber-Screen Name.txt
52+
53+
eg: +49123456789-Max ExampleName.txt
54+
55+
Only the phone number important for WhatsApp DB imports.
56+
The phone number needs to in international format starting with + and must only contain numbers.
57+
58+
4. Run MoveToSignal in terminal to import the prepared messages
59+
60+
Mac arm64 binary
61+
62+
```bash
63+
cd path/to/working/folder/
64+
65+
path/to/MoveToSignal/move_to_signal_Darwin_arm64 \
66+
--command=ImportWhatsAppDb \
67+
--signalBackup=./signal-YYYY-MM-DD-HH-mm-ss.backup \
68+
--signalBackupKey=123451234512345123451234512345 \
69+
--signalPhoneNumber=+49123456789 \
70+
--whatsAppExports=. \
71+
--whatsAppMode=Import \
72+
--verbose
73+
```
74+
75+
From source
76+
77+
```bash
78+
cd path/to/working/folder/
79+
80+
dart run path/to/MoveToSignal/bin/move_to_signal.dart \
81+
--command=ImportWhatsAppDb \
82+
--signalBackup=./signal-YYYY-MM-DD-HH-mm-ss.backup \
83+
--signalBackupKey=123451234512345123451234512345 \
84+
--signalPhoneNumber=+49123456789 \
85+
--whatsAppExports=. \
86+
--whatsAppMode=Import \
87+
--verbose
88+
```
89+
90+
Once done, a new Signal backup file is created, like: signal-signal-YYYY-MM-DD-HH-mm-ss.backup (new timestamp)
91+
92+
5. Follow the "After importing all messages" steps from [Signal](docs/Signal.md)

docs/WhatApp_Export.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Always start by creating a [Signal](docs/Signal.md) backup.
3939
--signalBackup=./signal-YYYY-MM-DD-HH-mm-ss.backup \
4040
--signalBackupKey=123451234512345123451234512345 \
4141
--signalPhoneNumber=+49123456789 \
42-
--whatsappExports=./whatsapp \
42+
--whatsAppExports=./whatsapp \
4343
--verbose
4444
```
4545

@@ -53,7 +53,7 @@ Always start by creating a [Signal](docs/Signal.md) backup.
5353
--signalBackup=./signal-YYYY-MM-DD-HH-mm-ss.backup \
5454
--signalBackupKey=123451234512345123451234512345 \
5555
--signalPhoneNumber=+49123456789 \
56-
--whatsappExports=./whatsapp \
56+
--whatsAppExports=./whatsapp \
5757
--verbose
5858
```
5959

lib/import/signal.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,15 @@ class Signal {
200200
'VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
201201
);
202202

203+
// Prepare a statement to run it multiple times:
204+
final reactionImport = _database!.prepare(
205+
'INSERT INTO reaction '
206+
'('
207+
'message_id, author_id, emoji, date_sent, date_received'
208+
') '
209+
'VALUES (?, ?, ?, ?, ?)',
210+
);
211+
203212
// Import all messages
204213
var counter = 0;
205214
var step = (_signalMessages.length / 10).ceil();
@@ -251,6 +260,25 @@ class Signal {
251260
signalMessageImportSuccess = true;
252261
}
253262

263+
// Get last insert id as signalMessageId
264+
int signalMessageId =
265+
_database!.select('select last_insert_rowid()').first.columnAt(0);
266+
267+
// Import reactions
268+
for (var reaction in signalMessage.reactions) {
269+
if (reaction.sendTimestamp == null) {
270+
continue;
271+
}
272+
273+
reactionImport.execute([
274+
signalMessageId,
275+
reaction.authorId,
276+
reaction.reaction,
277+
reaction.sendTimestamp,
278+
reaction.receivedTimestamp ?? reaction.sendTimestamp! + 500,
279+
]);
280+
}
281+
254282
if (!verbose) continue;
255283

256284
counter++;

lib/model/signal_message.dart

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'package:move_to_signal/model/signal_reaction.dart';
2+
13
class SignalMessage {
24
int messageDateTime = 0;
35
int? dateSent;
@@ -18,8 +20,10 @@ class SignalMessage {
1820
int unidentified = 1;
1921
int reactionsLastSeen = -1;
2022
int notifiedTimestamp = 0;
23+
List<SignalReaction> reactions = [];
2124

22-
Map<String, dynamic> toJson() => {
25+
@override
26+
String toString() => {
2327
"dateSent": dateSent,
2428
"dateReceived": dateReceived,
2529
"dateServer": dateServer,
@@ -38,11 +42,8 @@ class SignalMessage {
3842
"unidentified": unidentified,
3943
"reactionsLastSeen": reactionsLastSeen,
4044
"notifiedTimestamp": notifiedTimestamp,
41-
};
42-
43-
@override
44-
String toString() =>
45-
'$dateSent|$dateReceived|$dateServer|$threadId|$fromRecipientId|$fromDeviceId|$toRecipientId|$type|$body|$read|$mType|$st|$receiptTimestamp|$hasDeliveryReceipt|$hasReadReceipt|$unidentified|$reactionsLastSeen|$notifiedTimestamp';
45+
"reactions": reactions,
46+
}.toString();
4647

4748
void setReceived() {
4849
dateSent = messageDateTime;

lib/model/signal_reaction.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import 'dart:convert';
2+
3+
class SignalReaction {
4+
int? authorId;
5+
String? reaction;
6+
bool? fromMe;
7+
int? sendTimestamp;
8+
int? receivedTimestamp;
9+
10+
@override
11+
String toString() => {
12+
'"authorId"': authorId,
13+
'"reaction"': jsonEncode(reaction),
14+
'"fromMe"': fromMe,
15+
'"sendTimestamp"': sendTimestamp,
16+
'"receivedTimestamp"': receivedTimestamp,
17+
}.toString();
18+
}

0 commit comments

Comments
 (0)