wexa-chat 0.2.0 → 0.2.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 +32 -5
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +40 -17
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +36 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -121,7 +121,7 @@ export type InitOptions = {
|
|
|
121
121
|
- `searchByParticipantPair({ organizationId, a, b })`
|
|
122
122
|
|
|
123
123
|
- Messages
|
|
124
|
-
- `sendMessage({ organizationId, conversationId, senderModel, senderId, text, source, kind?, parentMessageId?, rootThreadId? })`
|
|
124
|
+
- `sendMessage({ organizationId, conversationId, senderModel, senderId, text, source, kind?, parentMessageId?, rootThreadId?, connectorIds? })`
|
|
125
125
|
- `listMessages({ organizationId, conversationId, limit?, cursor? })`
|
|
126
126
|
- `listMessagesWithSenders({ organizationId, conversationId, limit?, cursor?, populateSenders: true, populateOptions? })`
|
|
127
127
|
- `markRead({ organizationId, conversationId, participantModel, participantId, messageId })`
|
|
@@ -209,9 +209,9 @@ The method returns messages sorted by creation time (newest first), making it id
|
|
|
209
209
|
|
|
210
210
|
The type `MessageWithSender` is exported from the package for proper TypeScript support.
|
|
211
211
|
|
|
212
|
-
###
|
|
212
|
+
### Sending messages and connectors
|
|
213
213
|
|
|
214
|
-
Messages
|
|
214
|
+
Messages include a required `source` field (array) to indicate one or more origins for a message. Valid values are enforced by an enum of strings:
|
|
215
215
|
|
|
216
216
|
```ts
|
|
217
217
|
export const sourceType = {
|
|
@@ -224,8 +224,35 @@ export const sourceType = {
|
|
|
224
224
|
export type SourceType = (typeof sourceType)[keyof typeof sourceType];
|
|
225
225
|
```
|
|
226
226
|
|
|
227
|
-
When sending a message via `sendMessage`, you must provide `source: SourceType[]`.
|
|
228
|
-
|
|
227
|
+
When sending a message via `sendMessage`, you must provide `source: SourceType[]`. For internal server‑generated messages, use `[sourceType.CORE]`.
|
|
228
|
+
|
|
229
|
+
If you include external connectors in the `source` (e.g., `linkedin`, `whatsapp`), you can optionally pass `connectorIds` to perform the external send and to persist identifiers for quick follow‑ups:
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
await chat.services.messages.sendMessage({
|
|
233
|
+
organizationId: 'org-123',
|
|
234
|
+
conversationId: 'conv-456',
|
|
235
|
+
senderModel: 'User',
|
|
236
|
+
senderId: 'user-1',
|
|
237
|
+
text: 'Hello from Wexa',
|
|
238
|
+
source: [sourceType.CORE, sourceType.LINKEDIN, sourceType.WHATSAPP],
|
|
239
|
+
connectorIds: {
|
|
240
|
+
linkedin: { connectorId: 'ln-connector-1', contactId: 'https://linkedin.com/in/someone' },
|
|
241
|
+
whatsapp: { connectorId: 'wa-connector-1', contactId: '+1 234 567 8901' },
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Under the hood (`src/services/messages.service.ts`):
|
|
247
|
+
|
|
248
|
+
- LinkedIn: posts to the LinkedIn connector with `{ linkedin_url, text }`.
|
|
249
|
+
- WhatsApp: posts to the WhatsApp connector with `{ phone_numbers, text }` (phone is sanitized to remove spaces and a leading `+`).
|
|
250
|
+
- Each external task runs concurrently. A successful task's `source` remains in the stored message. Failures are collected into a `failed_source` array attached to the returned message instance at runtime: `message.failed_source: Array<{ source: string; message: string }>`.
|
|
251
|
+
- The conversation document is updated with last message metadata and, when applicable, with connector identifiers:
|
|
252
|
+
- `lastLinkedInId: string` — set from `connectorIds.linkedin.contactId` when `source` includes `linkedin`.
|
|
253
|
+
- `lastWhatsAppId: string` — set from a sanitized `connectorIds.whatsapp.contactId` when `source` includes `whatsapp`.
|
|
254
|
+
|
|
255
|
+
This allows subsequent UI actions to prefill the last known contact handles for LinkedIn and WhatsApp.
|
|
229
256
|
|
|
230
257
|
## Models
|
|
231
258
|
|
package/dist/index.d.cts
CHANGED
|
@@ -147,7 +147,7 @@ interface IMessage extends Document {
|
|
|
147
147
|
/**
|
|
148
148
|
* Create (or retrieve) Message model safely
|
|
149
149
|
*/
|
|
150
|
-
declare function createMessageModel(options: InitOptions, conn
|
|
150
|
+
declare function createMessageModel(options: InitOptions, conn: Connection): Model<IMessage>;
|
|
151
151
|
|
|
152
152
|
declare const modelRegistry: Map<string, mongoose.Model<any, {}, {}, {}, any, any>>;
|
|
153
153
|
/**
|
|
@@ -259,6 +259,8 @@ interface IConversation extends Document {
|
|
|
259
259
|
lastMessagePreview?: string;
|
|
260
260
|
lastMessageSenderId?: string;
|
|
261
261
|
lastMessageSenderModel?: string;
|
|
262
|
+
lastLinkedInId?: string;
|
|
263
|
+
lastWhatsAppId?: string;
|
|
262
264
|
metadata?: Record<string, any>;
|
|
263
265
|
createdAt: Date;
|
|
264
266
|
updatedAt: Date;
|
|
@@ -266,7 +268,7 @@ interface IConversation extends Document {
|
|
|
266
268
|
/**
|
|
267
269
|
* Create (or retrieve) Conversation model safely
|
|
268
270
|
*/
|
|
269
|
-
declare function createConversationModel(options: InitOptions, conn
|
|
271
|
+
declare function createConversationModel(options: InitOptions, conn: Connection): Model<IConversation>;
|
|
270
272
|
|
|
271
273
|
/**
|
|
272
274
|
* Pagination result interface
|
package/dist/index.d.ts
CHANGED
|
@@ -147,7 +147,7 @@ interface IMessage extends Document {
|
|
|
147
147
|
/**
|
|
148
148
|
* Create (or retrieve) Message model safely
|
|
149
149
|
*/
|
|
150
|
-
declare function createMessageModel(options: InitOptions, conn
|
|
150
|
+
declare function createMessageModel(options: InitOptions, conn: Connection): Model<IMessage>;
|
|
151
151
|
|
|
152
152
|
declare const modelRegistry: Map<string, mongoose.Model<any, {}, {}, {}, any, any>>;
|
|
153
153
|
/**
|
|
@@ -259,6 +259,8 @@ interface IConversation extends Document {
|
|
|
259
259
|
lastMessagePreview?: string;
|
|
260
260
|
lastMessageSenderId?: string;
|
|
261
261
|
lastMessageSenderModel?: string;
|
|
262
|
+
lastLinkedInId?: string;
|
|
263
|
+
lastWhatsAppId?: string;
|
|
262
264
|
metadata?: Record<string, any>;
|
|
263
265
|
createdAt: Date;
|
|
264
266
|
updatedAt: Date;
|
|
@@ -266,7 +268,7 @@ interface IConversation extends Document {
|
|
|
266
268
|
/**
|
|
267
269
|
* Create (or retrieve) Conversation model safely
|
|
268
270
|
*/
|
|
269
|
-
declare function createConversationModel(options: InitOptions, conn
|
|
271
|
+
declare function createConversationModel(options: InitOptions, conn: Connection): Model<IConversation>;
|
|
270
272
|
|
|
271
273
|
/**
|
|
272
274
|
* Pagination result interface
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var mongoose = require('mongoose');
|
|
4
3
|
var axios = require('axios');
|
|
5
4
|
var Redis = require('ioredis');
|
|
6
5
|
var ws = require('ws');
|
|
@@ -8,13 +7,12 @@ var url = require('url');
|
|
|
8
7
|
|
|
9
8
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
9
|
|
|
11
|
-
var mongoose__default = /*#__PURE__*/_interopDefault(mongoose);
|
|
12
10
|
var axios__default = /*#__PURE__*/_interopDefault(axios);
|
|
13
11
|
var Redis__default = /*#__PURE__*/_interopDefault(Redis);
|
|
14
12
|
var url__default = /*#__PURE__*/_interopDefault(url);
|
|
15
13
|
|
|
16
14
|
// src/models/Conversation.model.ts
|
|
17
|
-
function createConversationModel(options, conn
|
|
15
|
+
function createConversationModel(options, conn) {
|
|
18
16
|
const connectionId = conn.id || conn.name || "default";
|
|
19
17
|
const registryKey = `${connectionId}:Conversation`;
|
|
20
18
|
if (modelRegistry.has(registryKey)) {
|
|
@@ -26,7 +24,8 @@ function createConversationModel(options, conn = mongoose__default.default.conne
|
|
|
26
24
|
modelRegistry.set(registryKey, existingModel);
|
|
27
25
|
return existingModel;
|
|
28
26
|
}
|
|
29
|
-
const
|
|
27
|
+
const Schema = conn.base.Schema;
|
|
28
|
+
const ParticipantSchema = new Schema(
|
|
30
29
|
{
|
|
31
30
|
entityModel: {
|
|
32
31
|
type: String,
|
|
@@ -45,7 +44,7 @@ function createConversationModel(options, conn = mongoose__default.default.conne
|
|
|
45
44
|
},
|
|
46
45
|
{ _id: false }
|
|
47
46
|
);
|
|
48
|
-
const ConversationSchema = new
|
|
47
|
+
const ConversationSchema = new Schema(
|
|
49
48
|
{
|
|
50
49
|
organizationId: {
|
|
51
50
|
type: String,
|
|
@@ -71,7 +70,9 @@ function createConversationModel(options, conn = mongoose__default.default.conne
|
|
|
71
70
|
lastMessagePreview: String,
|
|
72
71
|
lastMessageSenderId: String,
|
|
73
72
|
lastMessageSenderModel: String,
|
|
74
|
-
|
|
73
|
+
lastLinkedInId: String,
|
|
74
|
+
lastWhatsAppId: String,
|
|
75
|
+
metadata: Schema.Types.Mixed
|
|
75
76
|
},
|
|
76
77
|
{ timestamps: true }
|
|
77
78
|
);
|
|
@@ -79,13 +80,15 @@ function createConversationModel(options, conn = mongoose__default.default.conne
|
|
|
79
80
|
modelRegistry.set(registryKey, model);
|
|
80
81
|
return model;
|
|
81
82
|
}
|
|
83
|
+
|
|
84
|
+
// src/models/Message.model.ts
|
|
82
85
|
var sourceType = {
|
|
83
86
|
LINKEDIN: "linkedin",
|
|
84
87
|
WHATSAPP: "whatsapp",
|
|
85
88
|
EMAIL: "email",
|
|
86
89
|
CORE: "core"
|
|
87
90
|
};
|
|
88
|
-
function createMessageModel(options, conn
|
|
91
|
+
function createMessageModel(options, conn) {
|
|
89
92
|
const connectionId = conn.id || conn.name || "default";
|
|
90
93
|
const registryKey = `${connectionId}:Message`;
|
|
91
94
|
if (modelRegistry.has(registryKey)) {
|
|
@@ -97,7 +100,8 @@ function createMessageModel(options, conn = mongoose__default.default.connection
|
|
|
97
100
|
modelRegistry.set(registryKey, existingModel);
|
|
98
101
|
return existingModel;
|
|
99
102
|
}
|
|
100
|
-
const
|
|
103
|
+
const Schema = conn.base.Schema;
|
|
104
|
+
const MessageSchema = new Schema(
|
|
101
105
|
{
|
|
102
106
|
organizationId: {
|
|
103
107
|
type: String,
|
|
@@ -305,13 +309,23 @@ function createConversationsService(models) {
|
|
|
305
309
|
* Create a new conversation or find existing one between participants
|
|
306
310
|
*/
|
|
307
311
|
async createOrFindConversation(args) {
|
|
312
|
+
console.log("Creating or finding conversation for users:", {
|
|
313
|
+
args
|
|
314
|
+
});
|
|
308
315
|
const { organizationId, a, b } = args;
|
|
316
|
+
console.log("Creating or finding conversation for users:", {
|
|
317
|
+
organizationId,
|
|
318
|
+
a,
|
|
319
|
+
b
|
|
320
|
+
});
|
|
309
321
|
try {
|
|
322
|
+
console.log("asdfa");
|
|
310
323
|
const existingConversation = await searchByParticipantPair(Conversation, {
|
|
311
324
|
organizationId,
|
|
312
325
|
a,
|
|
313
326
|
b
|
|
314
327
|
});
|
|
328
|
+
console.log("Existing conversation:", existingConversation);
|
|
315
329
|
if (existingConversation) {
|
|
316
330
|
return existingConversation;
|
|
317
331
|
}
|
|
@@ -334,7 +348,9 @@ function createConversationsService(models) {
|
|
|
334
348
|
lastMessageAt: /* @__PURE__ */ new Date()
|
|
335
349
|
// Set initial lastMessageAt
|
|
336
350
|
});
|
|
351
|
+
console.log("New conversation:", newConversation);
|
|
337
352
|
await newConversation.save();
|
|
353
|
+
console.log("Saved conversation:", newConversation);
|
|
338
354
|
return newConversation;
|
|
339
355
|
} catch (error) {
|
|
340
356
|
console.error("Error in createOrFindConversation:", error);
|
|
@@ -535,8 +551,11 @@ function createMessagesService(models, hooks = {}) {
|
|
|
535
551
|
const success_source = ["core"];
|
|
536
552
|
const failed_source = [];
|
|
537
553
|
const tasks = [];
|
|
554
|
+
let lastLinkedInId;
|
|
555
|
+
let lastWhatsAppId;
|
|
538
556
|
if (Array.isArray(source) && source.includes("linkedin")) {
|
|
539
557
|
const LinkedinConnector = connectorIds && connectorIds.linkedin;
|
|
558
|
+
lastLinkedInId = LinkedinConnector == null ? void 0 : LinkedinConnector.connectorId;
|
|
540
559
|
if (LinkedinConnector) {
|
|
541
560
|
tasks.push({
|
|
542
561
|
name: "linkedin",
|
|
@@ -554,6 +573,7 @@ function createMessagesService(models, hooks = {}) {
|
|
|
554
573
|
}
|
|
555
574
|
if (Array.isArray(source) && source.includes("whatsapp")) {
|
|
556
575
|
const whatsappConnector = connectorIds && connectorIds.whatsapp;
|
|
576
|
+
lastWhatsAppId = whatsappConnector == null ? void 0 : whatsappConnector.connectorId;
|
|
557
577
|
if (whatsappConnector) {
|
|
558
578
|
tasks.push({
|
|
559
579
|
name: "whatsapp",
|
|
@@ -599,13 +619,16 @@ function createMessagesService(models, hooks = {}) {
|
|
|
599
619
|
rootThreadId: rootThreadId || parentMessageId
|
|
600
620
|
});
|
|
601
621
|
await message.save();
|
|
622
|
+
const conversationUpdate = {
|
|
623
|
+
lastMessageAt: message.createdAt,
|
|
624
|
+
lastMessagePreview: text.substring(0, 100),
|
|
625
|
+
lastMessageSenderId: senderId,
|
|
626
|
+
lastMessageSenderModel: senderModel
|
|
627
|
+
};
|
|
628
|
+
if (lastLinkedInId) conversationUpdate.lastLinkedInId = lastLinkedInId;
|
|
629
|
+
if (lastWhatsAppId) conversationUpdate.lastWhatsAppId = lastWhatsAppId;
|
|
602
630
|
await Conversation.findByIdAndUpdate(conversationId, {
|
|
603
|
-
$set:
|
|
604
|
-
lastMessageAt: message.createdAt,
|
|
605
|
-
lastMessagePreview: text.substring(0, 100),
|
|
606
|
-
lastMessageSenderId: senderId,
|
|
607
|
-
lastMessageSenderModel: senderModel
|
|
608
|
-
}
|
|
631
|
+
$set: conversationUpdate
|
|
609
632
|
});
|
|
610
633
|
if (onMessageCreated) {
|
|
611
634
|
onMessageCreated(message);
|
|
@@ -687,18 +710,18 @@ function createMessagesService(models, hooks = {}) {
|
|
|
687
710
|
}
|
|
688
711
|
sendersByModel[senderModel].add(senderId);
|
|
689
712
|
});
|
|
690
|
-
const
|
|
713
|
+
const mongoose = Message.base;
|
|
691
714
|
const senders = {};
|
|
692
715
|
await Promise.all(
|
|
693
716
|
Object.entries(sendersByModel).map(async ([senderModel, senderIdsSet]) => {
|
|
694
717
|
const modelName = modelMapping[senderModel] || senderModel;
|
|
695
718
|
const senderIds = Array.from(senderIdsSet);
|
|
696
|
-
if (!
|
|
719
|
+
if (!mongoose.modelNames().includes(modelName)) {
|
|
697
720
|
console.warn(`Model ${modelName} not found for sender population`);
|
|
698
721
|
return;
|
|
699
722
|
}
|
|
700
723
|
try {
|
|
701
|
-
const SenderModel =
|
|
724
|
+
const SenderModel = mongoose.model(modelName);
|
|
702
725
|
const modelSenders = await SenderModel.find({ _id: { $in: senderIds } }).select(fields).lean().exec();
|
|
703
726
|
senders[senderModel] = {};
|
|
704
727
|
modelSenders.forEach((sender) => {
|