whatsapp-pi 1.0.48 → 1.0.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -13
- package/package.json +1 -1
- package/src/i18n.ts +48 -48
- package/src/services/incoming-message.resolver.ts +150 -150
- package/src/services/message.sender.ts +121 -121
- package/src/services/whatsapp.service.ts +665 -665
- package/src/ui/menu.handler.ts +18 -18
package/README.md
CHANGED
|
@@ -8,15 +8,15 @@ A WhatsApp integration extension for the **[Pi Coding Agent](https://github.com/
|
|
|
8
8
|
|
|
9
9
|
[](https://github.com/RaphaCastelloes/whatsapp-pi)
|
|
10
10
|
|
|
11
|
-
Pi is a powerful agentic AI coding assistant that operates in your terminal. This extension
|
|
11
|
+
Pi is a powerful agentic AI coding assistant that operates in your terminal. This extension lets you chat and pair-program with your Pi agent through WhatsApp, with message filtering, allowed contacts/groups, recents/history browsing, message detail/reply, group-only binding, and reliable message delivery.
|
|
12
12
|
|
|
13
13
|
## Features
|
|
14
14
|
|
|
15
15
|
- **Manual WhatsApp Connection**: QR code-based authentication with session persistence
|
|
16
|
-
- **
|
|
16
|
+
- **Allowed Contacts**: Control which phone numbers can interact with Pi
|
|
17
17
|
- Add contacts with optional names for easy identification
|
|
18
|
-
- View ignored numbers (not
|
|
19
|
-
- Manage aliases and print allowed
|
|
18
|
+
- View ignored numbers (not yet allowed) and add them when needed
|
|
19
|
+
- Manage aliases and print allowed contacts from the menu
|
|
20
20
|
- **Allowed Groups**: Control which WhatsApp groups can interact with Pi
|
|
21
21
|
- Add group JIDs with optional aliases
|
|
22
22
|
- Only groups in Allowed Groups are processed by the agent
|
|
@@ -48,18 +48,25 @@ To enable PDF reading capabilities (required for the agent to process documents)
|
|
|
48
48
|
pi install npm:whatsapp-pi
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
2. Start Pi
|
|
51
|
+
2. Start Pi:
|
|
52
52
|
```bash
|
|
53
53
|
pi
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
3. Open `/whatsapp` and choose **Connect / Reconnect WhatsApp**.
|
|
57
|
+
- QR appears only on first pair or after logoff.
|
|
58
|
+
|
|
59
|
+
4. Add the chat you will use with Pi to **Allowed Contacts** or **Allowed Groups**.
|
|
60
|
+
|
|
61
|
+
5. Send a message from that allowed chat to Pi.
|
|
62
|
+
- Pi replies in same thread.
|
|
63
|
+
- Use **Recents** only to browse history or reply manually.
|
|
64
|
+
|
|
65
|
+
After first pairing, you can start Pi with auto-connect enabled:
|
|
57
66
|
```bash
|
|
58
67
|
pi --whatsapp-pi-online
|
|
59
68
|
```
|
|
60
69
|
|
|
61
|
-
3. Use the menu to connect WhatsApp and manage allowed numbers and groups
|
|
62
|
-
|
|
63
70
|
## Development / Testing
|
|
64
71
|
|
|
65
72
|
If you are developing or testing the extension locally, you can clone the repository from [GitHub](https://github.com/RaphaCastelloes/whatsapp-pi):
|
|
@@ -86,6 +93,20 @@ To test startup auto-connect locally after you have already paired WhatsApp:
|
|
|
86
93
|
pi -e whatsapp-pi.ts --whatsapp-pi-online
|
|
87
94
|
```
|
|
88
95
|
|
|
96
|
+
## How It Works
|
|
97
|
+
|
|
98
|
+
- Pi processes **incoming** messages only from allowed contacts or allowed groups.
|
|
99
|
+
- **Recents** is history browser, not trigger.
|
|
100
|
+
- **Send Message** and `send_wa_message` are outbound only.
|
|
101
|
+
- If you message yourself, WhatsApp may show sent/read ticks, but that does not guarantee Pi will treat it as a trigger.
|
|
102
|
+
|
|
103
|
+
## WhatsApp Numbers and JIDs
|
|
104
|
+
|
|
105
|
+
- Contacts use phone format in UI: `+5511999999999`
|
|
106
|
+
- Internally, contacts map to JIDs like `5511999999999@s.whatsapp.net`
|
|
107
|
+
- Groups use JIDs like `120363012345@g.us`
|
|
108
|
+
- Recents may show normalized values from WhatsApp, so use **Print Contact** / **Print Group JID** and aliases to avoid confusion.
|
|
109
|
+
|
|
89
110
|
## Commands
|
|
90
111
|
|
|
91
112
|
- `/whatsapp` - Open the WhatsApp management menu
|
|
@@ -95,12 +116,12 @@ pi -e whatsapp-pi.ts --whatsapp-pi-online
|
|
|
95
116
|
- **Disconnect WhatsApp** - Stop WhatsApp connection
|
|
96
117
|
- **Logoff (Delete Session)** - Remove all credentials and session data
|
|
97
118
|
- **Recents** - Open recent conversations, view history, and reply
|
|
98
|
-
- **Allowed
|
|
119
|
+
- **Allowed Contacts** - Manage contacts that can interact with Pi
|
|
99
120
|
- **Allowed Groups** - Manage WhatsApp groups that can interact with Pi
|
|
100
121
|
|
|
101
|
-
### Allowed
|
|
102
|
-
- **Add
|
|
103
|
-
- **Select a contact** - Open a submenu with **History**, **Send Message**, **Print
|
|
122
|
+
### Allowed Contacts Management
|
|
123
|
+
- **Add Contact** - Add a new contact to the allowed contacts list (format: +5511999999999)
|
|
124
|
+
- **Select a contact** - Open a submenu with **History**, **Send Message**, **Print Contact**, alias actions, **Remove Contact**, and **Back**
|
|
104
125
|
- **Back** - Return to main menu
|
|
105
126
|
|
|
106
127
|
### Allowed Groups Management
|
|
@@ -112,7 +133,7 @@ pi -e whatsapp-pi.ts --whatsapp-pi-online
|
|
|
112
133
|
- **History** - Open full message history for that conversation
|
|
113
134
|
- **Send Message** - Send a new message without Pi suffix
|
|
114
135
|
- **Reply** - Open message detail, then press `R` to reply
|
|
115
|
-
- **Allow
|
|
136
|
+
- **Allow Contact / Allow Group** - Move a recent sender into the appropriate allowed list
|
|
116
137
|
- **Remove Alias** - Clear saved alias for that sender
|
|
117
138
|
- **Back** - Return to main menu
|
|
118
139
|
|
package/package.json
CHANGED
package/src/i18n.ts
CHANGED
|
@@ -53,7 +53,7 @@ const fallback = {
|
|
|
53
53
|
"service.whatsapp.failedMarkRead": "Failed to mark message as read:",
|
|
54
54
|
"menu.whatsapp.title": "WhatsApp (Status: {status})",
|
|
55
55
|
"menu.root.recents": "Recents",
|
|
56
|
-
"menu.root.allowedNumbers": "Allowed
|
|
56
|
+
"menu.root.allowedNumbers": "Allowed Contacts",
|
|
57
57
|
"menu.root.allowedGroups": "Allowed Groups",
|
|
58
58
|
"menu.root.disconnectWhatsApp": "Disconnect WhatsApp",
|
|
59
59
|
"menu.root.connectWhatsApp": "Connect WhatsApp",
|
|
@@ -67,25 +67,25 @@ const fallback = {
|
|
|
67
67
|
"menu.root.logoffTitle": "Logoff",
|
|
68
68
|
"menu.root.logoffConfirmMessage": "Delete all credentials?",
|
|
69
69
|
"menu.root.loggedOffAndDeleted": "Logged off and credentials deleted",
|
|
70
|
-
"menu.allowed.printAllowedNumbersTitle": "[WhatsApp-Pi] Allowed
|
|
71
|
-
"menu.allowed.title": "Allowed
|
|
72
|
-
"menu.allowed.addNumber": "Add
|
|
73
|
-
"menu.allowed.enterNumber": "Enter
|
|
74
|
-
"menu.allowed.invalidNumber": "Invalid
|
|
75
|
-
"menu.allowed.contact.title": "Allowed
|
|
70
|
+
"menu.allowed.printAllowedNumbersTitle": "[WhatsApp-Pi] Allowed contacts",
|
|
71
|
+
"menu.allowed.title": "Allowed Contacts",
|
|
72
|
+
"menu.allowed.addNumber": "Add Contact",
|
|
73
|
+
"menu.allowed.enterNumber": "Enter contact (e.g. +5511999999999):",
|
|
74
|
+
"menu.allowed.invalidNumber": "Invalid contact format",
|
|
75
|
+
"menu.allowed.contact.title": "Allowed Contact • {displayName}",
|
|
76
76
|
"menu.allowed.contact.history": "History",
|
|
77
77
|
"menu.allowed.contact.sendMessage": "Send Message",
|
|
78
|
-
"menu.allowed.contact.printNumber": "Print
|
|
78
|
+
"menu.allowed.contact.printNumber": "Print Contact",
|
|
79
79
|
"menu.allowed.contact.removeAlias": "Remove Alias",
|
|
80
80
|
"menu.allowed.contact.addAlias": "Add Alias",
|
|
81
|
-
"menu.allowed.contact.removeNumber": "Remove
|
|
81
|
+
"menu.allowed.contact.removeNumber": "Remove Contact",
|
|
82
82
|
"menu.allowed.contact.back": "Back",
|
|
83
83
|
"menu.allowed.enterAlias": "Enter alias for {number}:",
|
|
84
84
|
"menu.allowed.pleaseEnterAlias": "Please enter an alias.",
|
|
85
85
|
"menu.allowed.aliasAdded": "Alias added for {number}",
|
|
86
86
|
"menu.allowed.aliasRemoved": "Alias removed for {number}",
|
|
87
|
-
"menu.allowed.removeConfirmTitle": "Remove
|
|
88
|
-
"menu.allowed.removeConfirmMessage": "Remove {displayName} from Allowed
|
|
87
|
+
"menu.allowed.removeConfirmTitle": "Remove Contact",
|
|
88
|
+
"menu.allowed.removeConfirmMessage": "Remove {displayName} from Allowed Contacts?",
|
|
89
89
|
"menu.allowed.removed": "Removed {displayName}",
|
|
90
90
|
"menu.allowed.alreadyAllowed": "{number} is already in the allow list",
|
|
91
91
|
"menu.allowed.addedToAllowList": "Added {number} to the allow list",
|
|
@@ -118,7 +118,7 @@ const fallback = {
|
|
|
118
118
|
"menu.recents.empty": "No recent individual conversations yet.",
|
|
119
119
|
"menu.recents.contact.title": "Recents • {displayName}",
|
|
120
120
|
"menu.recents.contact.history": "History",
|
|
121
|
-
"menu.recents.contact.allowNumber": "Allow
|
|
121
|
+
"menu.recents.contact.allowNumber": "Allow Contact",
|
|
122
122
|
"menu.recents.contact.allowGroup": "Allow Group",
|
|
123
123
|
"menu.recents.contact.sendMessage": "Send Message",
|
|
124
124
|
"menu.recents.contact.removeAlias": "Remove Alias",
|
|
@@ -255,7 +255,7 @@ const translations: Record<Locale, Partial<Record<Key, string>>> = {
|
|
|
255
255
|
"service.whatsapp.failedMarkRead": "Falha ao marcar mensagem como lida:",
|
|
256
256
|
"menu.whatsapp.title": "WhatsApp (Status: {status})",
|
|
257
257
|
"menu.root.recents": "Recentes",
|
|
258
|
-
"menu.root.allowedNumbers": "
|
|
258
|
+
"menu.root.allowedNumbers": "Contactos permitidos",
|
|
259
259
|
"menu.root.allowedGroups": "Grupos permitidos",
|
|
260
260
|
"menu.root.disconnectWhatsApp": "Desconectar WhatsApp",
|
|
261
261
|
"menu.root.connectWhatsApp": "Conectar WhatsApp",
|
|
@@ -269,25 +269,25 @@ const translations: Record<Locale, Partial<Record<Key, string>>> = {
|
|
|
269
269
|
"menu.root.logoffTitle": "Fazer logoff",
|
|
270
270
|
"menu.root.logoffConfirmMessage": "Excluir todas as credenciais?",
|
|
271
271
|
"menu.root.loggedOffAndDeleted": "Logout feito e credenciais excluídas",
|
|
272
|
-
"menu.allowed.printAllowedNumbersTitle": "[WhatsApp-Pi]
|
|
273
|
-
"menu.allowed.title": "
|
|
274
|
-
"menu.allowed.addNumber": "Adicionar
|
|
275
|
-
"menu.allowed.enterNumber": "Digite o
|
|
276
|
-
"menu.allowed.invalidNumber": "Formato de
|
|
277
|
-
"menu.allowed.contact.title": "
|
|
272
|
+
"menu.allowed.printAllowedNumbersTitle": "[WhatsApp-Pi] Contactos permitidos",
|
|
273
|
+
"menu.allowed.title": "Contactos permitidos",
|
|
274
|
+
"menu.allowed.addNumber": "Adicionar contato",
|
|
275
|
+
"menu.allowed.enterNumber": "Digite o contato (ex.: +5511999999999):",
|
|
276
|
+
"menu.allowed.invalidNumber": "Formato de contacto inválido",
|
|
277
|
+
"menu.allowed.contact.title": "Contacto permitido • {displayName}",
|
|
278
278
|
"menu.allowed.contact.history": "Histórico",
|
|
279
279
|
"menu.allowed.contact.sendMessage": "Enviar mensagem",
|
|
280
|
-
"menu.allowed.contact.printNumber": "Mostrar
|
|
280
|
+
"menu.allowed.contact.printNumber": "Mostrar contacto",
|
|
281
281
|
"menu.allowed.contact.removeAlias": "Remover apelido",
|
|
282
282
|
"menu.allowed.contact.addAlias": "Adicionar apelido",
|
|
283
|
-
"menu.allowed.contact.removeNumber": "Remover
|
|
283
|
+
"menu.allowed.contact.removeNumber": "Remover contato",
|
|
284
284
|
"menu.allowed.contact.back": "Voltar",
|
|
285
285
|
"menu.allowed.enterAlias": "Digite apelido para {number}:",
|
|
286
286
|
"menu.allowed.pleaseEnterAlias": "Digite um apelido.",
|
|
287
287
|
"menu.allowed.aliasAdded": "Apelido adicionado para {number}",
|
|
288
288
|
"menu.allowed.aliasRemoved": "Apelido removido para {number}",
|
|
289
|
-
"menu.allowed.removeConfirmTitle": "Remover
|
|
290
|
-
"menu.allowed.removeConfirmMessage": "Remover {displayName} dos
|
|
289
|
+
"menu.allowed.removeConfirmTitle": "Remover contato",
|
|
290
|
+
"menu.allowed.removeConfirmMessage": "Remover {displayName} dos Contactos permitidos?",
|
|
291
291
|
"menu.allowed.removed": "Removido {displayName}",
|
|
292
292
|
"menu.allowed.alreadyAllowed": "{number} já está na lista permitida",
|
|
293
293
|
"menu.allowed.addedToAllowList": "Adicionado {number} à lista permitida",
|
|
@@ -320,7 +320,7 @@ const translations: Record<Locale, Partial<Record<Key, string>>> = {
|
|
|
320
320
|
"menu.recents.empty": "Ainda não há conversas individuais recentes.",
|
|
321
321
|
"menu.recents.contact.title": "Recentes • {displayName}",
|
|
322
322
|
"menu.recents.contact.history": "Histórico",
|
|
323
|
-
"menu.recents.contact.allowNumber": "Permitir
|
|
323
|
+
"menu.recents.contact.allowNumber": "Permitir contacto",
|
|
324
324
|
"menu.recents.contact.allowGroup": "Permitir grupo",
|
|
325
325
|
"menu.recents.contact.sendMessage": "Enviar mensagem",
|
|
326
326
|
"menu.recents.contact.removeAlias": "Remover apelido",
|
|
@@ -420,7 +420,7 @@ const translations: Record<Locale, Partial<Record<Key, string>>> = {
|
|
|
420
420
|
"service.whatsapp.failedMarkRead": "Error al marcar mensaje como leído:",
|
|
421
421
|
"menu.whatsapp.title": "WhatsApp (Estado: {status})",
|
|
422
422
|
"menu.root.recents": "Recientes",
|
|
423
|
-
"menu.root.allowedNumbers": "
|
|
423
|
+
"menu.root.allowedNumbers": "Contactos permitidos",
|
|
424
424
|
"menu.root.allowedGroups": "Grupos permitidos",
|
|
425
425
|
"menu.root.disconnectWhatsApp": "Desconectar WhatsApp",
|
|
426
426
|
"menu.root.connectWhatsApp": "Conectar WhatsApp",
|
|
@@ -434,25 +434,25 @@ const translations: Record<Locale, Partial<Record<Key, string>>> = {
|
|
|
434
434
|
"menu.root.logoffTitle": "Cerrar sesión",
|
|
435
435
|
"menu.root.logoffConfirmMessage": "¿Eliminar todas las credenciales?",
|
|
436
436
|
"menu.root.loggedOffAndDeleted": "Sesión cerrada y credenciales eliminadas",
|
|
437
|
-
"menu.allowed.printAllowedNumbersTitle": "[WhatsApp-Pi]
|
|
438
|
-
"menu.allowed.title": "
|
|
439
|
-
"menu.allowed.addNumber": "Agregar
|
|
440
|
-
"menu.allowed.enterNumber": "Ingresa el
|
|
441
|
-
"menu.allowed.invalidNumber": "Formato de
|
|
442
|
-
"menu.allowed.contact.title": "
|
|
437
|
+
"menu.allowed.printAllowedNumbersTitle": "[WhatsApp-Pi] Contactos permitidos",
|
|
438
|
+
"menu.allowed.title": "Contactos permitidos",
|
|
439
|
+
"menu.allowed.addNumber": "Agregar contacto",
|
|
440
|
+
"menu.allowed.enterNumber": "Ingresa el contacto (p. ej. +5511999999999):",
|
|
441
|
+
"menu.allowed.invalidNumber": "Formato de contacto inválido",
|
|
442
|
+
"menu.allowed.contact.title": "Contacto permitido • {displayName}",
|
|
443
443
|
"menu.allowed.contact.history": "Historial",
|
|
444
444
|
"menu.allowed.contact.sendMessage": "Enviar mensaje",
|
|
445
|
-
"menu.allowed.contact.printNumber": "Mostrar
|
|
445
|
+
"menu.allowed.contact.printNumber": "Mostrar contacto",
|
|
446
446
|
"menu.allowed.contact.removeAlias": "Quitar alias",
|
|
447
447
|
"menu.allowed.contact.addAlias": "Agregar alias",
|
|
448
|
-
"menu.allowed.contact.removeNumber": "Quitar
|
|
448
|
+
"menu.allowed.contact.removeNumber": "Quitar contacto",
|
|
449
449
|
"menu.allowed.contact.back": "Volver",
|
|
450
450
|
"menu.allowed.enterAlias": "Ingresa alias para {number}:",
|
|
451
451
|
"menu.allowed.pleaseEnterAlias": "Ingresa un alias.",
|
|
452
452
|
"menu.allowed.aliasAdded": "Alias agregado para {number}",
|
|
453
453
|
"menu.allowed.aliasRemoved": "Alias quitado para {number}",
|
|
454
|
-
"menu.allowed.removeConfirmTitle": "Quitar
|
|
455
|
-
"menu.allowed.removeConfirmMessage": "¿Quitar {displayName} de
|
|
454
|
+
"menu.allowed.removeConfirmTitle": "Quitar contacto",
|
|
455
|
+
"menu.allowed.removeConfirmMessage": "¿Quitar {displayName} de Contactos permitidos?",
|
|
456
456
|
"menu.allowed.removed": "Quitado {displayName}",
|
|
457
457
|
"menu.allowed.alreadyAllowed": "{number} ya está en la lista permitida",
|
|
458
458
|
"menu.allowed.addedToAllowList": "Agregado {number} a la lista permitida",
|
|
@@ -464,7 +464,7 @@ const translations: Record<Locale, Partial<Record<Key, string>>> = {
|
|
|
464
464
|
"menu.recents.empty": "Todavía no hay conversaciones individuales recientes.",
|
|
465
465
|
"menu.recents.contact.title": "Recientes • {displayName}",
|
|
466
466
|
"menu.recents.contact.history": "Historial",
|
|
467
|
-
"menu.recents.contact.allowNumber": "Permitir
|
|
467
|
+
"menu.recents.contact.allowNumber": "Permitir contacto",
|
|
468
468
|
"menu.recents.contact.sendMessage": "Enviar mensaje",
|
|
469
469
|
"menu.recents.contact.removeAlias": "Quitar alias",
|
|
470
470
|
"menu.recents.contact.back": "Volver",
|
|
@@ -547,7 +547,7 @@ const translations: Record<Locale, Partial<Record<Key, string>>> = {
|
|
|
547
547
|
"service.whatsapp.failedMarkRead": "Échec du marquage du message comme lu :",
|
|
548
548
|
"menu.whatsapp.title": "WhatsApp (Statut: {status})",
|
|
549
549
|
"menu.root.recents": "Récents",
|
|
550
|
-
"menu.root.allowedNumbers": "
|
|
550
|
+
"menu.root.allowedNumbers": "Contacts autorisés",
|
|
551
551
|
"menu.root.allowedGroups": "Groupes autorisés",
|
|
552
552
|
"menu.root.disconnectWhatsApp": "Déconnecter WhatsApp",
|
|
553
553
|
"menu.root.connectWhatsApp": "Connecter WhatsApp",
|
|
@@ -561,25 +561,25 @@ const translations: Record<Locale, Partial<Record<Key, string>>> = {
|
|
|
561
561
|
"menu.root.logoffTitle": "Déconnexion",
|
|
562
562
|
"menu.root.logoffConfirmMessage": "Supprimer toutes les informations d’identification ?",
|
|
563
563
|
"menu.root.loggedOffAndDeleted": "Déconnexion effectuée et identifiants supprimés",
|
|
564
|
-
"menu.allowed.printAllowedNumbersTitle": "[WhatsApp-Pi]
|
|
565
|
-
"menu.allowed.title": "
|
|
566
|
-
"menu.allowed.addNumber": "Ajouter un
|
|
567
|
-
"menu.allowed.enterNumber": "Entrez le
|
|
568
|
-
"menu.allowed.invalidNumber": "Format de
|
|
569
|
-
"menu.allowed.contact.title": "
|
|
564
|
+
"menu.allowed.printAllowedNumbersTitle": "[WhatsApp-Pi] Contacts autorisés",
|
|
565
|
+
"menu.allowed.title": "Contacts autorisés",
|
|
566
|
+
"menu.allowed.addNumber": "Ajouter un contact",
|
|
567
|
+
"menu.allowed.enterNumber": "Entrez le contact (ex. +5511999999999) :",
|
|
568
|
+
"menu.allowed.invalidNumber": "Format de contact invalide",
|
|
569
|
+
"menu.allowed.contact.title": "Contact autorisé • {displayName}",
|
|
570
570
|
"menu.allowed.contact.history": "Historique",
|
|
571
571
|
"menu.allowed.contact.sendMessage": "Envoyer un message",
|
|
572
|
-
"menu.allowed.contact.printNumber": "Afficher le
|
|
572
|
+
"menu.allowed.contact.printNumber": "Afficher le contact",
|
|
573
573
|
"menu.allowed.contact.removeAlias": "Supprimer l’alias",
|
|
574
574
|
"menu.allowed.contact.addAlias": "Ajouter un alias",
|
|
575
|
-
"menu.allowed.contact.removeNumber": "Supprimer le
|
|
575
|
+
"menu.allowed.contact.removeNumber": "Supprimer le contact",
|
|
576
576
|
"menu.allowed.contact.back": "Retour",
|
|
577
577
|
"menu.allowed.enterAlias": "Entrez un alias pour {number} :",
|
|
578
578
|
"menu.allowed.pleaseEnterAlias": "Veuillez saisir un alias.",
|
|
579
579
|
"menu.allowed.aliasAdded": "Alias ajouté pour {number}",
|
|
580
580
|
"menu.allowed.aliasRemoved": "Alias supprimé pour {number}",
|
|
581
|
-
"menu.allowed.removeConfirmTitle": "Supprimer le
|
|
582
|
-
"menu.allowed.removeConfirmMessage": "Supprimer {displayName} des
|
|
581
|
+
"menu.allowed.removeConfirmTitle": "Supprimer le contact",
|
|
582
|
+
"menu.allowed.removeConfirmMessage": "Supprimer {displayName} des Contacts autorisés ?",
|
|
583
583
|
"menu.allowed.removed": "Supprimé {displayName}",
|
|
584
584
|
"menu.allowed.alreadyAllowed": "{number} est déjà dans la liste autorisée",
|
|
585
585
|
"menu.allowed.addedToAllowList": "Ajouté {number} à la liste autorisée",
|
|
@@ -591,7 +591,7 @@ const translations: Record<Locale, Partial<Record<Key, string>>> = {
|
|
|
591
591
|
"menu.recents.empty": "Aucune conversation individuelle récente.",
|
|
592
592
|
"menu.recents.contact.title": "Récents • {displayName}",
|
|
593
593
|
"menu.recents.contact.history": "Historique",
|
|
594
|
-
"menu.recents.contact.allowNumber": "Autoriser le
|
|
594
|
+
"menu.recents.contact.allowNumber": "Autoriser le contact",
|
|
595
595
|
"menu.recents.contact.sendMessage": "Envoyer un message",
|
|
596
596
|
"menu.recents.contact.removeAlias": "Supprimer l’alias",
|
|
597
597
|
"menu.recents.contact.back": "Retour",
|
|
@@ -1,150 +1,150 @@
|
|
|
1
|
-
import { t } from '../i18n.js';
|
|
2
|
-
import { extractMessageContent } from 'baileys';
|
|
3
|
-
|
|
4
|
-
export type IncomingResolution =
|
|
5
|
-
| { kind: 'text'; text: string }
|
|
6
|
-
| { kind: 'audio'; text: string; audioMessage: any }
|
|
7
|
-
| { kind: 'image'; text: string; imageMessage: any }
|
|
8
|
-
| { kind: 'document'; text: string; documentMessage: any }
|
|
9
|
-
| { kind: 'contact'; text: string }
|
|
10
|
-
| { kind: 'location'; text: string }
|
|
11
|
-
| { kind: 'system'; text: string }
|
|
12
|
-
| { kind: 'unsupported'; text: string };
|
|
13
|
-
|
|
14
|
-
const protocolTypes: Record<number, keyof typeof protocolLabels> = {
|
|
15
|
-
0: 'messageDeleted',
|
|
16
|
-
3: 'disappearingMessagesUpdated',
|
|
17
|
-
4: 'disappearingMessageSyncResponse',
|
|
18
|
-
5: 'historySyncNotification',
|
|
19
|
-
6: 'appStateSyncKeyShare',
|
|
20
|
-
7: 'appStateSyncKeyRequest',
|
|
21
|
-
8: 'messageBackfillRequest',
|
|
22
|
-
9: 'securityNotificationSync',
|
|
23
|
-
10: 'fatalAppStateSyncNotification',
|
|
24
|
-
11: 'phoneNumberShared',
|
|
25
|
-
14: 'messageEdited',
|
|
26
|
-
16: 'peerDataRequest',
|
|
27
|
-
17: 'peerDataResponse',
|
|
28
|
-
18: 'welcomeMessageRequest',
|
|
29
|
-
19: 'botFeedback',
|
|
30
|
-
20: 'mediaNotification'
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const protocolLabels = {
|
|
34
|
-
messageDeleted: t('incoming.protocol.messageDeleted'),
|
|
35
|
-
disappearingMessagesUpdated: t('incoming.protocol.disappearingMessagesUpdated'),
|
|
36
|
-
disappearingMessageSyncResponse: t('incoming.protocol.disappearingMessageSyncResponse'),
|
|
37
|
-
historySyncNotification: t('incoming.protocol.historySyncNotification'),
|
|
38
|
-
appStateSyncKeyShare: t('incoming.protocol.appStateSyncKeyShare'),
|
|
39
|
-
appStateSyncKeyRequest: t('incoming.protocol.appStateSyncKeyRequest'),
|
|
40
|
-
messageBackfillRequest: t('incoming.protocol.messageBackfillRequest'),
|
|
41
|
-
securityNotificationSync: t('incoming.protocol.securityNotificationSync'),
|
|
42
|
-
fatalAppStateSyncNotification: t('incoming.protocol.fatalAppStateSyncNotification'),
|
|
43
|
-
phoneNumberShared: t('incoming.protocol.phoneNumberShared'),
|
|
44
|
-
messageEdited: t('incoming.protocol.messageEdited'),
|
|
45
|
-
peerDataRequest: t('incoming.protocol.peerDataRequest'),
|
|
46
|
-
peerDataResponse: t('incoming.protocol.peerDataResponse'),
|
|
47
|
-
welcomeMessageRequest: t('incoming.protocol.welcomeMessageRequest'),
|
|
48
|
-
botFeedback: t('incoming.protocol.botFeedback'),
|
|
49
|
-
mediaNotification: t('incoming.protocol.mediaNotification')
|
|
50
|
-
} as const;
|
|
51
|
-
|
|
52
|
-
const unwrapMessageContent = (content: any): any => extractMessageContent(content) ?? content;
|
|
53
|
-
|
|
54
|
-
const getTypeName = (payload: any): string => {
|
|
55
|
-
if (!payload || typeof payload !== 'object') return 'unknown';
|
|
56
|
-
return Object.keys(payload)[0] || 'unknown';
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const formatProtocolMessage = (protocolMessage: any): string => {
|
|
60
|
-
const typeLabelKey = protocolTypes[Number(protocolMessage?.type)];
|
|
61
|
-
const typeLabel = typeLabelKey ? protocolLabels[typeLabelKey] : t('incoming.protocol.systemUpdate');
|
|
62
|
-
const editedText = protocolMessage?.editedMessage?.conversation
|
|
63
|
-
|| protocolMessage?.editedMessage?.extendedTextMessage?.text;
|
|
64
|
-
|
|
65
|
-
if (editedText) {
|
|
66
|
-
return `[${typeLabel}: ${editedText}]`;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return `[${typeLabel}]`;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export const extractIncomingText = (message: any): IncomingResolution => {
|
|
73
|
-
const content = unwrapMessageContent(message);
|
|
74
|
-
const inner = content?.ephemeralMessage?.message
|
|
75
|
-
|| content?.viewOnceMessage?.message
|
|
76
|
-
|| content?.viewOnceMessageV2?.message
|
|
77
|
-
|| content?.viewOnceMessageV2Extension?.message
|
|
78
|
-
|| content?.message;
|
|
79
|
-
|
|
80
|
-
const resolved = inner ? unwrapMessageContent(inner) : content;
|
|
81
|
-
const typeName = getTypeName(resolved);
|
|
82
|
-
const protocolMessage = resolved?.protocolMessage
|
|
83
|
-
|| (typeName === 'protocolMessage' ? resolved : undefined)
|
|
84
|
-
|| content?.protocolMessage;
|
|
85
|
-
|
|
86
|
-
if (protocolMessage) {
|
|
87
|
-
return { kind: 'system', text: formatProtocolMessage(protocolMessage) };
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (resolved?.conversation) {
|
|
91
|
-
return { kind: 'text', text: resolved.conversation };
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (resolved?.extendedTextMessage?.text) {
|
|
95
|
-
return { kind: 'text', text: resolved.extendedTextMessage.text };
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (resolved?.imageMessage) {
|
|
99
|
-
return {
|
|
100
|
-
kind: 'image',
|
|
101
|
-
text: resolved.imageMessage.caption || t('incoming.media.image'),
|
|
102
|
-
imageMessage: resolved.imageMessage
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (resolved?.videoMessage) {
|
|
107
|
-
return {
|
|
108
|
-
kind: 'text',
|
|
109
|
-
text: resolved.videoMessage.caption || t('incoming.media.video')
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (resolved?.audioMessage) {
|
|
114
|
-
return {
|
|
115
|
-
kind: 'audio',
|
|
116
|
-
text: t('incoming.media.audio'),
|
|
117
|
-
audioMessage: resolved.audioMessage
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (resolved?.documentMessage) {
|
|
122
|
-
return {
|
|
123
|
-
kind: 'document',
|
|
124
|
-
text: resolved.documentMessage.caption || t('incoming.media.document'),
|
|
125
|
-
documentMessage: resolved.documentMessage
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (resolved?.contactMessage || resolved?.contactsArrayMessage) {
|
|
130
|
-
return { kind: 'contact', text: t('incoming.media.contact') };
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (resolved?.locationMessage) {
|
|
134
|
-
return { kind: 'location', text: t('incoming.media.location') };
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (resolved?.buttonsResponseMessage?.selectedDisplayText) {
|
|
138
|
-
return { kind: 'text', text: resolved.buttonsResponseMessage.selectedDisplayText };
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (resolved?.listResponseMessage?.title) {
|
|
142
|
-
return { kind: 'text', text: resolved.listResponseMessage.title };
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (resolved?.templateButtonReplyMessage?.selectedDisplayText) {
|
|
146
|
-
return { kind: 'text', text: resolved.templateButtonReplyMessage.selectedDisplayText };
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return { kind: 'unsupported', text: t('incoming.media.unsupported', { typeName }) };
|
|
150
|
-
};
|
|
1
|
+
import { t } from '../i18n.js';
|
|
2
|
+
import { extractMessageContent } from 'baileys';
|
|
3
|
+
|
|
4
|
+
export type IncomingResolution =
|
|
5
|
+
| { kind: 'text'; text: string }
|
|
6
|
+
| { kind: 'audio'; text: string; audioMessage: any }
|
|
7
|
+
| { kind: 'image'; text: string; imageMessage: any }
|
|
8
|
+
| { kind: 'document'; text: string; documentMessage: any }
|
|
9
|
+
| { kind: 'contact'; text: string }
|
|
10
|
+
| { kind: 'location'; text: string }
|
|
11
|
+
| { kind: 'system'; text: string }
|
|
12
|
+
| { kind: 'unsupported'; text: string };
|
|
13
|
+
|
|
14
|
+
const protocolTypes: Record<number, keyof typeof protocolLabels> = {
|
|
15
|
+
0: 'messageDeleted',
|
|
16
|
+
3: 'disappearingMessagesUpdated',
|
|
17
|
+
4: 'disappearingMessageSyncResponse',
|
|
18
|
+
5: 'historySyncNotification',
|
|
19
|
+
6: 'appStateSyncKeyShare',
|
|
20
|
+
7: 'appStateSyncKeyRequest',
|
|
21
|
+
8: 'messageBackfillRequest',
|
|
22
|
+
9: 'securityNotificationSync',
|
|
23
|
+
10: 'fatalAppStateSyncNotification',
|
|
24
|
+
11: 'phoneNumberShared',
|
|
25
|
+
14: 'messageEdited',
|
|
26
|
+
16: 'peerDataRequest',
|
|
27
|
+
17: 'peerDataResponse',
|
|
28
|
+
18: 'welcomeMessageRequest',
|
|
29
|
+
19: 'botFeedback',
|
|
30
|
+
20: 'mediaNotification'
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const protocolLabels = {
|
|
34
|
+
messageDeleted: t('incoming.protocol.messageDeleted'),
|
|
35
|
+
disappearingMessagesUpdated: t('incoming.protocol.disappearingMessagesUpdated'),
|
|
36
|
+
disappearingMessageSyncResponse: t('incoming.protocol.disappearingMessageSyncResponse'),
|
|
37
|
+
historySyncNotification: t('incoming.protocol.historySyncNotification'),
|
|
38
|
+
appStateSyncKeyShare: t('incoming.protocol.appStateSyncKeyShare'),
|
|
39
|
+
appStateSyncKeyRequest: t('incoming.protocol.appStateSyncKeyRequest'),
|
|
40
|
+
messageBackfillRequest: t('incoming.protocol.messageBackfillRequest'),
|
|
41
|
+
securityNotificationSync: t('incoming.protocol.securityNotificationSync'),
|
|
42
|
+
fatalAppStateSyncNotification: t('incoming.protocol.fatalAppStateSyncNotification'),
|
|
43
|
+
phoneNumberShared: t('incoming.protocol.phoneNumberShared'),
|
|
44
|
+
messageEdited: t('incoming.protocol.messageEdited'),
|
|
45
|
+
peerDataRequest: t('incoming.protocol.peerDataRequest'),
|
|
46
|
+
peerDataResponse: t('incoming.protocol.peerDataResponse'),
|
|
47
|
+
welcomeMessageRequest: t('incoming.protocol.welcomeMessageRequest'),
|
|
48
|
+
botFeedback: t('incoming.protocol.botFeedback'),
|
|
49
|
+
mediaNotification: t('incoming.protocol.mediaNotification')
|
|
50
|
+
} as const;
|
|
51
|
+
|
|
52
|
+
const unwrapMessageContent = (content: any): any => extractMessageContent(content) ?? content;
|
|
53
|
+
|
|
54
|
+
const getTypeName = (payload: any): string => {
|
|
55
|
+
if (!payload || typeof payload !== 'object') return 'unknown';
|
|
56
|
+
return Object.keys(payload)[0] || 'unknown';
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const formatProtocolMessage = (protocolMessage: any): string => {
|
|
60
|
+
const typeLabelKey = protocolTypes[Number(protocolMessage?.type)];
|
|
61
|
+
const typeLabel = typeLabelKey ? protocolLabels[typeLabelKey] : t('incoming.protocol.systemUpdate');
|
|
62
|
+
const editedText = protocolMessage?.editedMessage?.conversation
|
|
63
|
+
|| protocolMessage?.editedMessage?.extendedTextMessage?.text;
|
|
64
|
+
|
|
65
|
+
if (editedText) {
|
|
66
|
+
return `[${typeLabel}: ${editedText}]`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return `[${typeLabel}]`;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const extractIncomingText = (message: any): IncomingResolution => {
|
|
73
|
+
const content = unwrapMessageContent(message);
|
|
74
|
+
const inner = content?.ephemeralMessage?.message
|
|
75
|
+
|| content?.viewOnceMessage?.message
|
|
76
|
+
|| content?.viewOnceMessageV2?.message
|
|
77
|
+
|| content?.viewOnceMessageV2Extension?.message
|
|
78
|
+
|| content?.message;
|
|
79
|
+
|
|
80
|
+
const resolved = inner ? unwrapMessageContent(inner) : content;
|
|
81
|
+
const typeName = getTypeName(resolved);
|
|
82
|
+
const protocolMessage = resolved?.protocolMessage
|
|
83
|
+
|| (typeName === 'protocolMessage' ? resolved : undefined)
|
|
84
|
+
|| content?.protocolMessage;
|
|
85
|
+
|
|
86
|
+
if (protocolMessage) {
|
|
87
|
+
return { kind: 'system', text: formatProtocolMessage(protocolMessage) };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (resolved?.conversation) {
|
|
91
|
+
return { kind: 'text', text: resolved.conversation };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (resolved?.extendedTextMessage?.text) {
|
|
95
|
+
return { kind: 'text', text: resolved.extendedTextMessage.text };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (resolved?.imageMessage) {
|
|
99
|
+
return {
|
|
100
|
+
kind: 'image',
|
|
101
|
+
text: resolved.imageMessage.caption || t('incoming.media.image'),
|
|
102
|
+
imageMessage: resolved.imageMessage
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (resolved?.videoMessage) {
|
|
107
|
+
return {
|
|
108
|
+
kind: 'text',
|
|
109
|
+
text: resolved.videoMessage.caption || t('incoming.media.video')
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (resolved?.audioMessage) {
|
|
114
|
+
return {
|
|
115
|
+
kind: 'audio',
|
|
116
|
+
text: t('incoming.media.audio'),
|
|
117
|
+
audioMessage: resolved.audioMessage
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (resolved?.documentMessage) {
|
|
122
|
+
return {
|
|
123
|
+
kind: 'document',
|
|
124
|
+
text: resolved.documentMessage.caption || t('incoming.media.document'),
|
|
125
|
+
documentMessage: resolved.documentMessage
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (resolved?.contactMessage || resolved?.contactsArrayMessage) {
|
|
130
|
+
return { kind: 'contact', text: t('incoming.media.contact') };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (resolved?.locationMessage) {
|
|
134
|
+
return { kind: 'location', text: t('incoming.media.location') };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (resolved?.buttonsResponseMessage?.selectedDisplayText) {
|
|
138
|
+
return { kind: 'text', text: resolved.buttonsResponseMessage.selectedDisplayText };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (resolved?.listResponseMessage?.title) {
|
|
142
|
+
return { kind: 'text', text: resolved.listResponseMessage.title };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (resolved?.templateButtonReplyMessage?.selectedDisplayText) {
|
|
146
|
+
return { kind: 'text', text: resolved.templateButtonReplyMessage.selectedDisplayText };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return { kind: 'unsupported', text: t('incoming.media.unsupported', { typeName }) };
|
|
150
|
+
};
|