@jep182/n8n-nodes-whatsthat 0.4.2 → 0.5.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 CHANGED
@@ -1,124 +1,198 @@
1
1
  # @jep182/n8n-nodes-whatsthat
2
2
 
3
- WhatsThat is an n8n community package for sending WhatsApp messages, with multiple sessions.
3
+ WhatsThat lets you connect one or more WhatsApp numbers inside n8n, link chats or groups with simple names, send messages, and react to incoming events.
4
4
 
5
- It lets you:
5
+ You will mainly use:
6
6
 
7
- - manage multiple sessions and numbers
8
- - connect with pairing code or QR
9
- - discover chats and groups
10
- - link chats/groups to friendly aliases
11
- - send text, media, documents, reactions, contacts, locations, and polls
12
- - receive inbound events through a trigger node
7
+ - `WhatsThat`
8
+ - `WhatsThat Trigger`
13
9
 
14
- ## Included Nodes
10
+ You also need one credential:
15
11
 
16
- ### `WhatsThat Session`
12
+ - `WhatsThat Runtime`
17
13
 
18
- Use this node to:
14
+ ## Before You Start
19
15
 
20
- - create a session
21
- - connect a session
22
- - list sessions
23
- - inspect session status
24
- - disconnect a session
25
- - remove a session
16
+ Create `WhatsThat Runtime` credentials and choose a persistent storage path, for example:
26
17
 
27
- When pairing is available, the node returns:
18
+ ```text
19
+ /home/node/.n8n/whatsthat
20
+ ```
28
21
 
29
- - `pairingCode`
30
- - `qrCodeUrl`
31
- - `qr`
32
- - `qrDataUrl`
22
+ This folder is used to store session files and local metadata.
33
23
 
34
- ### `WhatsThat Targets`
24
+ ## How To Connect Your Number
35
25
 
36
- Use this node to:
26
+ 1. Add a `WhatsThat` node.
27
+ 2. Set `Resource` to `Session`.
28
+ 3. Set `Operation` to `Connect Session`.
29
+ 4. Fill in:
30
+ - `Session Name`: a stable internal name like `main-phone`
31
+ - `Display Name`: a friendly label like `Luca phone`
32
+ - `WhatsApp Number`: optional, only if you want a pairing code instead of relying only on QR
33
+ 5. Run the node.
34
+ 6. In the output, open `qrCodeUrl`.
37
35
 
38
- - list discovered chats and groups
39
- - list linked aliases
40
- - link a target to an alias
41
- - unlink an alias
36
+ Open `qrCodeUrl` as the standard and recommended way to connect the number.
42
37
 
43
- ### `WhatsThat Message`
38
+ Important:
44
39
 
45
- Use this node to send:
40
+ - open `qrCodeUrl` whenever possible
41
+ - do not rely on `pairingCode`
42
+ - `pairingCode` is not stable and may fail or stop working depending on the session state and device behavior
43
+ - `qrDataUrl` is available only if you specifically need the raw embedded QR image data
46
44
 
47
- - text
48
- - image
49
- - video
50
- - audio
51
- - document
52
- - reaction
53
- - location
54
- - contact
55
- - poll
45
+ Example:
56
46
 
57
- For images and videos, the node can decide internally whether to send them as:
47
+ ```text
48
+ Session Name: main-phone
49
+ Display Name: Luca phone
50
+ WhatsApp Number: 393331234567
51
+ ```
58
52
 
59
- - native media
60
- - file/document attachment
53
+ ## How To Wait Until The Number Is Fully Connected
61
54
 
62
- ### `WhatsThat Trigger`
55
+ After `Connect Session`, add another `WhatsThat` node:
63
56
 
64
- Use this node to listen for:
57
+ 1. Set `Resource` to `Session`
58
+ 2. Set `Operation` to `Wait Until Connect`
59
+ 3. Use the same `Session Name`
60
+ 4. Choose how many seconds to wait in `Timeout Seconds`
65
61
 
66
- - incoming messages
67
- - your own sent messages
68
- - session pairing events
69
- - session connected/disconnected events
70
- - group updates
62
+ This second node waits for the already-started session to become fully connected.
63
+
64
+ ## How To Link A Group Or Chat Manually
65
+
66
+ 1. Add a `WhatsThat` node
67
+ 2. Set `Resource` to `Linked Chat`
68
+ 3. Start with `Operation = List Discovered Chats`
69
+ 4. Pick the chat or group you want to use
70
+ 5. Change to `Operation = Link Chat`
71
+ 6. Fill in:
72
+ - `Session Name`
73
+ - `Chat JID`
74
+ - `Linked Chat`: the simple name you want to use later, for example `support` or `team`
71
75
 
72
- ## How It Works
76
+ Example:
77
+
78
+ ```text
79
+ Linked Chat: support
80
+ ```
73
81
 
74
- WhatsThat embeds Baileys directly in n8n.
82
+ After that, you can send messages by choosing that linked chat instead of writing the raw JID every time.
75
83
 
76
- - session auth files are stored on disk
77
- - session metadata and linked targets are stored as local JSON files under the runtime storage path
84
+ ## How To Link A Group Or Chat From WhatsApp
78
85
 
79
- ## Quick Start
86
+ Use `WhatsThat Trigger`.
80
87
 
81
- 1. Create `WhatsThat Runtime` credentials.
82
- 2. Set a storage path, for example:
88
+ 1. Add a `WhatsThat Trigger` node
89
+ 2. Select the same `Session Name`
90
+ 3. Set `Event` to `Link Chat Command`
91
+ 4. Leave the default command or change it
92
+
93
+ By default, users can send a message like:
83
94
 
84
95
  ```text
85
- /home/node/.n8n/whatsthat
96
+ /link-whatsthat support
86
97
  ```
87
98
 
88
- 3. Add `WhatsThat Session`.
89
- 4. Choose `Ensure Session`, then provide:
90
- - `Session ID (Internal)`: a stable unique ID such as `main-phone`
91
- - `Label (Visible Name)`: a human-readable name such as `Luca personal phone`
92
- - optional `Phone Number For Pairing`: full number with country code, digits only, without `00` or `+`
93
- 5. Set `Return When` to `Pairing Is Ready Or Connected` for first-time pairing.
94
- 6. Use the returned `pairingCode`, `qrCodeUrl`, or `qrDataUrl` to connect the device.
95
- 7. Use `WhatsThat Targets` to discover and link chats/groups.
96
- 8. Use `WhatsThat Message` to send messages by alias or raw JID.
99
+ If that message is sent inside a group or chat, WhatsThat links that conversation with alias `support`.
97
100
 
