strapi-plugin-magic-mail 2.2.6 → 2.3.1
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/README.md +168 -10
- package/dist/_chunks/{App-Bze8Ixs_.js → App-BMaJu77a.js} +787 -71
- package/dist/_chunks/{App-BZaHrE0R.mjs → App-DxiMl-Zd.mjs} +790 -74
- package/dist/_chunks/{de-DS04rP54.mjs → de-CpIQf94q.mjs} +21 -1
- package/dist/_chunks/{de-CN-G9j1S.js → de-YhjDItIL.js} +21 -1
- package/dist/_chunks/{en-BEFQJXvR.mjs → en-BHmOVzsP.mjs} +21 -1
- package/dist/_chunks/{en-BDc7Jk8u.js → en-BcdTnA2-.js} +21 -1
- package/dist/admin/index.js +2 -2
- package/dist/admin/index.mjs +2 -2
- package/dist/server/index.js +1124 -20
- package/dist/server/index.mjs +1122 -20
- package/package.json +10 -5
package/dist/server/index.js
CHANGED
|
@@ -5,6 +5,8 @@ const require$$1$1 = require("os");
|
|
|
5
5
|
const require$$0$3 = require("mustache");
|
|
6
6
|
const require$$1$2 = require("html-to-text");
|
|
7
7
|
const require$$2$2 = require("decode-html");
|
|
8
|
+
const require$$0$4 = require("path");
|
|
9
|
+
const require$$1$3 = require("fs");
|
|
8
10
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
9
11
|
const require$$0__default$1 = /* @__PURE__ */ _interopDefault(require$$0$2);
|
|
10
12
|
const require$$0__default = /* @__PURE__ */ _interopDefault(require$$0$1);
|
|
@@ -12,6 +14,8 @@ const require$$1__default = /* @__PURE__ */ _interopDefault(require$$1$1);
|
|
|
12
14
|
const require$$0__default$2 = /* @__PURE__ */ _interopDefault(require$$0$3);
|
|
13
15
|
const require$$1__default$1 = /* @__PURE__ */ _interopDefault(require$$1$2);
|
|
14
16
|
const require$$2__default = /* @__PURE__ */ _interopDefault(require$$2$2);
|
|
17
|
+
const require$$0__default$3 = /* @__PURE__ */ _interopDefault(require$$0$4);
|
|
18
|
+
const require$$1__default$2 = /* @__PURE__ */ _interopDefault(require$$1$3);
|
|
15
19
|
var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
|
|
16
20
|
function getDefaultExportFromCjs(x2) {
|
|
17
21
|
return x2 && x2.__esModule && Object.prototype.hasOwnProperty.call(x2, "default") ? x2["default"] : x2;
|
|
@@ -265,7 +269,7 @@ const collectionName$5 = "magic_mail_routing_rules";
|
|
|
265
269
|
const info$5 = { "singularName": "routing-rule", "pluralName": "routing-rules", "displayName": "Email Routing Rule", "description": "Rules for routing emails to specific accounts" };
|
|
266
270
|
const options$5 = { "draftAndPublish": false };
|
|
267
271
|
const pluginOptions$5 = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
|
|
268
|
-
const attributes$5 = { "name": { "type": "string", "required": true, "unique": true }, "description": { "type": "text" }, "isActive": { "type": "boolean", "default": true, "required": true }, "priority": { "type": "integer", "default": 1, "min": 1 }, "matchType": { "type": "enumeration", "enum": ["emailType", "subject", "recipient", "template", "custom"], "required": true, "default": "emailType" }, "matchValue": { "type": "text", "required": true }, "accountName": { "type": "string", "required": true }, "fallbackAccountName": { "type": "string" } };
|
|
272
|
+
const attributes$5 = { "name": { "type": "string", "required": true, "unique": true }, "description": { "type": "text" }, "isActive": { "type": "boolean", "default": true, "required": true }, "priority": { "type": "integer", "default": 1, "min": 1 }, "matchType": { "type": "enumeration", "enum": ["emailType", "subject", "recipient", "template", "custom"], "required": true, "default": "emailType" }, "matchValue": { "type": "text", "required": true }, "accountName": { "type": "string", "required": true }, "fallbackAccountName": { "type": "string" }, "whatsappFallback": { "type": "boolean", "default": false }, "whatsappPhoneField": { "type": "string" } };
|
|
269
273
|
const require$$1 = {
|
|
270
274
|
kind: kind$5,
|
|
271
275
|
collectionName: collectionName$5,
|
|
@@ -402,6 +406,88 @@ function requireController() {
|
|
|
402
406
|
strapi.log.error("[magic-mail] Error sending email:", err);
|
|
403
407
|
ctx.throw(500, err.message || "Failed to send email");
|
|
404
408
|
}
|
|
409
|
+
},
|
|
410
|
+
/**
|
|
411
|
+
* Send message via Email or WhatsApp (unified API)
|
|
412
|
+
* POST /api/magic-mail/send-message
|
|
413
|
+
* Body: { channel: 'email' | 'whatsapp' | 'auto', to, phoneNumber, subject, message, ... }
|
|
414
|
+
*/
|
|
415
|
+
async sendMessage(ctx) {
|
|
416
|
+
try {
|
|
417
|
+
const emailRouter2 = strapi.plugin("magic-mail").service("email-router");
|
|
418
|
+
const result = await emailRouter2.sendMessage(ctx.request.body);
|
|
419
|
+
ctx.body = {
|
|
420
|
+
success: true,
|
|
421
|
+
...result
|
|
422
|
+
};
|
|
423
|
+
} catch (err) {
|
|
424
|
+
strapi.log.error("[magic-mail] Error sending message:", err);
|
|
425
|
+
ctx.throw(500, err.message || "Failed to send message");
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
/**
|
|
429
|
+
* Send WhatsApp message
|
|
430
|
+
* POST /api/magic-mail/send-whatsapp
|
|
431
|
+
* Body: { phoneNumber, message, templateId?, templateData? }
|
|
432
|
+
*/
|
|
433
|
+
async sendWhatsApp(ctx) {
|
|
434
|
+
try {
|
|
435
|
+
const emailRouter2 = strapi.plugin("magic-mail").service("email-router");
|
|
436
|
+
const result = await emailRouter2.sendWhatsApp(ctx.request.body);
|
|
437
|
+
ctx.body = {
|
|
438
|
+
success: true,
|
|
439
|
+
...result
|
|
440
|
+
};
|
|
441
|
+
} catch (err) {
|
|
442
|
+
strapi.log.error("[magic-mail] Error sending WhatsApp:", err);
|
|
443
|
+
ctx.throw(500, err.message || "Failed to send WhatsApp message");
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
/**
|
|
447
|
+
* Get WhatsApp connection status
|
|
448
|
+
* GET /api/magic-mail/whatsapp/status
|
|
449
|
+
*/
|
|
450
|
+
async getWhatsAppStatus(ctx) {
|
|
451
|
+
try {
|
|
452
|
+
const emailRouter2 = strapi.plugin("magic-mail").service("email-router");
|
|
453
|
+
const status = emailRouter2.getWhatsAppStatus();
|
|
454
|
+
ctx.body = {
|
|
455
|
+
success: true,
|
|
456
|
+
data: status
|
|
457
|
+
};
|
|
458
|
+
} catch (err) {
|
|
459
|
+
strapi.log.error("[magic-mail] Error getting WhatsApp status:", err);
|
|
460
|
+
ctx.body = {
|
|
461
|
+
success: false,
|
|
462
|
+
data: {
|
|
463
|
+
isConnected: false,
|
|
464
|
+
status: "error",
|
|
465
|
+
error: err.message
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
},
|
|
470
|
+
/**
|
|
471
|
+
* Check if phone number is on WhatsApp
|
|
472
|
+
* GET /api/magic-mail/whatsapp/check/:phoneNumber
|
|
473
|
+
*/
|
|
474
|
+
async checkWhatsAppNumber(ctx) {
|
|
475
|
+
try {
|
|
476
|
+
const { phoneNumber } = ctx.params;
|
|
477
|
+
if (!phoneNumber) {
|
|
478
|
+
ctx.throw(400, "Phone number is required");
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
const emailRouter2 = strapi.plugin("magic-mail").service("email-router");
|
|
482
|
+
const result = await emailRouter2.checkWhatsAppNumber(phoneNumber);
|
|
483
|
+
ctx.body = {
|
|
484
|
+
success: true,
|
|
485
|
+
data: result
|
|
486
|
+
};
|
|
487
|
+
} catch (err) {
|
|
488
|
+
strapi.log.error("[magic-mail] Error checking WhatsApp number:", err);
|
|
489
|
+
ctx.throw(500, err.message || "Failed to check phone number");
|
|
490
|
+
}
|
|
405
491
|
}
|
|
406
492
|
};
|
|
407
493
|
return controller;
|
|
@@ -520,7 +606,7 @@ function requireAccounts() {
|
|
|
520
606
|
if (!testEmail) {
|
|
521
607
|
ctx.throw(400, "testEmail is required");
|
|
522
608
|
}
|
|
523
|
-
strapi.log.info("[magic-mail]
|
|
609
|
+
strapi.log.info("[magic-mail] [TEST] Testing Strapi Email Service integration...");
|
|
524
610
|
strapi.log.info('[magic-mail] [EMAIL] Calling strapi.plugin("email").service("email").send()');
|
|
525
611
|
if (accountName) {
|
|
526
612
|
strapi.log.info(`[magic-mail] [FORCE] Forcing specific account: ${accountName}`);
|
|
@@ -2236,7 +2322,7 @@ function requireTest() {
|
|
|
2236
2322
|
async testRelations(ctx) {
|
|
2237
2323
|
try {
|
|
2238
2324
|
console.log("\n" + "=".repeat(60));
|
|
2239
|
-
console.log("
|
|
2325
|
+
console.log("[TEST] Template - Version Relations (Document Service API)");
|
|
2240
2326
|
console.log("=".repeat(60));
|
|
2241
2327
|
let test1Success = false;
|
|
2242
2328
|
let test1ReverseSuccess = false;
|
|
@@ -2404,7 +2490,7 @@ function requireTest() {
|
|
|
2404
2490
|
} else {
|
|
2405
2491
|
console.log(` [ERROR] FEHLER: Falsche Anzahl Versionen!`);
|
|
2406
2492
|
}
|
|
2407
|
-
console.log("\n
|
|
2493
|
+
console.log("\n[CLEANUP] Cleanup Test 3...");
|
|
2408
2494
|
if (afterSecondUpdate.versions) {
|
|
2409
2495
|
for (const version3 of afterSecondUpdate.versions) {
|
|
2410
2496
|
await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).delete({ documentId: version3.documentId });
|
|
@@ -2422,7 +2508,7 @@ function requireTest() {
|
|
|
2422
2508
|
console.log(`
|
|
2423
2509
|
[INFO] Template: "${finalTemplate.name}" (documentId: ${finalTemplate.documentId})`);
|
|
2424
2510
|
console.log(` Anzahl Versionen: ${finalTemplate.versions?.length || 0}`);
|
|
2425
|
-
console.log("\n
|
|
2511
|
+
console.log("\n[CLEANUP] Aufraumen...");
|
|
2426
2512
|
await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).delete({ documentId: version1.documentId });
|
|
2427
2513
|
await strapi.documents(EMAIL_TEMPLATE_VERSION_UID).delete({ documentId: version2.documentId });
|
|
2428
2514
|
await strapi.documents(EMAIL_TEMPLATE_UID).delete({ documentId: testTemplate.documentId });
|
|
@@ -2452,6 +2538,219 @@ function requireTest() {
|
|
|
2452
2538
|
};
|
|
2453
2539
|
return test;
|
|
2454
2540
|
}
|
|
2541
|
+
var whatsapp$1;
|
|
2542
|
+
var hasRequiredWhatsapp$1;
|
|
2543
|
+
function requireWhatsapp$1() {
|
|
2544
|
+
if (hasRequiredWhatsapp$1) return whatsapp$1;
|
|
2545
|
+
hasRequiredWhatsapp$1 = 1;
|
|
2546
|
+
whatsapp$1 = {
|
|
2547
|
+
/**
|
|
2548
|
+
* Check if WhatsApp/Baileys is available
|
|
2549
|
+
* GET /magic-mail/whatsapp/available
|
|
2550
|
+
*/
|
|
2551
|
+
async checkAvailable(ctx) {
|
|
2552
|
+
try {
|
|
2553
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2554
|
+
const available = await whatsappService.isAvailable();
|
|
2555
|
+
ctx.body = {
|
|
2556
|
+
success: true,
|
|
2557
|
+
data: {
|
|
2558
|
+
available,
|
|
2559
|
+
message: available ? "WhatsApp integration is available" : "Baileys not installed. Run: npm install @whiskeysockets/baileys pino qrcode"
|
|
2560
|
+
}
|
|
2561
|
+
};
|
|
2562
|
+
} catch (error) {
|
|
2563
|
+
ctx.throw(500, error.message);
|
|
2564
|
+
}
|
|
2565
|
+
},
|
|
2566
|
+
/**
|
|
2567
|
+
* Get WhatsApp connection status
|
|
2568
|
+
* GET /magic-mail/whatsapp/status
|
|
2569
|
+
*/
|
|
2570
|
+
async getStatus(ctx) {
|
|
2571
|
+
try {
|
|
2572
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2573
|
+
const status = whatsappService.getStatus();
|
|
2574
|
+
const sessionInfo = await whatsappService.getSessionInfo();
|
|
2575
|
+
ctx.body = {
|
|
2576
|
+
success: true,
|
|
2577
|
+
data: {
|
|
2578
|
+
...status,
|
|
2579
|
+
session: sessionInfo
|
|
2580
|
+
}
|
|
2581
|
+
};
|
|
2582
|
+
} catch (error) {
|
|
2583
|
+
ctx.throw(500, error.message);
|
|
2584
|
+
}
|
|
2585
|
+
},
|
|
2586
|
+
/**
|
|
2587
|
+
* Connect to WhatsApp (generates QR code if needed)
|
|
2588
|
+
* POST /magic-mail/whatsapp/connect
|
|
2589
|
+
*/
|
|
2590
|
+
async connect(ctx) {
|
|
2591
|
+
try {
|
|
2592
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2593
|
+
const result = await whatsappService.connect();
|
|
2594
|
+
ctx.body = {
|
|
2595
|
+
success: result.success,
|
|
2596
|
+
data: result
|
|
2597
|
+
};
|
|
2598
|
+
} catch (error) {
|
|
2599
|
+
ctx.throw(500, error.message);
|
|
2600
|
+
}
|
|
2601
|
+
},
|
|
2602
|
+
/**
|
|
2603
|
+
* Disconnect from WhatsApp
|
|
2604
|
+
* POST /magic-mail/whatsapp/disconnect
|
|
2605
|
+
*/
|
|
2606
|
+
async disconnect(ctx) {
|
|
2607
|
+
try {
|
|
2608
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2609
|
+
const result = await whatsappService.disconnect();
|
|
2610
|
+
ctx.body = {
|
|
2611
|
+
success: result.success,
|
|
2612
|
+
data: result
|
|
2613
|
+
};
|
|
2614
|
+
} catch (error) {
|
|
2615
|
+
ctx.throw(500, error.message);
|
|
2616
|
+
}
|
|
2617
|
+
},
|
|
2618
|
+
/**
|
|
2619
|
+
* Send a test message
|
|
2620
|
+
* POST /magic-mail/whatsapp/send-test
|
|
2621
|
+
*/
|
|
2622
|
+
async sendTest(ctx) {
|
|
2623
|
+
try {
|
|
2624
|
+
const { phoneNumber, message } = ctx.request.body;
|
|
2625
|
+
if (!phoneNumber) {
|
|
2626
|
+
return ctx.badRequest("Phone number is required");
|
|
2627
|
+
}
|
|
2628
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2629
|
+
const testMessage = message || `[MagicMail Test] This is a test message sent at ${(/* @__PURE__ */ new Date()).toLocaleString()}`;
|
|
2630
|
+
const result = await whatsappService.sendMessage(phoneNumber, testMessage);
|
|
2631
|
+
ctx.body = {
|
|
2632
|
+
success: result.success,
|
|
2633
|
+
data: result
|
|
2634
|
+
};
|
|
2635
|
+
} catch (error) {
|
|
2636
|
+
ctx.throw(500, error.message);
|
|
2637
|
+
}
|
|
2638
|
+
},
|
|
2639
|
+
/**
|
|
2640
|
+
* Send a message using a template
|
|
2641
|
+
* POST /magic-mail/whatsapp/send-template
|
|
2642
|
+
*/
|
|
2643
|
+
async sendTemplateMessage(ctx) {
|
|
2644
|
+
try {
|
|
2645
|
+
const { phoneNumber, templateName, variables } = ctx.request.body;
|
|
2646
|
+
if (!phoneNumber || !templateName) {
|
|
2647
|
+
return ctx.badRequest("Phone number and template name are required");
|
|
2648
|
+
}
|
|
2649
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2650
|
+
const result = await whatsappService.sendTemplateMessage(phoneNumber, templateName, variables || {});
|
|
2651
|
+
ctx.body = {
|
|
2652
|
+
success: result.success,
|
|
2653
|
+
data: result
|
|
2654
|
+
};
|
|
2655
|
+
} catch (error) {
|
|
2656
|
+
ctx.throw(500, error.message);
|
|
2657
|
+
}
|
|
2658
|
+
},
|
|
2659
|
+
/**
|
|
2660
|
+
* Check if a phone number is on WhatsApp
|
|
2661
|
+
* POST /magic-mail/whatsapp/check-number
|
|
2662
|
+
*/
|
|
2663
|
+
async checkNumber(ctx) {
|
|
2664
|
+
try {
|
|
2665
|
+
const { phoneNumber } = ctx.request.body;
|
|
2666
|
+
if (!phoneNumber) {
|
|
2667
|
+
return ctx.badRequest("Phone number is required");
|
|
2668
|
+
}
|
|
2669
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2670
|
+
const result = await whatsappService.checkNumber(phoneNumber);
|
|
2671
|
+
ctx.body = {
|
|
2672
|
+
success: result.success,
|
|
2673
|
+
data: result
|
|
2674
|
+
};
|
|
2675
|
+
} catch (error) {
|
|
2676
|
+
ctx.throw(500, error.message);
|
|
2677
|
+
}
|
|
2678
|
+
},
|
|
2679
|
+
/**
|
|
2680
|
+
* Get all WhatsApp templates
|
|
2681
|
+
* GET /magic-mail/whatsapp/templates
|
|
2682
|
+
*/
|
|
2683
|
+
async getTemplates(ctx) {
|
|
2684
|
+
try {
|
|
2685
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2686
|
+
const templates = await whatsappService.getTemplates();
|
|
2687
|
+
ctx.body = {
|
|
2688
|
+
success: true,
|
|
2689
|
+
data: templates
|
|
2690
|
+
};
|
|
2691
|
+
} catch (error) {
|
|
2692
|
+
ctx.throw(500, error.message);
|
|
2693
|
+
}
|
|
2694
|
+
},
|
|
2695
|
+
/**
|
|
2696
|
+
* Save a WhatsApp template
|
|
2697
|
+
* POST /magic-mail/whatsapp/templates
|
|
2698
|
+
*/
|
|
2699
|
+
async saveTemplate(ctx) {
|
|
2700
|
+
try {
|
|
2701
|
+
const { templateName, templateContent } = ctx.request.body;
|
|
2702
|
+
if (!templateName || !templateContent) {
|
|
2703
|
+
return ctx.badRequest("Template name and content are required");
|
|
2704
|
+
}
|
|
2705
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2706
|
+
const result = await whatsappService.saveTemplate(templateName, templateContent);
|
|
2707
|
+
ctx.body = {
|
|
2708
|
+
success: result.success,
|
|
2709
|
+
data: result
|
|
2710
|
+
};
|
|
2711
|
+
} catch (error) {
|
|
2712
|
+
ctx.throw(500, error.message);
|
|
2713
|
+
}
|
|
2714
|
+
},
|
|
2715
|
+
/**
|
|
2716
|
+
* Delete a WhatsApp template
|
|
2717
|
+
* DELETE /magic-mail/whatsapp/templates/:templateName
|
|
2718
|
+
*/
|
|
2719
|
+
async deleteTemplate(ctx) {
|
|
2720
|
+
try {
|
|
2721
|
+
const { templateName } = ctx.params;
|
|
2722
|
+
if (!templateName) {
|
|
2723
|
+
return ctx.badRequest("Template name is required");
|
|
2724
|
+
}
|
|
2725
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2726
|
+
const result = await whatsappService.deleteTemplate(templateName);
|
|
2727
|
+
ctx.body = {
|
|
2728
|
+
success: result.success,
|
|
2729
|
+
data: result
|
|
2730
|
+
};
|
|
2731
|
+
} catch (error) {
|
|
2732
|
+
ctx.throw(500, error.message);
|
|
2733
|
+
}
|
|
2734
|
+
},
|
|
2735
|
+
/**
|
|
2736
|
+
* Get session info
|
|
2737
|
+
* GET /magic-mail/whatsapp/session
|
|
2738
|
+
*/
|
|
2739
|
+
async getSession(ctx) {
|
|
2740
|
+
try {
|
|
2741
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2742
|
+
const sessionInfo = await whatsappService.getSessionInfo();
|
|
2743
|
+
ctx.body = {
|
|
2744
|
+
success: true,
|
|
2745
|
+
data: sessionInfo
|
|
2746
|
+
};
|
|
2747
|
+
} catch (error) {
|
|
2748
|
+
ctx.throw(500, error.message);
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
};
|
|
2752
|
+
return whatsapp$1;
|
|
2753
|
+
}
|
|
2455
2754
|
var controllers;
|
|
2456
2755
|
var hasRequiredControllers;
|
|
2457
2756
|
function requireControllers() {
|
|
@@ -2465,6 +2764,7 @@ function requireControllers() {
|
|
|
2465
2764
|
const emailDesigner2 = requireEmailDesigner$1();
|
|
2466
2765
|
const analytics2 = requireAnalytics$1();
|
|
2467
2766
|
const test2 = requireTest();
|
|
2767
|
+
const whatsapp2 = requireWhatsapp$1();
|
|
2468
2768
|
controllers = {
|
|
2469
2769
|
controller: controller2,
|
|
2470
2770
|
accounts: accounts2,
|
|
@@ -2473,7 +2773,8 @@ function requireControllers() {
|
|
|
2473
2773
|
license: license2,
|
|
2474
2774
|
emailDesigner: emailDesigner2,
|
|
2475
2775
|
analytics: analytics2,
|
|
2476
|
-
test: test2
|
|
2776
|
+
test: test2,
|
|
2777
|
+
whatsapp: whatsapp2
|
|
2477
2778
|
};
|
|
2478
2779
|
return controllers;
|
|
2479
2780
|
}
|
|
@@ -2948,6 +3249,106 @@ function requireAdmin() {
|
|
|
2948
3249
|
policies: ["admin::isAuthenticatedAdmin"],
|
|
2949
3250
|
description: "Test template-version relations"
|
|
2950
3251
|
}
|
|
3252
|
+
},
|
|
3253
|
+
// WhatsApp Routes
|
|
3254
|
+
{
|
|
3255
|
+
method: "GET",
|
|
3256
|
+
path: "/whatsapp/available",
|
|
3257
|
+
handler: "whatsapp.checkAvailable",
|
|
3258
|
+
config: {
|
|
3259
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3260
|
+
description: "Check if WhatsApp/Baileys is available"
|
|
3261
|
+
}
|
|
3262
|
+
},
|
|
3263
|
+
{
|
|
3264
|
+
method: "GET",
|
|
3265
|
+
path: "/whatsapp/status",
|
|
3266
|
+
handler: "whatsapp.getStatus",
|
|
3267
|
+
config: {
|
|
3268
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3269
|
+
description: "Get WhatsApp connection status"
|
|
3270
|
+
}
|
|
3271
|
+
},
|
|
3272
|
+
{
|
|
3273
|
+
method: "POST",
|
|
3274
|
+
path: "/whatsapp/connect",
|
|
3275
|
+
handler: "whatsapp.connect",
|
|
3276
|
+
config: {
|
|
3277
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3278
|
+
description: "Connect to WhatsApp (generates QR if needed)"
|
|
3279
|
+
}
|
|
3280
|
+
},
|
|
3281
|
+
{
|
|
3282
|
+
method: "POST",
|
|
3283
|
+
path: "/whatsapp/disconnect",
|
|
3284
|
+
handler: "whatsapp.disconnect",
|
|
3285
|
+
config: {
|
|
3286
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3287
|
+
description: "Disconnect from WhatsApp"
|
|
3288
|
+
}
|
|
3289
|
+
},
|
|
3290
|
+
{
|
|
3291
|
+
method: "POST",
|
|
3292
|
+
path: "/whatsapp/send-test",
|
|
3293
|
+
handler: "whatsapp.sendTest",
|
|
3294
|
+
config: {
|
|
3295
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3296
|
+
description: "Send a test WhatsApp message"
|
|
3297
|
+
}
|
|
3298
|
+
},
|
|
3299
|
+
{
|
|
3300
|
+
method: "POST",
|
|
3301
|
+
path: "/whatsapp/send-template",
|
|
3302
|
+
handler: "whatsapp.sendTemplateMessage",
|
|
3303
|
+
config: {
|
|
3304
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3305
|
+
description: "Send WhatsApp message using template"
|
|
3306
|
+
}
|
|
3307
|
+
},
|
|
3308
|
+
{
|
|
3309
|
+
method: "POST",
|
|
3310
|
+
path: "/whatsapp/check-number",
|
|
3311
|
+
handler: "whatsapp.checkNumber",
|
|
3312
|
+
config: {
|
|
3313
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3314
|
+
description: "Check if phone number is on WhatsApp"
|
|
3315
|
+
}
|
|
3316
|
+
},
|
|
3317
|
+
{
|
|
3318
|
+
method: "GET",
|
|
3319
|
+
path: "/whatsapp/templates",
|
|
3320
|
+
handler: "whatsapp.getTemplates",
|
|
3321
|
+
config: {
|
|
3322
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3323
|
+
description: "Get all WhatsApp message templates"
|
|
3324
|
+
}
|
|
3325
|
+
},
|
|
3326
|
+
{
|
|
3327
|
+
method: "POST",
|
|
3328
|
+
path: "/whatsapp/templates",
|
|
3329
|
+
handler: "whatsapp.saveTemplate",
|
|
3330
|
+
config: {
|
|
3331
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3332
|
+
description: "Save a WhatsApp message template"
|
|
3333
|
+
}
|
|
3334
|
+
},
|
|
3335
|
+
{
|
|
3336
|
+
method: "DELETE",
|
|
3337
|
+
path: "/whatsapp/templates/:templateName",
|
|
3338
|
+
handler: "whatsapp.deleteTemplate",
|
|
3339
|
+
config: {
|
|
3340
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3341
|
+
description: "Delete a WhatsApp message template"
|
|
3342
|
+
}
|
|
3343
|
+
},
|
|
3344
|
+
{
|
|
3345
|
+
method: "GET",
|
|
3346
|
+
path: "/whatsapp/session",
|
|
3347
|
+
handler: "whatsapp.getSession",
|
|
3348
|
+
config: {
|
|
3349
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3350
|
+
description: "Get WhatsApp session info"
|
|
3351
|
+
}
|
|
2951
3352
|
}
|
|
2952
3353
|
]
|
|
2953
3354
|
};
|
|
@@ -2961,6 +3362,7 @@ function requireContentApi() {
|
|
|
2961
3362
|
contentApi = {
|
|
2962
3363
|
type: "content-api",
|
|
2963
3364
|
routes: [
|
|
3365
|
+
// ============= EMAIL ROUTES =============
|
|
2964
3366
|
{
|
|
2965
3367
|
method: "POST",
|
|
2966
3368
|
path: "/send",
|
|
@@ -2971,7 +3373,45 @@ function requireContentApi() {
|
|
|
2971
3373
|
description: "Send email via MagicMail router"
|
|
2972
3374
|
}
|
|
2973
3375
|
},
|
|
2974
|
-
//
|
|
3376
|
+
// ============= UNIFIED MESSAGE ROUTE =============
|
|
3377
|
+
{
|
|
3378
|
+
method: "POST",
|
|
3379
|
+
path: "/send-message",
|
|
3380
|
+
handler: "controller.sendMessage",
|
|
3381
|
+
config: {
|
|
3382
|
+
auth: false,
|
|
3383
|
+
description: "Send message via Email or WhatsApp (unified API)"
|
|
3384
|
+
}
|
|
3385
|
+
},
|
|
3386
|
+
// ============= WHATSAPP ROUTES =============
|
|
3387
|
+
{
|
|
3388
|
+
method: "POST",
|
|
3389
|
+
path: "/send-whatsapp",
|
|
3390
|
+
handler: "controller.sendWhatsApp",
|
|
3391
|
+
config: {
|
|
3392
|
+
auth: false,
|
|
3393
|
+
description: "Send WhatsApp message"
|
|
3394
|
+
}
|
|
3395
|
+
},
|
|
3396
|
+
{
|
|
3397
|
+
method: "GET",
|
|
3398
|
+
path: "/whatsapp/status",
|
|
3399
|
+
handler: "controller.getWhatsAppStatus",
|
|
3400
|
+
config: {
|
|
3401
|
+
auth: false,
|
|
3402
|
+
description: "Get WhatsApp connection status"
|
|
3403
|
+
}
|
|
3404
|
+
},
|
|
3405
|
+
{
|
|
3406
|
+
method: "GET",
|
|
3407
|
+
path: "/whatsapp/check/:phoneNumber",
|
|
3408
|
+
handler: "controller.checkWhatsAppNumber",
|
|
3409
|
+
config: {
|
|
3410
|
+
auth: false,
|
|
3411
|
+
description: "Check if phone number is on WhatsApp"
|
|
3412
|
+
}
|
|
3413
|
+
},
|
|
3414
|
+
// ============= TRACKING ROUTES =============
|
|
2975
3415
|
{
|
|
2976
3416
|
method: "GET",
|
|
2977
3417
|
path: "/track/open/:emailId/:recipientHash",
|
|
@@ -3051,7 +3491,7 @@ function requireEncryption() {
|
|
|
3051
3491
|
let encrypted = cipher.update(jsonData, "utf8", "hex");
|
|
3052
3492
|
encrypted += cipher.final("hex");
|
|
3053
3493
|
const authTag = cipher.getAuthTag();
|
|
3054
|
-
return `${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted}
|
|
3494
|
+
return { encrypted: `${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted}` };
|
|
3055
3495
|
} catch (err) {
|
|
3056
3496
|
console.error("[magic-mail] Encryption failed:", err);
|
|
3057
3497
|
throw new Error("Failed to encrypt credentials");
|
|
@@ -3061,7 +3501,8 @@ function requireEncryption() {
|
|
|
3061
3501
|
if (!encryptedData) return null;
|
|
3062
3502
|
try {
|
|
3063
3503
|
const key = getEncryptionKey();
|
|
3064
|
-
const
|
|
3504
|
+
const encryptedString = typeof encryptedData === "object" && encryptedData.encrypted ? encryptedData.encrypted : encryptedData;
|
|
3505
|
+
const parts = encryptedString.split(":");
|
|
3065
3506
|
if (parts.length !== 3) {
|
|
3066
3507
|
throw new Error("Invalid encrypted data format");
|
|
3067
3508
|
}
|
|
@@ -3135,7 +3576,7 @@ function requireEmailRouter() {
|
|
|
3135
3576
|
let templateRecord = null;
|
|
3136
3577
|
if (emailData.templateReferenceId) {
|
|
3137
3578
|
resolvedTemplateReferenceId = String(emailData.templateReferenceId).trim();
|
|
3138
|
-
strapi2.log.info(`[magic-mail]
|
|
3579
|
+
strapi2.log.info(`[magic-mail] [TEMPLATE] Using provided templateReferenceId="${resolvedTemplateReferenceId}"`);
|
|
3139
3580
|
}
|
|
3140
3581
|
if (!resolvedTemplateReferenceId && templateId) {
|
|
3141
3582
|
const numericTemplateId = Number(templateId);
|
|
@@ -3155,7 +3596,7 @@ function requireEmailRouter() {
|
|
|
3155
3596
|
);
|
|
3156
3597
|
} else {
|
|
3157
3598
|
resolvedTemplateReferenceId = String(templateId).trim();
|
|
3158
|
-
strapi2.log.info(`[magic-mail]
|
|
3599
|
+
strapi2.log.info(`[magic-mail] [TEMPLATE] Treating templateId value as referenceId="${resolvedTemplateReferenceId}"`);
|
|
3159
3600
|
}
|
|
3160
3601
|
}
|
|
3161
3602
|
if (!resolvedTemplateReferenceId) {
|
|
@@ -3216,6 +3657,39 @@ function requireEmailRouter() {
|
|
|
3216
3657
|
emailData.html = html;
|
|
3217
3658
|
emailData.text = text;
|
|
3218
3659
|
emailData.subject = subject;
|
|
3660
|
+
let matchedRule = null;
|
|
3661
|
+
try {
|
|
3662
|
+
const allRules = await strapi2.documents("plugin::magic-mail.routing-rule").findMany({
|
|
3663
|
+
filters: { isActive: true },
|
|
3664
|
+
sort: [{ priority: "desc" }]
|
|
3665
|
+
});
|
|
3666
|
+
for (const rule of allRules) {
|
|
3667
|
+
let matches = false;
|
|
3668
|
+
switch (rule.matchType) {
|
|
3669
|
+
case "emailType":
|
|
3670
|
+
matches = rule.matchValue === type;
|
|
3671
|
+
break;
|
|
3672
|
+
case "recipient":
|
|
3673
|
+
matches = to && to.toLowerCase().includes(rule.matchValue.toLowerCase());
|
|
3674
|
+
break;
|
|
3675
|
+
case "subject":
|
|
3676
|
+
matches = subject && subject.toLowerCase().includes(rule.matchValue.toLowerCase());
|
|
3677
|
+
break;
|
|
3678
|
+
case "template":
|
|
3679
|
+
matches = emailData.template && emailData.template === rule.matchValue;
|
|
3680
|
+
break;
|
|
3681
|
+
case "custom":
|
|
3682
|
+
matches = emailData.customField && emailData.customField === rule.matchValue;
|
|
3683
|
+
break;
|
|
3684
|
+
}
|
|
3685
|
+
if (matches) {
|
|
3686
|
+
matchedRule = rule;
|
|
3687
|
+
break;
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
} catch (ruleError) {
|
|
3691
|
+
strapi2.log.warn("[magic-mail] [WARNING] Failed to check routing rules for WhatsApp fallback:", ruleError.message);
|
|
3692
|
+
}
|
|
3219
3693
|
try {
|
|
3220
3694
|
const licenseGuard2 = strapi2.plugin("magic-mail").service("license-guard");
|
|
3221
3695
|
if (priority === "high") {
|
|
@@ -3235,7 +3709,7 @@ function requireEmailRouter() {
|
|
|
3235
3709
|
}
|
|
3236
3710
|
const canSend = await this.checkRateLimits(account);
|
|
3237
3711
|
if (!canSend) {
|
|
3238
|
-
const fallbackAccount = await this.selectAccount(type, priority, [account.
|
|
3712
|
+
const fallbackAccount = await this.selectAccount(type, priority, [account.documentId], emailData);
|
|
3239
3713
|
if (fallbackAccount) {
|
|
3240
3714
|
strapi2.log.info(`[magic-mail] Rate limit hit on ${account.name}, using fallback: ${fallbackAccount.name}`);
|
|
3241
3715
|
return await this.sendViaAccount(fallbackAccount, emailData);
|
|
@@ -3266,18 +3740,56 @@ function requireEmailRouter() {
|
|
|
3266
3740
|
};
|
|
3267
3741
|
} catch (error) {
|
|
3268
3742
|
strapi2.log.error("[magic-mail] [ERROR] Email send failed:", error);
|
|
3743
|
+
if (matchedRule?.whatsappFallback) {
|
|
3744
|
+
strapi2.log.info("[magic-mail] [FALLBACK] Email failed, attempting WhatsApp fallback...");
|
|
3745
|
+
try {
|
|
3746
|
+
const whatsapp2 = strapi2.plugin("magic-mail").service("whatsapp");
|
|
3747
|
+
const whatsappStatus = whatsapp2.getStatus();
|
|
3748
|
+
if (whatsappStatus.isConnected) {
|
|
3749
|
+
const phoneNumber = emailData.phoneNumber || emailData.whatsappPhone;
|
|
3750
|
+
if (phoneNumber) {
|
|
3751
|
+
const whatsappMessage = `*${subject}*
|
|
3752
|
+
|
|
3753
|
+
${text || "Email delivery failed. Please check your email settings."}`;
|
|
3754
|
+
const waResult = await whatsapp2.sendMessage(phoneNumber, whatsappMessage);
|
|
3755
|
+
if (waResult.success) {
|
|
3756
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] WhatsApp fallback sent to ${phoneNumber}`);
|
|
3757
|
+
return {
|
|
3758
|
+
success: true,
|
|
3759
|
+
fallbackUsed: "whatsapp",
|
|
3760
|
+
phoneNumber
|
|
3761
|
+
};
|
|
3762
|
+
} else {
|
|
3763
|
+
strapi2.log.warn("[magic-mail] [WARNING] WhatsApp fallback failed:", waResult.error);
|
|
3764
|
+
}
|
|
3765
|
+
} else {
|
|
3766
|
+
strapi2.log.warn("[magic-mail] [WARNING] WhatsApp fallback enabled but no phone number provided");
|
|
3767
|
+
}
|
|
3768
|
+
} else {
|
|
3769
|
+
strapi2.log.warn("[magic-mail] [WARNING] WhatsApp fallback enabled but WhatsApp not connected");
|
|
3770
|
+
}
|
|
3771
|
+
} catch (waError) {
|
|
3772
|
+
strapi2.log.error("[magic-mail] [ERROR] WhatsApp fallback error:", waError.message);
|
|
3773
|
+
}
|
|
3774
|
+
}
|
|
3269
3775
|
throw error;
|
|
3270
3776
|
}
|
|
3271
3777
|
},
|
|
3272
3778
|
/**
|
|
3273
3779
|
* Select best account based on rules
|
|
3780
|
+
* @param {string} type - Email type (transactional, marketing, notification)
|
|
3781
|
+
* @param {string} priority - Priority level (high, normal, low)
|
|
3782
|
+
* @param {Array<string>} excludeDocumentIds - Array of documentIds to exclude from selection
|
|
3783
|
+
* @param {Object} emailData - Email data for routing rule matching
|
|
3784
|
+
* @returns {Promise<Object|null>} Selected account or null
|
|
3274
3785
|
*/
|
|
3275
|
-
async selectAccount(type, priority,
|
|
3786
|
+
async selectAccount(type, priority, excludeDocumentIds = [], emailData = {}) {
|
|
3787
|
+
const filters = { isActive: true };
|
|
3788
|
+
if (excludeDocumentIds.length > 0) {
|
|
3789
|
+
filters.documentId = { $notIn: excludeDocumentIds };
|
|
3790
|
+
}
|
|
3276
3791
|
const accounts2 = await strapi2.documents("plugin::magic-mail.email-account").findMany({
|
|
3277
|
-
filters
|
|
3278
|
-
isActive: true,
|
|
3279
|
-
id: { $notIn: excludeIds }
|
|
3280
|
-
},
|
|
3792
|
+
filters,
|
|
3281
3793
|
sort: [{ priority: "desc" }]
|
|
3282
3794
|
});
|
|
3283
3795
|
if (!accounts2 || accounts2.length === 0) {
|
|
@@ -4174,6 +4686,168 @@ function requireEmailRouter() {
|
|
|
4174
4686
|
...headers
|
|
4175
4687
|
}
|
|
4176
4688
|
};
|
|
4689
|
+
},
|
|
4690
|
+
// ============================================================================
|
|
4691
|
+
// UNIFIED MESSAGE API - Send via Email OR WhatsApp
|
|
4692
|
+
// ============================================================================
|
|
4693
|
+
/**
|
|
4694
|
+
* Send a message via WhatsApp
|
|
4695
|
+
* Same pattern as send() but for WhatsApp
|
|
4696
|
+
* @param {Object} messageData - { phoneNumber, message, templateId, templateData }
|
|
4697
|
+
* @returns {Promise<Object>} Send result
|
|
4698
|
+
*/
|
|
4699
|
+
async sendWhatsApp(messageData) {
|
|
4700
|
+
const {
|
|
4701
|
+
phoneNumber,
|
|
4702
|
+
message,
|
|
4703
|
+
templateId = null,
|
|
4704
|
+
templateData = {}
|
|
4705
|
+
} = messageData;
|
|
4706
|
+
if (!phoneNumber) {
|
|
4707
|
+
throw new Error("Phone number is required for WhatsApp messages");
|
|
4708
|
+
}
|
|
4709
|
+
const cleanPhone = phoneNumber.replace(/[^\d]/g, "");
|
|
4710
|
+
if (cleanPhone.length < 10) {
|
|
4711
|
+
throw new Error("Invalid phone number format. Use format: 491234567890 (country code + number)");
|
|
4712
|
+
}
|
|
4713
|
+
const whatsapp2 = strapi2.plugin("magic-mail").service("whatsapp");
|
|
4714
|
+
const status = whatsapp2.getStatus();
|
|
4715
|
+
if (!status.isConnected) {
|
|
4716
|
+
throw new Error("WhatsApp is not connected. Please connect WhatsApp first in the admin panel.");
|
|
4717
|
+
}
|
|
4718
|
+
let finalMessage = message;
|
|
4719
|
+
if (templateId) {
|
|
4720
|
+
try {
|
|
4721
|
+
const template = await whatsapp2.getTemplate(templateId);
|
|
4722
|
+
if (template) {
|
|
4723
|
+
finalMessage = template.content;
|
|
4724
|
+
Object.keys(templateData).forEach((key) => {
|
|
4725
|
+
finalMessage = finalMessage.replace(new RegExp(`{{${key}}}`, "g"), templateData[key]);
|
|
4726
|
+
});
|
|
4727
|
+
}
|
|
4728
|
+
} catch (error) {
|
|
4729
|
+
strapi2.log.warn(`[magic-mail] WhatsApp template ${templateId} not found, using plain message`);
|
|
4730
|
+
}
|
|
4731
|
+
}
|
|
4732
|
+
if (!finalMessage) {
|
|
4733
|
+
throw new Error("Message content is required");
|
|
4734
|
+
}
|
|
4735
|
+
strapi2.log.info(`[magic-mail] [WHATSAPP] Sending message to ${cleanPhone}`);
|
|
4736
|
+
const result = await whatsapp2.sendMessage(cleanPhone, finalMessage);
|
|
4737
|
+
if (result.success) {
|
|
4738
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] WhatsApp message sent to ${cleanPhone}`);
|
|
4739
|
+
return {
|
|
4740
|
+
success: true,
|
|
4741
|
+
channel: "whatsapp",
|
|
4742
|
+
phoneNumber: cleanPhone,
|
|
4743
|
+
jid: result.jid
|
|
4744
|
+
};
|
|
4745
|
+
} else {
|
|
4746
|
+
strapi2.log.error(`[magic-mail] [ERROR] WhatsApp send failed: ${result.error}`);
|
|
4747
|
+
throw new Error(result.error || "Failed to send WhatsApp message");
|
|
4748
|
+
}
|
|
4749
|
+
},
|
|
4750
|
+
/**
|
|
4751
|
+
* Unified send method - automatically chooses Email or WhatsApp
|
|
4752
|
+
* @param {Object} messageData - Combined email and WhatsApp data
|
|
4753
|
+
* @param {string} messageData.channel - 'email' | 'whatsapp' | 'auto' (default: 'auto')
|
|
4754
|
+
* @param {string} messageData.to - Email address (for email channel)
|
|
4755
|
+
* @param {string} messageData.phoneNumber - Phone number (for whatsapp channel)
|
|
4756
|
+
* @param {string} messageData.subject - Email subject
|
|
4757
|
+
* @param {string} messageData.message - Plain text message (used for WhatsApp, or as email text)
|
|
4758
|
+
* @param {string} messageData.html - HTML content (email only)
|
|
4759
|
+
* @param {string} messageData.templateId - Template ID (works for both channels)
|
|
4760
|
+
* @param {Object} messageData.templateData - Template variables
|
|
4761
|
+
* @returns {Promise<Object>} Send result with channel info
|
|
4762
|
+
*/
|
|
4763
|
+
async sendMessage(messageData) {
|
|
4764
|
+
const {
|
|
4765
|
+
channel = "auto",
|
|
4766
|
+
to,
|
|
4767
|
+
phoneNumber,
|
|
4768
|
+
subject,
|
|
4769
|
+
message,
|
|
4770
|
+
text,
|
|
4771
|
+
html,
|
|
4772
|
+
templateId,
|
|
4773
|
+
templateData,
|
|
4774
|
+
...rest
|
|
4775
|
+
} = messageData;
|
|
4776
|
+
let useChannel = channel;
|
|
4777
|
+
if (channel === "auto") {
|
|
4778
|
+
if (to && to.includes("@")) {
|
|
4779
|
+
useChannel = "email";
|
|
4780
|
+
} else if (phoneNumber) {
|
|
4781
|
+
useChannel = "whatsapp";
|
|
4782
|
+
} else {
|
|
4783
|
+
throw new Error("Either email (to) or phoneNumber is required");
|
|
4784
|
+
}
|
|
4785
|
+
}
|
|
4786
|
+
strapi2.log.info(`[magic-mail] [SEND] Channel: ${useChannel}, to: ${to || phoneNumber}`);
|
|
4787
|
+
if (useChannel === "whatsapp") {
|
|
4788
|
+
if (!phoneNumber) {
|
|
4789
|
+
throw new Error("Phone number is required for WhatsApp channel");
|
|
4790
|
+
}
|
|
4791
|
+
return await this.sendWhatsApp({
|
|
4792
|
+
phoneNumber,
|
|
4793
|
+
message: message || text || subject,
|
|
4794
|
+
// Use message, fallback to text or subject
|
|
4795
|
+
templateId,
|
|
4796
|
+
templateData
|
|
4797
|
+
});
|
|
4798
|
+
} else {
|
|
4799
|
+
if (!to) {
|
|
4800
|
+
throw new Error("Email address (to) is required for email channel");
|
|
4801
|
+
}
|
|
4802
|
+
const result = await this.send({
|
|
4803
|
+
to,
|
|
4804
|
+
subject,
|
|
4805
|
+
text: text || message,
|
|
4806
|
+
html,
|
|
4807
|
+
templateId,
|
|
4808
|
+
templateData,
|
|
4809
|
+
phoneNumber,
|
|
4810
|
+
// Pass for WhatsApp fallback
|
|
4811
|
+
...rest
|
|
4812
|
+
});
|
|
4813
|
+
return {
|
|
4814
|
+
...result,
|
|
4815
|
+
channel: "email"
|
|
4816
|
+
};
|
|
4817
|
+
}
|
|
4818
|
+
},
|
|
4819
|
+
/**
|
|
4820
|
+
* Check WhatsApp connection status
|
|
4821
|
+
* @returns {Object} Connection status
|
|
4822
|
+
*/
|
|
4823
|
+
getWhatsAppStatus() {
|
|
4824
|
+
try {
|
|
4825
|
+
const whatsapp2 = strapi2.plugin("magic-mail").service("whatsapp");
|
|
4826
|
+
return whatsapp2.getStatus();
|
|
4827
|
+
} catch (error) {
|
|
4828
|
+
return {
|
|
4829
|
+
isConnected: false,
|
|
4830
|
+
status: "unavailable",
|
|
4831
|
+
error: error.message
|
|
4832
|
+
};
|
|
4833
|
+
}
|
|
4834
|
+
},
|
|
4835
|
+
/**
|
|
4836
|
+
* Check if a phone number is registered on WhatsApp
|
|
4837
|
+
* @param {string} phoneNumber - Phone number to check
|
|
4838
|
+
* @returns {Promise<Object>} Check result
|
|
4839
|
+
*/
|
|
4840
|
+
async checkWhatsAppNumber(phoneNumber) {
|
|
4841
|
+
try {
|
|
4842
|
+
const whatsapp2 = strapi2.plugin("magic-mail").service("whatsapp");
|
|
4843
|
+
return await whatsapp2.checkNumber(phoneNumber);
|
|
4844
|
+
} catch (error) {
|
|
4845
|
+
return {
|
|
4846
|
+
success: false,
|
|
4847
|
+
exists: false,
|
|
4848
|
+
error: error.message
|
|
4849
|
+
};
|
|
4850
|
+
}
|
|
4177
4851
|
}
|
|
4178
4852
|
});
|
|
4179
4853
|
return emailRouter;
|
|
@@ -4870,7 +5544,7 @@ function requireOauth() {
|
|
|
4870
5544
|
});
|
|
4871
5545
|
return oauth;
|
|
4872
5546
|
}
|
|
4873
|
-
const version = "2.
|
|
5547
|
+
const version = "2.3.0";
|
|
4874
5548
|
const require$$2 = {
|
|
4875
5549
|
version
|
|
4876
5550
|
};
|
|
@@ -6038,7 +6712,7 @@ function requireAnalytics() {
|
|
|
6038
6712
|
const randomToken = crypto.randomBytes(8).toString("hex");
|
|
6039
6713
|
const trackingUrl = `${baseUrl}/api/magic-mail/track/open/${emailId}/${recipientHash}?r=${randomToken}`;
|
|
6040
6714
|
const trackingPixel = `<img src="${trackingUrl}" width="1" height="1" style="display:none;" alt="" />`;
|
|
6041
|
-
strapi2.log.info(`[magic-mail]
|
|
6715
|
+
strapi2.log.info(`[magic-mail] [PIXEL] Tracking pixel URL: ${trackingUrl}`);
|
|
6042
6716
|
if (html.includes("</body>")) {
|
|
6043
6717
|
return html.replace("</body>", `${trackingPixel}</body>`);
|
|
6044
6718
|
}
|
|
@@ -6174,6 +6848,434 @@ function requireAnalytics() {
|
|
|
6174
6848
|
});
|
|
6175
6849
|
return analytics;
|
|
6176
6850
|
}
|
|
6851
|
+
var whatsapp;
|
|
6852
|
+
var hasRequiredWhatsapp;
|
|
6853
|
+
function requireWhatsapp() {
|
|
6854
|
+
if (hasRequiredWhatsapp) return whatsapp;
|
|
6855
|
+
hasRequiredWhatsapp = 1;
|
|
6856
|
+
const path = require$$0__default$3.default;
|
|
6857
|
+
const fs = require$$1__default$2.default;
|
|
6858
|
+
let baileys = null;
|
|
6859
|
+
const loadBaileys = async () => {
|
|
6860
|
+
if (!baileys) {
|
|
6861
|
+
try {
|
|
6862
|
+
baileys = require("@whiskeysockets/baileys");
|
|
6863
|
+
if (process.env.DEBUG) {
|
|
6864
|
+
console.log("[MagicMail WhatsApp] Baileys loaded successfully");
|
|
6865
|
+
}
|
|
6866
|
+
return true;
|
|
6867
|
+
} catch (error) {
|
|
6868
|
+
console.warn("[MagicMail WhatsApp] Baileys not installed. WhatsApp features disabled.");
|
|
6869
|
+
console.warn("[MagicMail WhatsApp] Install with: npm install @whiskeysockets/baileys pino qrcode");
|
|
6870
|
+
return false;
|
|
6871
|
+
}
|
|
6872
|
+
}
|
|
6873
|
+
return true;
|
|
6874
|
+
};
|
|
6875
|
+
whatsapp = ({ strapi: strapi2 }) => {
|
|
6876
|
+
let sock = null;
|
|
6877
|
+
let qrCode = null;
|
|
6878
|
+
let connectionStatus = "disconnected";
|
|
6879
|
+
let lastError = null;
|
|
6880
|
+
let eventListeners = [];
|
|
6881
|
+
let wasConnectedBefore = false;
|
|
6882
|
+
let reconnectAttempts = 0;
|
|
6883
|
+
const MAX_RECONNECT_ATTEMPTS = 3;
|
|
6884
|
+
const isDebugEnabled = async () => {
|
|
6885
|
+
try {
|
|
6886
|
+
const pluginStore = strapi2.store({ type: "plugin", name: "magic-mail" });
|
|
6887
|
+
const settings = await pluginStore.get({ key: "settings" });
|
|
6888
|
+
return settings?.whatsapp_debug === true;
|
|
6889
|
+
} catch {
|
|
6890
|
+
return false;
|
|
6891
|
+
}
|
|
6892
|
+
};
|
|
6893
|
+
const debugLog = async (message) => {
|
|
6894
|
+
if (await isDebugEnabled()) {
|
|
6895
|
+
strapi2.log.info(message);
|
|
6896
|
+
}
|
|
6897
|
+
};
|
|
6898
|
+
const getAuthPath = () => {
|
|
6899
|
+
const strapiRoot = strapi2.dirs?.app?.root || process.cwd();
|
|
6900
|
+
return path.join(strapiRoot, ".magicmail-whatsapp-auth");
|
|
6901
|
+
};
|
|
6902
|
+
const emit = (event, data) => {
|
|
6903
|
+
eventListeners.forEach((listener) => {
|
|
6904
|
+
try {
|
|
6905
|
+
listener(event, data);
|
|
6906
|
+
} catch (e) {
|
|
6907
|
+
console.error("[MagicMail WhatsApp] Event listener error:", e);
|
|
6908
|
+
}
|
|
6909
|
+
});
|
|
6910
|
+
};
|
|
6911
|
+
const service = {
|
|
6912
|
+
/**
|
|
6913
|
+
* Check if Baileys is available
|
|
6914
|
+
* @returns {Promise<boolean>} True if Baileys is installed
|
|
6915
|
+
*/
|
|
6916
|
+
async isAvailable() {
|
|
6917
|
+
return await loadBaileys();
|
|
6918
|
+
},
|
|
6919
|
+
/**
|
|
6920
|
+
* Get current connection status
|
|
6921
|
+
* @returns {object} Status object with status, qrCode, lastError, isConnected
|
|
6922
|
+
*/
|
|
6923
|
+
getStatus() {
|
|
6924
|
+
return {
|
|
6925
|
+
status: connectionStatus,
|
|
6926
|
+
qrCode,
|
|
6927
|
+
lastError,
|
|
6928
|
+
isConnected: connectionStatus === "connected"
|
|
6929
|
+
};
|
|
6930
|
+
},
|
|
6931
|
+
/**
|
|
6932
|
+
* Add event listener for WhatsApp events
|
|
6933
|
+
* @param {function} callback - Callback function(event, data)
|
|
6934
|
+
* @returns {function} Unsubscribe function
|
|
6935
|
+
*/
|
|
6936
|
+
on(callback) {
|
|
6937
|
+
eventListeners.push(callback);
|
|
6938
|
+
return () => {
|
|
6939
|
+
eventListeners = eventListeners.filter((l) => l !== callback);
|
|
6940
|
+
};
|
|
6941
|
+
},
|
|
6942
|
+
/**
|
|
6943
|
+
* Initialize WhatsApp connection
|
|
6944
|
+
* @returns {Promise<object>} Connection result with success status
|
|
6945
|
+
*/
|
|
6946
|
+
async connect() {
|
|
6947
|
+
const available = await loadBaileys();
|
|
6948
|
+
if (!available) {
|
|
6949
|
+
lastError = "Baileys not installed. Run: npm install @whiskeysockets/baileys pino qrcode";
|
|
6950
|
+
strapi2.log.error("[MagicMail WhatsApp] [ERROR] Baileys library not available");
|
|
6951
|
+
return { success: false, error: lastError };
|
|
6952
|
+
}
|
|
6953
|
+
if (sock && connectionStatus === "connected") {
|
|
6954
|
+
await debugLog("[MagicMail WhatsApp] Already connected");
|
|
6955
|
+
return { success: true, status: "already_connected" };
|
|
6956
|
+
}
|
|
6957
|
+
if (sock) {
|
|
6958
|
+
try {
|
|
6959
|
+
sock.end();
|
|
6960
|
+
} catch (e) {
|
|
6961
|
+
}
|
|
6962
|
+
sock = null;
|
|
6963
|
+
}
|
|
6964
|
+
return new Promise(async (resolve) => {
|
|
6965
|
+
try {
|
|
6966
|
+
connectionStatus = "connecting";
|
|
6967
|
+
emit("status", { status: connectionStatus });
|
|
6968
|
+
await debugLog("[MagicMail WhatsApp] Starting connection...");
|
|
6969
|
+
const authPath = getAuthPath();
|
|
6970
|
+
if (!fs.existsSync(authPath)) {
|
|
6971
|
+
fs.mkdirSync(authPath, { recursive: true });
|
|
6972
|
+
}
|
|
6973
|
+
await debugLog(`[MagicMail WhatsApp] Auth path: ${authPath}`);
|
|
6974
|
+
const { state, saveCreds } = await baileys.useMultiFileAuthState(authPath);
|
|
6975
|
+
await debugLog("[MagicMail WhatsApp] Auth state loaded");
|
|
6976
|
+
const pino = require("pino");
|
|
6977
|
+
const logger2 = pino({ level: "silent" });
|
|
6978
|
+
await debugLog("[MagicMail WhatsApp] Creating WhatsApp socket...");
|
|
6979
|
+
const makeSocket = baileys.default || baileys.makeWASocket;
|
|
6980
|
+
const browserConfig = baileys.Browsers.ubuntu("Chrome");
|
|
6981
|
+
await debugLog(`[MagicMail WhatsApp] Browser config: ${JSON.stringify(browserConfig)}`);
|
|
6982
|
+
sock = makeSocket({
|
|
6983
|
+
auth: state,
|
|
6984
|
+
logger: logger2,
|
|
6985
|
+
browser: browserConfig,
|
|
6986
|
+
syncFullHistory: false,
|
|
6987
|
+
markOnlineOnConnect: false,
|
|
6988
|
+
generateHighQualityLinkPreview: false,
|
|
6989
|
+
getMessage: async (key) => {
|
|
6990
|
+
return { conversation: "" };
|
|
6991
|
+
}
|
|
6992
|
+
});
|
|
6993
|
+
await debugLog("[MagicMail WhatsApp] Socket created, registering event handlers...");
|
|
6994
|
+
let resolved = false;
|
|
6995
|
+
const resolveOnce = (result) => {
|
|
6996
|
+
if (!resolved) {
|
|
6997
|
+
resolved = true;
|
|
6998
|
+
resolve(result);
|
|
6999
|
+
}
|
|
7000
|
+
};
|
|
7001
|
+
setTimeout(() => {
|
|
7002
|
+
if (!resolved) {
|
|
7003
|
+
strapi2.log.warn("[MagicMail WhatsApp] Connection timeout - no QR or connection");
|
|
7004
|
+
resolveOnce({ success: true, status: connectionStatus, qrCode });
|
|
7005
|
+
}
|
|
7006
|
+
}, 3e4);
|
|
7007
|
+
sock.ev.on("connection.update", async (update) => {
|
|
7008
|
+
await debugLog(`[MagicMail WhatsApp] connection.update: ${JSON.stringify(update)}`);
|
|
7009
|
+
const { connection, lastDisconnect, qr } = update;
|
|
7010
|
+
if (qr) {
|
|
7011
|
+
await debugLog("[MagicMail WhatsApp] QR code received");
|
|
7012
|
+
try {
|
|
7013
|
+
const QRCode = require("qrcode");
|
|
7014
|
+
qrCode = await QRCode.toDataURL(qr);
|
|
7015
|
+
connectionStatus = "qr_pending";
|
|
7016
|
+
emit("qr", { qrCode });
|
|
7017
|
+
emit("status", { status: connectionStatus });
|
|
7018
|
+
strapi2.log.info("[MagicMail WhatsApp] [SUCCESS] QR Code generated - scan with WhatsApp");
|
|
7019
|
+
resolveOnce({ success: true, status: connectionStatus, qrCode });
|
|
7020
|
+
} catch (qrError) {
|
|
7021
|
+
strapi2.log.error("[MagicMail WhatsApp] QR generation error:", qrError.message);
|
|
7022
|
+
}
|
|
7023
|
+
}
|
|
7024
|
+
if (connection === "close") {
|
|
7025
|
+
const statusCode = lastDisconnect?.error?.output?.statusCode;
|
|
7026
|
+
const isLoggedOut = statusCode === baileys.DisconnectReason.loggedOut;
|
|
7027
|
+
const isRestartRequired = statusCode === baileys.DisconnectReason.restartRequired;
|
|
7028
|
+
const isConnectionFailure = statusCode === 405;
|
|
7029
|
+
await debugLog(`[MagicMail WhatsApp] Connection closed - statusCode: ${statusCode}`);
|
|
7030
|
+
if (isLoggedOut) {
|
|
7031
|
+
connectionStatus = "disconnected";
|
|
7032
|
+
lastError = "Logged out from WhatsApp";
|
|
7033
|
+
qrCode = null;
|
|
7034
|
+
wasConnectedBefore = false;
|
|
7035
|
+
reconnectAttempts = 0;
|
|
7036
|
+
try {
|
|
7037
|
+
fs.rmSync(authPath, { recursive: true, force: true });
|
|
7038
|
+
} catch (e) {
|
|
7039
|
+
}
|
|
7040
|
+
strapi2.log.warn("[MagicMail WhatsApp] Logged out - auth cleared");
|
|
7041
|
+
} else if (isRestartRequired) {
|
|
7042
|
+
await debugLog("[MagicMail WhatsApp] Restart required - reconnecting...");
|
|
7043
|
+
connectionStatus = "connecting";
|
|
7044
|
+
setTimeout(() => {
|
|
7045
|
+
service.connect();
|
|
7046
|
+
}, 1e3);
|
|
7047
|
+
} else if (isConnectionFailure && reconnectAttempts < 2) {
|
|
7048
|
+
reconnectAttempts++;
|
|
7049
|
+
await debugLog(`[MagicMail WhatsApp] Connection rejected (405) - retrying (${reconnectAttempts}/2)`);
|
|
7050
|
+
try {
|
|
7051
|
+
fs.rmSync(authPath, { recursive: true, force: true });
|
|
7052
|
+
} catch (e) {
|
|
7053
|
+
}
|
|
7054
|
+
connectionStatus = "disconnected";
|
|
7055
|
+
qrCode = null;
|
|
7056
|
+
setTimeout(() => {
|
|
7057
|
+
service.connect();
|
|
7058
|
+
}, 3e3);
|
|
7059
|
+
} else if (isConnectionFailure) {
|
|
7060
|
+
connectionStatus = "disconnected";
|
|
7061
|
+
lastError = "WhatsApp connection rejected (405). Please try again later.";
|
|
7062
|
+
strapi2.log.error("[MagicMail WhatsApp] [ERROR] Connection rejected after retries.");
|
|
7063
|
+
resolveOnce({ success: false, status: connectionStatus, error: lastError });
|
|
7064
|
+
} else if (wasConnectedBefore && reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
|
|
7065
|
+
reconnectAttempts++;
|
|
7066
|
+
connectionStatus = "connecting";
|
|
7067
|
+
await debugLog(`[MagicMail WhatsApp] Reconnecting (${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})...`);
|
|
7068
|
+
setTimeout(() => {
|
|
7069
|
+
service.connect();
|
|
7070
|
+
}, 3e3 * reconnectAttempts);
|
|
7071
|
+
} else if (!wasConnectedBefore) {
|
|
7072
|
+
connectionStatus = "disconnected";
|
|
7073
|
+
qrCode = null;
|
|
7074
|
+
await debugLog("[MagicMail WhatsApp] Connection closed - waiting for QR scan");
|
|
7075
|
+
} else {
|
|
7076
|
+
connectionStatus = "disconnected";
|
|
7077
|
+
lastError = "Max reconnect attempts reached";
|
|
7078
|
+
strapi2.log.warn("[MagicMail WhatsApp] Max reconnect attempts reached");
|
|
7079
|
+
}
|
|
7080
|
+
emit("status", { status: connectionStatus, error: lastError });
|
|
7081
|
+
}
|
|
7082
|
+
if (connection === "open") {
|
|
7083
|
+
connectionStatus = "connected";
|
|
7084
|
+
qrCode = null;
|
|
7085
|
+
lastError = null;
|
|
7086
|
+
wasConnectedBefore = true;
|
|
7087
|
+
reconnectAttempts = 0;
|
|
7088
|
+
emit("status", { status: connectionStatus });
|
|
7089
|
+
strapi2.log.info("[MagicMail WhatsApp] [SUCCESS] Connected successfully!");
|
|
7090
|
+
resolveOnce({ success: true, status: connectionStatus });
|
|
7091
|
+
}
|
|
7092
|
+
});
|
|
7093
|
+
sock.ev.on("creds.update", saveCreds);
|
|
7094
|
+
} catch (error) {
|
|
7095
|
+
lastError = error.message;
|
|
7096
|
+
connectionStatus = "disconnected";
|
|
7097
|
+
strapi2.log.error("[MagicMail WhatsApp] Connection error:", error);
|
|
7098
|
+
resolve({ success: false, error: error.message });
|
|
7099
|
+
}
|
|
7100
|
+
});
|
|
7101
|
+
},
|
|
7102
|
+
/**
|
|
7103
|
+
* Disconnect WhatsApp and clear session
|
|
7104
|
+
* @returns {Promise<object>} Result with success status
|
|
7105
|
+
*/
|
|
7106
|
+
async disconnect() {
|
|
7107
|
+
if (sock) {
|
|
7108
|
+
try {
|
|
7109
|
+
await sock.logout();
|
|
7110
|
+
} catch (e) {
|
|
7111
|
+
}
|
|
7112
|
+
sock = null;
|
|
7113
|
+
}
|
|
7114
|
+
connectionStatus = "disconnected";
|
|
7115
|
+
qrCode = null;
|
|
7116
|
+
emit("status", { status: connectionStatus });
|
|
7117
|
+
strapi2.log.info("[MagicMail WhatsApp] Disconnected");
|
|
7118
|
+
return { success: true };
|
|
7119
|
+
},
|
|
7120
|
+
/**
|
|
7121
|
+
* Send a text message to a phone number
|
|
7122
|
+
* @param {string} phoneNumber - Phone number with country code (e.g., "491234567890")
|
|
7123
|
+
* @param {string} message - Message text
|
|
7124
|
+
* @returns {Promise<object>} Result with success status
|
|
7125
|
+
*/
|
|
7126
|
+
async sendMessage(phoneNumber, message) {
|
|
7127
|
+
if (connectionStatus !== "connected" || !sock) {
|
|
7128
|
+
return {
|
|
7129
|
+
success: false,
|
|
7130
|
+
error: "WhatsApp not connected. Please connect first."
|
|
7131
|
+
};
|
|
7132
|
+
}
|
|
7133
|
+
try {
|
|
7134
|
+
const formattedNumber = phoneNumber.replace(/[^\d]/g, "");
|
|
7135
|
+
const jid = `${formattedNumber}@s.whatsapp.net`;
|
|
7136
|
+
const [exists] = await sock.onWhatsApp(formattedNumber);
|
|
7137
|
+
if (!exists?.exists) {
|
|
7138
|
+
return {
|
|
7139
|
+
success: false,
|
|
7140
|
+
error: `Phone number ${phoneNumber} is not registered on WhatsApp`
|
|
7141
|
+
};
|
|
7142
|
+
}
|
|
7143
|
+
await sock.sendMessage(jid, { text: message });
|
|
7144
|
+
await debugLog(`[MagicMail WhatsApp] Message sent to ${formattedNumber}`);
|
|
7145
|
+
return { success: true, jid };
|
|
7146
|
+
} catch (error) {
|
|
7147
|
+
strapi2.log.error("[MagicMail WhatsApp] Send error:", error);
|
|
7148
|
+
return { success: false, error: error.message };
|
|
7149
|
+
}
|
|
7150
|
+
},
|
|
7151
|
+
/**
|
|
7152
|
+
* Send message using a template
|
|
7153
|
+
* @param {string} phoneNumber - Phone number
|
|
7154
|
+
* @param {string} templateName - Template identifier
|
|
7155
|
+
* @param {object} variables - Template variables to replace
|
|
7156
|
+
* @returns {Promise<object>} Result with success status
|
|
7157
|
+
*/
|
|
7158
|
+
async sendTemplateMessage(phoneNumber, templateName, variables = {}) {
|
|
7159
|
+
try {
|
|
7160
|
+
const pluginStore = strapi2.store({ type: "plugin", name: "magic-mail" });
|
|
7161
|
+
const templates = await pluginStore.get({ key: "whatsapp_templates" }) || {};
|
|
7162
|
+
let template = templates[templateName];
|
|
7163
|
+
if (!template) {
|
|
7164
|
+
template = `*{{subject}}*
|
|
7165
|
+
|
|
7166
|
+
{{body}}`;
|
|
7167
|
+
}
|
|
7168
|
+
let message = template;
|
|
7169
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
7170
|
+
message = message.replace(new RegExp(`{{${key}}}`, "g"), value);
|
|
7171
|
+
}
|
|
7172
|
+
return this.sendMessage(phoneNumber, message);
|
|
7173
|
+
} catch (error) {
|
|
7174
|
+
return { success: false, error: error.message };
|
|
7175
|
+
}
|
|
7176
|
+
},
|
|
7177
|
+
/**
|
|
7178
|
+
* Check if a phone number is on WhatsApp
|
|
7179
|
+
* @param {string} phoneNumber - Phone number to check
|
|
7180
|
+
* @returns {Promise<object>} Result with exists boolean
|
|
7181
|
+
*/
|
|
7182
|
+
async checkNumber(phoneNumber) {
|
|
7183
|
+
if (connectionStatus !== "connected" || !sock) {
|
|
7184
|
+
return { success: false, error: "WhatsApp not connected" };
|
|
7185
|
+
}
|
|
7186
|
+
try {
|
|
7187
|
+
const formattedNumber = phoneNumber.replace(/[^\d]/g, "");
|
|
7188
|
+
const [result] = await sock.onWhatsApp(formattedNumber);
|
|
7189
|
+
return {
|
|
7190
|
+
success: true,
|
|
7191
|
+
exists: result?.exists || false,
|
|
7192
|
+
jid: result?.jid
|
|
7193
|
+
};
|
|
7194
|
+
} catch (error) {
|
|
7195
|
+
return { success: false, error: error.message };
|
|
7196
|
+
}
|
|
7197
|
+
},
|
|
7198
|
+
/**
|
|
7199
|
+
* Get session info
|
|
7200
|
+
* @returns {Promise<object|null>} Session info or null if not connected
|
|
7201
|
+
*/
|
|
7202
|
+
async getSessionInfo() {
|
|
7203
|
+
if (connectionStatus !== "connected" || !sock) {
|
|
7204
|
+
return null;
|
|
7205
|
+
}
|
|
7206
|
+
try {
|
|
7207
|
+
const user = sock.user;
|
|
7208
|
+
return {
|
|
7209
|
+
phoneNumber: user?.id?.split(":")[0] || user?.id?.split("@")[0],
|
|
7210
|
+
name: user?.name,
|
|
7211
|
+
platform: "WhatsApp Web"
|
|
7212
|
+
};
|
|
7213
|
+
} catch (error) {
|
|
7214
|
+
return null;
|
|
7215
|
+
}
|
|
7216
|
+
},
|
|
7217
|
+
/**
|
|
7218
|
+
* Reset connection state (for manual cleanup)
|
|
7219
|
+
*/
|
|
7220
|
+
reset() {
|
|
7221
|
+
sock = null;
|
|
7222
|
+
qrCode = null;
|
|
7223
|
+
connectionStatus = "disconnected";
|
|
7224
|
+
lastError = null;
|
|
7225
|
+
wasConnectedBefore = false;
|
|
7226
|
+
reconnectAttempts = 0;
|
|
7227
|
+
},
|
|
7228
|
+
/**
|
|
7229
|
+
* Save WhatsApp template
|
|
7230
|
+
* @param {string} templateName - Template identifier
|
|
7231
|
+
* @param {string} templateContent - Template content with {{variables}}
|
|
7232
|
+
* @returns {Promise<object>} Result with success status
|
|
7233
|
+
*/
|
|
7234
|
+
async saveTemplate(templateName, templateContent) {
|
|
7235
|
+
try {
|
|
7236
|
+
const pluginStore = strapi2.store({ type: "plugin", name: "magic-mail" });
|
|
7237
|
+
const templates = await pluginStore.get({ key: "whatsapp_templates" }) || {};
|
|
7238
|
+
templates[templateName] = templateContent;
|
|
7239
|
+
await pluginStore.set({ key: "whatsapp_templates", value: templates });
|
|
7240
|
+
return { success: true };
|
|
7241
|
+
} catch (error) {
|
|
7242
|
+
return { success: false, error: error.message };
|
|
7243
|
+
}
|
|
7244
|
+
},
|
|
7245
|
+
/**
|
|
7246
|
+
* Get all WhatsApp templates
|
|
7247
|
+
* @returns {Promise<object>} Templates object
|
|
7248
|
+
*/
|
|
7249
|
+
async getTemplates() {
|
|
7250
|
+
try {
|
|
7251
|
+
const pluginStore = strapi2.store({ type: "plugin", name: "magic-mail" });
|
|
7252
|
+
const templates = await pluginStore.get({ key: "whatsapp_templates" }) || {};
|
|
7253
|
+
return templates;
|
|
7254
|
+
} catch (error) {
|
|
7255
|
+
return {};
|
|
7256
|
+
}
|
|
7257
|
+
},
|
|
7258
|
+
/**
|
|
7259
|
+
* Delete a WhatsApp template
|
|
7260
|
+
* @param {string} templateName - Template identifier
|
|
7261
|
+
* @returns {Promise<object>} Result with success status
|
|
7262
|
+
*/
|
|
7263
|
+
async deleteTemplate(templateName) {
|
|
7264
|
+
try {
|
|
7265
|
+
const pluginStore = strapi2.store({ type: "plugin", name: "magic-mail" });
|
|
7266
|
+
const templates = await pluginStore.get({ key: "whatsapp_templates" }) || {};
|
|
7267
|
+
delete templates[templateName];
|
|
7268
|
+
await pluginStore.set({ key: "whatsapp_templates", value: templates });
|
|
7269
|
+
return { success: true };
|
|
7270
|
+
} catch (error) {
|
|
7271
|
+
return { success: false, error: error.message };
|
|
7272
|
+
}
|
|
7273
|
+
}
|
|
7274
|
+
};
|
|
7275
|
+
return service;
|
|
7276
|
+
};
|
|
7277
|
+
return whatsapp;
|
|
7278
|
+
}
|
|
6177
7279
|
var services;
|
|
6178
7280
|
var hasRequiredServices;
|
|
6179
7281
|
function requireServices() {
|
|
@@ -6185,13 +7287,15 @@ function requireServices() {
|
|
|
6185
7287
|
const licenseGuard2 = requireLicenseGuard();
|
|
6186
7288
|
const emailDesigner2 = requireEmailDesigner();
|
|
6187
7289
|
const analytics2 = requireAnalytics();
|
|
7290
|
+
const whatsapp2 = requireWhatsapp();
|
|
6188
7291
|
services = {
|
|
6189
7292
|
"email-router": emailRouter2,
|
|
6190
7293
|
"account-manager": accountManager2,
|
|
6191
7294
|
oauth: oauth2,
|
|
6192
7295
|
"license-guard": licenseGuard2,
|
|
6193
7296
|
"email-designer": emailDesigner2,
|
|
6194
|
-
analytics: analytics2
|
|
7297
|
+
analytics: analytics2,
|
|
7298
|
+
whatsapp: whatsapp2
|
|
6195
7299
|
};
|
|
6196
7300
|
return services;
|
|
6197
7301
|
}
|