@sjawhar/whatsapp-mcp 2.0.0 → 2.2.0

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.
Files changed (114) hide show
  1. package/dist/db.d.ts +28 -1
  2. package/dist/db.d.ts.map +1 -1
  3. package/dist/db.js +153 -30
  4. package/dist/db.js.map +1 -1
  5. package/dist/http-server.d.ts +8 -0
  6. package/dist/http-server.d.ts.map +1 -0
  7. package/dist/http-server.js +198 -0
  8. package/dist/http-server.js.map +1 -0
  9. package/dist/import-contacts.d.ts +6 -3
  10. package/dist/import-contacts.d.ts.map +1 -1
  11. package/dist/import-contacts.js +28 -14
  12. package/dist/import-contacts.js.map +1 -1
  13. package/dist/index.js +192 -26
  14. package/dist/index.js.map +1 -1
  15. package/dist/resources.d.ts +16 -0
  16. package/dist/resources.d.ts.map +1 -0
  17. package/dist/resources.js +84 -0
  18. package/dist/resources.js.map +1 -0
  19. package/dist/tools.d.ts.map +1 -1
  20. package/dist/tools.js +38 -15
  21. package/dist/tools.js.map +1 -1
  22. package/dist/whatsapp.d.ts +13 -1
  23. package/dist/whatsapp.d.ts.map +1 -1
  24. package/dist/whatsapp.js +167 -31
  25. package/dist/whatsapp.js.map +1 -1
  26. package/package.json +5 -1
  27. package/dist/__tests__/connection.test.d.ts +0 -2
  28. package/dist/__tests__/connection.test.d.ts.map +0 -1
  29. package/dist/__tests__/connection.test.js +0 -105
  30. package/dist/__tests__/connection.test.js.map +0 -1
  31. package/dist/__tests__/disconnect.test.d.ts +0 -2
  32. package/dist/__tests__/disconnect.test.d.ts.map +0 -1
  33. package/dist/__tests__/disconnect.test.js +0 -166
  34. package/dist/__tests__/disconnect.test.js.map +0 -1
  35. package/dist/__tests__/download-media.test.d.ts +0 -2
  36. package/dist/__tests__/download-media.test.d.ts.map +0 -1
  37. package/dist/__tests__/download-media.test.js +0 -110
  38. package/dist/__tests__/download-media.test.js.map +0 -1
  39. package/dist/__tests__/failures/connection-failures.test.d.ts +0 -2
  40. package/dist/__tests__/failures/connection-failures.test.d.ts.map +0 -1
  41. package/dist/__tests__/failures/connection-failures.test.js +0 -146
  42. package/dist/__tests__/failures/connection-failures.test.js.map +0 -1
  43. package/dist/__tests__/failures/edge-cases.test.d.ts +0 -2
  44. package/dist/__tests__/failures/edge-cases.test.d.ts.map +0 -1
  45. package/dist/__tests__/failures/edge-cases.test.js +0 -121
  46. package/dist/__tests__/failures/edge-cases.test.js.map +0 -1
  47. package/dist/__tests__/failures/resource-failures.test.d.ts +0 -2
  48. package/dist/__tests__/failures/resource-failures.test.d.ts.map +0 -1
  49. package/dist/__tests__/failures/resource-failures.test.js +0 -136
  50. package/dist/__tests__/failures/resource-failures.test.js.map +0 -1
  51. package/dist/__tests__/failures/security-failures.test.d.ts +0 -2
  52. package/dist/__tests__/failures/security-failures.test.d.ts.map +0 -1
  53. package/dist/__tests__/failures/security-failures.test.js +0 -0
  54. package/dist/__tests__/failures/security-failures.test.js.map +0 -1
  55. package/dist/__tests__/helpers/fake-baileys.d.ts +0 -52
  56. package/dist/__tests__/helpers/fake-baileys.d.ts.map +0 -1
  57. package/dist/__tests__/helpers/fake-baileys.js +0 -60
  58. package/dist/__tests__/helpers/fake-baileys.js.map +0 -1
  59. package/dist/__tests__/helpers/mcp-test-client.d.ts +0 -9
  60. package/dist/__tests__/helpers/mcp-test-client.d.ts.map +0 -1
  61. package/dist/__tests__/helpers/mcp-test-client.js +0 -40
  62. package/dist/__tests__/helpers/mcp-test-client.js.map +0 -1
  63. package/dist/__tests__/helpers/test-db.d.ts +0 -4
  64. package/dist/__tests__/helpers/test-db.d.ts.map +0 -1
  65. package/dist/__tests__/helpers/test-db.js +0 -32
  66. package/dist/__tests__/helpers/test-db.js.map +0 -1
  67. package/dist/__tests__/integration/chat-navigation.test.d.ts +0 -2
  68. package/dist/__tests__/integration/chat-navigation.test.d.ts.map +0 -1
  69. package/dist/__tests__/integration/chat-navigation.test.js +0 -171
  70. package/dist/__tests__/integration/chat-navigation.test.js.map +0 -1
  71. package/dist/__tests__/integration/contacts-flow.test.d.ts +0 -2
  72. package/dist/__tests__/integration/contacts-flow.test.d.ts.map +0 -1
  73. package/dist/__tests__/integration/contacts-flow.test.js +0 -144
  74. package/dist/__tests__/integration/contacts-flow.test.js.map +0 -1
  75. package/dist/__tests__/integration/media-flow.test.d.ts +0 -2
  76. package/dist/__tests__/integration/media-flow.test.d.ts.map +0 -1
  77. package/dist/__tests__/integration/media-flow.test.js +0 -225
  78. package/dist/__tests__/integration/media-flow.test.js.map +0 -1
  79. package/dist/__tests__/integration/search-flow.test.d.ts +0 -2
  80. package/dist/__tests__/integration/search-flow.test.d.ts.map +0 -1
  81. package/dist/__tests__/integration/search-flow.test.js +0 -44
  82. package/dist/__tests__/integration/search-flow.test.js.map +0 -1
  83. package/dist/__tests__/integration/send-message.test.d.ts +0 -2
  84. package/dist/__tests__/integration/send-message.test.d.ts.map +0 -1
  85. package/dist/__tests__/integration/send-message.test.js +0 -160
  86. package/dist/__tests__/integration/send-message.test.js.map +0 -1
  87. package/dist/__tests__/lock-file.test.d.ts +0 -2
  88. package/dist/__tests__/lock-file.test.d.ts.map +0 -1
  89. package/dist/__tests__/lock-file.test.js +0 -63
  90. package/dist/__tests__/lock-file.test.js.map +0 -1
  91. package/dist/__tests__/medium-fixes.test.d.ts +0 -2
  92. package/dist/__tests__/medium-fixes.test.d.ts.map +0 -1
  93. package/dist/__tests__/medium-fixes.test.js +0 -141
  94. package/dist/__tests__/medium-fixes.test.js.map +0 -1
  95. package/dist/__tests__/rate-limit.test.d.ts +0 -2
  96. package/dist/__tests__/rate-limit.test.d.ts.map +0 -1
  97. package/dist/__tests__/rate-limit.test.js +0 -193
  98. package/dist/__tests__/rate-limit.test.js.map +0 -1
  99. package/dist/__tests__/send-file.test.d.ts +0 -2
  100. package/dist/__tests__/send-file.test.d.ts.map +0 -1
  101. package/dist/__tests__/send-file.test.js +0 -237
  102. package/dist/__tests__/send-file.test.js.map +0 -1
  103. package/dist/__tests__/smoke.test.d.ts +0 -2
  104. package/dist/__tests__/smoke.test.d.ts.map +0 -1
  105. package/dist/__tests__/smoke.test.js +0 -28
  106. package/dist/__tests__/smoke.test.js.map +0 -1
  107. package/dist/__tests__/transcribe.test.d.ts +0 -2
  108. package/dist/__tests__/transcribe.test.d.ts.map +0 -1
  109. package/dist/__tests__/transcribe.test.js +0 -71
  110. package/dist/__tests__/transcribe.test.js.map +0 -1
  111. package/dist/__tests__/zombie.test.d.ts +0 -2
  112. package/dist/__tests__/zombie.test.d.ts.map +0 -1
  113. package/dist/__tests__/zombie.test.js +0 -145
  114. package/dist/__tests__/zombie.test.js.map +0 -1
