@jep182/n8n-nodes-whatsthat 0.5.0 → 0.5.1

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
@@ -2,9 +2,9 @@
2
2
 
3
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
- You will mainly use:
5
+ In the n8n node picker, you will find:
6
6
 
7
- - `WhatsThat`
7
+ - `WhatsThat` actions
8
8
  - `WhatsThat Trigger`
9
9
 
10
10
  You also need one credential:
@@ -21,10 +21,16 @@ Create `WhatsThat Runtime` credentials and choose a persistent storage path, for
21
21
 
22
22
  This folder is used to store session files and local metadata.
23
23
 
24
+ The action nodes all appear under the same name, `WhatsThat`, but with different purposes:
25
+
26
+ - session actions
27
+ - linked chat actions
28
+ - message actions
29
+
24
30
  ## How To Connect Your Number
25
31
 
26
32
  1. Add a `WhatsThat` node.
27
- 2. Set `Resource` to `Session`.
33
+ 2. Choose the action for sessions.
28
34
  3. Set `Operation` to `Connect Session`.
29
35
  4. Fill in:
30
36
  - `Session Name`: a stable internal name like `main-phone`
@@ -54,7 +60,7 @@ WhatsApp Number: 393331234567
54
60
 
55
61
  After `Connect Session`, add another `WhatsThat` node:
56
62
 
57
- 1. Set `Resource` to `Session`
63
+ 1. Choose the action for sessions
58
64
  2. Set `Operation` to `Wait Until Connect`
59
65
  3. Use the same `Session Name`
60
66
  4. Choose how many seconds to wait in `Timeout Seconds`
@@ -64,7 +70,7 @@ This second node waits for the already-started session to become fully connected
64
70
  ## How To Link A Group Or Chat Manually
65
71
 
66
72
  1. Add a `WhatsThat` node
67
- 2. Set `Resource` to `Linked Chat`
73
+ 2. Choose the action for linked chats
68
74
  3. Start with `Operation = List Discovered Chats`
69
75
  4. Pick the chat or group you want to use
70
76
  5. Change to `Operation = Link Chat`
@@ -103,7 +109,7 @@ This is useful when you want users to self-register a group without opening n8n.
103
109
  ## How To Send A Message To A Linked Chat
104
110
 
105
111
  1. Add a `WhatsThat` node
106
- 2. Set `Resource` to `Send Message`
112
+ 2. Choose the action for sending messages
107
113
  3. Set `Session Name`
108
114
  4. Set `Send Message To` to `Linked Chat`
109
115
  5. Choose a linked chat from the dropdown
@@ -122,7 +128,7 @@ Message: Hello from n8n
122
128
  ## How To Send A Message To A Number
123
129
 
124
130
  1. Add a `WhatsThat` node
125
- 2. Set `Resource` to `Send Message`
131
+ 2. Choose the action for sending messages
126
132
  3. Set `Send Message To` to `WhatsApp Number`
127
133
  4. Enter the number with country code, digits only, without `00` and without `+`
128
134
 
@@ -135,7 +141,7 @@ WhatsApp Number: 393331234567
135
141
  ## How To Send A Message To Yourself
136
142
 
137
143
  1. Add a `WhatsThat` node
138
- 2. Set `Resource` to `Send Message`
144
+ 2. Choose the action for sending messages
139
145
  3. Set `Send Message To` to `Yourself`
140
146
 
141
147
  WhatsThat uses the number already connected for that session.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export * from './credentials/WhatsThatRuntime.credentials';
2
- export * from './nodes/WhatsThat/WhatsThat.node';
2
+ export * from './nodes/WhatsThatSession/WhatsThatSession.node';
3
+ export * from './nodes/WhatsThatTargets/WhatsThatTargets.node';
4
+ export * from './nodes/WhatsThatMessage/WhatsThatMessage.node';
3
5
  export * from './nodes/WhatsThatTrigger/WhatsThatTrigger.node';
