wexa-chat 0.2.9 → 0.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 +169 -4
- package/dist/index.d.cts +110 -1
- package/dist/index.d.ts +110 -1
- package/dist/index.js +328 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +328 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -72,12 +72,17 @@ function createConversationModel(options, conn) {
|
|
|
72
72
|
lastMessageSenderModel: String,
|
|
73
73
|
lastLinkedInId: String,
|
|
74
74
|
lastWhatsAppId: String,
|
|
75
|
+
lastEmailId: String,
|
|
75
76
|
linkedinChatId: String,
|
|
76
77
|
whatsappChatId: String,
|
|
78
|
+
emailChatId: String,
|
|
77
79
|
metadata: Schema.Types.Mixed
|
|
78
80
|
},
|
|
79
81
|
{ timestamps: true }
|
|
80
82
|
);
|
|
83
|
+
ConversationSchema.index({ linkedinChatId: 1 }, { unique: false, sparse: true });
|
|
84
|
+
ConversationSchema.index({ whatsappChatId: 1 }, { unique: false, sparse: true });
|
|
85
|
+
ConversationSchema.index({ emailChatId: 1 }, { unique: false, sparse: true });
|
|
81
86
|
const model = conn.model(name, ConversationSchema);
|
|
82
87
|
modelRegistry.set(registryKey, model);
|
|
83
88
|
return model;
|
|
@@ -140,6 +145,10 @@ function createMessageModel(options, conn) {
|
|
|
140
145
|
type: String,
|
|
141
146
|
trim: true
|
|
142
147
|
},
|
|
148
|
+
externalMessageId: {
|
|
149
|
+
type: String,
|
|
150
|
+
index: true
|
|
151
|
+
},
|
|
143
152
|
parentMessageId: {
|
|
144
153
|
type: String,
|
|
145
154
|
ref: "Message"
|
|
@@ -153,7 +162,8 @@ function createMessageModel(options, conn) {
|
|
|
153
162
|
},
|
|
154
163
|
deletedAt: {
|
|
155
164
|
type: Date
|
|
156
|
-
}
|
|
165
|
+
},
|
|
166
|
+
metadata: Schema.Types.Mixed
|
|
157
167
|
},
|
|
158
168
|
{ timestamps: true }
|
|
159
169
|
);
|
|
@@ -162,6 +172,21 @@ function createMessageModel(options, conn) {
|
|
|
162
172
|
conversationId: 1,
|
|
163
173
|
createdAt: -1
|
|
164
174
|
});
|
|
175
|
+
MessageSchema.index(
|
|
176
|
+
{
|
|
177
|
+
organizationId: 1,
|
|
178
|
+
conversationId: 1,
|
|
179
|
+
externalMessageId: 1
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
unique: true,
|
|
183
|
+
name: "deduplication_index",
|
|
184
|
+
partialFilterExpression: {
|
|
185
|
+
externalMessageId: { $type: "string" }
|
|
186
|
+
// Only index when field is a string (not null/undefined)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
);
|
|
165
190
|
if (options.enableTextSearch) {
|
|
166
191
|
MessageSchema.index(
|
|
167
192
|
{ text: "text" },
|
|
@@ -489,6 +514,9 @@ var paths = {
|
|
|
489
514
|
},
|
|
490
515
|
whatsapp: {
|
|
491
516
|
startChat: (connectorId) => `/actions/whatsapp/start_chat/${connectorId}`
|
|
517
|
+
},
|
|
518
|
+
email: {
|
|
519
|
+
sendEmail: (connectorId) => `/actions/mail/send_email/${connectorId}`
|
|
492
520
|
}
|
|
493
521
|
// Add more services here as needed
|
|
494
522
|
};
|
|
@@ -548,28 +576,40 @@ function createMessagesService(models, hooks = {}) {
|
|
|
548
576
|
source,
|
|
549
577
|
parentMessageId,
|
|
550
578
|
rootThreadId,
|
|
551
|
-
connectorIds
|
|
579
|
+
connectorIds,
|
|
580
|
+
metadata
|
|
552
581
|
} = args;
|
|
553
582
|
const success_source = ["core"];
|
|
554
583
|
const failed_source = [];
|
|
555
584
|
const tasks = [];
|
|
556
585
|
let lastLinkedInId;
|
|
557
586
|
let lastWhatsAppId;
|
|
587
|
+
let lastEmailId;
|
|
558
588
|
let linkedinChatId;
|
|
559
589
|
let whatsappChatId;
|
|
590
|
+
let emailChatId;
|
|
560
591
|
if (Array.isArray(source) && source.includes("linkedin")) {
|
|
561
592
|
const LinkedinConnector = connectorIds && connectorIds.linkedin;
|
|
593
|
+
const linkedinMeta = metadata == null ? void 0 : metadata.linkedin;
|
|
562
594
|
lastLinkedInId = LinkedinConnector == null ? void 0 : LinkedinConnector.connectorId;
|
|
563
595
|
if (LinkedinConnector) {
|
|
564
596
|
tasks.push({
|
|
565
597
|
name: "linkedin",
|
|
566
598
|
run: async () => {
|
|
567
599
|
const path = paths.linkedin.startChat(String(LinkedinConnector.connectorId));
|
|
600
|
+
const payload = {
|
|
601
|
+
linkedin_url: LinkedinConnector.contactId,
|
|
602
|
+
text
|
|
603
|
+
};
|
|
604
|
+
if (linkedinMeta == null ? void 0 : linkedinMeta.subject) {
|
|
605
|
+
payload.subject = linkedinMeta.subject;
|
|
606
|
+
}
|
|
607
|
+
if (linkedinMeta == null ? void 0 : linkedinMeta.isInMail) {
|
|
608
|
+
payload.linkedin_inmail = linkedinMeta.isInMail;
|
|
609
|
+
}
|
|
610
|
+
console.log("LinkedIn payload:", payload);
|
|
568
611
|
try {
|
|
569
|
-
const res = await getAxios().post(path,
|
|
570
|
-
linkedin_url: LinkedinConnector.contactId,
|
|
571
|
-
text
|
|
572
|
-
});
|
|
612
|
+
const res = await getAxios().post(path, payload);
|
|
573
613
|
const data = res.data;
|
|
574
614
|
console.log("linkedinChatId", data);
|
|
575
615
|
linkedinChatId = data.chat_data.chat_id;
|
|
@@ -611,6 +651,83 @@ function createMessagesService(models, hooks = {}) {
|
|
|
611
651
|
failed_source.push({ source: "whatsapp", message: "Missing connector configuration" });
|
|
612
652
|
}
|
|
613
653
|
}
|
|
654
|
+
if (Array.isArray(source) && source.includes("email")) {
|
|
655
|
+
const emailConnector = connectorIds && connectorIds.email;
|
|
656
|
+
const emailMeta = metadata == null ? void 0 : metadata.email;
|
|
657
|
+
lastEmailId = emailConnector == null ? void 0 : emailConnector.connectorId;
|
|
658
|
+
if (emailConnector) {
|
|
659
|
+
tasks.push({
|
|
660
|
+
name: "email",
|
|
661
|
+
run: async () => {
|
|
662
|
+
var _a;
|
|
663
|
+
const path = paths.email.sendEmail(String(emailConnector.connectorId));
|
|
664
|
+
const primaryRecipient = {
|
|
665
|
+
display_name: (emailMeta == null ? void 0 : emailMeta.recipientName) || "Recipient",
|
|
666
|
+
identifier: emailConnector.contactId
|
|
667
|
+
};
|
|
668
|
+
const toRecipients = [primaryRecipient];
|
|
669
|
+
if (emailMeta == null ? void 0 : emailMeta.additionalRecipients) {
|
|
670
|
+
toRecipients.push(...emailMeta.additionalRecipients);
|
|
671
|
+
}
|
|
672
|
+
const payload = {
|
|
673
|
+
// REQUIRED FIELDS
|
|
674
|
+
to: toRecipients,
|
|
675
|
+
body: text
|
|
676
|
+
};
|
|
677
|
+
if (emailMeta == null ? void 0 : emailMeta.subject) {
|
|
678
|
+
payload.subject = emailMeta.subject;
|
|
679
|
+
}
|
|
680
|
+
if ((emailMeta == null ? void 0 : emailMeta.cc) && emailMeta.cc.length > 0) {
|
|
681
|
+
payload.cc = emailMeta.cc;
|
|
682
|
+
}
|
|
683
|
+
if ((emailMeta == null ? void 0 : emailMeta.bcc) && emailMeta.bcc.length > 0) {
|
|
684
|
+
payload.bcc = emailMeta.bcc;
|
|
685
|
+
}
|
|
686
|
+
if ((emailMeta == null ? void 0 : emailMeta.attachments) && emailMeta.attachments.length > 0) {
|
|
687
|
+
payload.attachments = emailMeta.attachments;
|
|
688
|
+
}
|
|
689
|
+
if (emailMeta == null ? void 0 : emailMeta.replyTo) {
|
|
690
|
+
payload.reply_to = emailMeta.replyTo;
|
|
691
|
+
}
|
|
692
|
+
if (emailMeta == null ? void 0 : emailMeta.tracking) {
|
|
693
|
+
const trackingOpts = {};
|
|
694
|
+
if (emailMeta.tracking.opens !== void 0) {
|
|
695
|
+
trackingOpts.opens = emailMeta.tracking.opens;
|
|
696
|
+
}
|
|
697
|
+
if (emailMeta.tracking.links !== void 0) {
|
|
698
|
+
trackingOpts.links = emailMeta.tracking.links;
|
|
699
|
+
}
|
|
700
|
+
if (emailMeta.tracking.label) {
|
|
701
|
+
trackingOpts.label = emailMeta.tracking.label;
|
|
702
|
+
}
|
|
703
|
+
if (emailMeta.tracking.custom_domain) {
|
|
704
|
+
trackingOpts.custom_domain = emailMeta.tracking.custom_domain;
|
|
705
|
+
}
|
|
706
|
+
if (Object.keys(trackingOpts).length > 0) {
|
|
707
|
+
payload.tracking_options = trackingOpts;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
console.log("Email payload:", payload);
|
|
711
|
+
try {
|
|
712
|
+
const res = await getAxios().post(path, payload);
|
|
713
|
+
console.log("Email response:", res.data);
|
|
714
|
+
const data = res.data;
|
|
715
|
+
if ((_a = data.mail_sent_data) == null ? void 0 : _a.provider_id) {
|
|
716
|
+
emailChatId = data.mail_sent_data.provider_id;
|
|
717
|
+
console.log("Email provider_id extracted:", emailChatId);
|
|
718
|
+
} else {
|
|
719
|
+
console.warn("No provider_id in email response:", data);
|
|
720
|
+
}
|
|
721
|
+
return { status: "fulfilled", value: data };
|
|
722
|
+
} catch (error) {
|
|
723
|
+
return { status: "rejected", reason: error };
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
} else {
|
|
728
|
+
failed_source.push({ source: "email", message: "Missing connector configuration" });
|
|
729
|
+
}
|
|
730
|
+
}
|
|
614
731
|
if (tasks.length > 0) {
|
|
615
732
|
const results = await Promise.allSettled(tasks.map((t) => t.run()));
|
|
616
733
|
results.forEach((result, idx) => {
|
|
@@ -628,6 +745,16 @@ function createMessagesService(models, hooks = {}) {
|
|
|
628
745
|
}
|
|
629
746
|
});
|
|
630
747
|
}
|
|
748
|
+
let enhancedMetadata = args.metadata ? { ...args.metadata } : void 0;
|
|
749
|
+
if (emailChatId && success_source.includes("email")) {
|
|
750
|
+
if (!enhancedMetadata) {
|
|
751
|
+
enhancedMetadata = { email: { providerId: emailChatId } };
|
|
752
|
+
} else if (!enhancedMetadata.email) {
|
|
753
|
+
enhancedMetadata.email = { providerId: emailChatId };
|
|
754
|
+
} else {
|
|
755
|
+
enhancedMetadata.email.providerId = emailChatId;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
631
758
|
const message = new Message({
|
|
632
759
|
organizationId,
|
|
633
760
|
conversationId,
|
|
@@ -637,7 +764,8 @@ function createMessagesService(models, hooks = {}) {
|
|
|
637
764
|
kind,
|
|
638
765
|
source: success_source,
|
|
639
766
|
parentMessageId,
|
|
640
|
-
rootThreadId: rootThreadId || parentMessageId
|
|
767
|
+
rootThreadId: rootThreadId || parentMessageId,
|
|
768
|
+
metadata: enhancedMetadata
|
|
641
769
|
});
|
|
642
770
|
await message.save();
|
|
643
771
|
const conversationUpdate = {
|
|
@@ -648,8 +776,10 @@ function createMessagesService(models, hooks = {}) {
|
|
|
648
776
|
};
|
|
649
777
|
if (lastLinkedInId) conversationUpdate.lastLinkedInId = lastLinkedInId;
|
|
650
778
|
if (lastWhatsAppId) conversationUpdate.lastWhatsAppId = lastWhatsAppId;
|
|
779
|
+
if (lastEmailId) conversationUpdate.lastEmailId = lastEmailId;
|
|
651
780
|
if (linkedinChatId) conversationUpdate.linkedinChatId = linkedinChatId;
|
|
652
781
|
if (whatsappChatId) conversationUpdate.whatsappChatId = whatsappChatId;
|
|
782
|
+
if (emailChatId) conversationUpdate.emailChatId = emailChatId;
|
|
653
783
|
await Conversation.findByIdAndUpdate(conversationId, {
|
|
654
784
|
$set: conversationUpdate
|
|
655
785
|
});
|
|
@@ -659,6 +789,197 @@ function createMessagesService(models, hooks = {}) {
|
|
|
659
789
|
message.failed_source = failed_source;
|
|
660
790
|
return message;
|
|
661
791
|
},
|
|
792
|
+
/**
|
|
793
|
+
* Receive a new message by whatsappChatId
|
|
794
|
+
*/
|
|
795
|
+
async receiveWhatsappMessage(args) {
|
|
796
|
+
const { whatsappChatId, message, messageId, senderName } = args;
|
|
797
|
+
const conversation = await Conversation.findOne({ whatsappChatId }).lean();
|
|
798
|
+
if (!conversation) {
|
|
799
|
+
throw new Error("Conversation not found for given whatsappChatId");
|
|
800
|
+
}
|
|
801
|
+
if (messageId) {
|
|
802
|
+
const existingMessage = await Message.findOne({
|
|
803
|
+
organizationId: conversation.organizationId,
|
|
804
|
+
conversationId: String(conversation._id),
|
|
805
|
+
externalMessageId: messageId
|
|
806
|
+
}).lean();
|
|
807
|
+
if (existingMessage) {
|
|
808
|
+
console.log(`Duplicate WhatsApp message detected: ${messageId}`);
|
|
809
|
+
return existingMessage;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
const applicationParticipant = conversation.participants.find(
|
|
813
|
+
(p) => p.entityModel === "Application"
|
|
814
|
+
);
|
|
815
|
+
if (!applicationParticipant) {
|
|
816
|
+
throw new Error("Application participant not found in conversation");
|
|
817
|
+
}
|
|
818
|
+
const orgId = conversation.organizationId;
|
|
819
|
+
const senderModel = "Application";
|
|
820
|
+
const senderId = applicationParticipant.entityId;
|
|
821
|
+
const msgDoc = new Message({
|
|
822
|
+
organizationId: orgId,
|
|
823
|
+
conversationId: String(conversation._id),
|
|
824
|
+
senderModel,
|
|
825
|
+
senderId,
|
|
826
|
+
text: message,
|
|
827
|
+
kind: "text",
|
|
828
|
+
source: ["whatsapp"],
|
|
829
|
+
externalMessageId: messageId,
|
|
830
|
+
parentMessageId: void 0,
|
|
831
|
+
rootThreadId: void 0,
|
|
832
|
+
metadata: senderName ? {
|
|
833
|
+
whatsapp: {
|
|
834
|
+
senderName
|
|
835
|
+
}
|
|
836
|
+
} : void 0
|
|
837
|
+
});
|
|
838
|
+
await msgDoc.save();
|
|
839
|
+
await Conversation.findByIdAndUpdate(conversation._id, {
|
|
840
|
+
$set: {
|
|
841
|
+
lastMessageAt: msgDoc.createdAt,
|
|
842
|
+
lastMessagePreview: message.substring(0, 100),
|
|
843
|
+
lastMessageSenderId: senderId,
|
|
844
|
+
lastMessageSenderModel: senderModel
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
if (onMessageCreated) onMessageCreated(msgDoc);
|
|
848
|
+
return msgDoc;
|
|
849
|
+
},
|
|
850
|
+
/**
|
|
851
|
+
* Receive a new message by linkedinChatId
|
|
852
|
+
*/
|
|
853
|
+
async receiveLinkedinMessage(args) {
|
|
854
|
+
const { linkedinChatId, message, messageId, senderName, senderProfileUrl } = args;
|
|
855
|
+
const conversation = await Conversation.findOne({ linkedinChatId }).lean();
|
|
856
|
+
if (!conversation) {
|
|
857
|
+
throw new Error("Conversation not found for given linkedinChatId");
|
|
858
|
+
}
|
|
859
|
+
if (messageId) {
|
|
860
|
+
const existingMessage = await Message.findOne({
|
|
861
|
+
organizationId: conversation.organizationId,
|
|
862
|
+
conversationId: String(conversation._id),
|
|
863
|
+
externalMessageId: messageId
|
|
864
|
+
}).lean();
|
|
865
|
+
if (existingMessage) {
|
|
866
|
+
console.log(`Duplicate LinkedIn message detected: ${messageId}`);
|
|
867
|
+
return existingMessage;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
const applicationParticipant = conversation.participants.find(
|
|
871
|
+
(p) => p.entityModel === "Application"
|
|
872
|
+
);
|
|
873
|
+
if (!applicationParticipant) {
|
|
874
|
+
throw new Error("Application participant not found in conversation");
|
|
875
|
+
}
|
|
876
|
+
const orgId = conversation.organizationId;
|
|
877
|
+
const senderModel = "Application";
|
|
878
|
+
const senderId = applicationParticipant.entityId;
|
|
879
|
+
const msgDoc = new Message({
|
|
880
|
+
organizationId: orgId,
|
|
881
|
+
conversationId: String(conversation._id),
|
|
882
|
+
senderModel,
|
|
883
|
+
senderId,
|
|
884
|
+
text: message,
|
|
885
|
+
kind: "text",
|
|
886
|
+
source: ["linkedin"],
|
|
887
|
+
externalMessageId: messageId,
|
|
888
|
+
parentMessageId: void 0,
|
|
889
|
+
rootThreadId: void 0,
|
|
890
|
+
metadata: senderName || senderProfileUrl ? {
|
|
891
|
+
linkedin: {
|
|
892
|
+
senderName,
|
|
893
|
+
senderProfileUrl
|
|
894
|
+
}
|
|
895
|
+
} : void 0
|
|
896
|
+
});
|
|
897
|
+
await msgDoc.save();
|
|
898
|
+
await Conversation.findByIdAndUpdate(conversation._id, {
|
|
899
|
+
$set: {
|
|
900
|
+
lastMessageAt: msgDoc.createdAt,
|
|
901
|
+
lastMessagePreview: message.substring(0, 100),
|
|
902
|
+
lastMessageSenderId: senderId,
|
|
903
|
+
lastMessageSenderModel: senderModel
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
if (onMessageCreated) onMessageCreated(msgDoc);
|
|
907
|
+
return msgDoc;
|
|
908
|
+
},
|
|
909
|
+
/**
|
|
910
|
+
* Receive a new message by emailChatId with full email metadata
|
|
911
|
+
*/
|
|
912
|
+
async receiveEmailMessage(args) {
|
|
913
|
+
const {
|
|
914
|
+
emailChatId,
|
|
915
|
+
message,
|
|
916
|
+
subject,
|
|
917
|
+
senderEmail,
|
|
918
|
+
senderName,
|
|
919
|
+
providerId,
|
|
920
|
+
messageId,
|
|
921
|
+
attachments
|
|
922
|
+
} = args;
|
|
923
|
+
const conversation = await Conversation.findOne({ emailChatId }).lean();
|
|
924
|
+
if (!conversation) {
|
|
925
|
+
throw new Error("Conversation not found for given emailChatId");
|
|
926
|
+
}
|
|
927
|
+
if (messageId) {
|
|
928
|
+
const existingMessage = await Message.findOne({
|
|
929
|
+
organizationId: conversation.organizationId,
|
|
930
|
+
conversationId: String(conversation._id),
|
|
931
|
+
externalMessageId: messageId
|
|
932
|
+
}).lean();
|
|
933
|
+
if (existingMessage) {
|
|
934
|
+
console.log(`Duplicate email message detected: ${messageId}`);
|
|
935
|
+
return existingMessage;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
const applicationParticipant = conversation.participants.find(
|
|
939
|
+
(p) => p.entityModel === "Application"
|
|
940
|
+
);
|
|
941
|
+
if (!applicationParticipant) {
|
|
942
|
+
throw new Error("Application participant not found in conversation");
|
|
943
|
+
}
|
|
944
|
+
const orgId = conversation.organizationId;
|
|
945
|
+
const senderModel = "Application";
|
|
946
|
+
const senderId = applicationParticipant.entityId;
|
|
947
|
+
const msgDoc = new Message({
|
|
948
|
+
organizationId: orgId,
|
|
949
|
+
conversationId: String(conversation._id),
|
|
950
|
+
senderModel,
|
|
951
|
+
senderId,
|
|
952
|
+
text: message,
|
|
953
|
+
kind: "text",
|
|
954
|
+
source: ["email"],
|
|
955
|
+
externalMessageId: messageId,
|
|
956
|
+
parentMessageId: void 0,
|
|
957
|
+
rootThreadId: void 0,
|
|
958
|
+
metadata: {
|
|
959
|
+
email: {
|
|
960
|
+
subject,
|
|
961
|
+
from: senderEmail ? {
|
|
962
|
+
display_name: senderName || senderEmail,
|
|
963
|
+
identifier: senderEmail
|
|
964
|
+
} : void 0,
|
|
965
|
+
providerId,
|
|
966
|
+
messageId,
|
|
967
|
+
attachments
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
});
|
|
971
|
+
await msgDoc.save();
|
|
972
|
+
await Conversation.findByIdAndUpdate(conversation._id, {
|
|
973
|
+
$set: {
|
|
974
|
+
lastMessageAt: msgDoc.createdAt,
|
|
975
|
+
lastMessagePreview: message.substring(0, 100),
|
|
976
|
+
lastMessageSenderId: senderId,
|
|
977
|
+
lastMessageSenderModel: senderModel
|
|
978
|
+
}
|
|
979
|
+
});
|
|
980
|
+
if (onMessageCreated) onMessageCreated(msgDoc);
|
|
981
|
+
return msgDoc;
|
|
982
|
+
},
|
|
662
983
|
/**
|
|
663
984
|
* List messages for a conversation with pagination
|
|
664
985
|
* @param args Message listing arguments
|