whatsapp-pi 1.0.56 → 1.0.57

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whatsapp-pi",
3
- "version": "1.0.56",
3
+ "version": "1.0.57",
4
4
  "type": "module",
5
5
  "description": "WhatsApp integration extension for Pi",
6
6
  "main": "whatsapp-pi.ts",
package/src/i18n.ts CHANGED
@@ -31,6 +31,8 @@ const fallback = {
31
31
  "service.whatsapp.connecting": "| WhatsApp: Connecting...",
32
32
  "service.whatsapp.typeToConnect": "| WhatsApp: type /whatsapp to connect",
33
33
  "service.whatsapp.connected": "| WhatsApp: Connected",
34
+ "service.whatsapp.qrConnected": "WhatsApp connected",
35
+ "service.whatsapp.qrWelcomeMessage": "👋 To get started, send a message, enter in /whatsapp > Recents to Allow Contact with LID code, then add the whatsapp number in the Allowed Contact list.",
34
36
  "service.whatsapp.sessionErrorBadMac": "| WhatsApp: Session Error (Bad MAC)",
35
37
  "service.whatsapp.loggedOut": "| WhatsApp: Logged out",
36
38
  "service.whatsapp.conflict": "| WhatsApp: Conflict (Another Instance)",
@@ -247,6 +249,8 @@ const translations: Record<Locale, Partial<Record<Key, string>>> = {
247
249
  "service.whatsapp.connecting": "| WhatsApp: Conectando...",
248
250
  "service.whatsapp.typeToConnect": "| WhatsApp: digite /whatsapp para conectar",
249
251
  "service.whatsapp.connected": "| WhatsApp: Conectado",
252
+ "service.whatsapp.qrConnected": "WhatsApp conectado",
253
+ "service.whatsapp.qrWelcomeMessage": "👋 Para começar, envie uma mensagem, entre em /whatsapp > Recentes para Permitir o Contato com o código LID, depois adicione o número do whatsapp na lista de Contatos Permitidos.",
250
254
  "service.whatsapp.sessionErrorBadMac": "| WhatsApp: Erro de sessão (Bad MAC)",
251
255
  "service.whatsapp.loggedOut": "| WhatsApp: Desconectado",
252
256
  "service.whatsapp.conflict": "| WhatsApp: Conflito (Outra instância)",
@@ -429,6 +433,8 @@ const translations: Record<Locale, Partial<Record<Key, string>>> = {
429
433
  "service.whatsapp.connecting": "| WhatsApp: Conectando...",
430
434
  "service.whatsapp.typeToConnect": "| WhatsApp: escribe /whatsapp para conectar",
431
435
  "service.whatsapp.connected": "| WhatsApp: Conectado",
436
+ "service.whatsapp.qrConnected": "WhatsApp conectado",
437
+ "service.whatsapp.qrWelcomeMessage": "👋 Para empezar, envía un mensaje, entra en /whatsapp > Recientes para Permitir el Contacto con el código LID, luego añade el número de whatsapp en la lista de Contactos Permitidos.",
432
438
  "service.whatsapp.sessionErrorBadMac": "| WhatsApp: Error de sesión (Bad MAC)",
433
439
  "service.whatsapp.loggedOut": "| WhatsApp: Cerrado sesión",
434
440
  "service.whatsapp.conflict": "| WhatsApp: Conflicto (Otra instancia)",
@@ -563,6 +569,8 @@ const translations: Record<Locale, Partial<Record<Key, string>>> = {
563
569
  "service.whatsapp.connecting": "| WhatsApp: Connexion...",
564
570
  "service.whatsapp.typeToConnect": "| WhatsApp: tapez /whatsapp pour connecter",
565
571
  "service.whatsapp.connected": "| WhatsApp: Connecté",
572
+ "service.whatsapp.qrConnected": "WhatsApp connecté",
573
+ "service.whatsapp.qrWelcomeMessage": "👋 Pour commencer, envoyez un message, allez dans /whatsapp > Récents pour Autoriser le Contact avec le code LID, puis ajoutez le numéro whatsapp dans la liste des Contacts Autorisés.",
566
574
  "service.whatsapp.sessionErrorBadMac": "| WhatsApp: Erreur de session (Bad MAC)",
567
575
  "service.whatsapp.loggedOut": "| WhatsApp: Déconnecté",
568
576
  "service.whatsapp.conflict": "| WhatsApp: Conflit (Autre instance)",
@@ -38,6 +38,7 @@ export class SessionManager {
38
38
  private hasAuthState = false;
39
39
  private openaiKey: string = '';
40
40
  private visionModel: string = 'gpt-4o';
41
+ private operatorJid: string = '';
41
42
 
42
43
  constructor(baseDir = join(homedir(), '.pi', 'whatsapp-pi')) {
43
44
  this.baseDir = baseDir;
@@ -96,6 +97,7 @@ export class SessionManager {
96
97
  this.hasAuthState = Boolean(config.hasAuthState);
97
98
  this.openaiKey = config.openaiKey || '';
98
99
  this.visionModel = config.visionModel || 'gpt-4o';
100
+ this.operatorJid = config.operatorJid || '';
99
101
 
100
102
  if (recovered || needsReactionModeBackfill) {
101
103
  await this.saveConfig();
@@ -169,11 +171,19 @@ export class SessionManager {
169
171
  status: this.status,
170
172
  hasAuthState: this.hasAuthState,
171
173
  openaiKey: this.openaiKey,
172
- visionModel: this.visionModel
174
+ visionModel: this.visionModel,
175
+ operatorJid: this.operatorJid
173
176
  };
174
177
  await mkdir(this.baseDir, { recursive: true });
175
- await writeFile(tempPath, JSON.stringify(config, null, 2));
176
- await rename(tempPath, this.configPath);
178
+ const serialized = JSON.stringify(config, null, 2);
179
+ await writeFile(tempPath, serialized);
180
+ try {
181
+ await rename(tempPath, this.configPath);
182
+ } catch {
183
+ // Windows EPERM: atomic rename failed (file locked). Fall back to direct write.
184
+ await writeFile(this.configPath, serialized);
185
+ await rm(tempPath, { force: true }).catch(() => {});
186
+ }
177
187
  } catch (error) {
178
188
  await rm(tempPath, { force: true }).catch(() => {});
179
189
  console.error(t('session.manager.failedSaveConfig'), error);
@@ -470,6 +480,15 @@ export class SessionManager {
470
480
  await this.saveConfig();
471
481
  }
472
482
 
483
+ getOperatorJid(): string {
484
+ return this.operatorJid;
485
+ }
486
+
487
+ async setOperatorJid(jid: string) {
488
+ this.operatorJid = jid;
489
+ await this.saveConfig();
490
+ }
491
+
473
492
  getAuthStateDir(): string {
474
493
  return this.authStateDir;
475
494
  }
@@ -112,6 +112,7 @@ export class WhatsAppService {
112
112
  private onMessage?: (m: MessagesUpsertEvent) => void;
113
113
  private onStatusUpdate?: (status: string) => void;
114
114
  private lastRemoteJid: string | null = null;
115
+ private qrWasShown = false;
115
116
  private boundGroupJid: string | null = null;
116
117
  private groupMetadataCache: Map<string, { id: string; subject: string; participants: Array<{ id: string }> }> = new Map();
117
118
 
@@ -406,6 +407,7 @@ export class WhatsAppService {
406
407
  this.sessionManager.setStatus('pairing');
407
408
  this.onQRCode?.(qr);
408
409
  this.onStatusUpdate?.(t('service.whatsapp.typeToConnect'));
410
+ this.qrWasShown = true;
409
411
  }
410
412
 
411
413
  private async handleConnectionOpen() {
@@ -420,6 +422,29 @@ export class WhatsAppService {
420
422
  await this.sessionManager.markAuthStateAvailable();
421
423
  this.sessionManager.setStatus('connected');
422
424
  this.onStatusUpdate?.(t('service.whatsapp.connected'));
425
+
426
+ if (this.qrWasShown) {
427
+ this.qrWasShown = false;
428
+ console.log(t('service.whatsapp.qrConnected'));
429
+ console.log(t('service.whatsapp.qrWelcomeMessage'));
430
+ void this.sendQrWelcome();
431
+ }
432
+ }
433
+
434
+ private async sendQrWelcome(): Promise<void> {
435
+ const rawId = this.socket?.user?.id;
436
+ if (!rawId) return;
437
+ const selfJid = this.normalizeJidForComparison(rawId);
438
+ await this.sessionManager.setOperatorJid(selfJid);
439
+ try {
440
+ await this.socket?.sendMessage(selfJid, { text: t('service.whatsapp.qrWelcomeMessage') });
441
+ } catch {
442
+ // Best-effort — welcome send failure must not abort the session.
443
+ }
444
+ }
445
+
446
+ public getOperatorJid(): string {
447
+ return this.sessionManager.getOperatorJid();
423
448
  }
424
449
 
425
450
  private isBadMacError(errorMessage: string): boolean {
package/whatsapp-pi.ts CHANGED
@@ -268,8 +268,8 @@ export default function (pi: ExtensionAPI) {
268
268
  message: Type.String({ minLength: 1, description: "Plain-text message content to send" })
269
269
  }),
270
270
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
271
- // Resolve JID: jid > recipient_jid > lastRemoteJid
272
- const resolvedJid = params.jid || params.recipient_jid || whatsappService.getLastRemoteJid();
271
+ // Resolve JID: jid > recipient_jid > lastRemoteJid > operatorJid (QR-scanned number)
272
+ const resolvedJid = params.jid || params.recipient_jid || whatsappService.getLastRemoteJid() || whatsappService.getOperatorJid();
273
273
  if (!resolvedJid) {
274
274
  return {
275
275
  isError: true,