@theotherwillembotha/node-red-whatsapp 0.0.55 → 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.
@@ -50,6 +50,7 @@ class WhatsappClient {
50
50
  this.onQRCodeListeners = [];
51
51
  this.onConnectionSuccessListeners = [];
52
52
  this.messageSubscribers = {};
53
+ this.subscriberKeys = new Map();
53
54
  this.groupClients = {};
54
55
  this.pendingAlbums = new Map();
55
56
  this.config = config;
@@ -58,11 +59,13 @@ class WhatsappClient {
58
59
  return this.config;
59
60
  }
60
61
  details() {
61
- //@ts-ignore
62
- let userID = this.state.creds.me.id.replace(/:\d+(?=@)/, "");
62
+ if (!this.state?.creds?.me)
63
+ return null;
64
+ const userID = this.state.creds.me.id.replace(/:\d+(?=@)/, "");
63
65
  return {
64
- userID: userID,
65
- msisdn: userID.substring(0, userID.indexOf("@"))
66
+ userID,
67
+ msisdn: userID.substring(0, userID.indexOf("@")),
68
+ name: this.state.creds.me.name ?? undefined,
66
69
  };
67
70
  }
68
71
  onQRCode(listener) {
@@ -85,20 +88,21 @@ class WhatsappClient {
85
88
  subscribe(listener, filter) {
86
89
  let subscriberID = (0, uuid_1.v4)();
87
90
  filter = filter ? filter : {};
88
- console.log("subscribe to whatsapp messages", filter);
89
91
  this.messageSubscribers[subscriberID] = (message) => {
90
- console.log("Received message: ", message);
91
92
  if ((!filter.groupId || filter.groupId === message.chat.id) &&
92
93
  (!filter.types || filter.types.length === 0 || filter.types.indexOf(message.type) > -1)) {
93
94
  listener(message);
94
95
  }
95
96
  };
97
+ this.subscriberKeys.set(listener, subscriberID);
96
98
  return subscriberID;
97
99
  }
98
100
  unsubscribe(subscriber) {
99
- Object.entries(this.messageSubscribers)
100
- .filter(([key, value]) => value === subscriber)
101
- .forEach(([key, value]) => delete this.messageSubscribers[key]);
101
+ const id = this.subscriberKeys.get(subscriber);
102
+ if (id) {
103
+ delete this.messageSubscribers[id];
104
+ this.subscriberKeys.delete(subscriber);
105
+ }
102
106
  }
103
107
  subscribers() {
104
108
  return Object.values(this.messageSubscribers);
@@ -106,18 +110,30 @@ class WhatsappClient {
106
110
  async sendMessage(chatId, message) {
107
111
  let result;
108
112
  if (message.image) {
109
- // send image, with optional caption if text is provided
110
113
  result = await this.socket.sendMessage(chatId, {
111
114
  image: message.image,
112
115
  caption: message.text
113
116
  });
114
117
  }
118
+ else if (message.video) {
119
+ result = await this.socket.sendMessage(chatId, {
120
+ video: message.video,
121
+ caption: message.text
122
+ });
123
+ }
124
+ else if (message.document) {
125
+ result = await this.socket.sendMessage(chatId, {
126
+ document: message.document,
127
+ fileName: message.documentName ?? 'document',
128
+ mimetype: message.documentMimetype ?? 'application/octet-stream',
129
+ caption: message.text
130
+ });
131
+ }
115
132
  else if (message.text) {
116
- // send text-only message
117
133
  result = await this.socket.sendMessage(chatId, { text: message.text });
118
134
  }
119
135
  else {
120
- throw new Error("Message must contain either text or image");
136
+ throw new Error("Message must contain text, image, or video");
121
137
  }
122
138
  return { messageId: result.key.id };
123
139
  }
@@ -336,6 +352,27 @@ class WhatsappClient {
336
352
  this.start();
337
353
  }, delayMs);
338
354
  }
355
+ /**
356
+ * Normalises a JID for outbound sending:
357
+ * - @lid → resolves to phone-number JID via the contact store
358
+ * - contains "@" → assumed to be a fully-qualified JID; passed through unchanged
359
+ * - otherwise → treated as a bare phone number; non-digits are stripped and
360
+ * "@s.whatsapp.net" is appended (works for arbitrary contacts too)
361
+ */
362
+ async resolveJid(jid) {
363
+ if (jid.endsWith("@lid")) {
364
+ const contact = await this.store.contacts().getByLid(jid);
365
+ if (contact)
366
+ return contact.id;
367
+ console.log(`resolveJid: cannot resolve LID ${jid} — sending as-is`);
368
+ return jid;
369
+ }
370
+ if (jid.includes("@"))
371
+ return jid;
372
+ // Bare phone number — strip formatting characters and construct JID
373
+ const digits = jid.replace(/\D/g, "");
374
+ return `${digits}@s.whatsapp.net`;
375
+ }
339
376
  async stop() {
340
377
  if (this.reconnectTimer) {
341
378
  clearTimeout(this.reconnectTimer);
@@ -482,8 +519,11 @@ class WhatsappClient {
482
519
  message.message?.contactMessage ? WhatsappStore_1.MessageType.Contact :
483
520
  message.message?.locationMessage ? WhatsappStore_1.MessageType.Location :
484
521
  message.message?.liveLocationMessage ? WhatsappStore_1.MessageType.LiveLocation :
485
- message.messageStubType ? WhatsappStore_1.MessageType.Stub :
486
- WhatsappStore_1.MessageType.Unknown;
522
+ message.message?.eventMessage ? WhatsappStore_1.MessageType.Event :
523
+ message.message?.encEventResponseMessage ? WhatsappStore_1.MessageType.EventResponse :
524
+ message.message?.stickerMessage ? WhatsappStore_1.MessageType.Sticker :
525
+ message.messageStubType ? WhatsappStore_1.MessageType.Stub :
526
+ WhatsappStore_1.MessageType.Unknown;
487
527
  if (messageType === WhatsappStore_1.MessageType.Unknown) {
488
528
  if (baileys_1.proto.WebMessageInfo.StubType.BIZ_PRIVACY_MODE_TO_FB === message.messageStubType) {
489
529
  return;
@@ -583,6 +623,16 @@ class WhatsappClient {
583
623
  pageCount: docMsg.pageCount,
584
624
  caption: docMsg.caption,
585
625
  };
626
+ if (live) {
627
+ try {
628
+ const documentData = await (0, baileys_1.downloadMediaMessage)(message, 'buffer', {}, { logger: this.logger, reuploadRequest: this.socket.updateMediaMessage });
629
+ subscriberPayload = { ...messageContents, data: documentData };
630
+ }
631
+ catch (error) {
632
+ console.log("Failed to download document:", error);
633
+ subscriberPayload = { ...messageContents, data: null };
634
+ }
635
+ }
586
636
  }
587
637
  if (messageType === WhatsappStore_1.MessageType.Template) {
588
638
  // the sensible way to handle this for now is to change it to a Text Mesasge.
@@ -676,6 +726,82 @@ class WhatsappClient {
676
726
  sequenceNumber: loc.sequenceNumber != null ? String(loc.sequenceNumber) : undefined,
677
727
  };
678
728
  }
729
+ if (messageType === WhatsappStore_1.MessageType.Event) {
730
+ const evt = message.message?.eventMessage;
731
+ const secret = message.message?.messageContextInfo?.messageSecret;
732
+ messageContents = {
733
+ name: evt.name,
734
+ description: evt.description,
735
+ startTime: evt.startTime ? Number(evt.startTime) : undefined,
736
+ isCanceled: evt.isCanceled,
737
+ hasReminder: evt.hasReminder,
738
+ reminderOffsetSec: evt.reminderOffsetSec ? Number(evt.reminderOffsetSec) : undefined,
739
+ extraGuestsAllowed: evt.extraGuestsAllowed,
740
+ isScheduleCall: evt.isScheduleCall,
741
+ messageSecret: secret ? Buffer.from(secret).toString('base64') : undefined,
742
+ };
743
+ }
744
+ if (messageType === WhatsappStore_1.MessageType.EventResponse) {
745
+ const encResp = message.message?.encEventResponseMessage;
746
+ const creationKey = encResp.eventCreationMessageKey;
747
+ const originalMessage = await this.store.messages().get(creationKey.id);
748
+ const originalPayload = originalMessage ? JSON.parse(originalMessage.payload) : null;
749
+ const messageSecretB64 = originalPayload?.messageSecret;
750
+ if (!messageSecretB64) {
751
+ console.log("encEventResponseMessage: missing messageSecret for event", creationKey.id);
752
+ messageContents = { eventMessageId: creationKey.id, response: "unknown" };
753
+ }
754
+ else {
755
+ try {
756
+ // WhatsApp LID-addressed clients use raw LIDs (not resolved phone numbers) in key derivation.
757
+ const eventCreatorJid = creationKey.participant || creationKey.remoteJid || "";
758
+ const responderJid = message.key?.participant || message.key?.remoteJid || "";
759
+ const responseMsg = (0, baileys_1.decryptEventResponse)({ encPayload: encResp.encPayload, encIv: encResp.encIv }, {
760
+ eventEncKey: Buffer.from(messageSecretB64, 'base64'),
761
+ eventCreatorJid,
762
+ eventMsgId: creationKey.id,
763
+ responderJid,
764
+ });
765
+ const responseType = responseMsg.response;
766
+ const responseStr = responseType === baileys_1.proto.Message.EventResponseMessage.EventResponseType.GOING ? "going" :
767
+ responseType === baileys_1.proto.Message.EventResponseMessage.EventResponseType.NOT_GOING ? "not_going" :
768
+ responseType === baileys_1.proto.Message.EventResponseMessage.EventResponseType.MAYBE ? "maybe" :
769
+ "unknown";
770
+ messageContents = {
771
+ eventMessageId: creationKey.id,
772
+ response: responseStr,
773
+ timestampMs: responseMsg.timestampMs ? Number(responseMsg.timestampMs) : undefined,
774
+ extraGuestCount: responseMsg.extraGuestCount ?? undefined,
775
+ };
776
+ }
777
+ catch (error) {
778
+ console.log("encEventResponseMessage: decryption failed", error);
779
+ messageContents = { eventMessageId: creationKey.id, response: "unknown" };
780
+ }
781
+ }
782
+ }
783
+ if (messageType === WhatsappStore_1.MessageType.Sticker) {
784
+ const stickerMsg = message.message?.stickerMessage;
785
+ messageContents = {
786
+ mimetype: stickerMsg.mimetype,
787
+ width: stickerMsg.width,
788
+ height: stickerMsg.height,
789
+ fileLength: stickerMsg.fileLength ? Number(stickerMsg.fileLength) : undefined,
790
+ isAnimated: stickerMsg.isAnimated,
791
+ isLottie: stickerMsg.isLottie,
792
+ isAiSticker: stickerMsg.isAiSticker,
793
+ };
794
+ if (live) {
795
+ try {
796
+ const stickerData = await (0, baileys_1.downloadMediaMessage)(message, 'buffer', {}, { logger: this.logger, reuploadRequest: this.socket.updateMediaMessage });
797
+ subscriberPayload = { ...messageContents, data: stickerData };
798
+ }
799
+ catch (error) {
800
+ console.log("Failed to download sticker:", error);
801
+ subscriberPayload = { ...messageContents, data: null };
802
+ }
803
+ }
804
+ }
679
805
  if (messageType === WhatsappStore_1.MessageType.Stub) {
680
806
  // stub message. doesnt do much by the looks of it.
681
807
  }
@@ -1,15 +1,34 @@
1
1
  "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
2
11
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
13
  };
14
+ var WhatsappService_1;
5
15
  Object.defineProperty(exports, "__esModule", { value: true });
6
16
  exports.WhatsappService = void 0;
7
17
  const node_red_plugincore_1 = require("@theotherwillembotha/node-red-plugincore");
8
18
  const WhatsappClient_1 = require("./WhatsappClient");
9
19
  const fs_1 = __importDefault(require("fs"));
10
20
  const qrcode_1 = __importDefault(require("qrcode"));
11
- class WhatsappService extends node_red_plugincore_1.BaseService {
12
- static { this.clients = {}; }
21
+ let WhatsappService = class WhatsappService extends node_red_plugincore_1.BaseService {
22
+ static { WhatsappService_1 = this; }
23
+ // clients is stored in global so that Nodes.js and Plugins.js (which are separate esbuild
24
+ // bundles containing separate copies of this class) share the same runtime map.
25
+ static { this._CLIENTS_KEY = '__wa_service_clients__'; }
26
+ static clients() {
27
+ if (!global[WhatsappService_1._CLIENTS_KEY]) {
28
+ global[WhatsappService_1._CLIENTS_KEY] = {};
29
+ }
30
+ return global[WhatsappService_1._CLIENTS_KEY];
31
+ }
13
32
  constructor() {
14
33
  super("WhatsappService");
15
34
  this.connections = [];
@@ -20,6 +39,8 @@ class WhatsappService extends node_red_plugincore_1.BaseService {
20
39
  console.log("STARTING: WhatsappService");
21
40
  // whatsapp client commands.
22
41
  let writePermission = red.auth.needsPermission("inject.write");
42
+ let readPermission = red.auth.needsPermission("nodes.read");
43
+ red.httpAdmin.get("/whatsapp/accounts", readPermission, (request, response) => this.getAccounts(request, response));
23
44
  red.httpAdmin.post("/whatsapp/linkaccount", writePermission, (request, response) => this.linkAccount(request, response));
24
45
  red.httpAdmin.post("/whatsapp/unlinkaccount", writePermission, (request, response) => this.unlinkaccount(request, response));
25
46
  red.httpAdmin.post("/whatsapp/getgroups", writePermission, (request, response) => this.getGroups(request, response));
@@ -28,9 +49,6 @@ class WhatsappService extends node_red_plugincore_1.BaseService {
28
49
  red.httpAdmin.post("/whatsapp/groupupdateuser", writePermission, (request, response) => this.groupUpdateUser(request, response));
29
50
  red.httpAdmin.post("/whatsapp/groupremoveuser", writePermission, (request, response) => this.groupRemoveUser(request, response));
30
51
  try {
31
- // TODO: change the way we determine a suitable sorage location for the whatsapp data.
32
- // if the /data folder exists, then this is probably a custom image and we can safely store the information in /data
33
- // if it doesnt exist, then just save it to the current working folder.
34
52
  if (fs_1.default.existsSync("/data")) {
35
53
  console.log("Using /data as storage location");
36
54
  this.configDir = "/data/whatsapp";
@@ -39,7 +57,6 @@ class WhatsappService extends node_red_plugincore_1.BaseService {
39
57
  this.configDir = "whatsapp";
40
58
  }
41
59
  this.configFile = this.configDir + "/connections.json";
42
- // check if the directory exists.
43
60
  if (!fs_1.default.existsSync(this.configDir)) {
44
61
  fs_1.default.mkdirSync(this.configDir);
45
62
  }
@@ -56,7 +73,9 @@ class WhatsappService extends node_red_plugincore_1.BaseService {
56
73
  localConnectionId: connection.key,
57
74
  fileStorageRoot: this.configDir
58
75
  });
59
- WhatsappService.clients[connection.key] = client;
76
+ WhatsappService_1.clients()[connection.key] = client;
77
+ // save account details (name, phone) to connections.json after each reconnect
78
+ client.onConnectionSuccess(() => this.saveConnectionDetails(connection.key));
60
79
  return client.start();
61
80
  }));
62
81
  }
@@ -64,11 +83,47 @@ class WhatsappService extends node_red_plugincore_1.BaseService {
64
83
  console.log(error);
65
84
  }
66
85
  }
86
+ saveConnectionDetails(key) {
87
+ try {
88
+ const client = WhatsappService_1.clients()[key];
89
+ if (!client)
90
+ return;
91
+ const details = client.details();
92
+ if (!details)
93
+ return;
94
+ const conn = this.connections.find(c => c.key === key);
95
+ if (conn) {
96
+ conn.name = details.name;
97
+ conn.phoneNumber = details.msisdn;
98
+ fs_1.default.writeFileSync(this.configFile, JSON.stringify(this.connections, null, 2), { flag: "w+" });
99
+ }
100
+ }
101
+ catch (e) {
102
+ console.log("saveConnectionDetails failed", e);
103
+ }
104
+ }
105
+ getAccounts(request, response) {
106
+ // collect localConnectionIds claimed by WhatsappAccountConfigNodes in the current flow
107
+ const claimedKeys = new Set();
108
+ this.red.nodes.eachNode((nodeConfig) => {
109
+ if (nodeConfig.type === 'WhatsappAccountConfigNode' && nodeConfig.localConnectionId) {
110
+ claimedKeys.add(nodeConfig.localConnectionId);
111
+ }
112
+ });
113
+ const accounts = this.connections.map(connection => ({
114
+ key: connection.key,
115
+ name: connection.name,
116
+ phoneNumber: connection.phoneNumber,
117
+ connected: !!WhatsappService_1.clients()[connection.key],
118
+ claimed: claimedKeys.has(connection.key),
119
+ }));
120
+ response.send(accounts);
121
+ }
67
122
  deinit(red) {
68
123
  console.log("STOPPING: WhatsappService");
69
124
  }
70
125
  static getClient(localConnectionId) {
71
- return this.clients[localConnectionId];
126
+ return WhatsappService_1.clients()[localConnectionId];
72
127
  }
73
128
  linkAccount(request, response) {
74
129
  let linkRequest = request.body;
@@ -116,15 +171,16 @@ class WhatsappService extends node_red_plugincore_1.BaseService {
116
171
  // make sure we havent got this connection already.
117
172
  let knownConnection = this.connections.find(connection => connection.key === linkRequest.localConnectionId);
118
173
  if (!knownConnection) {
119
- this.connections.push({
120
- key: linkRequest.localConnectionId
121
- });
174
+ this.connections.push({ key: linkRequest.localConnectionId });
175
+ // ensure the new entry exists before saveConnectionDetails tries to update it
122
176
  fs_1.default.writeFileSync(this.configFile, JSON.stringify(this.connections, null, 2), { flag: "w+" });
123
177
  }
178
+ // save account details (name, phone) to disk
179
+ this.saveConnectionDetails(linkRequest.localConnectionId);
124
180
  // stop the timeout timer, and move client from tempclients to clients.
125
181
  clearTimeout(timeoutTimer);
126
182
  delete this.tempClients[linkRequest.localConnectionId];
127
- WhatsappService.clients[linkRequest.localConnectionId] = client;
183
+ WhatsappService_1.clients()[linkRequest.localConnectionId] = client;
128
184
  // send a notification to the frontend.
129
185
  red.events.emit("runtime-event", {
130
186
  id: "whatsapp/" + linkRequest.localConnectionId,
@@ -140,10 +196,10 @@ class WhatsappService extends node_red_plugincore_1.BaseService {
140
196
  unlinkaccount(request, response) {
141
197
  let unlinkAccountRequest = request.body;
142
198
  // get an instance of the client and stop it.
143
- let client = WhatsappService.clients[unlinkAccountRequest.localConnectionId];
199
+ let client = WhatsappService_1.clients()[unlinkAccountRequest.localConnectionId];
144
200
  let deleteAccountData = () => {
145
201
  // after stopping the client, remove it from the clients list, update the connections file, and remove its storage.
146
- delete WhatsappService.clients[unlinkAccountRequest.localConnectionId];
202
+ delete WhatsappService_1.clients()[unlinkAccountRequest.localConnectionId];
147
203
  this.connections = this.connections.filter(connection => connection.key !== unlinkAccountRequest.localConnectionId);
148
204
  fs_1.default.writeFileSync(this.configFile, JSON.stringify(this.connections, null, 2), { flag: "w+" });
149
205
  fs_1.default.rmSync(this.configDir + "/" + unlinkAccountRequest.localConnectionId, { recursive: true });
@@ -158,7 +214,7 @@ class WhatsappService extends node_red_plugincore_1.BaseService {
158
214
  }
159
215
  }
160
216
  async getGroups(request, response) {
161
- let client = WhatsappService.clients[request.body.localConnectionId];
217
+ let client = WhatsappService_1.clients()[request.body.localConnectionId];
162
218
  if (client) {
163
219
  response.send(await client.groups().getAll());
164
220
  }
@@ -167,7 +223,7 @@ class WhatsappService extends node_red_plugincore_1.BaseService {
167
223
  }
168
224
  }
169
225
  async createGroup(request, response) {
170
- let client = WhatsappService.clients[request.body.localConnectionId];
226
+ let client = WhatsappService_1.clients()[request.body.localConnectionId];
171
227
  if (client) {
172
228
  response.send(await client.groups().createGroup(request.body.groupName));
173
229
  }
@@ -176,7 +232,7 @@ class WhatsappService extends node_red_plugincore_1.BaseService {
176
232
  }
177
233
  }
178
234
  async groupAddUser(request, response) {
179
- let client = WhatsappService.clients[request.body.localConnectionId];
235
+ let client = WhatsappService_1.clients()[request.body.localConnectionId];
180
236
  if (client) {
181
237
  await client.groups().addUserToGroup(request.body.userId, request.body.groupId, request.body.role);
182
238
  response.send({ status: "ok" });
@@ -186,7 +242,7 @@ class WhatsappService extends node_red_plugincore_1.BaseService {
186
242
  }
187
243
  }
188
244
  async groupUpdateUser(request, response) {
189
- let client = WhatsappService.clients[request.body.localConnectionId];
245
+ let client = WhatsappService_1.clients()[request.body.localConnectionId];
190
246
  if (client) {
191
247
  await client.groups().updateUserRole(request.body.userId, request.body.groupId, request.body.role);
192
248
  response.send({ status: "ok" });
@@ -196,7 +252,7 @@ class WhatsappService extends node_red_plugincore_1.BaseService {
196
252
  }
197
253
  }
198
254
  async groupRemoveUser(request, response) {
199
- let client = WhatsappService.clients[request.body.localConnectionId];
255
+ let client = WhatsappService_1.clients()[request.body.localConnectionId];
200
256
  if (client) {
201
257
  await client.groups().removeUserFromGroup(request.body.userId, request.body.groupId);
202
258
  response.send({ status: "ok" });
@@ -205,8 +261,14 @@ class WhatsappService extends node_red_plugincore_1.BaseService {
205
261
  response.status(500).send({ message: "Client not linked" });
206
262
  }
207
263
  }
208
- static getServiceDescriptor() {
209
- return new node_red_plugincore_1.ServiceDescriptor("@theotherwillembotha/whatsappservice", "WhatsappService", "integration-plugin", "./whatsapp/service/WhatsappService", WhatsappService);
210
- }
211
- }
264
+ };
212
265
  exports.WhatsappService = WhatsappService;
266
+ exports.WhatsappService = WhatsappService = WhatsappService_1 = __decorate([
267
+ (0, node_red_plugincore_1.ServiceDescription)({
268
+ id: "@theotherwillembotha/whatsappservice",
269
+ name: "WhatsappService",
270
+ type: "integration-plugin",
271
+ sourceFile: "./whatsapp/service/WhatsappService",
272
+ }),
273
+ __metadata("design:paramtypes", [])
274
+ ], WhatsappService);
@@ -233,20 +233,20 @@ class ChatDAO extends DAO {
233
233
  async getWithParticipants(id) {
234
234
  return this.em().findOne(Chat, {
235
235
  where: { id },
236
- relations: ['participants', 'participants.contact']
236
+ relations: { participants: { contact: true } }
237
237
  });
238
238
  }
239
239
  async getGroups() {
240
240
  return this.em().find(Chat, {
241
241
  where: { type: WhatsappClient_1.ChatType.Group },
242
- relations: ['participants', 'participants.contact']
242
+ relations: { participants: { contact: true } }
243
243
  });
244
244
  }
245
245
  async save(chat) {
246
246
  return this.em().save(chat);
247
247
  }
248
248
  async delete(chatId) {
249
- const chat = await this.em().findOne(Chat, { where: { id: chatId }, relations: ['participants'] });
249
+ const chat = await this.em().findOne(Chat, { where: { id: chatId }, relations: { participants: true } });
250
250
  if (chat) {
251
251
  await this.em().remove(chat);
252
252
  }
@@ -254,7 +254,7 @@ class ChatDAO extends DAO {
254
254
  async removeParticipants(chatId, contactIds) {
255
255
  const chat = await this.em().findOne(Chat, {
256
256
  where: { id: chatId },
257
- relations: ['participants', 'participants.contact']
257
+ relations: { participants: { contact: true } }
258
258
  });
259
259
  if (!chat)
260
260
  return;
@@ -264,7 +264,7 @@ class ChatDAO extends DAO {
264
264
  async updateParticipantRole(chatId, contactId, role) {
265
265
  const chat = await this.em().findOne(Chat, {
266
266
  where: { id: chatId },
267
- relations: ['participants', 'participants.contact']
267
+ relations: { participants: { contact: true } }
268
268
  });
269
269
  if (!chat)
270
270
  return;
@@ -277,7 +277,7 @@ class ChatDAO extends DAO {
277
277
  async updateParticipantLabel(chatId, contactId, label) {
278
278
  const chat = await this.em().findOne(Chat, {
279
279
  where: { id: chatId },
280
- relations: ['participants', 'participants.contact']
280
+ relations: { participants: { contact: true } }
281
281
  });
282
282
  if (!chat)
283
283
  return;
@@ -367,7 +367,7 @@ class MessageDAO extends DAO {
367
367
  }
368
368
  async create(chat, id, timestamp, type, payload) {
369
369
  // first check if the client exists.
370
- let message = await this.em().findOneBy(Message, { id: id, chat: chat });
370
+ let message = await this.em().findOneBy(Message, { id: id, chat: { id: chat.id } });
371
371
  if (message) {
372
372
  let modified = true;
373
373
  if (message.timestamp !== timestamp) {
@@ -405,4 +405,7 @@ var MessageType;
405
405
  MessageType["Interactive"] = "Interactive";
406
406
  MessageType["Location"] = "Location";
407
407
  MessageType["LiveLocation"] = "LiveLocation";
408
+ MessageType["Event"] = "Event";
409
+ MessageType["EventResponse"] = "EventResponse";
410
+ MessageType["Sticker"] = "Sticker";
408
411
  })(MessageType || (exports.MessageType = MessageType = {}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theotherwillembotha/node-red-whatsapp",
3
- "version": "0.0.55",
3
+ "version": "0.3.0",
4
4
  "description": "Node-RED nodes for WhatsApp messaging via the Baileys library.",
5
5
  "author": "Willem Botha (@theotherwillembotha)",
6
6
  "license": "ISC",
@@ -15,10 +15,11 @@
15
15
  "exports": "./build/index.js",
16
16
  "scripts": {
17
17
  "test": "echo \"Error: no test specified\" && exit 1",
18
- "build": "npm run clean && tsc && npm run generate-nodes && npm run copy-files",
18
+ "build": "npm run clean && tsc && npm run generate-nodes && npm run bundle && npm run copy-files",
19
19
  "clean": "rm -rf ./build",
20
20
  "typeorm": "typeorm-ts-node-commonjs",
21
21
  "generate-nodes": "node build/GenerateNodes.js",
22
+ "bundle": "node esbuild.js",
22
23
  "copy-files": "cp -r ./icons ./build/"
23
24
  },
24
25
  "files": [
@@ -43,20 +44,21 @@
43
44
  "nodered": ">=4.0.0"
44
45
  },
45
46
  "dependencies": {
46
- "@theotherwillembotha/node-red-plugincore": "^0.0.55",
47
- "baileys": "7.0.0-rc11",
48
- "handlebars": "^4.7.8",
47
+ "@theotherwillembotha/node-red-plugincore": "^0.3.0",
48
+ "baileys": "7.0.0-rc14",
49
+ "handlebars": "^4.7.9",
49
50
  "node-cache": "^5.1.2",
50
51
  "qrcode": "^1.5.4",
51
- "sql.js": "^1.13.0",
52
- "typeorm": "^0.3.28",
53
- "uuid": "^13.0.0"
52
+ "sql.js": "^1.14.1",
53
+ "typeorm": "^1.0.0",
54
+ "uuid": "^14.0.1"
54
55
  },
55
56
  "devDependencies": {
56
57
  "@theotherwillembotha/node-red-plugincore": "../nodered_plugincore",
57
58
  "@types/node": "^22.15.16",
58
59
  "@types/node-red": "~1.3.5",
59
60
  "@types/qrcode": "^1.5.6",
61
+ "esbuild": "^0.25.0",
60
62
  "ts-node": "^10.9.2"
61
63
  },
62
64
  "node-red": {