package/dist/index.js CHANGED
@@ -15,5 +15,7 @@ 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/WhatsThat/WhatsThat.node"), 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);
19
21
  __exportStar(require("./nodes/WhatsThatTrigger/WhatsThatTrigger.node"), exports);
@@ -1,5 +1,10 @@
1
- import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
1
+ import type { IExecuteFunctions, ILoadOptionsFunctions, INodeExecutionData, INodePropertyOptions, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
2
  export declare class WhatsThatMessage implements INodeType {
3
+ methods: {
4
+ loadOptions: {
5
+ getLinkedChats(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
6
+ };
7
+ };
3
8
  description: INodeTypeDescription;
4
9
  execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
10
  }
@@ -7,18 +7,24 @@ const runtime_1 = require("../../shared/runtime");
7
7
  const validation_1 = require("../../shared/validation");
8
8
  function buildPayload(context, itemIndex) {
9
9
  const messageType = context.getNodeParameter('messageType', itemIndex);
10
- const targetMode = context.getNodeParameter('targetMode', itemIndex);
10
+ const sendMessageTo = context.getNodeParameter('sendMessageTo', itemIndex);
11
11
  const deliveryMode = context.getNodeParameter('deliveryMode', itemIndex, 'native');
12
12
  const payload = {
13
13
  sessionId: (0, validation_1.requireSessionId)(context.getNodeParameter('sessionId', itemIndex)),
14
14
  type: messageType,
15
15
  };
16
- if (targetMode === 'alias') {
17
- payload.channelAlias = context.getNodeParameter('alias', itemIndex);
16
+ if (sendMessageTo === 'linkedChat') {
17
+ payload.channelAlias = context.getNodeParameter('linkedAlias', itemIndex);
18
18
  }
19
- else {
19
+ else if (sendMessageTo === 'jid') {
20
20
  payload.jid = context.getNodeParameter('jid', itemIndex);
21
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
+ }
22
28
  payload.message = context.getNodeParameter('message', itemIndex, '');
23
29
  payload.replyToMessageId = context.getNodeParameter('replyToMessageId', itemIndex, '');
24
30
  if (['image', 'video', 'audio', 'document'].includes(messageType)) {
@@ -61,49 +67,93 @@ function buildPayload(context, itemIndex) {
61
67
  }
62
68
  class WhatsThatMessage {
63
69
  constructor() {
70
+ this.methods = {
71
+ loadOptions: {
72
+ async getLinkedChats() {
73
+ const access = await (0, access_1.buildAccess)(this);
74
+ const sessionId = (0, validation_1.requireSessionId)(this.getNodeParameter('sessionId'));
75
+ const linked = await runtime_1.registry.listLinkedTargets(access, sessionId);
76
+ return linked.map((item) => ({
77
+ name: `${item.alias} (${item.displayName})`,
78
+ value: item.alias,
79
+ }));
80
+ },
81
+ },
82
+ };
64
83
  this.description = {
65
- displayName: 'WhatsThat Message',
84
+ displayName: 'WhatsThat',
66
85
  name: 'whatsThatMessage',
67
86
  icon: 'file:../WhatsThatSession/whatsthat.svg',
68
87
  group: ['transform'],
69
88
  version: 1,
70
- description: 'Send messages and media through an embedded WhatsThat session',
71
- defaults: { name: 'WhatsThat Message' },
89
+ description: 'Send WhatsApp messages and media',
90
+ defaults: { name: 'Send Message' },
72
91
  inputs: ['main'],
73
92
  outputs: ['main'],
74
93
  credentials: [{ name: 'whatsThatRuntime', required: true }],
75
94
  properties: [
76
95
  {
77
- displayName: 'Session ID (Internal)',
96
+ displayName: 'Session Name',
78
97
  name: 'sessionId',
79
98
  type: 'string',
80
99
  default: '',
81
100
  required: true,
82
- description: 'The unique session ID created in the WhatsThat Session node.',
101
+ description: 'The connected session that will send the message.',
83
102
  },
84
103
  {
85
- displayName: 'Target Mode',
86
- name: 'targetMode',
104
+ displayName: 'Send Message To',
105
+ name: 'sendMessageTo',
87
106
  type: 'options',
88
- default: 'alias',
107
+ default: 'linkedChat',
89
108
  options: [
90
- { name: 'Linked Alias', value: 'alias' },
109
+ { name: 'Linked Chat', value: 'linkedChat' },
110
+ { name: 'WhatsApp Number', value: 'number' },
91
111
  { name: 'Raw JID', value: 'jid' },
112
+ { name: 'Yourself', value: 'yourself' },
92
113
  ],
93
114
  },
94
115
  {
95
- displayName: 'Alias',
96
- name: 'alias',
116
+ displayName: 'Linked Chat',
117
+ name: 'linkedAlias',
118
+ type: 'options',
119
+ default: '',
120
+ description: 'Choose one of the chats already linked for this session.',
121
+ typeOptions: {
122
+ loadOptionsMethod: 'getLinkedChats',
123
+ },
124
+ displayOptions: {
125
+ show: { sendMessageTo: ['linkedChat'] },
126
+ },
127
+ },
128
+ {
129
+ displayName: 'WhatsApp Number',
130
+ name: 'phoneNumber',
97
131
  type: 'string',
98
132
  default: '',
99
- displayOptions: { show: { targetMode: ['alias'] } },
133
+ description: 'Full number with country code, digits only, without 00 or +. Example: 393331234567.',
134
+ displayOptions: {
135
+ show: { sendMessageTo: ['number'] },
136
+ },
100
137
  },
101
138
  {
102
139
  displayName: 'JID',
103
140
  name: 'jid',
104
141
  type: 'string',
105
142
  default: '',
106
- displayOptions: { show: { targetMode: ['jid'] } },
143
+ description: 'Raw WhatsApp JID, for example 393331234567@s.whatsapp.net.',
144
+ displayOptions: {
145
+ show: { sendMessageTo: ['jid'] },
146
+ },
147
+ },
148
+ {
149
+ displayName: 'Send To Yourself',
150
+ name: 'yourselfNotice',
151
+ type: 'notice',
152
+ default: '',
153
+ description: 'The message will be sent to the WhatsApp number already connected for this session.',
154
+ displayOptions: {
155
+ show: { sendMessageTo: ['yourself'] },
156
+ },
107
157
  },
108
158
  {
109
159
  displayName: 'Message Type',
@@ -139,6 +189,7 @@ class WhatsThatMessage {
139
189
  type: 'string',
140
190
  typeOptions: { rows: 4 },
141
191
  default: '',
192
+ description: 'Main text body for the outbound message.',
142
193
  displayOptions: { hide: { messageType: ['location', 'contact', 'poll'] } },
143
194
  },
144
195
  {
@@ -146,6 +197,7 @@ class WhatsThatMessage {
146
197
  name: 'mediaUrl',
147
198
  type: 'string',
148
199
  default: '',
200
+ description: 'Public URL or reachable file URL for the media to send.',
149
201
  displayOptions: { show: { messageType: ['image', 'video', 'audio', 'document'] } },
150
202
  },
151
203
  {
@@ -169,7 +221,13 @@ class WhatsThatMessage {
169
221
  default: '',
170
222
  displayOptions: { show: { messageType: ['image', 'video', 'document'] } },
171
223
  },
172
- { displayName: 'Reply To Message ID', name: 'replyToMessageId', type: 'string', default: '' },
224
+ {
225
+ displayName: 'Reply To Message ID',
226
+ name: 'replyToMessageId',
227
+ type: 'string',
228
+ default: '',
229
+ description: 'Optional. Reply or react to a specific WhatsApp message ID.',
230
+ },
173
231
  {
174
232
  displayName: 'Reaction Text',
175
233
  name: 'reactionText',
@@ -240,7 +298,7 @@ class WhatsThatMessage {
240
298
  type: 'number',
241
299
  default: 1,
242
300
  displayOptions: { show: { messageType: ['poll'] } },
243
- }
301
+ },
244
302
  ],
245
303
  };
246
304
  }
@@ -5,16 +5,43 @@ const n8n_workflow_1 = require("n8n-workflow");
5
5
  const access_1 = require("../../shared/access");
6
6
  const runtime_1 = require("../../shared/runtime");
7
7
  const validation_1 = require("../../shared/validation");
8
+ function formatSessionOutput(value) {
9
+ if (Array.isArray(value)) {
10
+ return value.map((item) => formatSessionOutput(item));
11
+ }
12
+ if (!value || typeof value !== 'object') {
13
+ return value;
14
+ }
15
+ const record = value;
16
+ if (!('sessionId' in record) || !('status' in record)) {
17
+ return value;
18
+ }
19
+ return {
20
+ qrCodeUrl: record.qrCodeUrl,
21
+ sessionId: record.sessionId,
22
+ status: record.status,
23
+ pairingCode: record.pairingCode,
24
+ qr: record.qr,
25
+ qrDataUrl: record.qrDataUrl,
26
+ phone: record.phone,
27
+ label: record.label,
28
+ phoneNumberForPairing: record.phoneNumberForPairing,
29
+ lastDisconnectReason: record.lastDisconnectReason,
30
+ createdAt: record.createdAt,
31
+ updatedAt: record.updatedAt,
32
+ lastSeenAt: record.lastSeenAt,
33
+ };
34
+ }
8
35
  class WhatsThatSession {
9
36
  constructor() {
10
37
  this.description = {
11
- displayName: 'WhatsThat Session',
38
+ displayName: 'WhatsThat',
12
39
  name: 'whatsThatSession',
13
40
  icon: 'file:whatsthat.svg',
14
41
  group: ['transform'],
15
42
  version: 1,
16
- description: 'Create, connect, and manage embedded Baileys sessions for WhatsThat',
17
- defaults: { name: 'WhatsThat Session' },
43
+ description: 'Connect and manage WhatsApp sessions',
44
+ defaults: { name: 'Connect Session' },
18
45
  inputs: ['main'],
19
46
  outputs: ['main'],
20
47
  credentials: [{ name: 'whatsThatRuntime', required: true }],
@@ -26,56 +53,42 @@ class WhatsThatSession {
26
53
  default: 'connect',
27
54
  options: [
28
55
  { name: 'Connect Session', value: 'connect' },
29
- { name: 'Ensure Session', value: 'ensure' },
56
+ { name: 'Wait Until Connect', value: 'ensure' },
30
57
  { name: 'List Sessions', value: 'list' },
31
- { name: 'Get Session Status', value: 'status' },
58
+ { name: 'Get Session', value: 'status' },
32
59
  { name: 'Disconnect Session', value: 'disconnect' },
33
60
  { name: 'Remove Session', value: 'remove' },
34
61
  ],
35
62
  },
36
63
  {
37
- displayName: 'Session ID (Internal)',
64
+ displayName: 'Session Name',
38
65
  name: 'sessionId',
39
66
  type: 'string',
40
67
  default: '',
41
68
  required: true,
42
- description: 'Required unique ID for this session. Use a stable internal value such as "main-phone" or "support-team".',
69
+ description: 'Stable internal name for this session, for example main-phone or support-phone.',
43
70
  displayOptions: {
44
71
  hide: { operation: ['list'] },
45
72
  },
46
73
  },
47
74
  {
48
- displayName: 'Label (Visible Name)',
75
+ displayName: 'Display Name',
49
76
  name: 'label',
50
77
  type: 'string',
51
78
  default: '',
52
- description: 'Human-readable name shown in results. Example: "Luca personal phone" or "Support number".',
79
+ description: 'Friendly label for this session, for example Luca phone or Support phone.',
53
80
  displayOptions: {
54
- show: { operation: ['connect', 'ensure'] },
81
+ show: { operation: ['connect'] },
55
82
  },
56
83
  },
57
84
  {
58
- displayName: 'Phone Number For Pairing',
85
+ displayName: 'WhatsApp Number',
59
86
  name: 'phoneNumberForPairing',
60
87
  type: 'string',
61
88
  default: '',
62
- description: 'Optional. Full phone number with country code, digits only, without 00 or +. Example: 393331234567.',
63
- displayOptions: {
64
- show: { operation: ['connect', 'ensure'] },
65
- },
66
- },
67
- {
68
- displayName: 'Return When',
69
- name: 'waitFor',
70
- type: 'options',
71
- default: 'pairing_or_connected',
72
- options: [
73
- { name: 'Pairing Is Ready Or Connected', value: 'pairing_or_connected' },
74
- { name: 'Connected', value: 'connected' },
75
- ],
76
- description: 'Return as soon as pairing data is ready, or wait until the session is fully connected.',
89
+ description: 'Optional. Full number with country code, digits only, without 00 or +. Open qrCodeUrl if available.',
77
90
  displayOptions: {
78
- show: { operation: ['ensure'] },
91
+ show: { operation: ['connect'] },
79
92
  },
80
93
  },
81
94
  {
@@ -85,8 +98,8 @@ class WhatsThatSession {
85
98
  typeOptions: {
86
99
  minValue: 1,
87
100
  },
88
- default: 20,
89
- description: 'Maximum time to wait before returning the latest known session status.',
101
+ default: 300,
102
+ description: 'How long to wait for the already-started session to become connected.',
90
103
  displayOptions: {
91
104
  show: { operation: ['ensure'] },
92
105
  },
@@ -117,16 +130,12 @@ class WhatsThatSession {
117
130
  break;
118
131
  }
119
132
  case 'ensure': {
120
- const label = this.getNodeParameter('label', itemIndex, '').trim();
121
- const phoneNumberForPairing = this.getNodeParameter('phoneNumberForPairing', itemIndex, '').trim();
122
- const waitFor = this.getNodeParameter('waitFor', itemIndex);
123
- const timeoutSeconds = this.getNodeParameter('timeoutSeconds', itemIndex, 20);
133
+ const timeoutSeconds = this.getNodeParameter('timeoutSeconds', itemIndex, 300);
124
134
  json = await runtime_1.registry.ensureConnectedSession(access.paths.root, access, {
125
135
  sessionId,
126
- label: label || sessionId,
127
- phoneNumberForPairing,
136
+ label: sessionId,
128
137
  }, {
129
- waitFor,
138
+ waitFor: 'connected',
130
139
  timeoutMs: timeoutSeconds * 1000,
131
140
  });
132
141
  break;
@@ -146,7 +155,7 @@ class WhatsThatSession {
146
155
  default:
147
156
  throw new Error(`Unsupported operation ${operation}`);
148
157
  }
149
- returnData.push({ json: json, pairedItem: itemIndex });
158
+ returnData.push({ json: formatSessionOutput(json), pairedItem: itemIndex });
150
159
  }
151
160
  catch (error) {
152
161
  if (this.continueOnFail()) {
@@ -8,13 +8,13 @@ const validation_1 = require("../../shared/validation");
8
8
  class WhatsThatTargets {
9
9
  constructor() {
10
10
  this.description = {
11
- displayName: 'WhatsThat Targets',
11
+ displayName: 'WhatsThat',
12
12
  name: 'whatsThatTargets',
13
13
  icon: 'file:../WhatsThatSession/whatsthat.svg',
14
14
  group: ['transform'],
15
15
  version: 1,
16
- description: 'Discover, link, and manage chats and groups for WhatsThat',
17
- defaults: { name: 'WhatsThat Targets' },
16
+ description: 'Link chats and groups with simple names',
17
+ defaults: { name: 'Link Chat' },
18
18
  inputs: ['main'],
19
19
  outputs: ['main'],
20
20
  credentials: [{ name: 'whatsThatRuntime', required: true }],
@@ -25,34 +25,36 @@ class WhatsThatTargets {
25
25
  type: 'options',
26
26
  default: 'listDiscovered',
27
27
  options: [
28
- { name: 'List Discovered Targets', value: 'listDiscovered' },
29
- { name: 'List Linked Targets', value: 'listLinked' },
30
- { name: 'Link Target', value: 'link' },
31
- { name: 'Unlink Target', value: 'unlink' },
28
+ { name: 'List Discovered Chats', value: 'listDiscovered' },
29
+ { name: 'List Linked Chats', value: 'listLinked' },
30
+ { name: 'Link Chat', value: 'link' },
31
+ { name: 'Unlink Chat', value: 'unlink' },
32
32
  ],
33
33
  },
34
34
  {
35
- displayName: 'Session ID (Internal)',
35
+ displayName: 'Session Name',
36
36
  name: 'sessionId',
37
37
  type: 'string',
38
38
  default: '',
39
39
  required: true,
40
- description: 'The unique session ID created in the WhatsThat Session node.',
40
+ description: 'The session that owns these chats and groups.',
41
41
  },
42
42
  {
43
- displayName: 'Target JID',
43
+ displayName: 'Chat JID',
44
44
  name: 'jid',
45
45
  type: 'string',
46
46
  default: '',
47
+ description: 'Raw WhatsApp chat or group JID. Usually copied from List Discovered Chats.',
47
48
  displayOptions: {
48
49
  show: { operation: ['link'] },
49
50
  },
50
51
  },
51
52
  {
52
- displayName: 'Alias',
53
+ displayName: 'Linked Chat',
53
54
  name: 'alias',
54
55
  type: 'string',
55
56
  default: '',
57
+ description: 'Simple name you will use later, for example support, sales, or team.',
56
58
  displayOptions: {
57
59
  show: { operation: ['link', 'unlink'] },
58
60
  },
@@ -19,12 +19,12 @@ class WhatsThatTrigger {
19
19
  credentials: [{ name: 'whatsThatRuntime', required: true }],
20
20
  properties: [
21
21
  {
22
- displayName: 'Session ID (Internal)',
22
+ displayName: 'Session Name',
23
23
  name: 'sessionId',
24
24
  type: 'string',
25
25
  default: '',
26
26
  required: true,
27
- description: 'The unique session ID used in the WhatsThat node resource Session.',
27
+ description: 'The session that owns the incoming events for this trigger.',
28
28
  },
29
29
  {
30
30
  displayName: 'Event',
@@ -62,7 +62,7 @@ class WhatsThatTrigger {
62
62
  async trigger() {
63
63
  const sessionId = (0, validation_1.requireSessionId)(this.getNodeParameter('sessionId'));
64
64
  const eventName = this.getNodeParameter('eventName');
65
- const linkCommand = this.getNodeParameter('linkCommand', '');
65
+ const linkCommand = this.getNodeParameter('linkCommand') || '/link-whatsthat';
66
66
  const access = await (0, access_1.buildAccess)(this);
67
67
  const handler = async (event) => {
68
68
  if (event.sessionId !== sessionId)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jep182/n8n-nodes-whatsthat",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
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,7 +57,9 @@
57
57
  "dist/credentials/WhatsThatRuntime.credentials.js"
58
58
  ],
59
59
  "nodes": [
60
- "dist/nodes/WhatsThat/WhatsThat.node.js",
60
+ "dist/nodes/WhatsThatSession/WhatsThatSession.node.js",
61
+ "dist/nodes/WhatsThatTargets/WhatsThatTargets.node.js",
62
+ "dist/nodes/WhatsThatMessage/WhatsThatMessage.node.js",
61
63
  "dist/nodes/WhatsThatTrigger/WhatsThatTrigger.node.js"
62
64
  ]
63
65
  }