@sjawhar/whatsapp-mcp 1.0.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.
- package/LICENSE +21 -0
- package/README.md +122 -0
- package/dist/__tests__/connection.test.d.ts +2 -0
- package/dist/__tests__/connection.test.d.ts.map +1 -0
- package/dist/__tests__/connection.test.js +105 -0
- package/dist/__tests__/connection.test.js.map +1 -0
- package/dist/__tests__/disconnect.test.d.ts +2 -0
- package/dist/__tests__/disconnect.test.d.ts.map +1 -0
- package/dist/__tests__/disconnect.test.js +166 -0
- package/dist/__tests__/disconnect.test.js.map +1 -0
- package/dist/__tests__/download-media.test.d.ts +2 -0
- package/dist/__tests__/download-media.test.d.ts.map +1 -0
- package/dist/__tests__/download-media.test.js +110 -0
- package/dist/__tests__/download-media.test.js.map +1 -0
- package/dist/__tests__/failures/connection-failures.test.d.ts +2 -0
- package/dist/__tests__/failures/connection-failures.test.d.ts.map +1 -0
- package/dist/__tests__/failures/connection-failures.test.js +146 -0
- package/dist/__tests__/failures/connection-failures.test.js.map +1 -0
- package/dist/__tests__/failures/edge-cases.test.d.ts +2 -0
- package/dist/__tests__/failures/edge-cases.test.d.ts.map +1 -0
- package/dist/__tests__/failures/edge-cases.test.js +121 -0
- package/dist/__tests__/failures/edge-cases.test.js.map +1 -0
- package/dist/__tests__/failures/resource-failures.test.d.ts +2 -0
- package/dist/__tests__/failures/resource-failures.test.d.ts.map +1 -0
- package/dist/__tests__/failures/resource-failures.test.js +136 -0
- package/dist/__tests__/failures/resource-failures.test.js.map +1 -0
- package/dist/__tests__/failures/security-failures.test.d.ts +2 -0
- package/dist/__tests__/failures/security-failures.test.d.ts.map +1 -0
- package/dist/__tests__/failures/security-failures.test.js +0 -0
- package/dist/__tests__/failures/security-failures.test.js.map +1 -0
- package/dist/__tests__/helpers/fake-baileys.d.ts +52 -0
- package/dist/__tests__/helpers/fake-baileys.d.ts.map +1 -0
- package/dist/__tests__/helpers/fake-baileys.js +60 -0
- package/dist/__tests__/helpers/fake-baileys.js.map +1 -0
- package/dist/__tests__/helpers/mcp-test-client.d.ts +9 -0
- package/dist/__tests__/helpers/mcp-test-client.d.ts.map +1 -0
- package/dist/__tests__/helpers/mcp-test-client.js +40 -0
- package/dist/__tests__/helpers/mcp-test-client.js.map +1 -0
- package/dist/__tests__/helpers/test-db.d.ts +4 -0
- package/dist/__tests__/helpers/test-db.d.ts.map +1 -0
- package/dist/__tests__/helpers/test-db.js +32 -0
- package/dist/__tests__/helpers/test-db.js.map +1 -0
- package/dist/__tests__/integration/chat-navigation.test.d.ts +2 -0
- package/dist/__tests__/integration/chat-navigation.test.d.ts.map +1 -0
- package/dist/__tests__/integration/chat-navigation.test.js +171 -0
- package/dist/__tests__/integration/chat-navigation.test.js.map +1 -0
- package/dist/__tests__/integration/contacts-flow.test.d.ts +2 -0
- package/dist/__tests__/integration/contacts-flow.test.d.ts.map +1 -0
- package/dist/__tests__/integration/contacts-flow.test.js +144 -0
- package/dist/__tests__/integration/contacts-flow.test.js.map +1 -0
- package/dist/__tests__/integration/media-flow.test.d.ts +2 -0
- package/dist/__tests__/integration/media-flow.test.d.ts.map +1 -0
- package/dist/__tests__/integration/media-flow.test.js +225 -0
- package/dist/__tests__/integration/media-flow.test.js.map +1 -0
- package/dist/__tests__/integration/search-flow.test.d.ts +2 -0
- package/dist/__tests__/integration/search-flow.test.d.ts.map +1 -0
- package/dist/__tests__/integration/search-flow.test.js +44 -0
- package/dist/__tests__/integration/search-flow.test.js.map +1 -0
- package/dist/__tests__/integration/send-message.test.d.ts +2 -0
- package/dist/__tests__/integration/send-message.test.d.ts.map +1 -0
- package/dist/__tests__/integration/send-message.test.js +160 -0
- package/dist/__tests__/integration/send-message.test.js.map +1 -0
- package/dist/__tests__/lock-file.test.d.ts +2 -0
- package/dist/__tests__/lock-file.test.d.ts.map +1 -0
- package/dist/__tests__/lock-file.test.js +63 -0
- package/dist/__tests__/lock-file.test.js.map +1 -0
- package/dist/__tests__/medium-fixes.test.d.ts +2 -0
- package/dist/__tests__/medium-fixes.test.d.ts.map +1 -0
- package/dist/__tests__/medium-fixes.test.js +141 -0
- package/dist/__tests__/medium-fixes.test.js.map +1 -0
- package/dist/__tests__/rate-limit.test.d.ts +2 -0
- package/dist/__tests__/rate-limit.test.d.ts.map +1 -0
- package/dist/__tests__/rate-limit.test.js +193 -0
- package/dist/__tests__/rate-limit.test.js.map +1 -0
- package/dist/__tests__/send-file.test.d.ts +2 -0
- package/dist/__tests__/send-file.test.d.ts.map +1 -0
- package/dist/__tests__/send-file.test.js +237 -0
- package/dist/__tests__/send-file.test.js.map +1 -0
- package/dist/__tests__/smoke.test.d.ts +2 -0
- package/dist/__tests__/smoke.test.d.ts.map +1 -0
- package/dist/__tests__/smoke.test.js +28 -0
- package/dist/__tests__/smoke.test.js.map +1 -0
- package/dist/__tests__/transcribe.test.d.ts +2 -0
- package/dist/__tests__/transcribe.test.d.ts.map +1 -0
- package/dist/__tests__/transcribe.test.js +71 -0
- package/dist/__tests__/transcribe.test.js.map +1 -0
- package/dist/__tests__/zombie.test.d.ts +2 -0
- package/dist/__tests__/zombie.test.d.ts.map +1 -0
- package/dist/__tests__/zombie.test.js +145 -0
- package/dist/__tests__/zombie.test.js.map +1 -0
- package/dist/db.d.ts +53 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +509 -0
- package/dist/db.js.map +1 -0
- package/dist/import-contacts.d.ts +37 -0
- package/dist/import-contacts.d.ts.map +1 -0
- package/dist/import-contacts.js +242 -0
- package/dist/import-contacts.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +62 -0
- package/dist/index.js.map +1 -0
- package/dist/lock.d.ts +16 -0
- package/dist/lock.d.ts.map +1 -0
- package/dist/lock.js +65 -0
- package/dist/lock.js.map +1 -0
- package/dist/tools.d.ts +6 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +339 -0
- package/dist/tools.js.map +1 -0
- package/dist/transcribe.d.ts +8 -0
- package/dist/transcribe.d.ts.map +1 -0
- package/dist/transcribe.js +63 -0
- package/dist/transcribe.js.map +1 -0
- package/dist/utils.d.ts +51 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +156 -0
- package/dist/utils.js.map +1 -0
- package/dist/whatsapp.d.ts +50 -0
- package/dist/whatsapp.d.ts.map +1 -0
- package/dist/whatsapp.js +896 -0
- package/dist/whatsapp.js.map +1 -0
- package/package.json +52 -0
- package/patches/@whiskeysockets+baileys+6.7.21.patch +46 -0
- package/patches/libsignal+2.0.1.patch +84 -0
package/dist/db.js
ADDED
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { fromJid, formatMessageRow } from "./utils.js";
|
|
5
|
+
const __project_root = path.resolve(path.dirname(new URL(import.meta.url).pathname), "..");
|
|
6
|
+
const STORE_DIR = path.join(__project_root, "store");
|
|
7
|
+
const DB_PATH = path.join(STORE_DIR, "whatsapp.db");
|
|
8
|
+
let db;
|
|
9
|
+
// ─── Message helpers (moved from utils.ts) ──────────────────────────
|
|
10
|
+
function getMessageText(msg) {
|
|
11
|
+
const m = msg.message;
|
|
12
|
+
if (!m)
|
|
13
|
+
return null;
|
|
14
|
+
return (m.conversation ||
|
|
15
|
+
m.extendedTextMessage?.text ||
|
|
16
|
+
m.imageMessage?.caption ||
|
|
17
|
+
m.videoMessage?.caption ||
|
|
18
|
+
m.documentMessage?.caption ||
|
|
19
|
+
m.listResponseMessage?.title ||
|
|
20
|
+
m.buttonsResponseMessage?.selectedDisplayText ||
|
|
21
|
+
null);
|
|
22
|
+
}
|
|
23
|
+
function getMessageType(msg) {
|
|
24
|
+
const m = msg.message;
|
|
25
|
+
if (!m)
|
|
26
|
+
return "unknown";
|
|
27
|
+
if (m.conversation || m.extendedTextMessage)
|
|
28
|
+
return "text";
|
|
29
|
+
if (m.imageMessage)
|
|
30
|
+
return "image";
|
|
31
|
+
if (m.videoMessage)
|
|
32
|
+
return "video";
|
|
33
|
+
if (m.audioMessage)
|
|
34
|
+
return m.audioMessage.ptt ? "voice_note" : "audio";
|
|
35
|
+
if (m.documentMessage)
|
|
36
|
+
return "document";
|
|
37
|
+
if (m.stickerMessage)
|
|
38
|
+
return "sticker";
|
|
39
|
+
if (m.contactMessage)
|
|
40
|
+
return "contact";
|
|
41
|
+
if (m.locationMessage)
|
|
42
|
+
return "location";
|
|
43
|
+
if (m.reactionMessage)
|
|
44
|
+
return "reaction";
|
|
45
|
+
if (m.protocolMessage)
|
|
46
|
+
return "protocol";
|
|
47
|
+
return "other";
|
|
48
|
+
}
|
|
49
|
+
function isUserMessage(msg) {
|
|
50
|
+
const m = msg.message;
|
|
51
|
+
if (!m)
|
|
52
|
+
return false;
|
|
53
|
+
if (m.protocolMessage || m.reactionMessage || m.senderKeyDistributionMessage)
|
|
54
|
+
return false;
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
const MEDIA_TYPES = new Set(["image", "video", "audio", "voice_note", "document", "sticker"]);
|
|
58
|
+
// ─── Schema ─────────────────────────────────────────────────────────
|
|
59
|
+
function createTables() {
|
|
60
|
+
db.exec(`
|
|
61
|
+
PRAGMA journal_mode = WAL;
|
|
62
|
+
PRAGMA foreign_keys = ON;
|
|
63
|
+
|
|
64
|
+
CREATE TABLE IF NOT EXISTS chats (
|
|
65
|
+
jid TEXT PRIMARY KEY,
|
|
66
|
+
name TEXT,
|
|
67
|
+
conversation_ts INTEGER DEFAULT 0,
|
|
68
|
+
unread_count INTEGER DEFAULT 0
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
CREATE TABLE IF NOT EXISTS contacts (
|
|
72
|
+
jid TEXT PRIMARY KEY,
|
|
73
|
+
name TEXT,
|
|
74
|
+
notify TEXT
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
78
|
+
id TEXT NOT NULL,
|
|
79
|
+
chat_jid TEXT NOT NULL,
|
|
80
|
+
from_me INTEGER NOT NULL DEFAULT 0,
|
|
81
|
+
sender_jid TEXT,
|
|
82
|
+
sender_name TEXT,
|
|
83
|
+
type TEXT NOT NULL DEFAULT 'text',
|
|
84
|
+
text TEXT,
|
|
85
|
+
timestamp INTEGER NOT NULL DEFAULT 0,
|
|
86
|
+
has_media INTEGER NOT NULL DEFAULT 0,
|
|
87
|
+
message_blob TEXT,
|
|
88
|
+
PRIMARY KEY (chat_jid, id)
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
CREATE INDEX IF NOT EXISTS idx_messages_chat_ts ON messages(chat_jid, timestamp);
|
|
92
|
+
CREATE INDEX IF NOT EXISTS idx_messages_text ON messages(text) WHERE text IS NOT NULL;
|
|
93
|
+
|
|
94
|
+
CREATE TABLE IF NOT EXISTS jid_mapping (
|
|
95
|
+
lid_jid TEXT PRIMARY KEY,
|
|
96
|
+
phone_jid TEXT NOT NULL
|
|
97
|
+
);
|
|
98
|
+
CREATE INDEX IF NOT EXISTS idx_jid_mapping_phone ON jid_mapping(phone_jid);
|
|
99
|
+
`);
|
|
100
|
+
// Migration: add transcription column for voice note caching
|
|
101
|
+
const hasCol = db.prepare(`SELECT COUNT(*) as cnt FROM pragma_table_info('messages') WHERE name = 'transcription'`).get();
|
|
102
|
+
if (hasCol.cnt === 0) {
|
|
103
|
+
db.exec(`ALTER TABLE messages ADD COLUMN transcription TEXT`);
|
|
104
|
+
}
|
|
105
|
+
// Seed JID mappings from name-matched chats (LID ↔ phone number).
|
|
106
|
+
// WhatsApp is migrating from phone JIDs to LID JIDs, so the same contact
|
|
107
|
+
// may have messages under both. This bootstraps the mapping from existing data.
|
|
108
|
+
db.exec(`
|
|
109
|
+
INSERT OR IGNORE INTO jid_mapping (lid_jid, phone_jid)
|
|
110
|
+
SELECT l.jid, p.jid
|
|
111
|
+
FROM chats l
|
|
112
|
+
JOIN chats p ON p.name = l.name AND p.jid LIKE '%@s.whatsapp.net'
|
|
113
|
+
WHERE l.jid LIKE '%@lid'
|
|
114
|
+
AND l.name IS NOT NULL AND l.name != ''
|
|
115
|
+
`);
|
|
116
|
+
}
|
|
117
|
+
// ─── Prepared Statements ────────────────────────────────────────────
|
|
118
|
+
let stmts;
|
|
119
|
+
function prepareStatements() {
|
|
120
|
+
stmts = {
|
|
121
|
+
upsertChat: db.prepare(`
|
|
122
|
+
INSERT INTO chats (jid, name, conversation_ts, unread_count)
|
|
123
|
+
VALUES (@jid, @name, @conversation_ts, @unread_count)
|
|
124
|
+
ON CONFLICT(jid) DO UPDATE SET
|
|
125
|
+
name = COALESCE(@name, chats.name),
|
|
126
|
+
conversation_ts = MAX(COALESCE(@conversation_ts, 0), chats.conversation_ts),
|
|
127
|
+
unread_count = COALESCE(@unread_count, chats.unread_count)
|
|
128
|
+
`),
|
|
129
|
+
upsertContact: db.prepare(`
|
|
130
|
+
INSERT INTO contacts (jid, name, notify)
|
|
131
|
+
VALUES (@jid, @name, @notify)
|
|
132
|
+
ON CONFLICT(jid) DO UPDATE SET
|
|
133
|
+
name = COALESCE(@name, contacts.name),
|
|
134
|
+
notify = COALESCE(@notify, contacts.notify)
|
|
135
|
+
`),
|
|
136
|
+
upsertMessage: db.prepare(`
|
|
137
|
+
INSERT INTO messages (id, chat_jid, from_me, sender_jid, sender_name, type, text, timestamp, has_media, message_blob)
|
|
138
|
+
VALUES (@id, @chat_jid, @from_me, @sender_jid, @sender_name, @type, @text, @timestamp, @has_media, @message_blob)
|
|
139
|
+
ON CONFLICT(chat_jid, id) DO UPDATE SET
|
|
140
|
+
sender_name = COALESCE(@sender_name, messages.sender_name),
|
|
141
|
+
text = COALESCE(@text, messages.text),
|
|
142
|
+
has_media = @has_media,
|
|
143
|
+
message_blob = COALESCE(@message_blob, messages.message_blob)
|
|
144
|
+
`),
|
|
145
|
+
deleteChat: db.prepare(`DELETE FROM chats WHERE jid = ?`),
|
|
146
|
+
deleteMessage: db.prepare(`DELETE FROM messages WHERE chat_jid = ? AND id = ?`),
|
|
147
|
+
getContactName: db.prepare(`
|
|
148
|
+
SELECT COALESCE(name, notify) AS display FROM contacts WHERE jid = ?
|
|
149
|
+
`),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
// ─── Write Operations ───────────────────────────────────────────────
|
|
153
|
+
export function upsertChat(jid, name, conversationTs, unreadCount) {
|
|
154
|
+
stmts.upsertChat.run({
|
|
155
|
+
jid,
|
|
156
|
+
name: name || null,
|
|
157
|
+
conversation_ts: conversationTs ? Number(conversationTs) : null,
|
|
158
|
+
unread_count: unreadCount ?? null,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
export function upsertContact(jid, name, notify) {
|
|
162
|
+
stmts.upsertContact.run({ jid, name: name || null, notify: notify || null });
|
|
163
|
+
}
|
|
164
|
+
function resolveSenderName(senderJid) {
|
|
165
|
+
if (!senderJid)
|
|
166
|
+
return null;
|
|
167
|
+
const row = stmts.getContactName.get(senderJid);
|
|
168
|
+
if (row?.display)
|
|
169
|
+
return row.display;
|
|
170
|
+
return fromJid(senderJid);
|
|
171
|
+
}
|
|
172
|
+
export function upsertMessage(chatJid, msg) {
|
|
173
|
+
if (!isUserMessage(msg))
|
|
174
|
+
return;
|
|
175
|
+
const type = getMessageType(msg);
|
|
176
|
+
const text = getMessageText(msg);
|
|
177
|
+
const fromMe = msg.key.fromMe ? 1 : 0;
|
|
178
|
+
const senderJid = fromMe ? null : (msg.key.participant || msg.key.remoteJid || null);
|
|
179
|
+
const senderName = fromMe ? null : resolveSenderName(senderJid);
|
|
180
|
+
const hasMedia = MEDIA_TYPES.has(type) ? 1 : 0;
|
|
181
|
+
const messageBlob = hasMedia ? JSON.stringify({ key: msg.key, message: msg.message, messageTimestamp: msg.messageTimestamp }) : null;
|
|
182
|
+
stmts.upsertMessage.run({
|
|
183
|
+
id: msg.key.id,
|
|
184
|
+
chat_jid: chatJid,
|
|
185
|
+
from_me: fromMe,
|
|
186
|
+
sender_jid: senderJid,
|
|
187
|
+
sender_name: senderName,
|
|
188
|
+
type,
|
|
189
|
+
text: text || null,
|
|
190
|
+
timestamp: Number(msg.messageTimestamp || 0),
|
|
191
|
+
has_media: hasMedia,
|
|
192
|
+
message_blob: messageBlob,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
export function upsertChats(chats) {
|
|
196
|
+
const run = db.transaction((items) => {
|
|
197
|
+
for (const chat of items) {
|
|
198
|
+
upsertChat(chat.id, chat.name, chat.conversationTimestamp ? Number(chat.conversationTimestamp) : null, chat.unreadCount);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
run(chats);
|
|
202
|
+
}
|
|
203
|
+
export function upsertContacts(contacts) {
|
|
204
|
+
const run = db.transaction((items) => {
|
|
205
|
+
for (const contact of items) {
|
|
206
|
+
upsertContact(contact.id, contact.name, contact.notify);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
run(contacts);
|
|
210
|
+
}
|
|
211
|
+
export function upsertMessages(chatJid, msgs) {
|
|
212
|
+
const run = db.transaction((items) => {
|
|
213
|
+
for (const msg of items) {
|
|
214
|
+
upsertMessage(chatJid, msg);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
run(msgs);
|
|
218
|
+
}
|
|
219
|
+
export function deleteChat(jid) {
|
|
220
|
+
stmts.deleteChat.run(jid);
|
|
221
|
+
}
|
|
222
|
+
export function deleteMessage(chatJid, messageId) {
|
|
223
|
+
const result = stmts.deleteMessage.run(chatJid, messageId);
|
|
224
|
+
return result.changes > 0;
|
|
225
|
+
}
|
|
226
|
+
// ─── JID Mapping (LID ↔ Phone) ──────────────────────────────────────
|
|
227
|
+
export function saveJidMapping(lidJid, phoneJid) {
|
|
228
|
+
db.prepare(`INSERT OR REPLACE INTO jid_mapping (lid_jid, phone_jid) VALUES (?, ?)`).run(lidJid, phoneJid);
|
|
229
|
+
}
|
|
230
|
+
export function getPhoneJid(lidJid) {
|
|
231
|
+
const row = db.prepare(`SELECT phone_jid FROM jid_mapping WHERE lid_jid = ?`).get(lidJid);
|
|
232
|
+
return row?.phone_jid || null;
|
|
233
|
+
}
|
|
234
|
+
export function getLidJid(phoneJid) {
|
|
235
|
+
const row = db.prepare(`SELECT lid_jid FROM jid_mapping WHERE phone_jid = ?`).get(phoneJid);
|
|
236
|
+
return row?.lid_jid || null;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Return all known JIDs for a contact — the input JID plus any mapped counterpart.
|
|
240
|
+
* If a phone JID is given, also returns the LID JID (and vice versa).
|
|
241
|
+
*/
|
|
242
|
+
export function getAllJidsFor(jid) {
|
|
243
|
+
const jids = [jid];
|
|
244
|
+
if (jid.endsWith("@lid")) {
|
|
245
|
+
const phone = getPhoneJid(jid);
|
|
246
|
+
if (phone)
|
|
247
|
+
jids.push(phone);
|
|
248
|
+
}
|
|
249
|
+
else if (jid.endsWith("@s.whatsapp.net")) {
|
|
250
|
+
const lid = getLidJid(jid);
|
|
251
|
+
if (lid)
|
|
252
|
+
jids.push(lid);
|
|
253
|
+
}
|
|
254
|
+
return jids;
|
|
255
|
+
}
|
|
256
|
+
export function getMessageFromMe(chatJid, messageId) {
|
|
257
|
+
const row = db.prepare(`SELECT from_me FROM messages WHERE chat_jid = ? AND id = ?`).get(chatJid, messageId);
|
|
258
|
+
if (!row)
|
|
259
|
+
return null;
|
|
260
|
+
return row.from_me === 1;
|
|
261
|
+
}
|
|
262
|
+
// ─── Read Operations ────────────────────────────────────────────────
|
|
263
|
+
export function resolveDisplayName(jid) {
|
|
264
|
+
const row = db.prepare(`
|
|
265
|
+
SELECT COALESCE(ch.name, co.name, co.notify, ch.jid) AS display_name
|
|
266
|
+
FROM chats ch
|
|
267
|
+
LEFT JOIN contacts co ON ch.jid = co.jid
|
|
268
|
+
WHERE ch.jid = ?
|
|
269
|
+
`).get(jid);
|
|
270
|
+
if (row?.display_name)
|
|
271
|
+
return row.display_name;
|
|
272
|
+
// Fallback: check contacts directly (chat may not exist yet)
|
|
273
|
+
const contact = stmts.getContactName.get(jid);
|
|
274
|
+
if (contact?.display)
|
|
275
|
+
return contact.display;
|
|
276
|
+
return fromJid(jid);
|
|
277
|
+
}
|
|
278
|
+
export function getChats(nameFilter, limit = 100) {
|
|
279
|
+
let sql = `
|
|
280
|
+
SELECT ch.jid, COALESCE(ch.name, co.name, co.notify) AS name,
|
|
281
|
+
ch.unread_count, m.max_ts AS effective_ts
|
|
282
|
+
FROM chats ch
|
|
283
|
+
LEFT JOIN contacts co ON ch.jid = co.jid
|
|
284
|
+
INNER JOIN (
|
|
285
|
+
SELECT chat_jid, MAX(timestamp) AS max_ts
|
|
286
|
+
FROM messages
|
|
287
|
+
WHERE timestamp > 0
|
|
288
|
+
GROUP BY chat_jid
|
|
289
|
+
) m ON ch.jid = m.chat_jid
|
|
290
|
+
WHERE ch.jid NOT LIKE '0@%'
|
|
291
|
+
AND ch.jid != 'status@broadcast'
|
|
292
|
+
`;
|
|
293
|
+
const params = [];
|
|
294
|
+
if (nameFilter) {
|
|
295
|
+
sql += ` AND COALESCE(ch.name, co.name, co.notify, ch.jid) LIKE ?`;
|
|
296
|
+
params.push(`%${nameFilter}%`);
|
|
297
|
+
}
|
|
298
|
+
sql += ` ORDER BY effective_ts DESC LIMIT ?`;
|
|
299
|
+
params.push(limit);
|
|
300
|
+
const rows = db.prepare(sql).all(...params);
|
|
301
|
+
// Merge LID chats into their phone JID counterparts to avoid duplicates.
|
|
302
|
+
// If both a LID and phone JID exist for the same contact, keep the phone JID
|
|
303
|
+
// entry with the latest timestamp from either.
|
|
304
|
+
const merged = new Map();
|
|
305
|
+
for (const r of rows) {
|
|
306
|
+
let canonicalJid = r.jid;
|
|
307
|
+
if (r.jid.endsWith("@lid")) {
|
|
308
|
+
const phone = getPhoneJid(r.jid);
|
|
309
|
+
if (phone)
|
|
310
|
+
canonicalJid = phone;
|
|
311
|
+
}
|
|
312
|
+
const existing = merged.get(canonicalJid);
|
|
313
|
+
if (existing) {
|
|
314
|
+
// Merge: keep the latest timestamp, sum unread, prefer non-null name
|
|
315
|
+
existing.effective_ts = Math.max(existing.effective_ts, r.effective_ts);
|
|
316
|
+
existing.unread_count += r.unread_count || 0;
|
|
317
|
+
if (!existing.name && r.name)
|
|
318
|
+
existing.name = r.name;
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
merged.set(canonicalJid, { jid: canonicalJid, name: r.name, unread_count: r.unread_count, effective_ts: r.effective_ts });
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
// Re-sort by effective_ts after merging
|
|
325
|
+
const result = [...merged.values()].sort((a, b) => b.effective_ts - a.effective_ts);
|
|
326
|
+
return result.map((r) => ({
|
|
327
|
+
jid: r.jid,
|
|
328
|
+
name: r.name || (r.jid.endsWith("@lid") ? "Unknown" : fromJid(r.jid)),
|
|
329
|
+
unreadCount: r.unread_count || 0,
|
|
330
|
+
lastMessageTime: r.effective_ts,
|
|
331
|
+
isGroup: r.jid.endsWith("@g.us"),
|
|
332
|
+
}));
|
|
333
|
+
}
|
|
334
|
+
export function getChat(jid) {
|
|
335
|
+
const jids = getAllJidsFor(jid);
|
|
336
|
+
const placeholders = jids.map(() => "?").join(", ");
|
|
337
|
+
// Query all JID variants and pick the one with the best data
|
|
338
|
+
const chats = db.prepare(`
|
|
339
|
+
SELECT ch.jid, COALESCE(ch.name, co.name, co.notify) AS name,
|
|
340
|
+
ch.unread_count, ch.conversation_ts
|
|
341
|
+
FROM chats ch
|
|
342
|
+
LEFT JOIN contacts co ON ch.jid = co.jid
|
|
343
|
+
WHERE ch.jid IN (${placeholders})
|
|
344
|
+
`).all(...jids);
|
|
345
|
+
if (chats.length === 0) {
|
|
346
|
+
throw new Error(`Chat not found: ${jid}`);
|
|
347
|
+
}
|
|
348
|
+
// Merge: use the best name, latest timestamp, sum unread counts
|
|
349
|
+
const name = chats.find(c => c.name)?.name || null;
|
|
350
|
+
const conversationTs = Math.max(...chats.map(c => c.conversation_ts || 0));
|
|
351
|
+
const unreadCount = chats.reduce((sum, c) => sum + (c.unread_count || 0), 0);
|
|
352
|
+
const primaryJid = chats.find(c => c.jid === jid)?.jid || chats[0].jid;
|
|
353
|
+
// getMessages already merges across JID variants
|
|
354
|
+
const recentMessages = getMessages(jid, 5).reverse();
|
|
355
|
+
return {
|
|
356
|
+
jid: primaryJid,
|
|
357
|
+
name: name || fromJid(primaryJid),
|
|
358
|
+
unreadCount,
|
|
359
|
+
lastMessageTime: conversationTs || null,
|
|
360
|
+
isGroup: primaryJid.endsWith("@g.us"),
|
|
361
|
+
recentMessages,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
export function getMessages(jid, limit = 50) {
|
|
365
|
+
const jids = getAllJidsFor(jid);
|
|
366
|
+
const placeholders = jids.map(() => "?").join(", ");
|
|
367
|
+
const rows = db.prepare(`
|
|
368
|
+
SELECT id, chat_jid, from_me, sender_jid, sender_name, type, text, timestamp, has_media
|
|
369
|
+
FROM messages
|
|
370
|
+
WHERE chat_jid IN (${placeholders})
|
|
371
|
+
ORDER BY timestamp DESC
|
|
372
|
+
LIMIT ?
|
|
373
|
+
`).all(...jids, limit);
|
|
374
|
+
return rows.map(formatMessageRow);
|
|
375
|
+
}
|
|
376
|
+
export function searchMessages(query, jid) {
|
|
377
|
+
let sql = `
|
|
378
|
+
SELECT id, chat_jid, from_me, sender_jid, sender_name, type, text, timestamp, has_media
|
|
379
|
+
FROM messages
|
|
380
|
+
WHERE text LIKE ? ESCAPE '\\'
|
|
381
|
+
`;
|
|
382
|
+
const escaped = query.replace(/[%_\\]/g, "\\$&");
|
|
383
|
+
const params = [`%${escaped}%`];
|
|
384
|
+
if (jid) {
|
|
385
|
+
const jids = getAllJidsFor(jid);
|
|
386
|
+
const placeholders = jids.map(() => "?").join(", ");
|
|
387
|
+
sql += ` AND chat_jid IN (${placeholders})`;
|
|
388
|
+
params.push(...jids);
|
|
389
|
+
}
|
|
390
|
+
sql += ` ORDER BY timestamp DESC LIMIT 50`;
|
|
391
|
+
const rows = db.prepare(sql).all(...params);
|
|
392
|
+
return rows.map((r) => ({
|
|
393
|
+
...formatMessageRow(r),
|
|
394
|
+
chat: r.chat_jid,
|
|
395
|
+
}));
|
|
396
|
+
}
|
|
397
|
+
export function searchContacts(query) {
|
|
398
|
+
const rows = db.prepare(`
|
|
399
|
+
SELECT jid, name, notify
|
|
400
|
+
FROM contacts
|
|
401
|
+
WHERE name LIKE ? OR notify LIKE ? OR jid LIKE ?
|
|
402
|
+
`).all(`%${query}%`, `%${query}%`, `%${query}%`);
|
|
403
|
+
return rows.map((r) => ({
|
|
404
|
+
jid: r.jid,
|
|
405
|
+
name: r.name || r.notify || fromJid(r.jid),
|
|
406
|
+
phone: fromJid(r.jid),
|
|
407
|
+
isGroup: r.jid.endsWith("@g.us"),
|
|
408
|
+
}));
|
|
409
|
+
}
|
|
410
|
+
export function getMessageContext(jid, messageId, count = 5) {
|
|
411
|
+
const jids = getAllJidsFor(jid);
|
|
412
|
+
const placeholders = jids.map(() => "?").join(", ");
|
|
413
|
+
// Find the target message timestamp
|
|
414
|
+
const target = db.prepare(`
|
|
415
|
+
SELECT timestamp FROM messages WHERE chat_jid IN (${placeholders}) AND id = ?
|
|
416
|
+
`).get(...jids, messageId);
|
|
417
|
+
if (!target) {
|
|
418
|
+
throw new Error(`Message ${messageId} not found in chat ${jid}`);
|
|
419
|
+
}
|
|
420
|
+
// Get messages before (inclusive of target)
|
|
421
|
+
const before = db.prepare(`
|
|
422
|
+
SELECT id, chat_jid, from_me, sender_jid, sender_name, type, text, timestamp, has_media
|
|
423
|
+
FROM messages
|
|
424
|
+
WHERE chat_jid IN (${placeholders}) AND timestamp <= ?
|
|
425
|
+
ORDER BY timestamp DESC
|
|
426
|
+
LIMIT ?
|
|
427
|
+
`).all(...jids, target.timestamp, count + 1);
|
|
428
|
+
// Get messages after target
|
|
429
|
+
const after = db.prepare(`
|
|
430
|
+
SELECT id, chat_jid, from_me, sender_jid, sender_name, type, text, timestamp, has_media
|
|
431
|
+
FROM messages
|
|
432
|
+
WHERE chat_jid IN (${placeholders}) AND timestamp > ?
|
|
433
|
+
ORDER BY timestamp ASC
|
|
434
|
+
LIMIT ?
|
|
435
|
+
`).all(...jids, target.timestamp, count);
|
|
436
|
+
const allMessages = [...before.reverse(), ...after];
|
|
437
|
+
const targetIndex = before.length - 1;
|
|
438
|
+
return {
|
|
439
|
+
messages: allMessages.map(formatMessageRow),
|
|
440
|
+
targetIndex,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
export function getMessageBlob(jid, messageId) {
|
|
444
|
+
const jids = getAllJidsFor(jid);
|
|
445
|
+
const placeholders = jids.map(() => "?").join(", ");
|
|
446
|
+
const row = db.prepare(`
|
|
447
|
+
SELECT message_blob FROM messages WHERE chat_jid IN (${placeholders}) AND id = ?
|
|
448
|
+
`).get(...jids, messageId);
|
|
449
|
+
return row?.message_blob || null;
|
|
450
|
+
}
|
|
451
|
+
export function getMessageTypeById(chatJid, messageId) {
|
|
452
|
+
const jids = getAllJidsFor(chatJid);
|
|
453
|
+
const placeholders = jids.map(() => "?").join(", ");
|
|
454
|
+
const row = db.prepare(`SELECT type FROM messages WHERE chat_jid IN (${placeholders}) AND id = ?`).get(...jids, messageId);
|
|
455
|
+
return row?.type || null;
|
|
456
|
+
}
|
|
457
|
+
export function getTranscription(chatJid, messageId) {
|
|
458
|
+
const jids = getAllJidsFor(chatJid);
|
|
459
|
+
const placeholders = jids.map(() => "?").join(", ");
|
|
460
|
+
const row = db.prepare(`SELECT transcription FROM messages WHERE chat_jid IN (${placeholders}) AND id = ?`).get(...jids, messageId);
|
|
461
|
+
return row?.transcription || null;
|
|
462
|
+
}
|
|
463
|
+
export function saveTranscription(chatJid, messageId, transcription) {
|
|
464
|
+
// Update across all JID variants since we don't know which one holds the message
|
|
465
|
+
const jids = getAllJidsFor(chatJid);
|
|
466
|
+
const placeholders = jids.map(() => "?").join(", ");
|
|
467
|
+
db.prepare(`UPDATE messages SET transcription = ? WHERE chat_jid IN (${placeholders}) AND id = ?`).run(transcription, ...jids, messageId);
|
|
468
|
+
}
|
|
469
|
+
export function getLastMessageKey(jid) {
|
|
470
|
+
const row = db.prepare(`
|
|
471
|
+
SELECT id, from_me, chat_jid, timestamp FROM messages
|
|
472
|
+
WHERE chat_jid = ?
|
|
473
|
+
ORDER BY timestamp DESC
|
|
474
|
+
LIMIT 1
|
|
475
|
+
`).get(jid);
|
|
476
|
+
if (!row)
|
|
477
|
+
return null;
|
|
478
|
+
return { id: row.id, fromMe: row.from_me === 1, remoteJid: row.chat_jid, timestamp: row.timestamp };
|
|
479
|
+
}
|
|
480
|
+
export function deleteChatMessages(jid) {
|
|
481
|
+
db.prepare(`DELETE FROM messages WHERE chat_jid = ?`).run(jid);
|
|
482
|
+
}
|
|
483
|
+
export function getChatName(jid) {
|
|
484
|
+
const row = db.prepare(`SELECT name FROM chats WHERE jid = ?`).get(jid);
|
|
485
|
+
return row?.name || null;
|
|
486
|
+
}
|
|
487
|
+
export function getContactName(jid) {
|
|
488
|
+
const row = db.prepare(`SELECT name, notify FROM contacts WHERE jid = ?`).get(jid);
|
|
489
|
+
return row?.name || row?.notify || null;
|
|
490
|
+
}
|
|
491
|
+
/** Return the raw better-sqlite3 instance (for use by import-contacts). */
|
|
492
|
+
export function getDb() {
|
|
493
|
+
return db;
|
|
494
|
+
}
|
|
495
|
+
// ─── Init / Close ───────────────────────────────────────────────────
|
|
496
|
+
export function initDb() {
|
|
497
|
+
fs.mkdirSync(STORE_DIR, { recursive: true });
|
|
498
|
+
db = new Database(DB_PATH);
|
|
499
|
+
createTables();
|
|
500
|
+
prepareStatements();
|
|
501
|
+
console.error(`Database initialized at ${DB_PATH}`);
|
|
502
|
+
}
|
|
503
|
+
export function closeDb() {
|
|
504
|
+
if (db) {
|
|
505
|
+
db.close();
|
|
506
|
+
console.error("Database closed");
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
//# sourceMappingURL=db.js.map
|
package/dist/db.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Import phone contacts from a .vcf (vCard) file into the WhatsApp MCP database.
|
|
4
|
+
*
|
|
5
|
+
* Usage (standalone CLI):
|
|
6
|
+
* npx tsx src/import-contacts.ts contacts.vcf
|
|
7
|
+
* npx tsx src/import-contacts.ts contacts.vcf --dry-run
|
|
8
|
+
*
|
|
9
|
+
* Also exports `importContactsFromVcf()` for use by the MCP tool.
|
|
10
|
+
*
|
|
11
|
+
* Export your contacts from iPhone:
|
|
12
|
+
* Settings > Contacts > Accounts > iCloud > Contacts (on)
|
|
13
|
+
* Go to iCloud.com > Contacts > Select All > Export vCard
|
|
14
|
+
*
|
|
15
|
+
* Or use any other method that produces a .vcf file.
|
|
16
|
+
*/
|
|
17
|
+
import Database from "better-sqlite3";
|
|
18
|
+
export interface ImportResult {
|
|
19
|
+
success: boolean;
|
|
20
|
+
vcfPath: string;
|
|
21
|
+
totalParsed: number;
|
|
22
|
+
exactMatches: number;
|
|
23
|
+
fuzzyMatches: number;
|
|
24
|
+
totalUpdated: number;
|
|
25
|
+
namelessChatsRemaining: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Import contacts from a VCF file into the database.
|
|
29
|
+
* Can be called from the MCP tool or standalone CLI.
|
|
30
|
+
*
|
|
31
|
+
* @param dbInstance - An open better-sqlite3 database instance.
|
|
32
|
+
* When called from the MCP server, pass the shared DB.
|
|
33
|
+
* When called from CLI, opens its own connection.
|
|
34
|
+
* @param vcfPath - Path to the .vcf file. Defaults to contacts/contacts.vcf.
|
|
35
|
+
*/
|
|
36
|
+
export declare function importContactsFromVcf(dbInstance: Database.Database, vcfPath?: string): ImportResult;
|
|
37
|
+
//# sourceMappingURL=import-contacts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-contacts.d.ts","sourceRoot":"","sources":["../src/import-contacts.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAwFtC,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,YAAY,CA2GnG"}
|