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 +1 -1
- package/src/services/whatsapp.service.ts +38 -0
- package/src/ui/menu.handler.ts +15 -11
- package/whatsapp-pi.ts +41 -1
package/package.json
CHANGED
|
@@ -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 {
|
package/src/ui/menu.handler.ts
CHANGED
|
@@ -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.
|
|
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
|
|
336
|
-
const trimmed = text?.trim() || '';
|
|
338
|
+
const inputText = (await ctx.ui.input(`Send a message to ${displayName}:`))?.trim() || '';
|
|
337
339
|
|
|
338
|
-
if (!
|
|
340
|
+
if (!inputText) {
|
|
339
341
|
ctx.ui.notify('Please enter a message before sending.', 'error');
|
|
340
342
|
continue;
|
|
341
343
|
}
|
|
342
344
|
|
|
343
|
-
const
|
|
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:
|
|
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
|
|
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",
|