@@ -1,171 +0,0 @@
1
- import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
2
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
3
- import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
4
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
- import { createFakeBaileysSocket } from "../helpers/fake-baileys.js";
6
- import { closeTestDb, seedTestDb, setupTestDb } from "../helpers/test-db.js";
7
- const mockState = vi.hoisted(() => ({
8
- sockets: [],
9
- }));
10
- vi.mock("@whiskeysockets/baileys", () => ({
11
- default: vi.fn(() => {
12
- const socket = createFakeBaileysSocket();
13
- mockState.sockets.push(socket);
14
- return socket;
15
- }),
16
- DisconnectReason: {
17
- loggedOut: 401,
18
- connectionReplaced: 440,
19
- },
20
- fetchLatestBaileysVersion: vi.fn(async () => ({ version: [2, 3000, 0] })),
21
- downloadMediaMessage: vi.fn(),
22
- getContentType: vi.fn(),
23
- initAuthCreds: vi.fn(() => ({})),
24
- BufferJSON: {
25
- replacer: (_key, value) => value,
26
- reviver: (_key, value) => value,
27
- },
28
- proto: {
29
- Message: {
30
- AppStateSyncKeyData: {
31
- fromObject: (value) => value,
32
- },
33
- },
34
- },
35
- }));
36
- function parseToolJson(result) {
37
- const text = result.content?.find((entry) => entry.type === "text")?.text || "";
38
- try {
39
- return JSON.parse(text);
40
- }
41
- catch {
42
- return text;
43
- }
44
- }
45
- describe("integration: chat navigation + deletion flow", () => {
46
- let server;
47
- let client;
48
- let closeWhatsApp;
49
- beforeAll(async () => {
50
- process.env.MIN_SEND_INTERVAL_MS = "0";
51
- process.env.SEND_JITTER_MS = "0";
52
- await setupTestDb();
53
- seedTestDb();
54
- const { getDb } = await import("../../db.js");
55
- const db = getDb();
56
- db.prepare("INSERT INTO messages (id, chat_jid, from_me, sender_jid, sender_name, type, text, timestamp, has_media, message_blob, transcription) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run("seed-msg-2", "15550001111@s.whatsapp.net", 1, "15559990000@s.whatsapp.net", "Me", "text", "Second seeded message", 1700000001, 0, null, null);
57
- db.prepare("INSERT INTO messages (id, chat_jid, from_me, sender_jid, sender_name, type, text, timestamp, has_media, message_blob, transcription) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run("seed-msg-3", "15550001111@s.whatsapp.net", 0, "15550001111@s.whatsapp.net", "Alice Test", "text", "Third seeded message", 1700000002, 0, null, null);
58
- const whatsapp = await import("../../whatsapp.js");
59
- closeWhatsApp = whatsapp.closeWhatsApp;
60
- await whatsapp.initWhatsApp();
61
- const socket = mockState.sockets.at(-1);
62
- if (!socket)
63
- throw new Error("Expected fake Baileys socket");
64
- socket.emitConnectionOpen();
65
- server = new McpServer({ name: "whatsapp-test", version: "1.0.0" });
66
- const { registerTools } = await import("../../tools.js");
67
- registerTools(server);
68
- const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
69
- client = new Client({ name: "whatsapp-test-client", version: "1.0.0" });
70
- await Promise.all([server.connect(serverTransport), client.connect(clientTransport)]);
71
- });
72
- afterAll(async () => {
73
- await Promise.all([client?.close(), server?.close()]);
74
- await closeWhatsApp?.();
75
- closeTestDb();
76
- });
77
- it("navigates chat data and performs delete previews + confirmed deletions", async () => {
78
- const chats = (await client.callTool({
79
- name: "list_chats",
80
- arguments: { limit: 10 },
81
- }));
82
- expect(chats.isError).toBeFalsy();
83
- expect(parseToolJson(chats)).toEqual(expect.arrayContaining([
84
- expect.objectContaining({ jid: "15550001111@s.whatsapp.net", name: "Alice Test" }),
85
- ]));
86
- const chat = (await client.callTool({
87
- name: "get_chat",
88
- arguments: { jid: "15550001111" },
89
- }));
90
- expect(chat.isError).toBeFalsy();
91
- expect(parseToolJson(chat)).toMatchObject({
92
- jid: "15550001111@s.whatsapp.net",
93
- name: "Alice Test",
94
- recentMessages: expect.any(Array),
95
- });
96
- const messages = (await client.callTool({
97
- name: "list_messages",
98
- arguments: { jid: "15550001111", limit: 3 },
99
- }));
100
- const messagePayload = parseToolJson(messages);
101
- expect(messages.isError).toBeFalsy();
102
- expect(messagePayload).toHaveLength(3);
103
- expect(messagePayload[0]).toMatchObject({ id: "seed-msg-3" });
104
- const context = (await client.callTool({
105
- name: "get_message_context",
106
- arguments: {
107
- jid: "15550001111",
108
- messageId: "seed-msg-2",
109
- count: 1,
110
- },
111
- }));
112
- expect(context.isError).toBeFalsy();
113
- expect(parseToolJson(context)).toMatchObject({
114
- messages: expect.any(Array),
115
- targetIndex: expect.any(Number),
116
- });
117
- const deleteMessagePreview = (await client.callTool({
118
- name: "delete_message",
119
- arguments: {
120
- jid: "15550001111",
121
- messageId: "seed-msg-2",
122
- confirmed: false,
123
- },
124
- }));
125
- expect(deleteMessagePreview.isError).toBeFalsy();
126
- expect(parseToolJson(deleteMessagePreview)).toMatchObject({
127
- status: "confirmation_required",
128
- jid: "15550001111@s.whatsapp.net",
129
- messageId: "seed-msg-2",
130
- });
131
- const deleteMessageConfirmed = (await client.callTool({
132
- name: "delete_message",
133
- arguments: {
134
- jid: "15550001111",
135
- messageId: "seed-msg-2",
136
- confirmed: true,
137
- },
138
- }));
139
- expect(deleteMessageConfirmed.isError).toBeFalsy();
140
- expect(parseToolJson(deleteMessageConfirmed)).toMatchObject({
141
- success: true,
142
- jid: "15550001111@s.whatsapp.net",
143
- messageId: "seed-msg-2",
144
- });
145
- const deleteChatPreview = (await client.callTool({
146
- name: "delete_chat",
147
- arguments: {
148
- jid: "15550001111",
149
- confirmed: false,
150
- },
151
- }));
152
- expect(deleteChatPreview.isError).toBeFalsy();
153
- expect(parseToolJson(deleteChatPreview)).toMatchObject({
154
- status: "confirmation_required",
155
- jid: "15550001111@s.whatsapp.net",
156
- });
157
- const deleteChatConfirmed = (await client.callTool({
158
- name: "delete_chat",
159
- arguments: {
160
- jid: "15550001111",
161
- confirmed: true,
162
- },
163
- }));
164
- expect(deleteChatConfirmed.isError).toBeFalsy();
165
- expect(parseToolJson(deleteChatConfirmed)).toMatchObject({
166
- success: true,
167
- jid: "15550001111@s.whatsapp.net",
168
- });
169
- });
170
- });
171
- //# sourceMappingURL=chat-navigation.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"chat-navigation.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/chat-navigation.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=contacts-flow.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"contacts-flow.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/integration/contacts-flow.test.ts"],"names":[],"mappings":""}
@@ -1,144 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
4
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
5
- import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
6
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
- import { createFakeBaileysSocket } from "../helpers/fake-baileys.js";
8
- import { closeTestDb, seedTestDb, setupTestDb } from "../helpers/test-db.js";
9
- const mockState = vi.hoisted(() => ({
10
- sockets: [],
11
- }));
12
- vi.mock("@whiskeysockets/baileys", () => ({
13
- default: vi.fn(() => {
14
- const socket = createFakeBaileysSocket();
15
- mockState.sockets.push(socket);
16
- return socket;
17
- }),
18
- DisconnectReason: {
19
- loggedOut: 401,
20
- connectionReplaced: 440,
21
- },
22
- fetchLatestBaileysVersion: vi.fn(async () => ({ version: [2, 3000, 0] })),
23
- downloadMediaMessage: vi.fn(),
24
- getContentType: vi.fn(),
25
- initAuthCreds: vi.fn(() => ({})),
26
- BufferJSON: {
27
- replacer: (_key, value) => value,
28
- reviver: (_key, value) => value,
29
- },
30
- proto: {
31
- Message: {
32
- AppStateSyncKeyData: {
33
- fromObject: (value) => value,
34
- },
35
- },
36
- },
37
- }));
38
- function parseToolJson(result) {
39
- const text = result.content?.find((entry) => entry.type === "text")?.text || "";
40
- try {
41
- return JSON.parse(text);
42
- }
43
- catch {
44
- return text;
45
- }
46
- }
47
- describe("integration: contacts flow", () => {
48
- const vcfPath = "/home/sami/projects/whatsapp-mcp/contacts/contacts.vcf";
49
- let originalVcf;
50
- let server;
51
- let client;
52
- let closeWhatsApp;
53
- beforeAll(async () => {
54
- process.env.MIN_SEND_INTERVAL_MS = "0";
55
- process.env.SEND_JITTER_MS = "0";
56
- fs.mkdirSync(path.dirname(vcfPath), { recursive: true });
57
- originalVcf = fs.existsSync(vcfPath) ? fs.readFileSync(vcfPath, "utf8") : null;
58
- fs.writeFileSync(vcfPath, [
59
- "BEGIN:VCARD",
60
- "VERSION:3.0",
61
- "FN:Alice Updated From VCF",
62
- "TEL:+1 555 000 1111",
63
- "END:VCARD",
64
- "",
65
- ].join("\n"));
66
- await setupTestDb();
67
- seedTestDb();
68
- const whatsapp = await import("../../whatsapp.js");
69
- closeWhatsApp = whatsapp.closeWhatsApp;
70
- await whatsapp.initWhatsApp();
71
- const socket = mockState.sockets.at(-1);
72
- if (!socket)
73
- throw new Error("Expected fake Baileys socket");
74
- socket.emitConnectionOpen();
75
- server = new McpServer({ name: "whatsapp-test", version: "1.0.0" });
76
- const { registerTools } = await import("../../tools.js");
77
- registerTools(server);
78
- const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
79
- client = new Client({ name: "whatsapp-test-client", version: "1.0.0" });
80
- await Promise.all([server.connect(serverTransport), client.connect(clientTransport)]);
81
- });
82
- afterAll(async () => {
83
- await Promise.all([client?.close(), server?.close()]);
84
- await closeWhatsApp?.();
85
- closeTestDb();
86
- if (originalVcf === null) {
87
- fs.rmSync(vcfPath, { force: true });
88
- }
89
- else {
90
- fs.writeFileSync(vcfPath, originalVcf);
91
- }
92
- });
93
- it("updates/searches contacts, returns own profile, and syncs from VCF", async () => {
94
- const updated = (await client.callTool({
95
- name: "update_contact",
96
- arguments: {
97
- jid: "15550001111",
98
- name: "Alice Integration",
99
- },
100
- }));
101
- expect(updated.isError).toBeFalsy();
102
- expect(parseToolJson(updated)).toMatchObject({
103
- success: true,
104
- jid: "15550001111@s.whatsapp.net",
105
- name: "Alice Integration",
106
- });
107
- const searched = (await client.callTool({
108
- name: "search_contacts",
109
- arguments: {
110
- query: "Integration",
111
- },
112
- }));
113
- expect(searched.isError).toBeFalsy();
114
- expect(parseToolJson(searched)).toEqual(expect.arrayContaining([
115
- expect.objectContaining({
116
- jid: "15550001111@s.whatsapp.net",
117
- name: "Alice Integration",
118
- }),
119
- ]));
120
- const profile = (await client.callTool({
121
- name: "get_my_profile",
122
- arguments: {},
123
- }));
124
- expect(profile.isError).toBeFalsy();
125
- expect(parseToolJson(profile)).toMatchObject({
126
- jid: "15559990000@s.whatsapp.net",
127
- lidJid: "15559990000@lid",
128
- name: "Test User",
129
- phone: "15559990000",
130
- });
131
- const synced = (await client.callTool({
132
- name: "sync_contacts",
133
- arguments: {},
134
- }));
135
- expect(synced.isError).toBeFalsy();
136
- expect(parseToolJson(synced)).toMatchObject({
137
- success: true,
138
- totalParsed: 1,
139
- totalUpdated: expect.any(Number),
140
- vcfPath: expect.stringContaining("contacts.vcf"),
141
- });
142
- });
143
- });
144
- //# sourceMappingURL=contacts-flow.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"contacts-flow.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/contacts-flow.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACvE,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,uBAAuB,EAA0B,MAAM,4BAA4B,CAAC;AAC7F,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAE7E,MAAM,SAAS,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAClC,OAAO,EAAE,EAAyB;CACnC,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,yBAAyB,EAAE,GAAG,EAAE,CAAC,CAAC;IACxC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE;QAClB,MAAM,MAAM,GAAG,uBAAuB,EAAE,CAAC;QACzC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC/B,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IACF,gBAAgB,EAAE;QAChB,SAAS,EAAE,GAAG;QACd,kBAAkB,EAAE,GAAG;KACxB;IACD,yBAAyB,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IACzE,oBAAoB,EAAE,EAAE,CAAC,EAAE,EAAE;IAC7B,cAAc,EAAE,EAAE,CAAC,EAAE,EAAE;IACvB,aAAa,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAChC,UAAU,EAAE;QACV,QAAQ,EAAE,CAAC,IAAY,EAAE,KAAc,EAAE,EAAE,CAAC,KAAK;QACjD,OAAO,EAAE,CAAC,IAAY,EAAE,KAAc,EAAE,EAAE,CAAC,KAAK;KACjD;IACD,KAAK,EAAE;QACL,OAAO,EAAE;YACP,mBAAmB,EAAE;gBACnB,UAAU,EAAE,CAAC,KAAc,EAAE,EAAE,CAAC,KAAK;aACtC;SACF;KACF;CACF,CAAC,CAAC,CAAC;AAOJ,SAAS,aAAa,CAAC,MAAqB;IAC1C,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC;IAChF,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,MAAM,OAAO,GAAG,wDAAwD,CAAC;IAEzE,IAAI,WAA0B,CAAC;IAC/B,IAAI,MAAiB,CAAC;IACtB,IAAI,MAAc,CAAC;IACnB,IAAI,aAAkC,CAAC;IAEvC,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,OAAO,CAAC,GAAG,CAAC,oBAAoB,GAAG,GAAG,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,GAAG,CAAC;QAEjC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,WAAW,GAAG,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/E,EAAE,CAAC,aAAa,CACd,OAAO,EACP;YACE,aAAa;YACb,aAAa;YACb,2BAA2B;YAC3B,qBAAqB;YACrB,WAAW;YACX,EAAE;SACH,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;QAEF,MAAM,WAAW,EAAE,CAAC;QACpB,UAAU,EAAE,CAAC;QAEb,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;QACnD,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC;QACvC,MAAM,QAAQ,CAAC,YAAY,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAC7D,MAAM,CAAC,kBAAkB,EAAE,CAAC;QAE5B,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QACpE,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;QACzD,aAAa,CAAC,MAAM,CAAC,CAAC;QAEtB,MAAM,CAAC,eAAe,EAAE,eAAe,CAAC,GAAG,iBAAiB,CAAC,gBAAgB,EAAE,CAAC;QAChF,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QACxE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IACxF,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,KAAK,IAAI,EAAE;QAClB,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACtD,MAAM,aAAa,EAAE,EAAE,CAAC;QACxB,WAAW,EAAE,CAAC;QAEd,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACtC,CAAC;aAAM,CAAC;YACN,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QACzC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;QAClF,MAAM,OAAO,GAAG,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC;YACrC,IAAI,EAAE,gBAAgB;YACtB,SAAS,EAAE;gBACT,GAAG,EAAE,aAAa;gBAClB,IAAI,EAAE,mBAAmB;aAC1B;SACF,CAAC,CAAkB,CAAC;QACrB,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC;QACpC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC;YAC3C,OAAO,EAAE,IAAI;YACb,GAAG,EAAE,4BAA4B;YACjC,IAAI,EAAE,mBAAmB;SAC1B,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC;YACtC,IAAI,EAAE,iBAAiB;YACvB,SAAS,EAAE;gBACT,KAAK,EAAE,aAAa;aACrB;SACF,CAAC,CAAkB,CAAC;QACrB,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC;QACrC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CACrC,MAAM,CAAC,eAAe,CAAC;YACrB,MAAM,CAAC,gBAAgB,CAAC;gBACtB,GAAG,EAAE,4BAA4B;gBACjC,IAAI,EAAE,mBAAmB;aAC1B,CAAC;SACH,CAAC,CACH,CAAC;QAEF,MAAM,OAAO,GAAG,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC;YACrC,IAAI,EAAE,gBAAgB;YACtB,SAAS,EAAE,EAAE;SACd,CAAC,CAAkB,CAAC;QACrB,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC;QACpC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC;YAC3C,GAAG,EAAE,4BAA4B;YACjC,MAAM,EAAE,iBAAiB;YACzB,IAAI,EAAE,WAAW;YACjB,KAAK,EAAE,aAAa;SACrB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC;YACpC,IAAI,EAAE,eAAe;YACrB,SAAS,EAAE,EAAE;SACd,CAAC,CAAkB,CAAC;QACrB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC;QACnC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC;YAC1C,OAAO,EAAE,IAAI;YACb,WAAW,EAAE,CAAC;YACd,YAAY,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;YAChC,OAAO,EAAE,MAAM,CAAC,gBAAgB,CAAC,cAAc,CAAC;SACjD,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=media-flow.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"media-flow.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/integration/media-flow.test.ts"],"names":[],"mappings":""}
@@ -1,225 +0,0 @@
1
- import fs from "node:fs";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
5
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
6
- import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
7
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
- import { createFakeBaileysSocket } from "../helpers/fake-baileys.js";
9
- import { closeTestDb, seedTestDb, setupTestDb } from "../helpers/test-db.js";
10
- const mockState = vi.hoisted(() => ({
11
- sockets: [],
12
- }));
13
- vi.mock("@whiskeysockets/baileys", () => ({
14
- default: vi.fn(() => {
15
- const socket = createFakeBaileysSocket();
16
- socket.sendMessage = async (jid, content) => {
17
- const streamLike = [content.image, content.video, content.audio, content.document].find((value) => value && typeof value.on === "function");
18
- if (streamLike) {
19
- await new Promise((resolve) => {
20
- streamLike.on("open", () => {
21
- streamLike.destroy();
22
- resolve();
23
- });
24
- streamLike.on("error", () => resolve());
25
- });
26
- }
27
- socket.sentMessages.push({ jid, content });
28
- return { key: { id: `fake-msg-${socket.sentMessages.length}` } };
29
- };
30
- mockState.sockets.push(socket);
31
- return socket;
32
- }),
33
- DisconnectReason: {
34
- loggedOut: 401,
35
- connectionReplaced: 440,
36
- },
37
- fetchLatestBaileysVersion: vi.fn(async () => ({ version: [2, 3000, 0] })),
38
- downloadMediaMessage: vi.fn(async () => Buffer.from("fake-media-bytes")),
39
- getContentType: vi.fn((message) => Object.keys(message)[0]),
40
- initAuthCreds: vi.fn(() => ({})),
41
- BufferJSON: {
42
- replacer: (_key, value) => value,
43
- reviver: (_key, value) => value,
44
- },
45
- proto: {
46
- Message: {
47
- AppStateSyncKeyData: {
48
- fromObject: (value) => value,
49
- },
50
- },
51
- },
52
- }));
53
- function parseToolJson(result) {
54
- const text = result.content?.find((entry) => entry.type === "text")?.text || "";
55
- try {
56
- return JSON.parse(text);
57
- }
58
- catch {
59
- return text;
60
- }
61
- }
62
- describe("integration: media flow", () => {
63
- const originalAllowedDir = process.env.ALLOWED_SEND_DIR;
64
- const originalDownloadsDir = process.env.DOWNLOADS_DIR;
65
- const originalWhisperApiKey = process.env.WHISPER_API_KEY;
66
- const originalWhisperApiUrl = process.env.WHISPER_API_URL;
67
- const originalWhisperModel = process.env.WHISPER_MODEL;
68
- let uploadDir;
69
- let downloadsDir;
70
- let server;
71
- let client;
72
- let closeWhatsApp;
73
- beforeAll(async () => {
74
- uploadDir = fs.mkdtempSync(path.join(os.tmpdir(), "integration-upload-"));
75
- downloadsDir = fs.mkdtempSync(path.join(os.tmpdir(), "integration-download-"));
76
- process.env.ALLOWED_SEND_DIR = uploadDir;
77
- process.env.DOWNLOADS_DIR = downloadsDir;
78
- process.env.MIN_SEND_INTERVAL_MS = "0";
79
- process.env.SEND_JITTER_MS = "0";
80
- process.env.WHISPER_API_KEY = "test-whisper-key";
81
- process.env.WHISPER_API_URL = "https://api.example.com/v1/audio/transcriptions";
82
- process.env.WHISPER_MODEL = "test-model";
83
- await setupTestDb();
84
- seedTestDb();
85
- const { getDb } = await import("../../db.js");
86
- const db = getDb();
87
- db.prepare("INSERT INTO messages (id, chat_jid, from_me, sender_jid, sender_name, type, text, timestamp, has_media, message_blob, transcription) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run("img-msg-1", "15550001111@s.whatsapp.net", 0, "15550001111@s.whatsapp.net", "Alice Test", "image", null, 1700000100, 1, JSON.stringify({ message: { imageMessage: { mimetype: "image/png" } } }), null);
88
- db.prepare("INSERT INTO messages (id, chat_jid, from_me, sender_jid, sender_name, type, text, timestamp, has_media, message_blob, transcription) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run("voice-msg-1", "15550001111@s.whatsapp.net", 0, "15550001111@s.whatsapp.net", "Alice Test", "voice_note", null, 1700000110, 1, JSON.stringify({ message: { audioMessage: { mimetype: "audio/ogg; codecs=opus" } } }), null);
89
- vi.stubGlobal("fetch", vi.fn(async () => ({
90
- ok: true,
91
- status: 200,
92
- text: async () => "",
93
- json: async () => ({
94
- text: "Transcribed hello world",
95
- language: "en",
96
- duration: 1.23,
97
- }),
98
- })));
99
- const whatsapp = await import("../../whatsapp.js");
100
- closeWhatsApp = whatsapp.closeWhatsApp;
101
- await whatsapp.initWhatsApp();
102
- const socket = mockState.sockets.at(-1);
103
- if (!socket)
104
- throw new Error("Expected fake Baileys socket");
105
- socket.emitConnectionOpen();
106
- server = new McpServer({ name: "whatsapp-test", version: "1.0.0" });
107
- const { registerTools } = await import("../../tools.js");
108
- registerTools(server);
109
- const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
110
- client = new Client({ name: "whatsapp-test-client", version: "1.0.0" });
111
- await Promise.all([server.connect(serverTransport), client.connect(clientTransport)]);
112
- });
113
- afterAll(async () => {
114
- await Promise.all([client?.close(), server?.close()]);
115
- await closeWhatsApp?.();
116
- closeTestDb();
117
- vi.unstubAllGlobals();
118
- fs.rmSync(uploadDir, { recursive: true, force: true });
119
- fs.rmSync(downloadsDir, { recursive: true, force: true });
120
- if (originalAllowedDir === undefined)
121
- delete process.env.ALLOWED_SEND_DIR;
122
- else
123
- process.env.ALLOWED_SEND_DIR = originalAllowedDir;
124
- if (originalDownloadsDir === undefined)
125
- delete process.env.DOWNLOADS_DIR;
126
- else
127
- process.env.DOWNLOADS_DIR = originalDownloadsDir;
128
- if (originalWhisperApiKey === undefined)
129
- delete process.env.WHISPER_API_KEY;
130
- else
131
- process.env.WHISPER_API_KEY = originalWhisperApiKey;
132
- if (originalWhisperApiUrl === undefined)
133
- delete process.env.WHISPER_API_URL;
134
- else
135
- process.env.WHISPER_API_URL = originalWhisperApiUrl;
136
- if (originalWhisperModel === undefined)
137
- delete process.env.WHISPER_MODEL;
138
- else
139
- process.env.WHISPER_MODEL = originalWhisperModel;
140
- });
141
- it("handles send_file preview + send flow", async () => {
142
- const filePath = path.join(uploadDir, "story.txt");
143
- fs.writeFileSync(filePath, "integration media test");
144
- const preview = (await client.callTool({
145
- name: "send_file",
146
- arguments: {
147
- jid: "15550001111@s.whatsapp.net",
148
- filePath,
149
- caption: "preview cap",
150
- confirmed: false,
151
- },
152
- }));
153
- expect(preview.isError).toBeFalsy();
154
- expect(parseToolJson(preview)).toMatchObject({
155
- preview: true,
156
- fileName: "story.txt",
157
- recipient: expect.objectContaining({ jid: "15550001111@s.whatsapp.net" }),
158
- });
159
- const sent = (await client.callTool({
160
- name: "send_file",
161
- arguments: {
162
- jid: "15550001111@s.whatsapp.net",
163
- filePath,
164
- caption: "send cap",
165
- confirmed: true,
166
- },
167
- }));
168
- expect(sent.isError).toBeFalsy();
169
- expect(parseToolJson(sent)).toMatchObject({
170
- success: true,
171
- to: "15550001111@s.whatsapp.net",
172
- fileType: "document",
173
- });
174
- });
175
- it("downloads media and transcribes voice note", async () => {
176
- const download = (await client.callTool({
177
- name: "download_media",
178
- arguments: {
179
- jid: "15550001111@s.whatsapp.net",
180
- messageId: "img-msg-1",
181
- },
182
- }));
183
- const downloadPayload = parseToolJson(download);
184
- expect(download.isError).toBeFalsy();
185
- expect(downloadPayload).toMatchObject({
186
- success: true,
187
- fileName: "img-msg-1.png",
188
- mimeType: "image/png",
189
- type: "imageMessage",
190
- size: "fake-media-bytes".length,
191
- });
192
- expect(fs.existsSync(downloadPayload.filePath)).toBe(true);
193
- const transcribed = (await client.callTool({
194
- name: "transcribe_voice_note",
195
- arguments: {
196
- jid: "15550001111@s.whatsapp.net",
197
- messageId: "voice-msg-1",
198
- },
199
- }));
200
- const transcribedPayload = parseToolJson(transcribed);
201
- expect(transcribed.isError).toBeFalsy();
202
- expect(transcribedPayload).toMatchObject({
203
- success: true,
204
- messageId: "voice-msg-1",
205
- chatJid: "15550001111@s.whatsapp.net",
206
- transcription: "Transcribed hello world",
207
- language: "en",
208
- cached: false,
209
- });
210
- const cached = (await client.callTool({
211
- name: "transcribe_voice_note",
212
- arguments: {
213
- jid: "15550001111@s.whatsapp.net",
214
- messageId: "voice-msg-1",
215
- },
216
- }));
217
- expect(cached.isError).toBeFalsy();
218
- expect(parseToolJson(cached)).toMatchObject({
219
- success: true,
220
- messageId: "voice-msg-1",
221
- cached: true,
222
- });
223
- });
224
- });
225
- //# sourceMappingURL=media-flow.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"media-flow.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/media-flow.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=search-flow.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"search-flow.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/integration/search-flow.test.ts"],"names":[],"mappings":""}
@@ -1,44 +0,0 @@
1
- import { afterAll, beforeAll, describe, expect, it } from "vitest";
2
- import { closeTestDb, seedTestDb, setupTestDb } from "../helpers/test-db.js";
3
- import { createMcpTestClient } from "../helpers/mcp-test-client.js";
4
- describe("integration: search and listing flow", () => {
5
- let client;
6
- beforeAll(async () => {
7
- await setupTestDb();
8
- seedTestDb();
9
- const { getDb } = await import("../../db.js");
10
- const db = getDb();
11
- db.prepare("INSERT INTO contacts (jid, name, notify) VALUES (?, ?, ?)").run("15550002222@s.whatsapp.net", "Bob Search", "Bob");
12
- db.prepare("INSERT INTO contacts (jid, name, notify) VALUES (?, ?, ?)").run("15550003333@s.whatsapp.net", "Charlie Search", "Charlie");
13
- db.prepare("INSERT INTO chats (jid, name, conversation_ts, unread_count) VALUES (?, ?, ?, ?)").run("15550002222@s.whatsapp.net", "Bob Search", 1700000010, 1);
14
- db.prepare("INSERT INTO chats (jid, name, conversation_ts, unread_count) VALUES (?, ?, ?, ?)").run("15550003333@s.whatsapp.net", "Charlie Search", 1700000020, 0);
15
- db.prepare("INSERT INTO messages (id, chat_jid, from_me, sender_jid, sender_name, type, text, timestamp, has_media, message_blob, transcription) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run("search-msg-1", "15550002222@s.whatsapp.net", 0, "15550002222@s.whatsapp.net", "Bob Search", "text", "Need coffee beans and filters", 1700000010, 0, null, null);
16
- db.prepare("INSERT INTO messages (id, chat_jid, from_me, sender_jid, sender_name, type, text, timestamp, has_media, message_blob, transcription) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run("search-msg-2", "15550003333@s.whatsapp.net", 0, "15550003333@s.whatsapp.net", "Charlie Search", "text", "Coffee meeting tomorrow", 1700000020, 0, null, null);
17
- client = await createMcpTestClient();
18
- });
19
- afterAll(async () => {
20
- await client?.close();
21
- closeTestDb();
22
- });
23
- it("searches messages and contacts, and honors list_chats limit", async () => {
24
- const messageResults = (await client.callTool("search_messages", {
25
- query: "coffee",
26
- }));
27
- expect(messageResults).toEqual(expect.arrayContaining([
28
- expect.objectContaining({ id: "search-msg-1" }),
29
- expect.objectContaining({ id: "search-msg-2" }),
30
- ]));
31
- const contactResults = (await client.callTool("search_contacts", {
32
- query: "Search",
33
- }));
34
- expect(contactResults).toEqual(expect.arrayContaining([
35
- expect.objectContaining({ jid: "15550002222@s.whatsapp.net", name: "Bob Search" }),
36
- expect.objectContaining({ jid: "15550003333@s.whatsapp.net", name: "Charlie Search" }),
37
- ]));
38
- const chats = (await client.callTool("list_chats", { limit: 2 }));
39
- expect(chats).toHaveLength(2);
40
- expect(chats[0]).toMatchObject({ jid: "15550003333@s.whatsapp.net" });
41
- expect(chats[1]).toMatchObject({ jid: "15550002222@s.whatsapp.net" });
42
- });
43
- });
44
- //# sourceMappingURL=search-flow.test.js.map