wexa-chat 0.3.0 → 0.3.2

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 CHANGED
@@ -312,6 +312,73 @@ await chat.services.messages.sendMessage({
312
312
  });
313
313
  ```
314
314
 
315
+ ##### Email Threading Explained
316
+
317
+ Email threading works by maintaining a stable `thread_id` across all messages in the same conversation:
318
+
319
+ **How it works:**
320
+
321
+ 1. **First Email (New Thread)**
322
+ - Recruiter sends first email → Unipile returns `provider_id: "A"`
323
+ - Conversation stores `emailChatId = "A"` (the thread identifier)
324
+ - Message stores `metadata.email.providerId = "A"`
325
+
326
+ 2. **Candidate Replies**
327
+ - Webhook arrives with `thread_id: "A"` and new `provider_id: "B"`
328
+ - System finds conversation by `emailChatId = "A"`
329
+ - Message stores `metadata.email.providerId = "B"`
330
+
331
+ 3. **Recruiter Replies (Threading)**
332
+ - To continue the thread, pass `replyTo: "B"` (candidate's provider_id)
333
+ - Unipile returns new `provider_id: "C"`
334
+ - `emailChatId` remains `"A"` (NOT updated for replies)
335
+ - Message stores `metadata.email.providerId = "C"`
336
+
337
+ **Key Points:**
338
+ - `emailChatId` = Set ONCE on first email, used to find conversation
339
+ - `replyTo` = Previous message's `provider_id`, maintains email thread
340
+ - `providerId` = Current message's ID, stored for future replies
341
+
342
+ **Example: Reply to candidate's email**
343
+
344
+ ```ts
345
+ // 1. Get the last message from candidate
346
+ const messages = await chat.services.messages.listMessages({
347
+ organizationId: 'org-123',
348
+ conversationId: 'conv-456',
349
+ limit: 10,
350
+ });
351
+
352
+ const lastCandidateMsg = messages.items
353
+ .find(m => m.senderModel === 'Application');
354
+
355
+ // 2. Extract their providerId for threading
356
+ const replyToId = lastCandidateMsg?.metadata?.email?.providerId;
357
+
358
+ // 3. Send reply with replyTo
359
+ await chat.services.messages.sendMessage({
360
+ organizationId: 'org-123',
361
+ conversationId: 'conv-456',
362
+ senderModel: 'User',
363
+ senderId: 'user-1',
364
+ text: 'Great to hear from you! Let me provide more details...',
365
+ source: [sourceType.EMAIL],
366
+ connectorIds: {
367
+ email: { connectorId: 'em-connector-1', contactId: 'john@candidate.com' },
368
+ },
369
+ metadata: {
370
+ email: {
371
+ subject: 'Re: Job Opportunity',
372
+ recipientName: 'John Doe',
373
+ replyTo: replyToId, // ← This keeps emails in the same thread
374
+ }
375
+ }
376
+ });
377
+ ```
378
+
379
+ **Without `replyTo`:** Each email creates a new thread (separate email chains)
380
+ **With `replyTo`:** All emails stay in one thread (Gmail conversation view)
381
+
315
382
  #### Multi-Platform Messages
316
383
 
317
384
  ```ts
package/dist/index.js CHANGED
@@ -566,6 +566,7 @@ function createMessagesService(models, hooks = {}) {
566
566
  * @returns The created message
567
567
  */
568
568
  async sendMessage(args) {
569
+ var _a, _b;
569
570
  const {
570
571
  organizationId,
571
572
  conversationId,
@@ -659,7 +660,7 @@ function createMessagesService(models, hooks = {}) {
659
660
  tasks.push({
660
661
  name: "email",
661
662
  run: async () => {
662
- var _a;
663
+ var _a2;
663
664
  const path = paths.email.sendEmail(String(emailConnector.connectorId));
664
665
  const primaryRecipient = {
665
666
  display_name: (emailMeta == null ? void 0 : emailMeta.recipientName) || "Recipient",
@@ -712,7 +713,7 @@ function createMessagesService(models, hooks = {}) {
712
713
  const res = await getAxios().post(path, payload);
713
714
  console.log("Email response:", res.data);
714
715
  const data = res.data;
715
- if ((_a = data.mail_sent_data) == null ? void 0 : _a.provider_id) {
716
+ if ((_a2 = data.mail_sent_data) == null ? void 0 : _a2.provider_id) {
716
717
  emailChatId = data.mail_sent_data.provider_id;
717
718
  console.log("Email provider_id extracted:", emailChatId);
718
719
  } else {
@@ -731,12 +732,12 @@ function createMessagesService(models, hooks = {}) {
731
732
  if (tasks.length > 0) {
732
733
  const results = await Promise.allSettled(tasks.map((t) => t.run()));
733
734
  results.forEach((result, idx) => {
734
- var _a, _b, _c, _d, _e, _f;
735
+ var _a2, _b2, _c, _d, _e, _f;
735
736
  const name = tasks[idx].name;
736
737
  if (result.status === "fulfilled") {
737
738
  success_source.push(name);
738
739
  } else {
739
- const error = ((_c = (_b = (_a = result.reason) == null ? void 0 : _a.response) == null ? void 0 : _b.data) == null ? void 0 : _c.detail) || ((_e = (_d = result.reason) == null ? void 0 : _d.response) == null ? void 0 : _e.data) || ((_f = result.reason) == null ? void 0 : _f.message) || "Unknown error";
740
+ const error = ((_c = (_b2 = (_a2 = result.reason) == null ? void 0 : _a2.response) == null ? void 0 : _b2.data) == null ? void 0 : _c.detail) || ((_e = (_d = result.reason) == null ? void 0 : _d.response) == null ? void 0 : _e.data) || ((_f = result.reason) == null ? void 0 : _f.message) || "Unknown error";
740
741
  failed_source.push({
741
742
  source: name,
742
743
  message: typeof error === "string" ? error : JSON.stringify(error)
@@ -779,7 +780,9 @@ function createMessagesService(models, hooks = {}) {
779
780
  if (lastEmailId) conversationUpdate.lastEmailId = lastEmailId;
780
781
  if (linkedinChatId) conversationUpdate.linkedinChatId = linkedinChatId;
781
782
  if (whatsappChatId) conversationUpdate.whatsappChatId = whatsappChatId;
782
- if (emailChatId) conversationUpdate.emailChatId = emailChatId;
783
+ if (emailChatId && !((_b = (_a = args.metadata) == null ? void 0 : _a.email) == null ? void 0 : _b.replyTo)) {
784
+ conversationUpdate.emailChatId = emailChatId;
785
+ }
783
786
  await Conversation.findByIdAndUpdate(conversationId, {
784
787
  $set: conversationUpdate
785
788
  });
@@ -852,7 +855,7 @@ function createMessagesService(models, hooks = {}) {
852
855
  */
853
856
  async receiveLinkedinMessage(args) {
854
857
  const { linkedinChatId, message, messageId, senderName, senderProfileUrl } = args;
855
- const conversation = await Conversation.findOne({ linkedinChatId }).lean();
858
+ const conversation = await Conversation.findOne({ linkedinChatId }).sort({ lastMessageAt: -1 }).lean();
856
859
  if (!conversation) {
857
860
  throw new Error("Conversation not found for given linkedinChatId");
858
861
  }