whatsapp-pi 1.0.24 → 1.0.26

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.24",
3
+ "version": "1.0.26",
4
4
  "type": "module",
5
5
  "description": "WhatsApp integration extension for Pi",
6
6
  "main": "whatsapp-pi.ts",
@@ -242,6 +242,44 @@ export class WhatsAppService {
242
242
  return result;
243
243
  }
244
244
 
245
+ private normalizeRecipientJid(jid: string): string {
246
+ if (jid.includes('@')) return jid;
247
+ const digits = jid.startsWith('+') ? jid.slice(1) : jid;
248
+ return `${digits}@s.whatsapp.net`;
249
+ }
250
+
251
+ async sendMenuMessage(jid: string, text: string) {
252
+ const normalizedJid = this.normalizeRecipientJid(jid);
253
+
254
+ if (!this.socket || this.getStatus() !== 'connected') {
255
+ return {
256
+ success: false,
257
+ error: 'WhatsApp is not connected',
258
+ attempts: 0
259
+ };
260
+ }
261
+
262
+ try {
263
+ await this.sendPresence(normalizedJid, 'composing');
264
+ const response = await this.socket.sendMessage(normalizedJid, { text });
265
+ await this.sendPresence(normalizedJid, 'paused');
266
+
267
+ return {
268
+ success: true,
269
+ messageId: response?.key?.id,
270
+ attempts: 1
271
+ };
272
+ } catch (error: any) {
273
+ await this.sendPresence(normalizedJid, 'paused');
274
+ console.error(`Failed to send menu message to ${normalizedJid}:`, error);
275
+ return {
276
+ success: false,
277
+ error: error?.message || 'Unknown error',
278
+ attempts: 1
279
+ };
280
+ }
281
+ }
282
+
245
283
  async sendPresence(jid: string, presence: 'composing' | 'recording' | 'paused') {
246
284
  if (!this.socket || this.getStatus() !== 'connected') return;
247
285
  try {
@@ -17,19 +17,22 @@ export class MenuHandler {
17
17
  const registered = await this.sessionManager.isRegistered();
18
18
  const options: string[] = [];
19
19
 
20
+ options.push('Recents');
21
+
20
22
  if (status === 'connected') {
23
+ options.push('Allowed Numbers');
24
+ options.push('Blocked Numbers');
21
25
  options.push('Disconnect WhatsApp');
22
26
  } else {
23
27
  options.push('Connect / Reconnect WhatsApp');
28
+ options.push('Allowed Numbers');
29
+ options.push('Blocked Numbers');
24
30
  }
25
31
 
26
32
  if (registered) {
27
33
  options.push('Logoff (Delete Session)');
28
34
  }
29
35
 
30
- options.push('Allowed Numbers');
31
- options.push('Blocked Numbers');
32
- options.push('Recents');
33
36
  options.push('Back');
34
37
 
35
38
  const choice = await ctx.ui.select(`WhatsApp (Status: ${status})`, options);
@@ -311,7 +314,7 @@ export class MenuHandler {
311
314
  continue;
312
315
  }
313
316
 
314
- const result = await this.whatsappService.sendMessage(this.toJid(conversation.senderNumber), trimmed);
317
+ const result = await this.whatsappService.sendMenuMessage(this.toJid(conversation.senderNumber), trimmed);
315
318
  if (result.success) {
316
319
  await this.recentsService.recordMessage({
317
320
  messageId: result.messageId ?? `${Date.now()}`,
@@ -323,7 +326,7 @@ export class MenuHandler {
323
326
  });
324
327
  ctx.ui.notify(`Sent message to ${displayName}`, 'info');
325
328
  } else {
326
- ctx.ui.notify(`Failed to send message to ${displayName}`, 'error');
329
+ ctx.ui.notify(`Failed to send message to ${displayName}: ${result.error ?? 'Unknown error'}`, 'error');
327
330
  }
328
331
  return;
329
332
  }
@@ -332,27 +335,28 @@ export class MenuHandler {
332
335
  private async sendMessageToAllowedNumber(ctx: ExtensionCommandContext, contact: Contact) {
333
336
  const displayName = contact.name ? `${contact.name} (${contact.number})` : contact.number;
334
337
  for (let attempt = 0; attempt < 2; attempt++) {
335
- const text = await ctx.ui.input(`Send a message to ${displayName}:`);
336
- const trimmed = text?.trim() || '';
338
+ const inputText = (await ctx.ui.input(`Send a message to ${displayName}:`))?.trim() || '';
337
339
 
338
- if (!trimmed) {
340
+ if (!inputText) {
339
341
  ctx.ui.notify('Please enter a message before sending.', 'error');
340
342
  continue;
341
343
  }
342
344
 
343
- const result = await this.whatsappService.sendMessage(this.toJid(contact.number), trimmed);
345
+ const inputTextWithPiSuffix = inputText + ' π';
346
+
347
+ const result = await this.whatsappService.sendMenuMessage(this.toJid(contact.number), inputTextWithPiSuffix);
344
348
  if (result.success) {
345
349
  await this.recentsService.recordMessage({
346
350
  messageId: result.messageId ?? `${Date.now()}`,
347
351
  senderNumber: contact.number,
348
352
  senderName: contact.name,
349
- text: trimmed,
353
+ text: inputTextWithPiSuffix,
350
354
  direction: 'outgoing',
351
355
  timestamp: Date.now()
352
356
  });
353
357
  ctx.ui.notify(`Sent message to ${displayName}`, 'info');
354
358
  } else {
355
- ctx.ui.notify(`Failed to send message to ${displayName}`, 'error');
359
+ ctx.ui.notify(`Failed to send message to ${displayName}: ${result.error ?? 'Unknown error'}`, 'error');
356
360
  }
357
361
  return;
358
362
  }
package/whatsapp-pi.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { ExtensionAPI, ExtensionCommandContext, ExtensionContext } from "@mariozechner/pi-coding-agent";
2
+ import { Type } from "@sinclair/typebox";
2
3
  import { extractMessageContent } from '@whiskeysockets/baileys';
3
4
  import { SessionManager } from './src/services/session.manager.js';
4
5
  import { WhatsAppService } from './src/services/whatsapp.service.js';
@@ -99,7 +100,7 @@ export default function (pi: ExtensionAPI) {
99
100
  .find(entry => entry.type === "custom" && entry.customType === "whatsapp-state");
100
101
 
101
102
  if (savedStateEntry) {
102
- const data = savedStateEntry.data as any;
103
+ const data = (savedStateEntry as { data?: any }).data;
103
104
  if (data.status) await sessionManager.setStatus(data.status);
104
105
  if (Array.isArray(data.allowList)) {
105
106
  for (const n of data.allowList) {
@@ -428,6 +429,45 @@ export default function (pi: ExtensionAPI) {
428
429
 
429
430
  });
430
431
 
432
+ // Register send_wa_message tool (LLM-callable)
433
+ pi.registerTool({
434
+ name: "send_wa_message",
435
+ label: "Send WhatsApp Message",
436
+ description: "Send a WhatsApp message to a contact identified by their JID (e.g. 5511999998888@s.whatsapp.net). Returns a JSON result with success status and messageId or error.",
437
+ promptSnippet: "send_wa_message(jid, message) - Send a WhatsApp message to a contact by JID",
438
+ parameters: Type.Object({
439
+ jid: Type.String({ minLength: 1, description: "WhatsApp JID of the recipient, e.g. 5511999998888@s.whatsapp.net" }),
440
+ message: Type.String({ minLength: 1, description: "Plain-text message content to send" })
441
+ }),
442
+ async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
443
+ if (whatsappService.getStatus() !== 'connected') {
444
+ return {
445
+ isError: true,
446
+ details: undefined,
447
+ content: [{ type: "text" as const, text: JSON.stringify({ success: false, error: "WhatsApp not connected", attempts: 0 }) }]
448
+ };
449
+ }
450
+
451
+ const result = await whatsappService.sendMessage(params.jid, params.message);
452
+
453
+ if (result.success) {
454
+ await recentsService.recordMessage({
455
+ messageId: result.messageId!,
456
+ senderNumber: `+${params.jid.split('@')[0]}`,
457
+ text: params.message,
458
+ direction: 'outgoing',
459
+ timestamp: Date.now()
460
+ });
461
+ }
462
+
463
+ return {
464
+ isError: !result.success,
465
+ details: undefined,
466
+ content: [{ type: "text" as const, text: JSON.stringify({ success: result.success, messageId: result.messageId, error: result.error, attempts: result.attempts }) }]
467
+ };
468
+ }
469
+ });
470
+
431
471
  // Register commands
432
472
  pi.registerCommand("whatsapp", {
433
473
  description: "Manage WhatsApp integration",