strapi-plugin-magic-mail 2.2.5 → 2.3.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/README.md +168 -12
- 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 +1102 -5
- package/dist/server/index.mjs +1100 -5
- package/package.json +10 -5
package/dist/server/index.mjs
CHANGED
|
@@ -4,6 +4,8 @@ import require$$1$1 from "os";
|
|
|
4
4
|
import require$$0$3 from "mustache";
|
|
5
5
|
import require$$1$2 from "html-to-text";
|
|
6
6
|
import require$$2$2 from "decode-html";
|
|
7
|
+
import require$$0$4 from "path";
|
|
8
|
+
import require$$1$3 from "fs";
|
|
7
9
|
var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
|
|
8
10
|
function getDefaultExportFromCjs(x2) {
|
|
9
11
|
return x2 && x2.__esModule && Object.prototype.hasOwnProperty.call(x2, "default") ? x2["default"] : x2;
|
|
@@ -257,7 +259,7 @@ const collectionName$5 = "magic_mail_routing_rules";
|
|
|
257
259
|
const info$5 = { "singularName": "routing-rule", "pluralName": "routing-rules", "displayName": "Email Routing Rule", "description": "Rules for routing emails to specific accounts" };
|
|
258
260
|
const options$5 = { "draftAndPublish": false };
|
|
259
261
|
const pluginOptions$5 = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
|
|
260
|
-
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" } };
|
|
262
|
+
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" } };
|
|
261
263
|
const require$$1 = {
|
|
262
264
|
kind: kind$5,
|
|
263
265
|
collectionName: collectionName$5,
|
|
@@ -394,6 +396,88 @@ function requireController() {
|
|
|
394
396
|
strapi.log.error("[magic-mail] Error sending email:", err);
|
|
395
397
|
ctx.throw(500, err.message || "Failed to send email");
|
|
396
398
|
}
|
|
399
|
+
},
|
|
400
|
+
/**
|
|
401
|
+
* Send message via Email or WhatsApp (unified API)
|
|
402
|
+
* POST /api/magic-mail/send-message
|
|
403
|
+
* Body: { channel: 'email' | 'whatsapp' | 'auto', to, phoneNumber, subject, message, ... }
|
|
404
|
+
*/
|
|
405
|
+
async sendMessage(ctx) {
|
|
406
|
+
try {
|
|
407
|
+
const emailRouter2 = strapi.plugin("magic-mail").service("email-router");
|
|
408
|
+
const result = await emailRouter2.sendMessage(ctx.request.body);
|
|
409
|
+
ctx.body = {
|
|
410
|
+
success: true,
|
|
411
|
+
...result
|
|
412
|
+
};
|
|
413
|
+
} catch (err) {
|
|
414
|
+
strapi.log.error("[magic-mail] Error sending message:", err);
|
|
415
|
+
ctx.throw(500, err.message || "Failed to send message");
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
/**
|
|
419
|
+
* Send WhatsApp message
|
|
420
|
+
* POST /api/magic-mail/send-whatsapp
|
|
421
|
+
* Body: { phoneNumber, message, templateId?, templateData? }
|
|
422
|
+
*/
|
|
423
|
+
async sendWhatsApp(ctx) {
|
|
424
|
+
try {
|
|
425
|
+
const emailRouter2 = strapi.plugin("magic-mail").service("email-router");
|
|
426
|
+
const result = await emailRouter2.sendWhatsApp(ctx.request.body);
|
|
427
|
+
ctx.body = {
|
|
428
|
+
success: true,
|
|
429
|
+
...result
|
|
430
|
+
};
|
|
431
|
+
} catch (err) {
|
|
432
|
+
strapi.log.error("[magic-mail] Error sending WhatsApp:", err);
|
|
433
|
+
ctx.throw(500, err.message || "Failed to send WhatsApp message");
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
/**
|
|
437
|
+
* Get WhatsApp connection status
|
|
438
|
+
* GET /api/magic-mail/whatsapp/status
|
|
439
|
+
*/
|
|
440
|
+
async getWhatsAppStatus(ctx) {
|
|
441
|
+
try {
|
|
442
|
+
const emailRouter2 = strapi.plugin("magic-mail").service("email-router");
|
|
443
|
+
const status = emailRouter2.getWhatsAppStatus();
|
|
444
|
+
ctx.body = {
|
|
445
|
+
success: true,
|
|
446
|
+
data: status
|
|
447
|
+
};
|
|
448
|
+
} catch (err) {
|
|
449
|
+
strapi.log.error("[magic-mail] Error getting WhatsApp status:", err);
|
|
450
|
+
ctx.body = {
|
|
451
|
+
success: false,
|
|
452
|
+
data: {
|
|
453
|
+
isConnected: false,
|
|
454
|
+
status: "error",
|
|
455
|
+
error: err.message
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
},
|
|
460
|
+
/**
|
|
461
|
+
* Check if phone number is on WhatsApp
|
|
462
|
+
* GET /api/magic-mail/whatsapp/check/:phoneNumber
|
|
463
|
+
*/
|
|
464
|
+
async checkWhatsAppNumber(ctx) {
|
|
465
|
+
try {
|
|
466
|
+
const { phoneNumber } = ctx.params;
|
|
467
|
+
if (!phoneNumber) {
|
|
468
|
+
ctx.throw(400, "Phone number is required");
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
const emailRouter2 = strapi.plugin("magic-mail").service("email-router");
|
|
472
|
+
const result = await emailRouter2.checkWhatsAppNumber(phoneNumber);
|
|
473
|
+
ctx.body = {
|
|
474
|
+
success: true,
|
|
475
|
+
data: result
|
|
476
|
+
};
|
|
477
|
+
} catch (err) {
|
|
478
|
+
strapi.log.error("[magic-mail] Error checking WhatsApp number:", err);
|
|
479
|
+
ctx.throw(500, err.message || "Failed to check phone number");
|
|
480
|
+
}
|
|
397
481
|
}
|
|
398
482
|
};
|
|
399
483
|
return controller;
|
|
@@ -2444,6 +2528,219 @@ function requireTest() {
|
|
|
2444
2528
|
};
|
|
2445
2529
|
return test;
|
|
2446
2530
|
}
|
|
2531
|
+
var whatsapp$1;
|
|
2532
|
+
var hasRequiredWhatsapp$1;
|
|
2533
|
+
function requireWhatsapp$1() {
|
|
2534
|
+
if (hasRequiredWhatsapp$1) return whatsapp$1;
|
|
2535
|
+
hasRequiredWhatsapp$1 = 1;
|
|
2536
|
+
whatsapp$1 = {
|
|
2537
|
+
/**
|
|
2538
|
+
* Check if WhatsApp/Baileys is available
|
|
2539
|
+
* GET /magic-mail/whatsapp/available
|
|
2540
|
+
*/
|
|
2541
|
+
async checkAvailable(ctx) {
|
|
2542
|
+
try {
|
|
2543
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2544
|
+
const available = await whatsappService.isAvailable();
|
|
2545
|
+
ctx.body = {
|
|
2546
|
+
success: true,
|
|
2547
|
+
data: {
|
|
2548
|
+
available,
|
|
2549
|
+
message: available ? "WhatsApp integration is available" : "Baileys not installed. Run: npm install @whiskeysockets/baileys pino qrcode"
|
|
2550
|
+
}
|
|
2551
|
+
};
|
|
2552
|
+
} catch (error) {
|
|
2553
|
+
ctx.throw(500, error.message);
|
|
2554
|
+
}
|
|
2555
|
+
},
|
|
2556
|
+
/**
|
|
2557
|
+
* Get WhatsApp connection status
|
|
2558
|
+
* GET /magic-mail/whatsapp/status
|
|
2559
|
+
*/
|
|
2560
|
+
async getStatus(ctx) {
|
|
2561
|
+
try {
|
|
2562
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2563
|
+
const status = whatsappService.getStatus();
|
|
2564
|
+
const sessionInfo = await whatsappService.getSessionInfo();
|
|
2565
|
+
ctx.body = {
|
|
2566
|
+
success: true,
|
|
2567
|
+
data: {
|
|
2568
|
+
...status,
|
|
2569
|
+
session: sessionInfo
|
|
2570
|
+
}
|
|
2571
|
+
};
|
|
2572
|
+
} catch (error) {
|
|
2573
|
+
ctx.throw(500, error.message);
|
|
2574
|
+
}
|
|
2575
|
+
},
|
|
2576
|
+
/**
|
|
2577
|
+
* Connect to WhatsApp (generates QR code if needed)
|
|
2578
|
+
* POST /magic-mail/whatsapp/connect
|
|
2579
|
+
*/
|
|
2580
|
+
async connect(ctx) {
|
|
2581
|
+
try {
|
|
2582
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2583
|
+
const result = await whatsappService.connect();
|
|
2584
|
+
ctx.body = {
|
|
2585
|
+
success: result.success,
|
|
2586
|
+
data: result
|
|
2587
|
+
};
|
|
2588
|
+
} catch (error) {
|
|
2589
|
+
ctx.throw(500, error.message);
|
|
2590
|
+
}
|
|
2591
|
+
},
|
|
2592
|
+
/**
|
|
2593
|
+
* Disconnect from WhatsApp
|
|
2594
|
+
* POST /magic-mail/whatsapp/disconnect
|
|
2595
|
+
*/
|
|
2596
|
+
async disconnect(ctx) {
|
|
2597
|
+
try {
|
|
2598
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2599
|
+
const result = await whatsappService.disconnect();
|
|
2600
|
+
ctx.body = {
|
|
2601
|
+
success: result.success,
|
|
2602
|
+
data: result
|
|
2603
|
+
};
|
|
2604
|
+
} catch (error) {
|
|
2605
|
+
ctx.throw(500, error.message);
|
|
2606
|
+
}
|
|
2607
|
+
},
|
|
2608
|
+
/**
|
|
2609
|
+
* Send a test message
|
|
2610
|
+
* POST /magic-mail/whatsapp/send-test
|
|
2611
|
+
*/
|
|
2612
|
+
async sendTest(ctx) {
|
|
2613
|
+
try {
|
|
2614
|
+
const { phoneNumber, message } = ctx.request.body;
|
|
2615
|
+
if (!phoneNumber) {
|
|
2616
|
+
return ctx.badRequest("Phone number is required");
|
|
2617
|
+
}
|
|
2618
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2619
|
+
const testMessage = message || `[MagicMail Test] This is a test message sent at ${(/* @__PURE__ */ new Date()).toLocaleString()}`;
|
|
2620
|
+
const result = await whatsappService.sendMessage(phoneNumber, testMessage);
|
|
2621
|
+
ctx.body = {
|
|
2622
|
+
success: result.success,
|
|
2623
|
+
data: result
|
|
2624
|
+
};
|
|
2625
|
+
} catch (error) {
|
|
2626
|
+
ctx.throw(500, error.message);
|
|
2627
|
+
}
|
|
2628
|
+
},
|
|
2629
|
+
/**
|
|
2630
|
+
* Send a message using a template
|
|
2631
|
+
* POST /magic-mail/whatsapp/send-template
|
|
2632
|
+
*/
|
|
2633
|
+
async sendTemplateMessage(ctx) {
|
|
2634
|
+
try {
|
|
2635
|
+
const { phoneNumber, templateName, variables } = ctx.request.body;
|
|
2636
|
+
if (!phoneNumber || !templateName) {
|
|
2637
|
+
return ctx.badRequest("Phone number and template name are required");
|
|
2638
|
+
}
|
|
2639
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2640
|
+
const result = await whatsappService.sendTemplateMessage(phoneNumber, templateName, variables || {});
|
|
2641
|
+
ctx.body = {
|
|
2642
|
+
success: result.success,
|
|
2643
|
+
data: result
|
|
2644
|
+
};
|
|
2645
|
+
} catch (error) {
|
|
2646
|
+
ctx.throw(500, error.message);
|
|
2647
|
+
}
|
|
2648
|
+
},
|
|
2649
|
+
/**
|
|
2650
|
+
* Check if a phone number is on WhatsApp
|
|
2651
|
+
* POST /magic-mail/whatsapp/check-number
|
|
2652
|
+
*/
|
|
2653
|
+
async checkNumber(ctx) {
|
|
2654
|
+
try {
|
|
2655
|
+
const { phoneNumber } = ctx.request.body;
|
|
2656
|
+
if (!phoneNumber) {
|
|
2657
|
+
return ctx.badRequest("Phone number is required");
|
|
2658
|
+
}
|
|
2659
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2660
|
+
const result = await whatsappService.checkNumber(phoneNumber);
|
|
2661
|
+
ctx.body = {
|
|
2662
|
+
success: result.success,
|
|
2663
|
+
data: result
|
|
2664
|
+
};
|
|
2665
|
+
} catch (error) {
|
|
2666
|
+
ctx.throw(500, error.message);
|
|
2667
|
+
}
|
|
2668
|
+
},
|
|
2669
|
+
/**
|
|
2670
|
+
* Get all WhatsApp templates
|
|
2671
|
+
* GET /magic-mail/whatsapp/templates
|
|
2672
|
+
*/
|
|
2673
|
+
async getTemplates(ctx) {
|
|
2674
|
+
try {
|
|
2675
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2676
|
+
const templates = await whatsappService.getTemplates();
|
|
2677
|
+
ctx.body = {
|
|
2678
|
+
success: true,
|
|
2679
|
+
data: templates
|
|
2680
|
+
};
|
|
2681
|
+
} catch (error) {
|
|
2682
|
+
ctx.throw(500, error.message);
|
|
2683
|
+
}
|
|
2684
|
+
},
|
|
2685
|
+
/**
|
|
2686
|
+
* Save a WhatsApp template
|
|
2687
|
+
* POST /magic-mail/whatsapp/templates
|
|
2688
|
+
*/
|
|
2689
|
+
async saveTemplate(ctx) {
|
|
2690
|
+
try {
|
|
2691
|
+
const { templateName, templateContent } = ctx.request.body;
|
|
2692
|
+
if (!templateName || !templateContent) {
|
|
2693
|
+
return ctx.badRequest("Template name and content are required");
|
|
2694
|
+
}
|
|
2695
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2696
|
+
const result = await whatsappService.saveTemplate(templateName, templateContent);
|
|
2697
|
+
ctx.body = {
|
|
2698
|
+
success: result.success,
|
|
2699
|
+
data: result
|
|
2700
|
+
};
|
|
2701
|
+
} catch (error) {
|
|
2702
|
+
ctx.throw(500, error.message);
|
|
2703
|
+
}
|
|
2704
|
+
},
|
|
2705
|
+
/**
|
|
2706
|
+
* Delete a WhatsApp template
|
|
2707
|
+
* DELETE /magic-mail/whatsapp/templates/:templateName
|
|
2708
|
+
*/
|
|
2709
|
+
async deleteTemplate(ctx) {
|
|
2710
|
+
try {
|
|
2711
|
+
const { templateName } = ctx.params;
|
|
2712
|
+
if (!templateName) {
|
|
2713
|
+
return ctx.badRequest("Template name is required");
|
|
2714
|
+
}
|
|
2715
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2716
|
+
const result = await whatsappService.deleteTemplate(templateName);
|
|
2717
|
+
ctx.body = {
|
|
2718
|
+
success: result.success,
|
|
2719
|
+
data: result
|
|
2720
|
+
};
|
|
2721
|
+
} catch (error) {
|
|
2722
|
+
ctx.throw(500, error.message);
|
|
2723
|
+
}
|
|
2724
|
+
},
|
|
2725
|
+
/**
|
|
2726
|
+
* Get session info
|
|
2727
|
+
* GET /magic-mail/whatsapp/session
|
|
2728
|
+
*/
|
|
2729
|
+
async getSession(ctx) {
|
|
2730
|
+
try {
|
|
2731
|
+
const whatsappService = strapi.plugin("magic-mail").service("whatsapp");
|
|
2732
|
+
const sessionInfo = await whatsappService.getSessionInfo();
|
|
2733
|
+
ctx.body = {
|
|
2734
|
+
success: true,
|
|
2735
|
+
data: sessionInfo
|
|
2736
|
+
};
|
|
2737
|
+
} catch (error) {
|
|
2738
|
+
ctx.throw(500, error.message);
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
};
|
|
2742
|
+
return whatsapp$1;
|
|
2743
|
+
}
|
|
2447
2744
|
var controllers;
|
|
2448
2745
|
var hasRequiredControllers;
|
|
2449
2746
|
function requireControllers() {
|
|
@@ -2457,6 +2754,7 @@ function requireControllers() {
|
|
|
2457
2754
|
const emailDesigner2 = requireEmailDesigner$1();
|
|
2458
2755
|
const analytics2 = requireAnalytics$1();
|
|
2459
2756
|
const test2 = requireTest();
|
|
2757
|
+
const whatsapp2 = requireWhatsapp$1();
|
|
2460
2758
|
controllers = {
|
|
2461
2759
|
controller: controller2,
|
|
2462
2760
|
accounts: accounts2,
|
|
@@ -2465,7 +2763,8 @@ function requireControllers() {
|
|
|
2465
2763
|
license: license2,
|
|
2466
2764
|
emailDesigner: emailDesigner2,
|
|
2467
2765
|
analytics: analytics2,
|
|
2468
|
-
test: test2
|
|
2766
|
+
test: test2,
|
|
2767
|
+
whatsapp: whatsapp2
|
|
2469
2768
|
};
|
|
2470
2769
|
return controllers;
|
|
2471
2770
|
}
|
|
@@ -2940,6 +3239,106 @@ function requireAdmin() {
|
|
|
2940
3239
|
policies: ["admin::isAuthenticatedAdmin"],
|
|
2941
3240
|
description: "Test template-version relations"
|
|
2942
3241
|
}
|
|
3242
|
+
},
|
|
3243
|
+
// WhatsApp Routes
|
|
3244
|
+
{
|
|
3245
|
+
method: "GET",
|
|
3246
|
+
path: "/whatsapp/available",
|
|
3247
|
+
handler: "whatsapp.checkAvailable",
|
|
3248
|
+
config: {
|
|
3249
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3250
|
+
description: "Check if WhatsApp/Baileys is available"
|
|
3251
|
+
}
|
|
3252
|
+
},
|
|
3253
|
+
{
|
|
3254
|
+
method: "GET",
|
|
3255
|
+
path: "/whatsapp/status",
|
|
3256
|
+
handler: "whatsapp.getStatus",
|
|
3257
|
+
config: {
|
|
3258
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3259
|
+
description: "Get WhatsApp connection status"
|
|
3260
|
+
}
|
|
3261
|
+
},
|
|
3262
|
+
{
|
|
3263
|
+
method: "POST",
|
|
3264
|
+
path: "/whatsapp/connect",
|
|
3265
|
+
handler: "whatsapp.connect",
|
|
3266
|
+
config: {
|
|
3267
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3268
|
+
description: "Connect to WhatsApp (generates QR if needed)"
|
|
3269
|
+
}
|
|
3270
|
+
},
|
|
3271
|
+
{
|
|
3272
|
+
method: "POST",
|
|
3273
|
+
path: "/whatsapp/disconnect",
|
|
3274
|
+
handler: "whatsapp.disconnect",
|
|
3275
|
+
config: {
|
|
3276
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3277
|
+
description: "Disconnect from WhatsApp"
|
|
3278
|
+
}
|
|
3279
|
+
},
|
|
3280
|
+
{
|
|
3281
|
+
method: "POST",
|
|
3282
|
+
path: "/whatsapp/send-test",
|
|
3283
|
+
handler: "whatsapp.sendTest",
|
|
3284
|
+
config: {
|
|
3285
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3286
|
+
description: "Send a test WhatsApp message"
|
|
3287
|
+
}
|
|
3288
|
+
},
|
|
3289
|
+
{
|
|
3290
|
+
method: "POST",
|
|
3291
|
+
path: "/whatsapp/send-template",
|
|
3292
|
+
handler: "whatsapp.sendTemplateMessage",
|
|
3293
|
+
config: {
|
|
3294
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3295
|
+
description: "Send WhatsApp message using template"
|
|
3296
|
+
}
|
|
3297
|
+
},
|
|
3298
|
+
{
|
|
3299
|
+
method: "POST",
|
|
3300
|
+
path: "/whatsapp/check-number",
|
|
3301
|
+
handler: "whatsapp.checkNumber",
|
|
3302
|
+
config: {
|
|
3303
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3304
|
+
description: "Check if phone number is on WhatsApp"
|
|
3305
|
+
}
|
|
3306
|
+
},
|
|
3307
|
+
{
|
|
3308
|
+
method: "GET",
|
|
3309
|
+
path: "/whatsapp/templates",
|
|
3310
|
+
handler: "whatsapp.getTemplates",
|
|
3311
|
+
config: {
|
|
3312
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3313
|
+
description: "Get all WhatsApp message templates"
|
|
3314
|
+
}
|
|
3315
|
+
},
|
|
3316
|
+
{
|
|
3317
|
+
method: "POST",
|
|
3318
|
+
path: "/whatsapp/templates",
|
|
3319
|
+
handler: "whatsapp.saveTemplate",
|
|
3320
|
+
config: {
|
|
3321
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3322
|
+
description: "Save a WhatsApp message template"
|
|
3323
|
+
}
|
|
3324
|
+
},
|
|
3325
|
+
{
|
|
3326
|
+
method: "DELETE",
|
|
3327
|
+
path: "/whatsapp/templates/:templateName",
|
|
3328
|
+
handler: "whatsapp.deleteTemplate",
|
|
3329
|
+
config: {
|
|
3330
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3331
|
+
description: "Delete a WhatsApp message template"
|
|
3332
|
+
}
|
|
3333
|
+
},
|
|
3334
|
+
{
|
|
3335
|
+
method: "GET",
|
|
3336
|
+
path: "/whatsapp/session",
|
|
3337
|
+
handler: "whatsapp.getSession",
|
|
3338
|
+
config: {
|
|
3339
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
3340
|
+
description: "Get WhatsApp session info"
|
|
3341
|
+
}
|
|
2943
3342
|
}
|
|
2944
3343
|
]
|
|
2945
3344
|
};
|
|
@@ -2953,6 +3352,7 @@ function requireContentApi() {
|
|
|
2953
3352
|
contentApi = {
|
|
2954
3353
|
type: "content-api",
|
|
2955
3354
|
routes: [
|
|
3355
|
+
// ============= EMAIL ROUTES =============
|
|
2956
3356
|
{
|
|
2957
3357
|
method: "POST",
|
|
2958
3358
|
path: "/send",
|
|
@@ -2963,7 +3363,45 @@ function requireContentApi() {
|
|
|
2963
3363
|
description: "Send email via MagicMail router"
|
|
2964
3364
|
}
|
|
2965
3365
|
},
|
|
2966
|
-
//
|
|
3366
|
+
// ============= UNIFIED MESSAGE ROUTE =============
|
|
3367
|
+
{
|
|
3368
|
+
method: "POST",
|
|
3369
|
+
path: "/send-message",
|
|
3370
|
+
handler: "controller.sendMessage",
|
|
3371
|
+
config: {
|
|
3372
|
+
auth: false,
|
|
3373
|
+
description: "Send message via Email or WhatsApp (unified API)"
|
|
3374
|
+
}
|
|
3375
|
+
},
|
|
3376
|
+
// ============= WHATSAPP ROUTES =============
|
|
3377
|
+
{
|
|
3378
|
+
method: "POST",
|
|
3379
|
+
path: "/send-whatsapp",
|
|
3380
|
+
handler: "controller.sendWhatsApp",
|
|
3381
|
+
config: {
|
|
3382
|
+
auth: false,
|
|
3383
|
+
description: "Send WhatsApp message"
|
|
3384
|
+
}
|
|
3385
|
+
},
|
|
3386
|
+
{
|
|
3387
|
+
method: "GET",
|
|
3388
|
+
path: "/whatsapp/status",
|
|
3389
|
+
handler: "controller.getWhatsAppStatus",
|
|
3390
|
+
config: {
|
|
3391
|
+
auth: false,
|
|
3392
|
+
description: "Get WhatsApp connection status"
|
|
3393
|
+
}
|
|
3394
|
+
},
|
|
3395
|
+
{
|
|
3396
|
+
method: "GET",
|
|
3397
|
+
path: "/whatsapp/check/:phoneNumber",
|
|
3398
|
+
handler: "controller.checkWhatsAppNumber",
|
|
3399
|
+
config: {
|
|
3400
|
+
auth: false,
|
|
3401
|
+
description: "Check if phone number is on WhatsApp"
|
|
3402
|
+
}
|
|
3403
|
+
},
|
|
3404
|
+
// ============= TRACKING ROUTES =============
|
|
2967
3405
|
{
|
|
2968
3406
|
method: "GET",
|
|
2969
3407
|
path: "/track/open/:emailId/:recipientHash",
|
|
@@ -3208,6 +3646,39 @@ function requireEmailRouter() {
|
|
|
3208
3646
|
emailData.html = html;
|
|
3209
3647
|
emailData.text = text;
|
|
3210
3648
|
emailData.subject = subject;
|
|
3649
|
+
let matchedRule = null;
|
|
3650
|
+
try {
|
|
3651
|
+
const allRules = await strapi2.documents("plugin::magic-mail.routing-rule").findMany({
|
|
3652
|
+
filters: { isActive: true },
|
|
3653
|
+
sort: [{ priority: "desc" }]
|
|
3654
|
+
});
|
|
3655
|
+
for (const rule of allRules) {
|
|
3656
|
+
let matches = false;
|
|
3657
|
+
switch (rule.matchType) {
|
|
3658
|
+
case "emailType":
|
|
3659
|
+
matches = rule.matchValue === type;
|
|
3660
|
+
break;
|
|
3661
|
+
case "recipient":
|
|
3662
|
+
matches = to && to.toLowerCase().includes(rule.matchValue.toLowerCase());
|
|
3663
|
+
break;
|
|
3664
|
+
case "subject":
|
|
3665
|
+
matches = subject && subject.toLowerCase().includes(rule.matchValue.toLowerCase());
|
|
3666
|
+
break;
|
|
3667
|
+
case "template":
|
|
3668
|
+
matches = emailData.template && emailData.template === rule.matchValue;
|
|
3669
|
+
break;
|
|
3670
|
+
case "custom":
|
|
3671
|
+
matches = emailData.customField && emailData.customField === rule.matchValue;
|
|
3672
|
+
break;
|
|
3673
|
+
}
|
|
3674
|
+
if (matches) {
|
|
3675
|
+
matchedRule = rule;
|
|
3676
|
+
break;
|
|
3677
|
+
}
|
|
3678
|
+
}
|
|
3679
|
+
} catch (ruleError) {
|
|
3680
|
+
strapi2.log.warn("[magic-mail] [WARNING] Failed to check routing rules for WhatsApp fallback:", ruleError.message);
|
|
3681
|
+
}
|
|
3211
3682
|
try {
|
|
3212
3683
|
const licenseGuard2 = strapi2.plugin("magic-mail").service("license-guard");
|
|
3213
3684
|
if (priority === "high") {
|
|
@@ -3258,6 +3729,38 @@ function requireEmailRouter() {
|
|
|
3258
3729
|
};
|
|
3259
3730
|
} catch (error) {
|
|
3260
3731
|
strapi2.log.error("[magic-mail] [ERROR] Email send failed:", error);
|
|
3732
|
+
if (matchedRule?.whatsappFallback) {
|
|
3733
|
+
strapi2.log.info("[magic-mail] [FALLBACK] Email failed, attempting WhatsApp fallback...");
|
|
3734
|
+
try {
|
|
3735
|
+
const whatsapp2 = strapi2.plugin("magic-mail").service("whatsapp");
|
|
3736
|
+
const whatsappStatus = whatsapp2.getStatus();
|
|
3737
|
+
if (whatsappStatus.isConnected) {
|
|
3738
|
+
const phoneNumber = emailData.phoneNumber || emailData.whatsappPhone;
|
|
3739
|
+
if (phoneNumber) {
|
|
3740
|
+
const whatsappMessage = `*${subject}*
|
|
3741
|
+
|
|
3742
|
+
${text || "Email delivery failed. Please check your email settings."}`;
|
|
3743
|
+
const waResult = await whatsapp2.sendMessage(phoneNumber, whatsappMessage);
|
|
3744
|
+
if (waResult.success) {
|
|
3745
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] WhatsApp fallback sent to ${phoneNumber}`);
|
|
3746
|
+
return {
|
|
3747
|
+
success: true,
|
|
3748
|
+
fallbackUsed: "whatsapp",
|
|
3749
|
+
phoneNumber
|
|
3750
|
+
};
|
|
3751
|
+
} else {
|
|
3752
|
+
strapi2.log.warn("[magic-mail] [WARNING] WhatsApp fallback failed:", waResult.error);
|
|
3753
|
+
}
|
|
3754
|
+
} else {
|
|
3755
|
+
strapi2.log.warn("[magic-mail] [WARNING] WhatsApp fallback enabled but no phone number provided");
|
|
3756
|
+
}
|
|
3757
|
+
} else {
|
|
3758
|
+
strapi2.log.warn("[magic-mail] [WARNING] WhatsApp fallback enabled but WhatsApp not connected");
|
|
3759
|
+
}
|
|
3760
|
+
} catch (waError) {
|
|
3761
|
+
strapi2.log.error("[magic-mail] [ERROR] WhatsApp fallback error:", waError.message);
|
|
3762
|
+
}
|
|
3763
|
+
}
|
|
3261
3764
|
throw error;
|
|
3262
3765
|
}
|
|
3263
3766
|
},
|
|
@@ -4166,6 +4669,168 @@ function requireEmailRouter() {
|
|
|
4166
4669
|
...headers
|
|
4167
4670
|
}
|
|
4168
4671
|
};
|
|
4672
|
+
},
|
|
4673
|
+
// ============================================================================
|
|
4674
|
+
// UNIFIED MESSAGE API - Send via Email OR WhatsApp
|
|
4675
|
+
// ============================================================================
|
|
4676
|
+
/**
|
|
4677
|
+
* Send a message via WhatsApp
|
|
4678
|
+
* Same pattern as send() but for WhatsApp
|
|
4679
|
+
* @param {Object} messageData - { phoneNumber, message, templateId, templateData }
|
|
4680
|
+
* @returns {Promise<Object>} Send result
|
|
4681
|
+
*/
|
|
4682
|
+
async sendWhatsApp(messageData) {
|
|
4683
|
+
const {
|
|
4684
|
+
phoneNumber,
|
|
4685
|
+
message,
|
|
4686
|
+
templateId = null,
|
|
4687
|
+
templateData = {}
|
|
4688
|
+
} = messageData;
|
|
4689
|
+
if (!phoneNumber) {
|
|
4690
|
+
throw new Error("Phone number is required for WhatsApp messages");
|
|
4691
|
+
}
|
|
4692
|
+
const cleanPhone = phoneNumber.replace(/[^\d]/g, "");
|
|
4693
|
+
if (cleanPhone.length < 10) {
|
|
4694
|
+
throw new Error("Invalid phone number format. Use format: 491234567890 (country code + number)");
|
|
4695
|
+
}
|
|
4696
|
+
const whatsapp2 = strapi2.plugin("magic-mail").service("whatsapp");
|
|
4697
|
+
const status = whatsapp2.getStatus();
|
|
4698
|
+
if (!status.isConnected) {
|
|
4699
|
+
throw new Error("WhatsApp is not connected. Please connect WhatsApp first in the admin panel.");
|
|
4700
|
+
}
|
|
4701
|
+
let finalMessage = message;
|
|
4702
|
+
if (templateId) {
|
|
4703
|
+
try {
|
|
4704
|
+
const template = await whatsapp2.getTemplate(templateId);
|
|
4705
|
+
if (template) {
|
|
4706
|
+
finalMessage = template.content;
|
|
4707
|
+
Object.keys(templateData).forEach((key) => {
|
|
4708
|
+
finalMessage = finalMessage.replace(new RegExp(`{{${key}}}`, "g"), templateData[key]);
|
|
4709
|
+
});
|
|
4710
|
+
}
|
|
4711
|
+
} catch (error) {
|
|
4712
|
+
strapi2.log.warn(`[magic-mail] WhatsApp template ${templateId} not found, using plain message`);
|
|
4713
|
+
}
|
|
4714
|
+
}
|
|
4715
|
+
if (!finalMessage) {
|
|
4716
|
+
throw new Error("Message content is required");
|
|
4717
|
+
}
|
|
4718
|
+
strapi2.log.info(`[magic-mail] [WHATSAPP] Sending message to ${cleanPhone}`);
|
|
4719
|
+
const result = await whatsapp2.sendMessage(cleanPhone, finalMessage);
|
|
4720
|
+
if (result.success) {
|
|
4721
|
+
strapi2.log.info(`[magic-mail] [SUCCESS] WhatsApp message sent to ${cleanPhone}`);
|
|
4722
|
+
return {
|
|
4723
|
+
success: true,
|
|
4724
|
+
channel: "whatsapp",
|
|
4725
|
+
phoneNumber: cleanPhone,
|
|
4726
|
+
jid: result.jid
|
|
4727
|
+
};
|
|
4728
|
+
} else {
|
|
4729
|
+
strapi2.log.error(`[magic-mail] [ERROR] WhatsApp send failed: ${result.error}`);
|
|
4730
|
+
throw new Error(result.error || "Failed to send WhatsApp message");
|
|
4731
|
+
}
|
|
4732
|
+
},
|
|
4733
|
+
/**
|
|
4734
|
+
* Unified send method - automatically chooses Email or WhatsApp
|
|
4735
|
+
* @param {Object} messageData - Combined email and WhatsApp data
|
|
4736
|
+
* @param {string} messageData.channel - 'email' | 'whatsapp' | 'auto' (default: 'auto')
|
|
4737
|
+
* @param {string} messageData.to - Email address (for email channel)
|
|
4738
|
+
* @param {string} messageData.phoneNumber - Phone number (for whatsapp channel)
|
|
4739
|
+
* @param {string} messageData.subject - Email subject
|
|
4740
|
+
* @param {string} messageData.message - Plain text message (used for WhatsApp, or as email text)
|
|
4741
|
+
* @param {string} messageData.html - HTML content (email only)
|
|
4742
|
+
* @param {string} messageData.templateId - Template ID (works for both channels)
|
|
4743
|
+
* @param {Object} messageData.templateData - Template variables
|
|
4744
|
+
* @returns {Promise<Object>} Send result with channel info
|
|
4745
|
+
*/
|
|
4746
|
+
async sendMessage(messageData) {
|
|
4747
|
+
const {
|
|
4748
|
+
channel = "auto",
|
|
4749
|
+
to,
|
|
4750
|
+
phoneNumber,
|
|
4751
|
+
subject,
|
|
4752
|
+
message,
|
|
4753
|
+
text,
|
|
4754
|
+
html,
|
|
4755
|
+
templateId,
|
|
4756
|
+
templateData,
|
|
4757
|
+
...rest
|
|
4758
|
+
} = messageData;
|
|
4759
|
+
let useChannel = channel;
|
|
4760
|
+
if (channel === "auto") {
|
|
4761
|
+
if (to && to.includes("@")) {
|
|
4762
|
+
useChannel = "email";
|
|
4763
|
+
} else if (phoneNumber) {
|
|
4764
|
+
useChannel = "whatsapp";
|
|
4765
|
+
} else {
|
|
4766
|
+
throw new Error("Either email (to) or phoneNumber is required");
|
|
4767
|
+
}
|
|
4768
|
+
}
|
|
4769
|
+
strapi2.log.info(`[magic-mail] [SEND] Channel: ${useChannel}, to: ${to || phoneNumber}`);
|
|
4770
|
+
if (useChannel === "whatsapp") {
|
|
4771
|
+
if (!phoneNumber) {
|
|
4772
|
+
throw new Error("Phone number is required for WhatsApp channel");
|
|
4773
|
+
}
|
|
4774
|
+
return await this.sendWhatsApp({
|
|
4775
|
+
phoneNumber,
|
|
4776
|
+
message: message || text || subject,
|
|
4777
|
+
// Use message, fallback to text or subject
|
|
4778
|
+
templateId,
|
|
4779
|
+
templateData
|
|
4780
|
+
});
|
|
4781
|
+
} else {
|
|
4782
|
+
if (!to) {
|
|
4783
|
+
throw new Error("Email address (to) is required for email channel");
|
|
4784
|
+
}
|
|
4785
|
+
const result = await this.send({
|
|
4786
|
+
to,
|
|
4787
|
+
subject,
|
|
4788
|
+
text: text || message,
|
|
4789
|
+
html,
|
|
4790
|
+
templateId,
|
|
4791
|
+
templateData,
|
|
4792
|
+
phoneNumber,
|
|
4793
|
+
// Pass for WhatsApp fallback
|
|
4794
|
+
...rest
|
|
4795
|
+
});
|
|
4796
|
+
return {
|
|
4797
|
+
...result,
|
|
4798
|
+
channel: "email"
|
|
4799
|
+
};
|
|
4800
|
+
}
|
|
4801
|
+
},
|
|
4802
|
+
/**
|
|
4803
|
+
* Check WhatsApp connection status
|
|
4804
|
+
* @returns {Object} Connection status
|
|
4805
|
+
*/
|
|
4806
|
+
getWhatsAppStatus() {
|
|
4807
|
+
try {
|
|
4808
|
+
const whatsapp2 = strapi2.plugin("magic-mail").service("whatsapp");
|
|
4809
|
+
return whatsapp2.getStatus();
|
|
4810
|
+
} catch (error) {
|
|
4811
|
+
return {
|
|
4812
|
+
isConnected: false,
|
|
4813
|
+
status: "unavailable",
|
|
4814
|
+
error: error.message
|
|
4815
|
+
};
|
|
4816
|
+
}
|
|
4817
|
+
},
|
|
4818
|
+
/**
|
|
4819
|
+
* Check if a phone number is registered on WhatsApp
|
|
4820
|
+
* @param {string} phoneNumber - Phone number to check
|
|
4821
|
+
* @returns {Promise<Object>} Check result
|
|
4822
|
+
*/
|
|
4823
|
+
async checkWhatsAppNumber(phoneNumber) {
|
|
4824
|
+
try {
|
|
4825
|
+
const whatsapp2 = strapi2.plugin("magic-mail").service("whatsapp");
|
|
4826
|
+
return await whatsapp2.checkNumber(phoneNumber);
|
|
4827
|
+
} catch (error) {
|
|
4828
|
+
return {
|
|
4829
|
+
success: false,
|
|
4830
|
+
exists: false,
|
|
4831
|
+
error: error.message
|
|
4832
|
+
};
|
|
4833
|
+
}
|
|
4169
4834
|
}
|
|
4170
4835
|
});
|
|
4171
4836
|
return emailRouter;
|
|
@@ -4862,7 +5527,7 @@ function requireOauth() {
|
|
|
4862
5527
|
});
|
|
4863
5528
|
return oauth;
|
|
4864
5529
|
}
|
|
4865
|
-
const version = "2.
|
|
5530
|
+
const version = "2.4.0";
|
|
4866
5531
|
const require$$2 = {
|
|
4867
5532
|
version
|
|
4868
5533
|
};
|
|
@@ -6166,6 +6831,434 @@ function requireAnalytics() {
|
|
|
6166
6831
|
});
|
|
6167
6832
|
return analytics;
|
|
6168
6833
|
}
|
|
6834
|
+
var whatsapp;
|
|
6835
|
+
var hasRequiredWhatsapp;
|
|
6836
|
+
function requireWhatsapp() {
|
|
6837
|
+
if (hasRequiredWhatsapp) return whatsapp;
|
|
6838
|
+
hasRequiredWhatsapp = 1;
|
|
6839
|
+
const path = require$$0$4;
|
|
6840
|
+
const fs = require$$1$3;
|
|
6841
|
+
let baileys = null;
|
|
6842
|
+
const loadBaileys = async () => {
|
|
6843
|
+
if (!baileys) {
|
|
6844
|
+
try {
|
|
6845
|
+
baileys = require("@whiskeysockets/baileys");
|
|
6846
|
+
if (process.env.DEBUG) {
|
|
6847
|
+
console.log("[MagicMail WhatsApp] Baileys loaded successfully");
|
|
6848
|
+
}
|
|
6849
|
+
return true;
|
|
6850
|
+
} catch (error) {
|
|
6851
|
+
console.warn("[MagicMail WhatsApp] Baileys not installed. WhatsApp features disabled.");
|
|
6852
|
+
console.warn("[MagicMail WhatsApp] Install with: npm install @whiskeysockets/baileys pino qrcode");
|
|
6853
|
+
return false;
|
|
6854
|
+
}
|
|
6855
|
+
}
|
|
6856
|
+
return true;
|
|
6857
|
+
};
|
|
6858
|
+
whatsapp = ({ strapi: strapi2 }) => {
|
|
6859
|
+
let sock = null;
|
|
6860
|
+
let qrCode = null;
|
|
6861
|
+
let connectionStatus = "disconnected";
|
|
6862
|
+
let lastError = null;
|
|
6863
|
+
let eventListeners = [];
|
|
6864
|
+
let wasConnectedBefore = false;
|
|
6865
|
+
let reconnectAttempts = 0;
|
|
6866
|
+
const MAX_RECONNECT_ATTEMPTS = 3;
|
|
6867
|
+
const isDebugEnabled = async () => {
|
|
6868
|
+
try {
|
|
6869
|
+
const pluginStore = strapi2.store({ type: "plugin", name: "magic-mail" });
|
|
6870
|
+
const settings = await pluginStore.get({ key: "settings" });
|
|
6871
|
+
return settings?.whatsapp_debug === true;
|
|
6872
|
+
} catch {
|
|
6873
|
+
return false;
|
|
6874
|
+
}
|
|
6875
|
+
};
|
|
6876
|
+
const debugLog = async (message) => {
|
|
6877
|
+
if (await isDebugEnabled()) {
|
|
6878
|
+
strapi2.log.info(message);
|
|
6879
|
+
}
|
|
6880
|
+
};
|
|
6881
|
+
const getAuthPath = () => {
|
|
6882
|
+
const strapiRoot = strapi2.dirs?.app?.root || process.cwd();
|
|
6883
|
+
return path.join(strapiRoot, ".magicmail-whatsapp-auth");
|
|
6884
|
+
};
|
|
6885
|
+
const emit = (event, data) => {
|
|
6886
|
+
eventListeners.forEach((listener) => {
|
|
6887
|
+
try {
|
|
6888
|
+
listener(event, data);
|
|
6889
|
+
} catch (e) {
|
|
6890
|
+
console.error("[MagicMail WhatsApp] Event listener error:", e);
|
|
6891
|
+
}
|
|
6892
|
+
});
|
|
6893
|
+
};
|
|
6894
|
+
const service = {
|
|
6895
|
+
/**
|
|
6896
|
+
* Check if Baileys is available
|
|
6897
|
+
* @returns {Promise<boolean>} True if Baileys is installed
|
|
6898
|
+
*/
|
|
6899
|
+
async isAvailable() {
|
|
6900
|
+
return await loadBaileys();
|
|
6901
|
+
},
|
|
6902
|
+
/**
|
|
6903
|
+
* Get current connection status
|
|
6904
|
+
* @returns {object} Status object with status, qrCode, lastError, isConnected
|
|
6905
|
+
*/
|
|
6906
|
+
getStatus() {
|
|
6907
|
+
return {
|
|
6908
|
+
status: connectionStatus,
|
|
6909
|
+
qrCode,
|
|
6910
|
+
lastError,
|
|
6911
|
+
isConnected: connectionStatus === "connected"
|
|
6912
|
+
};
|
|
6913
|
+
},
|
|
6914
|
+
/**
|
|
6915
|
+
* Add event listener for WhatsApp events
|
|
6916
|
+
* @param {function} callback - Callback function(event, data)
|
|
6917
|
+
* @returns {function} Unsubscribe function
|
|
6918
|
+
*/
|
|
6919
|
+
on(callback) {
|
|
6920
|
+
eventListeners.push(callback);
|
|
6921
|
+
return () => {
|
|
6922
|
+
eventListeners = eventListeners.filter((l) => l !== callback);
|
|
6923
|
+
};
|
|
6924
|
+
},
|
|
6925
|
+
/**
|
|
6926
|
+
* Initialize WhatsApp connection
|
|
6927
|
+
* @returns {Promise<object>} Connection result with success status
|
|
6928
|
+
*/
|
|
6929
|
+
async connect() {
|
|
6930
|
+
const available = await loadBaileys();
|
|
6931
|
+
if (!available) {
|
|
6932
|
+
lastError = "Baileys not installed. Run: npm install @whiskeysockets/baileys pino qrcode";
|
|
6933
|
+
strapi2.log.error("[MagicMail WhatsApp] [ERROR] Baileys library not available");
|
|
6934
|
+
return { success: false, error: lastError };
|
|
6935
|
+
}
|
|
6936
|
+
if (sock && connectionStatus === "connected") {
|
|
6937
|
+
await debugLog("[MagicMail WhatsApp] Already connected");
|
|
6938
|
+
return { success: true, status: "already_connected" };
|
|
6939
|
+
}
|
|
6940
|
+
if (sock) {
|
|
6941
|
+
try {
|
|
6942
|
+
sock.end();
|
|
6943
|
+
} catch (e) {
|
|
6944
|
+
}
|
|
6945
|
+
sock = null;
|
|
6946
|
+
}
|
|
6947
|
+
return new Promise(async (resolve) => {
|
|
6948
|
+
try {
|
|
6949
|
+
connectionStatus = "connecting";
|
|
6950
|
+
emit("status", { status: connectionStatus });
|
|
6951
|
+
await debugLog("[MagicMail WhatsApp] Starting connection...");
|
|
6952
|
+
const authPath = getAuthPath();
|
|
6953
|
+
if (!fs.existsSync(authPath)) {
|
|
6954
|
+
fs.mkdirSync(authPath, { recursive: true });
|
|
6955
|
+
}
|
|
6956
|
+
await debugLog(`[MagicMail WhatsApp] Auth path: ${authPath}`);
|
|
6957
|
+
const { state, saveCreds } = await baileys.useMultiFileAuthState(authPath);
|
|
6958
|
+
await debugLog("[MagicMail WhatsApp] Auth state loaded");
|
|
6959
|
+
const pino = require("pino");
|
|
6960
|
+
const logger2 = pino({ level: "silent" });
|
|
6961
|
+
await debugLog("[MagicMail WhatsApp] Creating WhatsApp socket...");
|
|
6962
|
+
const makeSocket = baileys.default || baileys.makeWASocket;
|
|
6963
|
+
const browserConfig = baileys.Browsers.ubuntu("Chrome");
|
|
6964
|
+
await debugLog(`[MagicMail WhatsApp] Browser config: ${JSON.stringify(browserConfig)}`);
|
|
6965
|
+
sock = makeSocket({
|
|
6966
|
+
auth: state,
|
|
6967
|
+
logger: logger2,
|
|
6968
|
+
browser: browserConfig,
|
|
6969
|
+
syncFullHistory: false,
|
|
6970
|
+
markOnlineOnConnect: false,
|
|
6971
|
+
generateHighQualityLinkPreview: false,
|
|
6972
|
+
getMessage: async (key) => {
|
|
6973
|
+
return { conversation: "" };
|
|
6974
|
+
}
|
|
6975
|
+
});
|
|
6976
|
+
await debugLog("[MagicMail WhatsApp] Socket created, registering event handlers...");
|
|
6977
|
+
let resolved = false;
|
|
6978
|
+
const resolveOnce = (result) => {
|
|
6979
|
+
if (!resolved) {
|
|
6980
|
+
resolved = true;
|
|
6981
|
+
resolve(result);
|
|
6982
|
+
}
|
|
6983
|
+
};
|
|
6984
|
+
setTimeout(() => {
|
|
6985
|
+
if (!resolved) {
|
|
6986
|
+
strapi2.log.warn("[MagicMail WhatsApp] Connection timeout - no QR or connection");
|
|
6987
|
+
resolveOnce({ success: true, status: connectionStatus, qrCode });
|
|
6988
|
+
}
|
|
6989
|
+
}, 3e4);
|
|
6990
|
+
sock.ev.on("connection.update", async (update) => {
|
|
6991
|
+
await debugLog(`[MagicMail WhatsApp] connection.update: ${JSON.stringify(update)}`);
|
|
6992
|
+
const { connection, lastDisconnect, qr } = update;
|
|
6993
|
+
if (qr) {
|
|
6994
|
+
await debugLog("[MagicMail WhatsApp] QR code received");
|
|
6995
|
+
try {
|
|
6996
|
+
const QRCode = require("qrcode");
|
|
6997
|
+
qrCode = await QRCode.toDataURL(qr);
|
|
6998
|
+
connectionStatus = "qr_pending";
|
|
6999
|
+
emit("qr", { qrCode });
|
|
7000
|
+
emit("status", { status: connectionStatus });
|
|
7001
|
+
strapi2.log.info("[MagicMail WhatsApp] [SUCCESS] QR Code generated - scan with WhatsApp");
|
|
7002
|
+
resolveOnce({ success: true, status: connectionStatus, qrCode });
|
|
7003
|
+
} catch (qrError) {
|
|
7004
|
+
strapi2.log.error("[MagicMail WhatsApp] QR generation error:", qrError.message);
|
|
7005
|
+
}
|
|
7006
|
+
}
|
|
7007
|
+
if (connection === "close") {
|
|
7008
|
+
const statusCode = lastDisconnect?.error?.output?.statusCode;
|
|
7009
|
+
const isLoggedOut = statusCode === baileys.DisconnectReason.loggedOut;
|
|
7010
|
+
const isRestartRequired = statusCode === baileys.DisconnectReason.restartRequired;
|
|
7011
|
+
const isConnectionFailure = statusCode === 405;
|
|
7012
|
+
await debugLog(`[MagicMail WhatsApp] Connection closed - statusCode: ${statusCode}`);
|
|
7013
|
+
if (isLoggedOut) {
|
|
7014
|
+
connectionStatus = "disconnected";
|
|
7015
|
+
lastError = "Logged out from WhatsApp";
|
|
7016
|
+
qrCode = null;
|
|
7017
|
+
wasConnectedBefore = false;
|
|
7018
|
+
reconnectAttempts = 0;
|
|
7019
|
+
try {
|
|
7020
|
+
fs.rmSync(authPath, { recursive: true, force: true });
|
|
7021
|
+
} catch (e) {
|
|
7022
|
+
}
|
|
7023
|
+
strapi2.log.warn("[MagicMail WhatsApp] Logged out - auth cleared");
|
|
7024
|
+
} else if (isRestartRequired) {
|
|
7025
|
+
await debugLog("[MagicMail WhatsApp] Restart required - reconnecting...");
|
|
7026
|
+
connectionStatus = "connecting";
|
|
7027
|
+
setTimeout(() => {
|
|
7028
|
+
service.connect();
|
|
7029
|
+
}, 1e3);
|
|
7030
|
+
} else if (isConnectionFailure && reconnectAttempts < 2) {
|
|
7031
|
+
reconnectAttempts++;
|
|
7032
|
+
await debugLog(`[MagicMail WhatsApp] Connection rejected (405) - retrying (${reconnectAttempts}/2)`);
|
|
7033
|
+
try {
|
|
7034
|
+
fs.rmSync(authPath, { recursive: true, force: true });
|
|
7035
|
+
} catch (e) {
|
|
7036
|
+
}
|
|
7037
|
+
connectionStatus = "disconnected";
|
|
7038
|
+
qrCode = null;
|
|
7039
|
+
setTimeout(() => {
|
|
7040
|
+
service.connect();
|
|
7041
|
+
}, 3e3);
|
|
7042
|
+
} else if (isConnectionFailure) {
|
|
7043
|
+
connectionStatus = "disconnected";
|
|
7044
|
+
lastError = "WhatsApp connection rejected (405). Please try again later.";
|
|
7045
|
+
strapi2.log.error("[MagicMail WhatsApp] [ERROR] Connection rejected after retries.");
|
|
7046
|
+
resolveOnce({ success: false, status: connectionStatus, error: lastError });
|
|
7047
|
+
} else if (wasConnectedBefore && reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
|
|
7048
|
+
reconnectAttempts++;
|
|
7049
|
+
connectionStatus = "connecting";
|
|
7050
|
+
await debugLog(`[MagicMail WhatsApp] Reconnecting (${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})...`);
|
|
7051
|
+
setTimeout(() => {
|
|
7052
|
+
service.connect();
|
|
7053
|
+
}, 3e3 * reconnectAttempts);
|
|
7054
|
+
} else if (!wasConnectedBefore) {
|
|
7055
|
+
connectionStatus = "disconnected";
|
|
7056
|
+
qrCode = null;
|
|
7057
|
+
await debugLog("[MagicMail WhatsApp] Connection closed - waiting for QR scan");
|
|
7058
|
+
} else {
|
|
7059
|
+
connectionStatus = "disconnected";
|
|
7060
|
+
lastError = "Max reconnect attempts reached";
|
|
7061
|
+
strapi2.log.warn("[MagicMail WhatsApp] Max reconnect attempts reached");
|
|
7062
|
+
}
|
|
7063
|
+
emit("status", { status: connectionStatus, error: lastError });
|
|
7064
|
+
}
|
|
7065
|
+
if (connection === "open") {
|
|
7066
|
+
connectionStatus = "connected";
|
|
7067
|
+
qrCode = null;
|
|
7068
|
+
lastError = null;
|
|
7069
|
+
wasConnectedBefore = true;
|
|
7070
|
+
reconnectAttempts = 0;
|
|
7071
|
+
emit("status", { status: connectionStatus });
|
|
7072
|
+
strapi2.log.info("[MagicMail WhatsApp] [SUCCESS] Connected successfully!");
|
|
7073
|
+
resolveOnce({ success: true, status: connectionStatus });
|
|
7074
|
+
}
|
|
7075
|
+
});
|
|
7076
|
+
sock.ev.on("creds.update", saveCreds);
|
|
7077
|
+
} catch (error) {
|
|
7078
|
+
lastError = error.message;
|
|
7079
|
+
connectionStatus = "disconnected";
|
|
7080
|
+
strapi2.log.error("[MagicMail WhatsApp] Connection error:", error);
|
|
7081
|
+
resolve({ success: false, error: error.message });
|
|
7082
|
+
}
|
|
7083
|
+
});
|
|
7084
|
+
},
|
|
7085
|
+
/**
|
|
7086
|
+
* Disconnect WhatsApp and clear session
|
|
7087
|
+
* @returns {Promise<object>} Result with success status
|
|
7088
|
+
*/
|
|
7089
|
+
async disconnect() {
|
|
7090
|
+
if (sock) {
|
|
7091
|
+
try {
|
|
7092
|
+
await sock.logout();
|
|
7093
|
+
} catch (e) {
|
|
7094
|
+
}
|
|
7095
|
+
sock = null;
|
|
7096
|
+
}
|
|
7097
|
+
connectionStatus = "disconnected";
|
|
7098
|
+
qrCode = null;
|
|
7099
|
+
emit("status", { status: connectionStatus });
|
|
7100
|
+
strapi2.log.info("[MagicMail WhatsApp] Disconnected");
|
|
7101
|
+
return { success: true };
|
|
7102
|
+
},
|
|
7103
|
+
/**
|
|
7104
|
+
* Send a text message to a phone number
|
|
7105
|
+
* @param {string} phoneNumber - Phone number with country code (e.g., "491234567890")
|
|
7106
|
+
* @param {string} message - Message text
|
|
7107
|
+
* @returns {Promise<object>} Result with success status
|
|
7108
|
+
*/
|
|
7109
|
+
async sendMessage(phoneNumber, message) {
|
|
7110
|
+
if (connectionStatus !== "connected" || !sock) {
|
|
7111
|
+
return {
|
|
7112
|
+
success: false,
|
|
7113
|
+
error: "WhatsApp not connected. Please connect first."
|
|
7114
|
+
};
|
|
7115
|
+
}
|
|
7116
|
+
try {
|
|
7117
|
+
const formattedNumber = phoneNumber.replace(/[^\d]/g, "");
|
|
7118
|
+
const jid = `${formattedNumber}@s.whatsapp.net`;
|
|
7119
|
+
const [exists] = await sock.onWhatsApp(formattedNumber);
|
|
7120
|
+
if (!exists?.exists) {
|
|
7121
|
+
return {
|
|
7122
|
+
success: false,
|
|
7123
|
+
error: `Phone number ${phoneNumber} is not registered on WhatsApp`
|
|
7124
|
+
};
|
|
7125
|
+
}
|
|
7126
|
+
await sock.sendMessage(jid, { text: message });
|
|
7127
|
+
await debugLog(`[MagicMail WhatsApp] Message sent to ${formattedNumber}`);
|
|
7128
|
+
return { success: true, jid };
|
|
7129
|
+
} catch (error) {
|
|
7130
|
+
strapi2.log.error("[MagicMail WhatsApp] Send error:", error);
|
|
7131
|
+
return { success: false, error: error.message };
|
|
7132
|
+
}
|
|
7133
|
+
},
|
|
7134
|
+
/**
|
|
7135
|
+
* Send message using a template
|
|
7136
|
+
* @param {string} phoneNumber - Phone number
|
|
7137
|
+
* @param {string} templateName - Template identifier
|
|
7138
|
+
* @param {object} variables - Template variables to replace
|
|
7139
|
+
* @returns {Promise<object>} Result with success status
|
|
7140
|
+
*/
|
|
7141
|
+
async sendTemplateMessage(phoneNumber, templateName, variables = {}) {
|
|
7142
|
+
try {
|
|
7143
|
+
const pluginStore = strapi2.store({ type: "plugin", name: "magic-mail" });
|
|
7144
|
+
const templates = await pluginStore.get({ key: "whatsapp_templates" }) || {};
|
|
7145
|
+
let template = templates[templateName];
|
|
7146
|
+
if (!template) {
|
|
7147
|
+
template = `*{{subject}}*
|
|
7148
|
+
|
|
7149
|
+
{{body}}`;
|
|
7150
|
+
}
|
|
7151
|
+
let message = template;
|
|
7152
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
7153
|
+
message = message.replace(new RegExp(`{{${key}}}`, "g"), value);
|
|
7154
|
+
}
|
|
7155
|
+
return this.sendMessage(phoneNumber, message);
|
|
7156
|
+
} catch (error) {
|
|
7157
|
+
return { success: false, error: error.message };
|
|
7158
|
+
}
|
|
7159
|
+
},
|
|
7160
|
+
/**
|
|
7161
|
+
* Check if a phone number is on WhatsApp
|
|
7162
|
+
* @param {string} phoneNumber - Phone number to check
|
|
7163
|
+
* @returns {Promise<object>} Result with exists boolean
|
|
7164
|
+
*/
|
|
7165
|
+
async checkNumber(phoneNumber) {
|
|
7166
|
+
if (connectionStatus !== "connected" || !sock) {
|
|
7167
|
+
return { success: false, error: "WhatsApp not connected" };
|
|
7168
|
+
}
|
|
7169
|
+
try {
|
|
7170
|
+
const formattedNumber = phoneNumber.replace(/[^\d]/g, "");
|
|
7171
|
+
const [result] = await sock.onWhatsApp(formattedNumber);
|
|
7172
|
+
return {
|
|
7173
|
+
success: true,
|
|
7174
|
+
exists: result?.exists || false,
|
|
7175
|
+
jid: result?.jid
|
|
7176
|
+
};
|
|
7177
|
+
} catch (error) {
|
|
7178
|
+
return { success: false, error: error.message };
|
|
7179
|
+
}
|
|
7180
|
+
},
|
|
7181
|
+
/**
|
|
7182
|
+
* Get session info
|
|
7183
|
+
* @returns {Promise<object|null>} Session info or null if not connected
|
|
7184
|
+
*/
|
|
7185
|
+
async getSessionInfo() {
|
|
7186
|
+
if (connectionStatus !== "connected" || !sock) {
|
|
7187
|
+
return null;
|
|
7188
|
+
}
|
|
7189
|
+
try {
|
|
7190
|
+
const user = sock.user;
|
|
7191
|
+
return {
|
|
7192
|
+
phoneNumber: user?.id?.split(":")[0] || user?.id?.split("@")[0],
|
|
7193
|
+
name: user?.name,
|
|
7194
|
+
platform: "WhatsApp Web"
|
|
7195
|
+
};
|
|
7196
|
+
} catch (error) {
|
|
7197
|
+
return null;
|
|
7198
|
+
}
|
|
7199
|
+
},
|
|
7200
|
+
/**
|
|
7201
|
+
* Reset connection state (for manual cleanup)
|
|
7202
|
+
*/
|
|
7203
|
+
reset() {
|
|
7204
|
+
sock = null;
|
|
7205
|
+
qrCode = null;
|
|
7206
|
+
connectionStatus = "disconnected";
|
|
7207
|
+
lastError = null;
|
|
7208
|
+
wasConnectedBefore = false;
|
|
7209
|
+
reconnectAttempts = 0;
|
|
7210
|
+
},
|
|
7211
|
+
/**
|
|
7212
|
+
* Save WhatsApp template
|
|
7213
|
+
* @param {string} templateName - Template identifier
|
|
7214
|
+
* @param {string} templateContent - Template content with {{variables}}
|
|
7215
|
+
* @returns {Promise<object>} Result with success status
|
|
7216
|
+
*/
|
|
7217
|
+
async saveTemplate(templateName, templateContent) {
|
|
7218
|
+
try {
|
|
7219
|
+
const pluginStore = strapi2.store({ type: "plugin", name: "magic-mail" });
|
|
7220
|
+
const templates = await pluginStore.get({ key: "whatsapp_templates" }) || {};
|
|
7221
|
+
templates[templateName] = templateContent;
|
|
7222
|
+
await pluginStore.set({ key: "whatsapp_templates", value: templates });
|
|
7223
|
+
return { success: true };
|
|
7224
|
+
} catch (error) {
|
|
7225
|
+
return { success: false, error: error.message };
|
|
7226
|
+
}
|
|
7227
|
+
},
|
|
7228
|
+
/**
|
|
7229
|
+
* Get all WhatsApp templates
|
|
7230
|
+
* @returns {Promise<object>} Templates object
|
|
7231
|
+
*/
|
|
7232
|
+
async getTemplates() {
|
|
7233
|
+
try {
|
|
7234
|
+
const pluginStore = strapi2.store({ type: "plugin", name: "magic-mail" });
|
|
7235
|
+
const templates = await pluginStore.get({ key: "whatsapp_templates" }) || {};
|
|
7236
|
+
return templates;
|
|
7237
|
+
} catch (error) {
|
|
7238
|
+
return {};
|
|
7239
|
+
}
|
|
7240
|
+
},
|
|
7241
|
+
/**
|
|
7242
|
+
* Delete a WhatsApp template
|
|
7243
|
+
* @param {string} templateName - Template identifier
|
|
7244
|
+
* @returns {Promise<object>} Result with success status
|
|
7245
|
+
*/
|
|
7246
|
+
async deleteTemplate(templateName) {
|
|
7247
|
+
try {
|
|
7248
|
+
const pluginStore = strapi2.store({ type: "plugin", name: "magic-mail" });
|
|
7249
|
+
const templates = await pluginStore.get({ key: "whatsapp_templates" }) || {};
|
|
7250
|
+
delete templates[templateName];
|
|
7251
|
+
await pluginStore.set({ key: "whatsapp_templates", value: templates });
|
|
7252
|
+
return { success: true };
|
|
7253
|
+
} catch (error) {
|
|
7254
|
+
return { success: false, error: error.message };
|
|
7255
|
+
}
|
|
7256
|
+
}
|
|
7257
|
+
};
|
|
7258
|
+
return service;
|
|
7259
|
+
};
|
|
7260
|
+
return whatsapp;
|
|
7261
|
+
}
|
|
6169
7262
|
var services;
|
|
6170
7263
|
var hasRequiredServices;
|
|
6171
7264
|
function requireServices() {
|
|
@@ -6177,13 +7270,15 @@ function requireServices() {
|
|
|
6177
7270
|
const licenseGuard2 = requireLicenseGuard();
|
|
6178
7271
|
const emailDesigner2 = requireEmailDesigner();
|
|
6179
7272
|
const analytics2 = requireAnalytics();
|
|
7273
|
+
const whatsapp2 = requireWhatsapp();
|
|
6180
7274
|
services = {
|
|
6181
7275
|
"email-router": emailRouter2,
|
|
6182
7276
|
"account-manager": accountManager2,
|
|
6183
7277
|
oauth: oauth2,
|
|
6184
7278
|
"license-guard": licenseGuard2,
|
|
6185
7279
|
"email-designer": emailDesigner2,
|
|
6186
|
-
analytics: analytics2
|
|
7280
|
+
analytics: analytics2,
|
|
7281
|
+
whatsapp: whatsapp2
|
|
6187
7282
|
};
|
|
6188
7283
|
return services;
|
|
6189
7284
|
}
|