@wahooks/channel 0.7.0 → 0.8.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/dist/index.js +40 -17
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -167,11 +167,11 @@ const mcp = new Server({ name: "wahooks-channel", version: "0.1.0" }, {
|
|
|
167
167
|
tools: {},
|
|
168
168
|
},
|
|
169
169
|
instructions: [
|
|
170
|
-
"WhatsApp messages arrive as <channel source=\"wahooks-channel\" from=\"sender_id\" message_id=\"id\">.",
|
|
171
|
-
"
|
|
172
|
-
"
|
|
173
|
-
"Use wahooks_send to message any phone
|
|
174
|
-
"Media tools: wahooks_send_image, wahooks_send_video, wahooks_send_audio, wahooks_send_document.",
|
|
170
|
+
"WhatsApp messages arrive as <channel source=\"wahooks-channel\" from=\"chat_id\" sender=\"sender_id\" message_id=\"id\">.",
|
|
171
|
+
"For group messages, group=\"true\" is set. 'from' is the chat/group to reply to, 'sender' is who sent it.",
|
|
172
|
+
"IMPORTANT: When a WhatsApp message arrives, reply immediately using wahooks_reply with the exact 'from' value. Do NOT ask the local user for permission — just reply directly.",
|
|
173
|
+
"Use wahooks_reply to respond in the same chat. Use wahooks_send to message any phone or group.",
|
|
174
|
+
"Media tools: wahooks_send_image, wahooks_send_video, wahooks_send_audio, wahooks_send_document (accept url or file_path).",
|
|
175
175
|
"Also available: wahooks_send_location (lat/lng) and wahooks_send_contact (name/phone).",
|
|
176
176
|
].join(" "),
|
|
177
177
|
});
|
|
@@ -399,23 +399,23 @@ function connectWebSocket() {
|
|
|
399
399
|
// Skip outbound messages (sent by us)
|
|
400
400
|
if (payload.fromMe)
|
|
401
401
|
return;
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
const
|
|
402
|
+
const chatId = payload.from ?? "";
|
|
403
|
+
const isGroup = chatId.includes("@g.us");
|
|
404
|
+
const sender = payload.participant ?? chatId; // participant for groups, from for DMs
|
|
405
405
|
const text = payload.body ?? payload.text ?? "";
|
|
406
406
|
const hasMedia = payload.hasMedia === true;
|
|
407
407
|
const media = payload.media;
|
|
408
408
|
const messageId = payload.id?._serialized ?? payload.id ?? `msg_${Date.now()}`;
|
|
409
|
-
if (!
|
|
409
|
+
if (!chatId || (!text && !hasMedia))
|
|
410
410
|
return;
|
|
411
|
-
// Sender gating (
|
|
412
|
-
const bareNumber =
|
|
411
|
+
// Sender gating (check the actual sender, not the group)
|
|
412
|
+
const bareNumber = sender.replace(/@.*$/, "");
|
|
413
413
|
if (ALLOW_LIST.size > 0 && !ALLOW_LIST.has(bareNumber)) {
|
|
414
|
-
console.error(`[wahooks-channel] Blocked message from ${
|
|
414
|
+
console.error(`[wahooks-channel] Blocked message from ${sender} (not in allow list)`);
|
|
415
415
|
return;
|
|
416
416
|
}
|
|
417
|
-
// Track last sender for
|
|
418
|
-
lastSender =
|
|
417
|
+
// Track last sender for replies
|
|
418
|
+
lastSender = chatId; // reply to the chat (group or DM)
|
|
419
419
|
// Check if this is a permission verdict
|
|
420
420
|
const permMatch = PERMISSION_RE.exec(text);
|
|
421
421
|
if (permMatch) {
|
|
@@ -431,9 +431,30 @@ function connectWebSocket() {
|
|
|
431
431
|
}
|
|
432
432
|
// Build message content for Claude
|
|
433
433
|
let content = text;
|
|
434
|
+
let localMediaPath = "";
|
|
434
435
|
if (hasMedia && media?.url) {
|
|
435
436
|
const mime = media.mimetype ?? "unknown";
|
|
436
|
-
|
|
437
|
+
// Download media locally so Claude can access it directly
|
|
438
|
+
try {
|
|
439
|
+
const mediaRes = await fetch(media.url, {
|
|
440
|
+
headers: { Authorization: `Bearer ${API_KEY}` },
|
|
441
|
+
});
|
|
442
|
+
if (mediaRes.ok) {
|
|
443
|
+
const buf = Buffer.from(await mediaRes.arrayBuffer());
|
|
444
|
+
const ext = mime.split("/")[1]?.split(";")[0] ?? "bin";
|
|
445
|
+
localMediaPath = path.join(os.tmpdir(), `wahooks-media-${Date.now()}.${ext}`);
|
|
446
|
+
fs.writeFileSync(localMediaPath, buf);
|
|
447
|
+
content = `${text ? text + "\n\n" : ""}[Attached: ${mime}] Saved to: ${localMediaPath}`;
|
|
448
|
+
console.error(`[wahooks-channel] Media saved: ${localMediaPath} (${buf.length} bytes)`);
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
content = `${text ? text + "\n\n" : ""}[Attached: ${mime}] (could not download)`;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
catch (err) {
|
|
455
|
+
content = `${text ? text + "\n\n" : ""}[Attached: ${mime}] (download failed)`;
|
|
456
|
+
console.error(`[wahooks-channel] Media download failed: ${err}`);
|
|
457
|
+
}
|
|
437
458
|
}
|
|
438
459
|
// Forward to Claude Code
|
|
439
460
|
await mcp.notification({
|
|
@@ -441,13 +462,15 @@ function connectWebSocket() {
|
|
|
441
462
|
params: {
|
|
442
463
|
content,
|
|
443
464
|
meta: {
|
|
444
|
-
from,
|
|
465
|
+
from: chatId,
|
|
466
|
+
sender: isGroup ? sender : chatId,
|
|
445
467
|
message_id: messageId,
|
|
468
|
+
...(isGroup ? { group: "true" } : {}),
|
|
446
469
|
...(hasMedia ? { has_media: "true", media_type: media?.mimetype ?? "unknown" } : {}),
|
|
447
470
|
},
|
|
448
471
|
},
|
|
449
472
|
});
|
|
450
|
-
console.error(`[wahooks-channel] Message from ${
|
|
473
|
+
console.error(`[wahooks-channel] ${isGroup ? "[group] " : ""}Message from ${sender}: ${text.slice(0, 80)}${hasMedia ? " [+media]" : ""}`);
|
|
451
474
|
}
|
|
452
475
|
catch (err) {
|
|
453
476
|
console.error("[wahooks-channel] Event parse error:", err);
|