98
- Example workflow:
101
+ This is useful when you want users to self-register a group without opening n8n.
99
102
 
100
- - [`examples/register-number.workflow.json`](./examples/register-number.workflow.json)
103
+ ## How To Send A Message To A Linked Chat
101
104
 
102
- ## Media Delivery
105
+ 1. Add a `WhatsThat` node
106
+ 2. Set `Resource` to `Send Message`
107
+ 3. Set `Session Name`
108
+ 4. Set `Send Message To` to `Linked Chat`
109
+ 5. Choose a linked chat from the dropdown
110
+ 6. Choose `Message Type`
111
+ 7. Write the message
103
112
 
104
- For `Image` and `Video` messages:
113
+ Example:
105
114
 
106
- - `Native Media` sends them as normal media with preview
107
- - `As File` sends them as a document/file attachment
115
+ ```text
116
+ Send Message To: Linked Chat
117
+ Linked Chat: support
118
+ Message Type: Text
119
+ Message: Hello from n8n
120
+ ```
108
121
 
109
- ## Notes
122
+ ## How To Send A Message To A Number
110
123
 
111
- - This package runs an active WebSocket client inside n8n.
112
- - For production, use persistent storage for the auth directory.
113
- - Use one n8n instance as the runtime owner for these sessions.
124
+ 1. Add a `WhatsThat` node
125
+ 2. Set `Resource` to `Send Message`
126
+ 3. Set `Send Message To` to `WhatsApp Number`
127
+ 4. Enter the number with country code, digits only, without `00` and without `+`
114
128
 
115
- ## Thanks
129
+ Example:
130
+
131
+ ```text
132
+ WhatsApp Number: 393331234567
133
+ ```
134
+
135
+ ## How To Send A Message To Yourself
136
+
137
+ 1. Add a `WhatsThat` node
138
+ 2. Set `Resource` to `Send Message`
139
+ 3. Set `Send Message To` to `Yourself`
116
140
 
117
- This project relies on [Baileys](https://github.com/WhiskeySockets/Baileys), the open-source TypeScript/Node.js library that powers the messaging client layer.
141
+ WhatsThat uses the number already connected for that session.
118
142
 
119
- Please review the Baileys project, its license, and its usage notes before running it in production:
143
+ This is useful for testing.
144
+
145
+ ## How To Send Media
146
+
147
+ For images and videos you can choose:
148
+
149
+ - `Native Media`
150
+ - `As File`
151
+
152
+ Use `Media URL` for the file you want to send.
153
+
154
+ Examples:
155
+
156
+ - send an image preview normally
157
+ - send a PDF as a document
158
+ - send a video as a file attachment
159
+
160
+ ## How To Receive Events
161
+
162
+ Use `WhatsThat Trigger` when you want to react to:
163
+
164
+ - incoming messages
165
+ - your own sent messages
166
+ - pairing events
167
+ - connection events
168
+ - group updates
169
+
170
+ Common examples:
171
+
172
+ - start a workflow when a message arrives
173
+ - auto-link a chat with `/link-whatsthat support`
174
+ - continue a workflow when a session becomes connected
175
+
176
+ ## Example Workflow
177
+
178
+ You can import this example:
179
+
180
+ - [`examples/register-number.workflow.json`](./examples/register-number.workflow.json)
181
+
182
+ It shows the basic flow:
183
+
184
+ 1. `Connect Session`
185
+ 2. `Wait Until Connect`
186
+
187
+ ## Notes
188
+
189
+ - Use one persistent n8n instance as the owner of these sessions
190
+ - Keep the runtime storage path persistent
191
+ - If n8n restarts, active in-memory sockets are restarted from the saved session files
192
+
193
+ ## Thanks
120
194
 
121
- - [Baileys repository](https://github.com/WhiskeySockets/Baileys)
195
+ This project uses [Baileys](https://github.com/WhiskeySockets/Baileys).
122
196
 
123
197
  ## License
124
198
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,3 @@
1
1
  export * from './credentials/WhatsThatRuntime.credentials';
2
- export * from './nodes/WhatsThatSession/WhatsThatSession.node';
3
- export * from './nodes/WhatsThatTargets/WhatsThatTargets.node';
4
- export * from './nodes/WhatsThatMessage/WhatsThatMessage.node';
2
+ export * from './nodes/WhatsThat/WhatsThat.node';
5
3
  export * from './nodes/WhatsThatTrigger/WhatsThatTrigger.node';
package/dist/index.js CHANGED
@@ -15,7 +15,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./credentials/WhatsThatRuntime.credentials"), exports);
18
- __exportStar(require("./nodes/WhatsThatSession/WhatsThatSession.node"), exports);
19
- __exportStar(require("./nodes/WhatsThatTargets/WhatsThatTargets.node"), exports);
20
- __exportStar(require("./nodes/WhatsThatMessage/WhatsThatMessage.node"), exports);
18
+ __exportStar(require("./nodes/WhatsThat/WhatsThat.node"), exports);
21
19
  __exportStar(require("./nodes/WhatsThatTrigger/WhatsThatTrigger.node"), exports);
@@ -0,0 +1,10 @@
1
+ import type { IExecuteFunctions, ILoadOptionsFunctions, INodeExecutionData, INodePropertyOptions, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class WhatsThat implements INodeType {
3
+ methods: {
4
+ loadOptions: {
5
+ getLinkedAliases(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
6
+ };
7
+ };
8
+ description: INodeTypeDescription;
9
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
10
+ }
@@ -0,0 +1,647 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WhatsThat = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const access_1 = require("../../shared/access");
6
+ const runtime_1 = require("../../shared/runtime");
7
+ const validation_1 = require("../../shared/validation");
8
+ function buildPayload(context, itemIndex) {
9
+ const messageType = context.getNodeParameter('messageType', itemIndex);
10
+ const sendMessageTo = context.getNodeParameter('sendMessageTo', itemIndex);
11
+ const deliveryMode = context.getNodeParameter('deliveryMode', itemIndex, 'native');
12
+ const payload = {
13
+ sessionId: (0, validation_1.requireSessionId)(context.getNodeParameter('sessionId', itemIndex)),
14
+ type: messageType,
15
+ };
16
+ if (sendMessageTo === 'linkedChat') {
17
+ payload.channelAlias = context.getNodeParameter('linkedAlias', itemIndex);
18
+ }
19
+ else if (sendMessageTo === 'jid') {
20
+ payload.jid = context.getNodeParameter('jid', itemIndex);
21
+ }
22
+ else if (sendMessageTo === 'number') {
23
+ payload.phoneNumber = (0, validation_1.requireWhatsappNumber)(context.getNodeParameter('phoneNumber', itemIndex));
24
+ }
25
+ else if (sendMessageTo === 'yourself') {
26
+ payload.sendToSelf = true;
27
+ }
28
+ payload.message = context.getNodeParameter('message', itemIndex, '');
29
+ payload.replyToMessageId = context.getNodeParameter('replyToMessageId', itemIndex, '');
30
+ if (['image', 'video', 'audio', 'document'].includes(messageType)) {
31
+ payload.mediaUrl = context.getNodeParameter('mediaUrl', itemIndex);
32
+ payload.mimetype = context.getNodeParameter('mimetype', itemIndex, '');
33
+ payload.fileName = context.getNodeParameter('fileName', itemIndex, '');
34
+ }
35
+ if (['image', 'video'].includes(messageType)) {
36
+ payload.sendAsDocument = deliveryMode === 'document';
37
+ }
38
+ if (['image', 'video', 'document'].includes(messageType)) {
39
+ payload.caption = context.getNodeParameter('caption', itemIndex, '');
40
+ }
41
+ if (messageType === 'reaction') {
42
+ payload.reactionText = context.getNodeParameter('reactionText', itemIndex, '👍');
43
+ }
44
+ if (messageType === 'location') {
45
+ payload.location = {
46
+ degreesLatitude: context.getNodeParameter('latitude', itemIndex),
47
+ degreesLongitude: context.getNodeParameter('longitude', itemIndex),
48
+ name: context.getNodeParameter('locationName', itemIndex, ''),
49
+ address: context.getNodeParameter('locationAddress', itemIndex, ''),
50
+ };
51
+ }
52
+ if (messageType === 'contact') {
53
+ payload.contact = {
54
+ displayName: context.getNodeParameter('contactName', itemIndex),
55
+ vcard: context.getNodeParameter('contactVcard', itemIndex),
56
+ };
57
+ }
58
+ if (messageType === 'poll') {
59
+ const raw = context.getNodeParameter('pollOptions', itemIndex);
60
+ payload.poll = {
61
+ name: context.getNodeParameter('pollName', itemIndex),
62
+ values: raw.split(',').map((value) => value.trim()).filter(Boolean),
63
+ selectableCount: context.getNodeParameter('pollSelectableCount', itemIndex, 1),
64
+ };
65
+ }
66
+ return payload;
67
+ }
68
+ function formatSessionOutput(value) {
69
+ if (Array.isArray(value)) {
70
+ return value.map((item) => formatSessionOutput(item));
71
+ }
72
+ if (!value || typeof value !== 'object') {
73
+ return value;
74
+ }
75
+ const record = value;
76
+ if (!('sessionId' in record) || !('status' in record)) {
77
+ return value;
78
+ }
79
+ return {
80
+ qrCodeUrl: record.qrCodeUrl,
81
+ sessionId: record.sessionId,
82
+ status: record.status,
83
+ pairingCode: record.pairingCode,
84
+ qr: record.qr,
85
+ qrDataUrl: record.qrDataUrl,
86
+ phone: record.phone,
87
+ label: record.label,
88
+ phoneNumberForPairing: record.phoneNumberForPairing,
89
+ lastDisconnectReason: record.lastDisconnectReason,
90
+ createdAt: record.createdAt,
91
+ updatedAt: record.updatedAt,
92
+ lastSeenAt: record.lastSeenAt,
93
+ };
94
+ }
95
+ class WhatsThat {
96
+ constructor() {
97
+ this.methods = {
98
+ loadOptions: {
99
+ async getLinkedAliases() {
100
+ const access = await (0, access_1.buildAccess)(this);
101
+ const sessionId = (0, validation_1.requireSessionId)(this.getNodeParameter('sessionId'));
102
+ const linked = await runtime_1.registry.listLinkedTargets(access, sessionId);
103
+ return linked.map((item) => ({
104
+ name: `${item.alias} (${item.displayName})`,
105
+ value: item.alias,
106
+ }));
107
+ },
108
+ },
109
+ };
110
+ this.description = {
111
+ displayName: 'WhatsThat',
112
+ name: 'whatsThat',
113
+ icon: 'file:../WhatsThatSession/whatsthat.svg',
114
+ group: ['transform'],
115
+ version: 1,
116
+ description: 'Manage sessions, chat links, and messages for WhatsThat',
117
+ defaults: { name: 'WhatsThat' },
118
+ inputs: ['main'],
119
+ outputs: ['main'],
120
+ credentials: [{ name: 'whatsThatRuntime', required: true }],
121
+ properties: [
122
+ {
123
+ displayName: 'Resource',
124
+ name: 'resource',
125
+ type: 'options',
126
+ default: 'session',
127
+ noDataExpression: true,
128
+ options: [
129
+ { name: 'Session', value: 'session' },
130
+ { name: 'Linked Chat', value: 'linkChat' },
131
+ { name: 'Send Message', value: 'sendMessage' },
132
+ ],
133
+ },
134
+ {
135
+ displayName: 'Operation',
136
+ name: 'sessionOperation',
137
+ type: 'options',
138
+ default: 'connect',
139
+ displayOptions: {
140
+ show: { resource: ['session'] },
141
+ },
142
+ options: [
143
+ { name: 'Connect Session', value: 'connect' },
144
+ { name: 'Wait Until Connect', value: 'ensure' },
145
+ { name: 'List Sessions', value: 'list' },
146
+ { name: 'Get Session', value: 'status' },
147
+ { name: 'Disconnect Session', value: 'disconnect' },
148
+ { name: 'Remove Session', value: 'remove' },
149
+ ],
150
+ },
151
+ {
152
+ displayName: 'Operation',
153
+ name: 'linkChatOperation',
154
+ type: 'options',
155
+ default: 'listDiscovered',
156
+ displayOptions: {
157
+ show: { resource: ['linkChat'] },
158
+ },
159
+ options: [
160
+ { name: 'List Discovered Chats', value: 'listDiscovered' },
161
+ { name: 'List Linked Chats', value: 'listLinked' },
162
+ { name: 'Link Chat', value: 'link' },
163
+ { name: 'Unlink Chat', value: 'unlink' },
164
+ ],
165
+ },
166
+ {
167
+ displayName: 'Session Name',
168
+ name: 'sessionId',
169
+ type: 'string',
170
+ default: '',
171
+ required: true,
172
+ description: 'Required unique ID for this session. Use a stable internal value such as "main-phone" or "support-team".',
173
+ displayOptions: {
174
+ hide: {
175
+ resource: ['session'],
176
+ sessionOperation: ['list'],
177
+ },
178
+ },
179
+ },
180
+ {
181
+ displayName: 'Display Name',
182
+ name: 'label',
183
+ type: 'string',
184
+ default: '',
185
+ description: 'Human-readable name shown in results. Example: "Luca personal phone" or "Support number".',
186
+ displayOptions: {
187
+ show: {
188
+ resource: ['session'],
189
+ sessionOperation: ['connect'],
190
+ },
191
+ },
192
+ },
193
+ {
194
+ displayName: 'WhatsApp Number',
195
+ name: 'phoneNumberForPairing',
196
+ type: 'string',
197
+ default: '',
198
+ description: 'Optional. Full phone number with country code, digits only, without 00 or +. Example: 393331234567.',
199
+ displayOptions: {
200
+ show: {
201
+ resource: ['session'],
202
+ sessionOperation: ['connect'],
203
+ },
204
+ },
205
+ },
206
+ {
207
+ displayName: 'Timeout Seconds',
208
+ name: 'timeoutSeconds',
209
+ type: 'number',
210
+ default: 300,
211
+ typeOptions: {
212
+ minValue: 1,
213
+ },
214
+ description: 'Maximum time to wait before returning the latest known session status.',
215
+ displayOptions: {
216
+ show: {
217
+ resource: ['session'],
218
+ sessionOperation: ['ensure'],
219
+ },
220
+ },
221
+ },
222
+ {
223
+ displayName: 'Chat JID',
224
+ name: 'linkJid',
225
+ type: 'string',
226
+ default: '',
227
+ description: 'The raw WhatsApp JID to link. Usually taken from List Discovered Chats.',
228
+ displayOptions: {
229
+ show: {
230
+ resource: ['linkChat'],
231
+ linkChatOperation: ['link'],
232
+ },
233
+ },
234
+ },
235
+ {
236
+ displayName: 'Linked Chat',
237
+ name: 'linkAlias',
238
+ type: 'string',
239
+ default: '',
240
+ description: 'Friendly name you will use later when sending messages by alias.',
241
+ displayOptions: {
242
+ show: {
243
+ resource: ['linkChat'],
244
+ linkChatOperation: ['link', 'unlink'],
245
+ },
246
+ },
247
+ },
248
+ {
249
+ displayName: 'Send Message To',
250
+ name: 'sendMessageTo',
251
+ type: 'options',
252
+ default: 'linkedChat',
253
+ description: 'Choose how to resolve the destination chat.',
254
+ options: [
255
+ { name: 'Linked Chat', value: 'linkedChat' },
256
+ { name: 'WhatsApp Number', value: 'number' },
257
+ { name: 'Raw JID', value: 'jid' },
258
+ { name: 'Yourself', value: 'yourself' },
259
+ ],
260
+ displayOptions: {
261
+ show: { resource: ['sendMessage'] },
262
+ },
263
+ },
264
+ {
265
+ displayName: 'Linked Chat',
266
+ name: 'linkedAlias',
267
+ type: 'options',
268
+ default: '',
269
+ description: 'Choose one of the linked chats saved for this session.',
270
+ typeOptions: {
271
+ loadOptionsMethod: 'getLinkedAliases',
272
+ },
273
+ displayOptions: {
274
+ show: {
275
+ resource: ['sendMessage'],
276
+ sendMessageTo: ['linkedChat'],
277
+ },
278
+ },
279
+ },
280
+ {
281
+ displayName: 'WhatsApp Number',
282
+ name: 'phoneNumber',
283
+ type: 'string',
284
+ default: '',
285
+ description: 'Full phone number with country code, digits only, without 00 or +. Example: 393331234567.',
286
+ displayOptions: {
287
+ show: {
288
+ resource: ['sendMessage'],
289
+ sendMessageTo: ['number'],
290
+ },
291
+ },
292
+ },
293
+ {
294
+ displayName: 'JID',
295
+ name: 'jid',
296
+ type: 'string',
297
+ default: '',
298
+ description: 'Raw WhatsApp JID, for example 393331234567@s.whatsapp.net or a group JID.',
299
+ displayOptions: {
300
+ show: {
301
+ resource: ['sendMessage'],
302
+ sendMessageTo: ['jid'],
303
+ },
304
+ },
305
+ },
306
+ {
307
+ displayName: 'Yourself',
308
+ name: 'yourselfNotice',
309
+ type: 'notice',
310
+ default: '',
311
+ description: 'Send the message to the WhatsApp number already connected for this session.',
312
+ displayOptions: {
313
+ show: {
314
+ resource: ['sendMessage'],
315
+ sendMessageTo: ['yourself'],
316
+ },
317
+ },
318
+ },
319
+ {
320
+ displayName: 'Message Type',
321
+ name: 'messageType',
322
+ type: 'options',
323
+ default: 'text',
324
+ description: 'The kind of outbound message to send.',
325
+ displayOptions: {
326
+ show: { resource: ['sendMessage'] },
327
+ },
328
+ options: [
329
+ { name: 'Text', value: 'text' },
330
+ { name: 'Image', value: 'image' },
331
+ { name: 'Video', value: 'video' },
332
+ { name: 'Audio', value: 'audio' },
333
+ { name: 'Document', value: 'document' },
334
+ { name: 'Reaction', value: 'reaction' },
335
+ { name: 'Location', value: 'location' },
336
+ { name: 'Contact', value: 'contact' },
337
+ { name: 'Poll', value: 'poll' },
338
+ ],
339
+ },
340
+ {
341
+ displayName: 'Delivery Mode',
342
+ name: 'deliveryMode',
343
+ type: 'options',
344
+ default: 'native',
345
+ displayOptions: {
346
+ show: {
347
+ resource: ['sendMessage'],
348
+ messageType: ['image', 'video'],
349
+ },
350
+ },
351
+ options: [
352
+ { name: 'Native Media', value: 'native' },
353
+ { name: 'As File', value: 'document' },
354
+ ],
355
+ },
356
+ {
357
+ displayName: 'Message',
358
+ name: 'message',
359
+ type: 'string',
360
+ typeOptions: { rows: 4 },
361
+ default: '',
362
+ description: 'Main text body for the outbound message.',
363
+ displayOptions: {
364
+ show: { resource: ['sendMessage'] },
365
+ hide: { messageType: ['location', 'contact', 'poll'] },
366
+ },
367
+ },
368
+ {
369
+ displayName: 'Media URL',
370
+ name: 'mediaUrl',
371
+ type: 'string',
372
+ default: '',
373
+ description: 'Public URL or reachable file URL for the media to send.',
374
+ displayOptions: {
375
+ show: {
376
+ resource: ['sendMessage'],
377
+ messageType: ['image', 'video', 'audio', 'document'],
378
+ },
379
+ },
380
+ },
381
+ {
382
+ displayName: 'Caption',
383
+ name: 'caption',
384
+ type: 'string',
385
+ default: '',
386
+ displayOptions: {
387
+ show: {
388
+ resource: ['sendMessage'],
389
+ messageType: ['image', 'video', 'document'],
390
+ },
391
+ },
392
+ },
393
+ {
394
+ displayName: 'Mimetype',
395
+ name: 'mimetype',
396
+ type: 'string',
397
+ default: '',
398
+ displayOptions: {
399
+ show: {
400
+ resource: ['sendMessage'],
401
+ messageType: ['image', 'video', 'audio', 'document'],
402
+ },
403
+ },
404
+ },
405
+ {
406
+ displayName: 'File Name',
407
+ name: 'fileName',
408
+ type: 'string',
409
+ default: '',
410
+ displayOptions: {
411
+ show: {
412
+ resource: ['sendMessage'],
413
+ messageType: ['image', 'video', 'document'],
414
+ },
415
+ },
416
+ },
417
+ {
418
+ displayName: 'Reply To Message ID',
419
+ name: 'replyToMessageId',
420
+ type: 'string',
421
+ default: '',
422
+ description: 'Optional. Reply or react to a specific WhatsApp message ID.',
423
+ displayOptions: {
424
+ show: { resource: ['sendMessage'] },
425
+ },
426
+ },
427
+ {
428
+ displayName: 'Reaction Text',
429
+ name: 'reactionText',
430
+ type: 'string',
431
+ default: '👍',
432
+ displayOptions: {
433
+ show: {
434
+ resource: ['sendMessage'],
435
+ messageType: ['reaction'],
436
+ },
437
+ },
438
+ },
439
+ {
440
+ displayName: 'Latitude',
441
+ name: 'latitude',
442
+ type: 'number',
443
+ default: 0,
444
+ displayOptions: {
445
+ show: {
446
+ resource: ['sendMessage'],
447
+ messageType: ['location'],
448
+ },
449
+ },
450
+ },
451
+ {
452
+ displayName: 'Longitude',
453
+ name: 'longitude',
454
+ type: 'number',
455
+ default: 0,
456
+ displayOptions: {
457
+ show: {
458
+ resource: ['sendMessage'],
459
+ messageType: ['location'],
460
+ },
461
+ },
462
+ },
463
+ {
464
+ displayName: 'Location Name',
465
+ name: 'locationName',
466
+ type: 'string',
467
+ default: '',
468
+ displayOptions: {
469
+ show: {
470
+ resource: ['sendMessage'],
471
+ messageType: ['location'],
472
+ },
473
+ },
474
+ },
475
+ {
476
+ displayName: 'Location Address',
477
+ name: 'locationAddress',
478
+ type: 'string',
479
+ default: '',
480
+ displayOptions: {
481
+ show: {
482
+ resource: ['sendMessage'],
483
+ messageType: ['location'],
484
+ },
485
+ },
486
+ },
487
+ {
488
+ displayName: 'Contact Name',
489
+ name: 'contactName',
490
+ type: 'string',
491
+ default: '',
492
+ displayOptions: {
493
+ show: {
494
+ resource: ['sendMessage'],
495
+ messageType: ['contact'],
496
+ },
497
+ },
498
+ },
499
+ {
500
+ displayName: 'Contact VCard',
501
+ name: 'contactVcard',
502
+ type: 'string',
503
+ typeOptions: { rows: 4 },
504
+ default: '',
505
+ displayOptions: {
506
+ show: {
507
+ resource: ['sendMessage'],
508
+ messageType: ['contact'],
509
+ },
510
+ },
511
+ },
512
+ {
513
+ displayName: 'Poll Name',
514
+ name: 'pollName',
515
+ type: 'string',
516
+ default: '',
517
+ displayOptions: {
518
+ show: {
519
+ resource: ['sendMessage'],
520
+ messageType: ['poll'],
521
+ },
522
+ },
523
+ },
524
+ {
525
+ displayName: 'Poll Options',
526
+ name: 'pollOptions',
527
+ type: 'string',
528
+ default: '',
529
+ displayOptions: {
530
+ show: {
531
+ resource: ['sendMessage'],
532
+ messageType: ['poll'],
533
+ },
534
+ },
535
+ },
536
+ {
537
+ displayName: 'Selectable Count',
538
+ name: 'pollSelectableCount',
539
+ type: 'number',
540
+ default: 1,
541
+ displayOptions: {
542
+ show: {
543
+ resource: ['sendMessage'],
544
+ messageType: ['poll'],
545
+ },
546
+ },
547
+ },
548
+ ],
549
+ };
550
+ }
551
+ async execute() {
552
+ const items = this.getInputData();
553
+ const returnData = [];
554
+ const access = await (0, access_1.buildAccess)(this);
555
+ for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
556
+ try {
557
+ const resource = this.getNodeParameter('resource', itemIndex);
558
+ const rawSessionId = this.getNodeParameter('sessionId', itemIndex, '');
559
+ let json;
560
+ if (resource === 'session') {
561
+ const operation = this.getNodeParameter('sessionOperation', itemIndex);
562
+ const sessionId = operation === 'list' ? (0, validation_1.normalizeSessionId)(rawSessionId) : (0, validation_1.requireSessionId)(rawSessionId);
563
+ switch (operation) {
564
+ case 'connect': {
565
+ const label = this.getNodeParameter('label', itemIndex, '').trim();
566
+ const phoneNumberForPairing = this.getNodeParameter('phoneNumberForPairing', itemIndex, '').trim();
567
+ await runtime_1.registry.ensureSession(access.paths.root, access, {
568
+ sessionId,
569
+ label: label || sessionId,
570
+ phoneNumberForPairing,
571
+ });
572
+ json = await runtime_1.registry.connectSession(access.paths.root, access, sessionId);
573
+ break;
574
+ }
575
+ case 'ensure': {
576
+ const label = this.getNodeParameter('label', itemIndex, '').trim();
577
+ const phoneNumberForPairing = this.getNodeParameter('phoneNumberForPairing', itemIndex, '').trim();
578
+ const timeoutSeconds = this.getNodeParameter('timeoutSeconds', itemIndex, 300);
579
+ json = await runtime_1.registry.ensureConnectedSession(access.paths.root, access, {
580
+ sessionId,
581
+ label: label || sessionId,
582
+ phoneNumberForPairing,
583
+ }, {
584
+ waitFor: 'connected',
585
+ timeoutMs: timeoutSeconds * 1000,
586
+ });
587
+ break;
588
+ }
589
+ case 'list':
590
+ json = await runtime_1.registry.listSessions(access);
591
+ break;
592
+ case 'status':
593
+ json = (await runtime_1.registry.getSession(access, sessionId)) ?? null;
594
+ break;
595
+ case 'disconnect':
596
+ json = (await runtime_1.registry.disconnectSession(access, sessionId)) ?? null;
597
+ break;
598
+ case 'remove':
599
+ json = { removed: await runtime_1.registry.removeSession(access, sessionId) };
600
+ break;
601
+ default:
602
+ throw new Error(`Unsupported session operation ${operation}`);
603
+ }
604
+ json = formatSessionOutput(json);
605
+ }
606
+ else if (resource === 'linkChat') {
607
+ const operation = this.getNodeParameter('linkChatOperation', itemIndex);
608
+ const sessionId = (0, validation_1.requireSessionId)(rawSessionId);
609
+ switch (operation) {
610
+ case 'listDiscovered':
611
+ json = await runtime_1.registry.listTargets(access, sessionId);
612
+ break;
613
+ case 'listLinked':
614
+ json = await runtime_1.registry.listLinkedTargets(access, sessionId);
615
+ break;
616
+ case 'link':
617
+ json = await runtime_1.registry.connectTarget(access, sessionId, this.getNodeParameter('linkAlias', itemIndex), this.getNodeParameter('linkJid', itemIndex));
618
+ break;
619
+ case 'unlink':
620
+ json = {
621
+ removed: await runtime_1.registry.unlinkTarget(access, sessionId, this.getNodeParameter('linkAlias', itemIndex)),
622
+ };
623
+ break;
624
+ default:
625
+ throw new Error(`Unsupported link chat operation ${operation}`);
626
+ }
627
+ }
628
+ else if (resource === 'sendMessage') {
629
+ json = await runtime_1.registry.sendMessage(access, buildPayload(this, itemIndex));
630
+ }
631
+ else {
632
+ throw new Error(`Unsupported resource ${resource}`);
633
+ }
634
+ returnData.push({ json: json, pairedItem: itemIndex });
635
+ }
636
+ catch (error) {
637
+ if (this.continueOnFail()) {
638
+ returnData.push({ json: { error: error.message }, pairedItem: itemIndex });
639
+ continue;
640
+ }
641
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex });
642
+ }
643
+ }
644
+ return [returnData];
645
+ }
646
+ }
647
+ exports.WhatsThat = WhatsThat;
@@ -23,8 +23,9 @@ class WhatsThatSession {
23
23
  displayName: 'Operation',
24
24
  name: 'operation',
25
25
  type: 'options',
26
- default: 'ensure',
26
+ default: 'connect',
27
27
  options: [
28
+ { name: 'Connect Session', value: 'connect' },
28
29
  { name: 'Ensure Session', value: 'ensure' },
29
30
  { name: 'List Sessions', value: 'list' },
30
31
  { name: 'Get Session Status', value: 'status' },
@@ -50,7 +51,7 @@ class WhatsThatSession {
50
51
  default: '',
51
52
  description: 'Human-readable name shown in results. Example: "Luca personal phone" or "Support number".',
52
53
  displayOptions: {
53
- show: { operation: ['ensure'] },
54
+ show: { operation: ['connect', 'ensure'] },
54
55
  },
55
56
  },
56
57
  {
@@ -60,7 +61,7 @@ class WhatsThatSession {
60
61
  default: '',
61
62
  description: 'Optional. Full phone number with country code, digits only, without 00 or +. Example: 393331234567.',
62
63
  displayOptions: {
63
- show: { operation: ['ensure'] },
64
+ show: { operation: ['connect', 'ensure'] },
64
65
  },
65
66
  },
66
67
  {
@@ -104,6 +105,17 @@ class WhatsThatSession {
104
105
  const sessionId = operation === 'list' ? (0, validation_1.normalizeSessionId)(rawSessionId) : (0, validation_1.requireSessionId)(rawSessionId);
105
106
  let json;
106
107
  switch (operation) {
108
+ case 'connect': {
109
+ const label = this.getNodeParameter('label', itemIndex, '').trim();
110
+ const phoneNumberForPairing = this.getNodeParameter('phoneNumberForPairing', itemIndex, '').trim();
111
+ await runtime_1.registry.ensureSession(access.paths.root, access, {
112
+ sessionId,
113
+ label: label || sessionId,
114
+ phoneNumberForPairing,
115
+ });
116
+ json = await runtime_1.registry.connectSession(access.paths.root, access, sessionId);
117
+ break;
118
+ }
107
119
  case 'ensure': {
108
120
  const label = this.getNodeParameter('label', itemIndex, '').trim();
109
121
  const phoneNumberForPairing = this.getNodeParameter('phoneNumberForPairing', itemIndex, '').trim();
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WhatsThatTrigger = void 0;
4
+ const access_1 = require("../../shared/access");
4
5
  const runtime_1 = require("../../shared/runtime");
5
6
  const validation_1 = require("../../shared/validation");
6
7
  class WhatsThatTrigger {
@@ -23,7 +24,7 @@ class WhatsThatTrigger {
23
24
  type: 'string',
24
25
  default: '',
25
26
  required: true,
26
- description: 'The unique session ID created in the WhatsThat Session node.',
27
+ description: 'The unique session ID used in the WhatsThat node resource Session.',
27
28
  },
28
29
  {
29
30
  displayName: 'Event',
@@ -31,6 +32,7 @@ class WhatsThatTrigger {
31
32
  type: 'options',
32
33
  default: 'message.received',
33
34
  options: [
35
+ { name: 'Link Chat Command', value: 'link.chat.command' },
34
36
  { name: 'Message Received', value: 'message.received' },
35
37
  { name: 'Message From Me', value: 'message.from_me' },
36
38
  { name: 'Message Sent', value: 'message.sent' },
@@ -41,6 +43,18 @@ class WhatsThatTrigger {
41
43
  { name: 'Group Participants', value: 'group.participants' },
42
44
  { name: 'Any Event', value: '*' }
43
45
  ],
46
+ },
47
+ {
48
+ displayName: 'Link Command',
49
+ name: 'linkCommand',
50
+ type: 'string',
51
+ default: '/link-whatsthat',
52
+ description: 'Users must send this command followed by a space and the alias in the chat to link. Example: /link-whatsthat support',
53
+ displayOptions: {
54
+ show: {
55
+ eventName: ['link.chat.command'],
56
+ },
57
+ },
44
58
  }
45
59
  ],
46
60
  };
@@ -48,9 +62,39 @@ class WhatsThatTrigger {
48
62
  async trigger() {
49
63
  const sessionId = (0, validation_1.requireSessionId)(this.getNodeParameter('sessionId'));
50
64
  const eventName = this.getNodeParameter('eventName');
51
- const handler = (event) => {
65
+ const linkCommand = this.getNodeParameter('linkCommand', '');
66
+ const access = await (0, access_1.buildAccess)(this);
67
+ const handler = async (event) => {
52
68
  if (event.sessionId !== sessionId)
53
69
  return;
70
+ if (eventName === 'link.chat.command') {
71
+ if (event.event !== 'message.received')
72
+ return;
73
+ const data = event.data;
74
+ const text = (0, runtime_1.extractMessageText)(data.message)?.trim();
75
+ const prefix = linkCommand.trim();
76
+ if (!text || !prefix || !text.startsWith(`${prefix} `))
77
+ return;
78
+ const alias = text.slice(prefix.length).trim();
79
+ const jid = data.remoteJid;
80
+ if (!alias || !jid)
81
+ return;
82
+ const linked = await runtime_1.registry.connectTarget(access, sessionId, alias, jid);
83
+ this.emit([
84
+ [
85
+ {
86
+ json: {
87
+ event: 'link.chat.command',
88
+ sessionId,
89
+ alias,
90
+ jid,
91
+ linked,
92
+ },
93
+ },
94
+ ],
95
+ ]);
96
+ return;
97
+ }
54
98
  if (eventName !== '*' && event.event !== eventName)
55
99
  return;
56
100
  this.emit([[{ json: event }]]);
@@ -1,5 +1,5 @@
1
- import type { IDataObject, IExecuteFunctions, ILoadOptionsFunctions } from 'n8n-workflow';
2
- export type NodeContext = IExecuteFunctions | ILoadOptionsFunctions;
1
+ import type { IDataObject, IExecuteFunctions, ILoadOptionsFunctions, ITriggerFunctions } from 'n8n-workflow';
2
+ export type NodeContext = IExecuteFunctions | ILoadOptionsFunctions | ITriggerFunctions;
3
3
  export interface RuntimeConfig {
4
4
  storagePath: string;
5
5
  }
@@ -13,6 +13,8 @@ type SendRequest = {
13
13
  sessionId: string;
14
14
  channelAlias?: string;
15
15
  jid?: string;
16
+ phoneNumber?: string;
17
+ sendToSelf?: boolean;
16
18
  type: 'text' | 'image' | 'video' | 'audio' | 'document' | 'reaction' | 'location' | 'contact' | 'poll';
17
19
  sendAsDocument?: boolean;
18
20
  message?: string;
@@ -46,7 +48,7 @@ declare class WhatsThatRegistry extends EventEmitter {
46
48
  phoneNumberForPairing?: string;
47
49
  }): Promise<SessionRecord>;
48
50
  connectSession(storageRoot: string, access: DataAccess, sessionId: string): Promise<SessionRecord>;
49
- ensureConnectedSession(storageRoot: string, access: DataAccess, input: {
51
+ ensureConnectedSession(_storageRoot: string, access: DataAccess, input: {
50
52
  sessionId: string;
51
53
  label: string;
52
54
  phoneNumberForPairing?: string;
@@ -64,6 +66,7 @@ declare class WhatsThatRegistry extends EventEmitter {
64
66
  unlinkTarget(access: DataAccess, sessionId: string, alias: string): Promise<boolean>;
65
67
  sendMessage(access: DataAccess, request: SendRequest): Promise<Record<string, unknown>>;
66
68
  private buildContent;
69
+ private resolveTargetJid;
67
70
  private syncGroups;
68
71
  private rememberTarget;
69
72
  private upsertSession;
@@ -71,6 +74,7 @@ declare class WhatsThatRegistry extends EventEmitter {
71
74
  private emitRuntime;
72
75
  private waitForSessionState;
73
76
  private matchesWaitTarget;
77
+ waitForConnectedSession(access: DataAccess, sessionId: string, timeoutMs: number): Promise<SessionRecord>;
74
78
  private required;
75
79
  private buildQrCodeUrl;
76
80
  }
@@ -219,14 +219,14 @@ class WhatsThatRegistry extends node_events_1.EventEmitter {
219
219
  this.sockets.set(sessionId, socket);
220
220
  return (await this.getSession(access, sessionId)) ?? starting;
221
221
  }
222
- async ensureConnectedSession(storageRoot, access, input, options) {
223
- await this.ensureSession(storageRoot, access, input);
222
+ async ensureConnectedSession(_storageRoot, access, input, options) {
223
+ await this.ensureSession(_storageRoot, access, input);
224
224
  const current = await this.getSession(access, input.sessionId);
225
225
  if (!current) {
226
226
  throw new Error(`Unknown session ${input.sessionId}`);
227
227
  }
228
228
  if (!this.sockets.has(input.sessionId) && current.status !== 'connected') {
229
- await this.connectSession(storageRoot, access, input.sessionId);
229
+ throw new Error(`Session ${input.sessionId} is not active. Run Connect Session first and keep the n8n runtime alive until pairing completes.`);
230
230
  }
231
231
  return this.waitForSessionState(access, input.sessionId, options?.waitFor ?? 'pairing_or_connected', options?.timeoutMs ?? 20000);
232
232
  }
@@ -317,11 +317,9 @@ class WhatsThatRegistry extends node_events_1.EventEmitter {
317
317
  if (!socket) {
318
318
  throw new Error(`Session ${request.sessionId} is not connected`);
319
319
  }
320
- const linked = await this.listLinkedTargets(access, request.sessionId);
321
- const targetJid = request.jid ??
322
- linked.find((item) => item.alias === request.channelAlias)?.jid;
320
+ const targetJid = await this.resolveTargetJid(access, request, socket);
323
321
  if (!targetJid) {
324
- throw new Error('Unknown target. Use a linked alias or raw JID.');
322
+ throw new Error('Unknown target. Use a linked chat, WhatsApp number, raw JID, or Yourself.');
325
323
  }
326
324
  const content = this.buildContent(request, targetJid);
327
325
  const response = await socket.sendMessage(targetJid, content, {
@@ -419,6 +417,28 @@ class WhatsThatRegistry extends node_events_1.EventEmitter {
419
417
  };
420
418
  }
421
419
  }
420
+ async resolveTargetJid(access, request, socket) {
421
+ if (request.jid) {
422
+ return request.jid;
423
+ }
424
+ if (request.phoneNumber) {
425
+ return `${request.phoneNumber}@s.whatsapp.net`;
426
+ }
427
+ if (request.sendToSelf) {
428
+ const session = await this.getSession(access, request.sessionId);
429
+ const raw = session?.phone ?? socket.user?.id;
430
+ const normalized = raw?.split(':')[0];
431
+ if (!normalized) {
432
+ throw new Error(`Session ${request.sessionId} does not have a known WhatsApp number yet. Connect the session first.`);
433
+ }
434
+ return normalized;
435
+ }
436
+ if (request.channelAlias) {
437
+ const linked = await this.listLinkedTargets(access, request.sessionId);
438
+ return linked.find((item) => item.alias === request.channelAlias)?.jid;
439
+ }
440
+ return undefined;
441
+ }
422
442
  async syncGroups(access, sessionId, socket) {
423
443
  const groups = await socket.groupFetchAllParticipating();
424
444
  for (const [jid, data] of Object.entries(groups)) {
@@ -508,6 +528,16 @@ class WhatsThatRegistry extends node_events_1.EventEmitter {
508
528
  }
509
529
  return false;
510
530
  }
531
+ async waitForConnectedSession(access, sessionId, timeoutMs) {
532
+ const current = await this.getSession(access, sessionId);
533
+ if (!current) {
534
+ throw new Error(`Unknown session ${sessionId}`);
535
+ }
536
+ if (!this.sockets.has(sessionId) && current.status !== 'connected') {
537
+ throw new Error(`Session ${sessionId} is not active. Run Connect Session first and keep the n8n runtime alive until pairing completes.`);
538
+ }
539
+ return this.waitForSessionState(access, sessionId, 'connected', timeoutMs);
540
+ }
511
541
  required(value, field) {
512
542
  if (value === undefined || value === null || value === '') {
513
543
  throw new Error(`Missing required field ${field}`);
@@ -1,2 +1,4 @@
1
1
  export declare function normalizeSessionId(sessionId: string): string;
2
2
  export declare function requireSessionId(sessionId: string): string;
3
+ export declare function normalizeWhatsappNumber(phoneNumber: string): string;
4
+ export declare function requireWhatsappNumber(phoneNumber: string): string;
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.normalizeSessionId = normalizeSessionId;
4
4
  exports.requireSessionId = requireSessionId;
5
+ exports.normalizeWhatsappNumber = normalizeWhatsappNumber;
6
+ exports.requireWhatsappNumber = requireWhatsappNumber;
5
7
  function normalizeSessionId(sessionId) {
6
8
  return sessionId.trim();
7
9
  }
@@ -12,3 +14,13 @@ function requireSessionId(sessionId) {
12
14
  }
13
15
  return normalized;
14
16
  }
17
+ function normalizeWhatsappNumber(phoneNumber) {
18
+ return phoneNumber.trim().replace(/\s+/g, '').replace(/^\+/, '').replace(/^00/, '');
19
+ }
20
+ function requireWhatsappNumber(phoneNumber) {
21
+ const normalized = normalizeWhatsappNumber(phoneNumber);
22
+ if (!normalized || !/^\d+$/.test(normalized)) {
23
+ throw new Error('WhatsApp Number must contain digits only, including country code, without 00 or +.');
24
+ }
25
+ return normalized;
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jep182/n8n-nodes-whatsthat",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "n8n community nodes with embedded Baileys runtime for multi-session chat automation",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -57,9 +57,7 @@
57
57
  "dist/credentials/WhatsThatRuntime.credentials.js"
58
58
  ],
59
59
  "nodes": [
60
- "dist/nodes/WhatsThatSession/WhatsThatSession.node.js",
61
- "dist/nodes/WhatsThatTargets/WhatsThatTargets.node.js",
62
- "dist/nodes/WhatsThatMessage/WhatsThatMessage.node.js",
60
+ "dist/nodes/WhatsThat/WhatsThat.node.js",
63
61
  "dist/nodes/WhatsThatTrigger/WhatsThatTrigger.node.js"
64
62
  ]
65
63
  }