node-red-contrib-whatsapp-api 0.1.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/LICENSE +6 -0
- package/README.md +267 -0
- package/dist/nodes/whatsapp-api.html +358 -0
- package/dist/nodes/whatsapp-api.js +165648 -0
- package/package.json +47 -0
package/LICENSE
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# node-red-contrib-whatsapp-api
|
|
2
|
+
|
|
3
|
+
Node-RED nodes for driving a WhatsApp Web session with [Baileys](https://github.com/WhiskeySockets/Baileys).
|
|
4
|
+
|
|
5
|
+
## What it includes
|
|
6
|
+
|
|
7
|
+
- `whatsapp-api-config`: stores reconnect settings and the local runtime data directory
|
|
8
|
+
- `whatsapp-api-in`: emits normalized incoming WhatsApp messages
|
|
9
|
+
- `whatsapp-api-send`: sends text or media to a chat
|
|
10
|
+
- `whatsapp-api-history`: reads recent messages from the local persisted store
|
|
11
|
+
|
|
12
|
+
## Runtime requirements
|
|
13
|
+
|
|
14
|
+
- Node.js 20+
|
|
15
|
+
- Node-RED 4+
|
|
16
|
+
- A phone that can scan the WhatsApp Web QR code
|
|
17
|
+
|
|
18
|
+
## Install locally
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install
|
|
22
|
+
npm run build
|
|
23
|
+
cd ~/.node-red
|
|
24
|
+
npm install /absolute/path/to/node-red-contrib-whatsapp-api-0.1.0.tgz
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Then restart Node-RED and add the `whatsapp-api` nodes from the palette.
|
|
28
|
+
|
|
29
|
+
The packed module ships as a bundled runtime, so target installs do not need to rebuild or reshuffle the Baileys dependency tree on the Node-RED host.
|
|
30
|
+
|
|
31
|
+
## Install from npm
|
|
32
|
+
|
|
33
|
+
After the package has been published:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
cd ~/.node-red
|
|
37
|
+
npm install node-red-contrib-whatsapp-api
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Login flow
|
|
41
|
+
|
|
42
|
+
This palette uses a deploy-first QR flow.
|
|
43
|
+
|
|
44
|
+
1. Add a `whatsapp-api-config` node and deploy the flow.
|
|
45
|
+
2. Open the config node again in the editor.
|
|
46
|
+
3. Click `Connect`.
|
|
47
|
+
4. Scan the QR code with WhatsApp on your phone.
|
|
48
|
+
5. Wait for the status to show `Connected`.
|
|
49
|
+
|
|
50
|
+
The runtime keeps its auth state on disk and now attempts to reuse it automatically after every Node-RED restart. Use `Disconnect` to clear the stored session and force a fresh QR login.
|
|
51
|
+
|
|
52
|
+
## Local message history
|
|
53
|
+
|
|
54
|
+
`whatsapp-api-history` reads from a local JSON store that is updated by the connected runtime. It returns messages the session has already synced or seen. It is not a server-side fetch of arbitrary WhatsApp chat history.
|
|
55
|
+
|
|
56
|
+
The history node can also return only the newest incoming messages that the linked WhatsApp account still has unread for the selected chat.
|
|
57
|
+
|
|
58
|
+
## Example Node-RED flow
|
|
59
|
+
|
|
60
|
+
Import this JSON from the Node-RED editor (`Menu -> Import -> Clipboard`) to get a starter flow with:
|
|
61
|
+
|
|
62
|
+
- one `whatsapp-api-config` node
|
|
63
|
+
- one `whatsapp-api-send` example
|
|
64
|
+
- one `whatsapp-api-in` listener
|
|
65
|
+
- one `whatsapp-api-history` example
|
|
66
|
+
|
|
67
|
+
Replace the sample peer with a real WhatsApp JID such as `393331234567@s.whatsapp.net`.
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
[
|
|
71
|
+
{
|
|
72
|
+
"id": "a1f4d7c2e9b00101",
|
|
73
|
+
"type": "tab",
|
|
74
|
+
"label": "WhatsApp Example",
|
|
75
|
+
"disabled": false,
|
|
76
|
+
"info": ""
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"id": "b2f4d7c2e9b00102",
|
|
80
|
+
"type": "whatsapp-api-config",
|
|
81
|
+
"name": "My WhatsApp",
|
|
82
|
+
"reconnectMinMs": "2000",
|
|
83
|
+
"reconnectMaxMs": "30000",
|
|
84
|
+
"dataDir": ""
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"id": "c3f4d7c2e9b00103",
|
|
88
|
+
"type": "inject",
|
|
89
|
+
"z": "a1f4d7c2e9b00101",
|
|
90
|
+
"name": "Send test message",
|
|
91
|
+
"props": [
|
|
92
|
+
{
|
|
93
|
+
"p": "payload"
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
"payload": "hello from Node-RED",
|
|
97
|
+
"payloadType": "str",
|
|
98
|
+
"x": 170,
|
|
99
|
+
"y": 100,
|
|
100
|
+
"wires": [
|
|
101
|
+
[
|
|
102
|
+
"d4f4d7c2e9b00104"
|
|
103
|
+
]
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"id": "d4f4d7c2e9b00104",
|
|
108
|
+
"type": "whatsapp-api-send",
|
|
109
|
+
"z": "a1f4d7c2e9b00101",
|
|
110
|
+
"name": "Send WhatsApp",
|
|
111
|
+
"account": "b2f4d7c2e9b00102",
|
|
112
|
+
"peer": "393331234567@s.whatsapp.net",
|
|
113
|
+
"x": 450,
|
|
114
|
+
"y": 100,
|
|
115
|
+
"wires": [
|
|
116
|
+
[
|
|
117
|
+
"e5f4d7c2e9b00105"
|
|
118
|
+
]
|
|
119
|
+
]
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"id": "e5f4d7c2e9b00105",
|
|
123
|
+
"type": "debug",
|
|
124
|
+
"z": "a1f4d7c2e9b00101",
|
|
125
|
+
"name": "Send result",
|
|
126
|
+
"active": true,
|
|
127
|
+
"tosidebar": true,
|
|
128
|
+
"complete": "true",
|
|
129
|
+
"targetType": "full",
|
|
130
|
+
"x": 720,
|
|
131
|
+
"y": 100,
|
|
132
|
+
"wires": []
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"id": "f6f4d7c2e9b00106",
|
|
136
|
+
"type": "whatsapp-api-in",
|
|
137
|
+
"z": "a1f4d7c2e9b00101",
|
|
138
|
+
"name": "Incoming messages",
|
|
139
|
+
"account": "b2f4d7c2e9b00102",
|
|
140
|
+
"includeRaw": false,
|
|
141
|
+
"x": 190,
|
|
142
|
+
"y": 200,
|
|
143
|
+
"wires": [
|
|
144
|
+
[
|
|
145
|
+
"07f4d7c2e9b00107"
|
|
146
|
+
]
|
|
147
|
+
]
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"id": "07f4d7c2e9b00107",
|
|
151
|
+
"type": "debug",
|
|
152
|
+
"z": "a1f4d7c2e9b00101",
|
|
153
|
+
"name": "Incoming debug",
|
|
154
|
+
"active": true,
|
|
155
|
+
"tosidebar": true,
|
|
156
|
+
"complete": "true",
|
|
157
|
+
"targetType": "full",
|
|
158
|
+
"x": 470,
|
|
159
|
+
"y": 200,
|
|
160
|
+
"wires": []
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"id": "18f4d7c2e9b00108",
|
|
164
|
+
"type": "inject",
|
|
165
|
+
"z": "a1f4d7c2e9b00101",
|
|
166
|
+
"name": "Read last 5",
|
|
167
|
+
"props": [
|
|
168
|
+
{
|
|
169
|
+
"p": "payload"
|
|
170
|
+
}
|
|
171
|
+
],
|
|
172
|
+
"payload": "",
|
|
173
|
+
"payloadType": "date",
|
|
174
|
+
"x": 150,
|
|
175
|
+
"y": 300,
|
|
176
|
+
"wires": [
|
|
177
|
+
[
|
|
178
|
+
"29f4d7c2e9b00109"
|
|
179
|
+
]
|
|
180
|
+
]
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"id": "29f4d7c2e9b00109",
|
|
184
|
+
"type": "whatsapp-api-history",
|
|
185
|
+
"z": "a1f4d7c2e9b00101",
|
|
186
|
+
"name": "Recent Chat History",
|
|
187
|
+
"account": "b2f4d7c2e9b00102",
|
|
188
|
+
"peer": "393331234567@s.whatsapp.net",
|
|
189
|
+
"limit": "5",
|
|
190
|
+
"includeRaw": false,
|
|
191
|
+
"x": 450,
|
|
192
|
+
"y": 300,
|
|
193
|
+
"wires": [
|
|
194
|
+
[
|
|
195
|
+
"3af4d7c2e9b0010a"
|
|
196
|
+
]
|
|
197
|
+
]
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"id": "3af4d7c2e9b0010a",
|
|
201
|
+
"type": "debug",
|
|
202
|
+
"z": "a1f4d7c2e9b00101",
|
|
203
|
+
"name": "History debug",
|
|
204
|
+
"active": true,
|
|
205
|
+
"tosidebar": true,
|
|
206
|
+
"complete": "true",
|
|
207
|
+
"targetType": "full",
|
|
208
|
+
"x": 730,
|
|
209
|
+
"y": 300,
|
|
210
|
+
"wires": []
|
|
211
|
+
}
|
|
212
|
+
]
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Message contract
|
|
216
|
+
|
|
217
|
+
Incoming and action nodes use this shape:
|
|
218
|
+
|
|
219
|
+
- `msg.payload`: the main payload for the node
|
|
220
|
+
- `msg.whatsapp`: normalized WhatsApp metadata
|
|
221
|
+
|
|
222
|
+
Common metadata fields:
|
|
223
|
+
|
|
224
|
+
- `msg.whatsapp.peer`: `{ id, type, title, ref }`
|
|
225
|
+
- `msg.whatsapp.chatId`
|
|
226
|
+
- `msg.whatsapp.senderId`
|
|
227
|
+
- `msg.whatsapp.messageId`
|
|
228
|
+
- `msg.whatsapp.media`
|
|
229
|
+
- `msg.whatsapp.raw` when the node is configured with `Include Raw`
|
|
230
|
+
|
|
231
|
+
## Send node input patterns
|
|
232
|
+
|
|
233
|
+
Text only:
|
|
234
|
+
|
|
235
|
+
```json
|
|
236
|
+
{
|
|
237
|
+
"payload": "hello from Node-RED",
|
|
238
|
+
"whatsapp": {
|
|
239
|
+
"peer": "393331234567@s.whatsapp.net"
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Media with a file path:
|
|
245
|
+
|
|
246
|
+
```json
|
|
247
|
+
{
|
|
248
|
+
"payload": "daily report",
|
|
249
|
+
"whatsapp": {
|
|
250
|
+
"peer": "393331234567@s.whatsapp.net",
|
|
251
|
+
"mediaPath": "/tmp/report.pdf"
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Media with a Buffer:
|
|
257
|
+
|
|
258
|
+
```js
|
|
259
|
+
msg.payload = Buffer.from("file-bytes");
|
|
260
|
+
msg.whatsapp = {
|
|
261
|
+
peer: "393331234567@s.whatsapp.net",
|
|
262
|
+
fileName: "image.jpg",
|
|
263
|
+
caption: "generated by a flow",
|
|
264
|
+
mimeType: "image/jpeg"
|
|
265
|
+
};
|
|
266
|
+
return msg;
|
|
267
|
+
```
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
<script type="text/html" data-template-name="whatsapp-api-config">
|
|
2
|
+
<div class="form-row">
|
|
3
|
+
<label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
4
|
+
<input type="text" id="node-config-input-name" placeholder="WhatsApp account" />
|
|
5
|
+
</div>
|
|
6
|
+
<div class="form-row">
|
|
7
|
+
<label for="node-config-input-reconnectMinMs"><i class="fa fa-repeat"></i> Reconnect Min</label>
|
|
8
|
+
<input type="number" id="node-config-input-reconnectMinMs" placeholder="2000" />
|
|
9
|
+
</div>
|
|
10
|
+
<div class="form-row">
|
|
11
|
+
<label for="node-config-input-reconnectMaxMs"><i class="fa fa-repeat"></i> Reconnect Max</label>
|
|
12
|
+
<input type="number" id="node-config-input-reconnectMaxMs" placeholder="30000" />
|
|
13
|
+
</div>
|
|
14
|
+
<div class="form-row">
|
|
15
|
+
<label for="node-config-input-dataDir"><i class="fa fa-folder-open"></i> Data Dir</label>
|
|
16
|
+
<input type="text" id="node-config-input-dataDir" placeholder="Default under Node-RED userDir" />
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<div class="form-row whatsapp-api-auth-panel">
|
|
20
|
+
<label><i class="fa fa-whatsapp"></i> Session</label>
|
|
21
|
+
<div class="whatsapp-api-auth-body">
|
|
22
|
+
<div id="whatsapp-api-auth-status" class="form-tips">Save and deploy before connecting.</div>
|
|
23
|
+
<div class="whatsapp-api-auth-actions">
|
|
24
|
+
<a href="#" class="editor-button" id="whatsapp-api-connect">Connect</a>
|
|
25
|
+
<a href="#" class="editor-button" id="whatsapp-api-test">Test Connection</a>
|
|
26
|
+
<a href="#" class="editor-button" id="whatsapp-api-disconnect">Disconnect</a>
|
|
27
|
+
</div>
|
|
28
|
+
<div id="whatsapp-api-qr-panel" class="whatsapp-api-qr-panel" style="display:none">
|
|
29
|
+
<img id="whatsapp-api-qr-image" alt="WhatsApp QR code" />
|
|
30
|
+
</div>
|
|
31
|
+
<div id="whatsapp-api-session-summary" class="form-tips">
|
|
32
|
+
Deploy the config node, then use Connect to start the WhatsApp QR login flow.
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<script type="text/html" data-help-name="whatsapp-api-config">
|
|
39
|
+
<p>Stores the runtime settings for the WhatsApp Web session used by the other nodes in this palette.</p>
|
|
40
|
+
<p>Save and deploy first, then use <b>Connect</b> in the editor to start the QR login flow against the deployed runtime.</p>
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<script type="text/html" data-template-name="whatsapp-api-in">
|
|
44
|
+
<div class="form-row">
|
|
45
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
46
|
+
<input type="text" id="node-input-name" />
|
|
47
|
+
</div>
|
|
48
|
+
<div class="form-row">
|
|
49
|
+
<label for="node-input-account"><i class="fa fa-user"></i> Account</label>
|
|
50
|
+
<input type="text" id="node-input-account" />
|
|
51
|
+
</div>
|
|
52
|
+
<div class="form-row">
|
|
53
|
+
<label for="node-input-includeRaw"><i class="fa fa-code"></i> Include Raw</label>
|
|
54
|
+
<input type="checkbox" id="node-input-includeRaw" style="width:auto" />
|
|
55
|
+
</div>
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<script type="text/html" data-help-name="whatsapp-api-in">
|
|
59
|
+
<p>Emits normalized incoming WhatsApp messages from the configured session.</p>
|
|
60
|
+
<p><code>msg.payload</code> contains the message body. <code>msg.whatsapp</code> carries normalized WhatsApp metadata.</p>
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<script type="text/html" data-template-name="whatsapp-api-send">
|
|
64
|
+
<div class="form-row">
|
|
65
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
66
|
+
<input type="text" id="node-input-name" />
|
|
67
|
+
</div>
|
|
68
|
+
<div class="form-row">
|
|
69
|
+
<label for="node-input-account"><i class="fa fa-user"></i> Account</label>
|
|
70
|
+
<input type="text" id="node-input-account" />
|
|
71
|
+
</div>
|
|
72
|
+
<div class="form-row">
|
|
73
|
+
<label for="node-input-peer"><i class="fa fa-comment"></i> Default Chat</label>
|
|
74
|
+
<input type="text" id="node-input-peer" placeholder="393331234567 or 393331234567@s.whatsapp.net" />
|
|
75
|
+
</div>
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<script type="text/html" data-help-name="whatsapp-api-send">
|
|
79
|
+
<p>Sends a WhatsApp text or media message.</p>
|
|
80
|
+
<p>Use <code>msg.payload</code> for text, or provide a Buffer/path through <code>msg.payload</code> and <code>msg.whatsapp.mediaPath</code>.</p>
|
|
81
|
+
</script>
|
|
82
|
+
|
|
83
|
+
<script type="text/html" data-template-name="whatsapp-api-history">
|
|
84
|
+
<div class="form-row">
|
|
85
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
86
|
+
<input type="text" id="node-input-name" />
|
|
87
|
+
</div>
|
|
88
|
+
<div class="form-row">
|
|
89
|
+
<label for="node-input-account"><i class="fa fa-user"></i> Account</label>
|
|
90
|
+
<input type="text" id="node-input-account" />
|
|
91
|
+
</div>
|
|
92
|
+
<div class="form-row">
|
|
93
|
+
<label for="node-input-peer"><i class="fa fa-comment"></i> Default Chat</label>
|
|
94
|
+
<input type="text" id="node-input-peer" placeholder="393331234567@s.whatsapp.net" />
|
|
95
|
+
</div>
|
|
96
|
+
<div class="form-row">
|
|
97
|
+
<label for="node-input-limit"><i class="fa fa-list-ol"></i> Limit</label>
|
|
98
|
+
<input type="number" id="node-input-limit" placeholder="10" />
|
|
99
|
+
</div>
|
|
100
|
+
<div class="form-row">
|
|
101
|
+
<label for="node-input-includeRaw"><i class="fa fa-code"></i> Include Raw</label>
|
|
102
|
+
<input type="checkbox" id="node-input-includeRaw" style="width:auto" />
|
|
103
|
+
</div>
|
|
104
|
+
<div class="form-row">
|
|
105
|
+
<label for="node-input-unreadOnly"><i class="fa fa-envelope"></i> Unread Only</label>
|
|
106
|
+
<input type="checkbox" id="node-input-unreadOnly" style="width:auto" />
|
|
107
|
+
</div>
|
|
108
|
+
</script>
|
|
109
|
+
|
|
110
|
+
<script type="text/html" data-help-name="whatsapp-api-history">
|
|
111
|
+
<p>Reads recent messages from the local WhatsApp message store maintained by the connected session.</p>
|
|
112
|
+
<p>The node outputs an array of normalized messages in <code>msg.payload</code>.</p>
|
|
113
|
+
<p>When <code>Unread Only</code> is enabled, the node returns the newest incoming messages that WhatsApp still marks as unread for that chat.</p>
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
<style>
|
|
117
|
+
.whatsapp-api-auth-panel .whatsapp-api-auth-body {
|
|
118
|
+
width: calc(100% - 110px);
|
|
119
|
+
margin-left: 110px;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.whatsapp-api-auth-actions {
|
|
123
|
+
display: flex;
|
|
124
|
+
gap: 8px;
|
|
125
|
+
margin-top: 8px;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.whatsapp-api-qr-panel {
|
|
129
|
+
margin-top: 12px;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.whatsapp-api-qr-panel img {
|
|
133
|
+
background: #fff;
|
|
134
|
+
border: 1px solid #d3d7db;
|
|
135
|
+
border-radius: 8px;
|
|
136
|
+
display: block;
|
|
137
|
+
max-width: 240px;
|
|
138
|
+
padding: 8px;
|
|
139
|
+
}
|
|
140
|
+
</style>
|
|
141
|
+
|
|
142
|
+
<script type="text/javascript">
|
|
143
|
+
(function() {
|
|
144
|
+
function apiRequest(method, url, data) {
|
|
145
|
+
return $.ajax({
|
|
146
|
+
contentType: "application/json; charset=utf-8",
|
|
147
|
+
data: data ? JSON.stringify(data) : undefined,
|
|
148
|
+
dataType: "json",
|
|
149
|
+
type: method,
|
|
150
|
+
url: url
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function showError(xhr) {
|
|
155
|
+
var message = xhr && xhr.responseJSON && xhr.responseJSON.error ? xhr.responseJSON.error : "WhatsApp request failed.";
|
|
156
|
+
RED.notify(message, "error");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function updateAuthUi(status, hasNodeId) {
|
|
160
|
+
var state = status && status.state ? status.state : "disconnected";
|
|
161
|
+
var label = status && status.label ? status.label : "Disconnected";
|
|
162
|
+
var qrDataUrl = status && status.qrDataUrl ? status.qrDataUrl : null;
|
|
163
|
+
|
|
164
|
+
$("#whatsapp-api-auth-status").text(label);
|
|
165
|
+
|
|
166
|
+
if (qrDataUrl && state === "awaiting_qr") {
|
|
167
|
+
$("#whatsapp-api-qr-image").attr("src", qrDataUrl);
|
|
168
|
+
$("#whatsapp-api-qr-panel").show();
|
|
169
|
+
$("#whatsapp-api-session-summary").text("Scan the QR code with WhatsApp on your phone to finish linking this runtime session.");
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
$("#whatsapp-api-qr-image").attr("src", "");
|
|
174
|
+
$("#whatsapp-api-qr-panel").hide();
|
|
175
|
+
|
|
176
|
+
if (!hasNodeId) {
|
|
177
|
+
$("#whatsapp-api-session-summary").text("Deploy the config node before starting the WhatsApp QR login flow.");
|
|
178
|
+
} else if (state === "connected") {
|
|
179
|
+
$("#whatsapp-api-session-summary").text("WhatsApp is connected. The runtime will reuse the stored auth state after restart.");
|
|
180
|
+
} else if (state === "connecting") {
|
|
181
|
+
$("#whatsapp-api-session-summary").text("Starting the WhatsApp session.");
|
|
182
|
+
} else if (state === "disconnected") {
|
|
183
|
+
$("#whatsapp-api-session-summary").text("Use Connect to start a fresh WhatsApp QR login flow.");
|
|
184
|
+
} else if (state === "error") {
|
|
185
|
+
$("#whatsapp-api-session-summary").text("The WhatsApp session hit an error. You can try Test Connection or Connect again.");
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function registerSimpleNode(type, options) {
|
|
190
|
+
RED.nodes.registerType(type, $.extend(true, {
|
|
191
|
+
align: "right",
|
|
192
|
+
category: "whatsapp-api",
|
|
193
|
+
color: "#c8f2cf",
|
|
194
|
+
defaults: {
|
|
195
|
+
account: { required: true, type: "whatsapp-api-config" },
|
|
196
|
+
name: { value: "" }
|
|
197
|
+
}
|
|
198
|
+
}, options));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
RED.nodes.registerType("whatsapp-api-config", {
|
|
202
|
+
category: "config",
|
|
203
|
+
defaults: {
|
|
204
|
+
dataDir: { value: "" },
|
|
205
|
+
name: { value: "" },
|
|
206
|
+
reconnectMaxMs: { value: 30000, validate: RED.validators.number() },
|
|
207
|
+
reconnectMinMs: { value: 2000, validate: RED.validators.number() }
|
|
208
|
+
},
|
|
209
|
+
label: function() {
|
|
210
|
+
return this.name || "whatsapp-api";
|
|
211
|
+
},
|
|
212
|
+
oneditprepare: function() {
|
|
213
|
+
var node = this;
|
|
214
|
+
var pollingHandle = null;
|
|
215
|
+
|
|
216
|
+
function stopPolling() {
|
|
217
|
+
if (pollingHandle) {
|
|
218
|
+
clearInterval(pollingHandle);
|
|
219
|
+
pollingHandle = null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function pollRuntimeStatus() {
|
|
224
|
+
if (!node.id) {
|
|
225
|
+
updateAuthUi({ label: "Disconnected", state: "disconnected" }, false);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
apiRequest("GET", "whatsapp-api/config/" + encodeURIComponent(node.id) + "/status")
|
|
230
|
+
.done(function(response) {
|
|
231
|
+
updateAuthUi(response.status, true);
|
|
232
|
+
})
|
|
233
|
+
.fail(function(xhr) {
|
|
234
|
+
if (xhr && xhr.status !== 404) {
|
|
235
|
+
showError(xhr);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function startPolling() {
|
|
241
|
+
stopPolling();
|
|
242
|
+
pollingHandle = setInterval(pollRuntimeStatus, 1500);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
$("#whatsapp-api-connect").on("click", function(event) {
|
|
246
|
+
event.preventDefault();
|
|
247
|
+
|
|
248
|
+
if (!node.id) {
|
|
249
|
+
RED.notify("Deploy the config node before connecting WhatsApp.", "warning");
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
apiRequest("POST", "whatsapp-api/config/" + encodeURIComponent(node.id) + "/connect")
|
|
254
|
+
.done(function(response) {
|
|
255
|
+
updateAuthUi(response.status, true);
|
|
256
|
+
startPolling();
|
|
257
|
+
})
|
|
258
|
+
.fail(showError);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
$("#whatsapp-api-test").on("click", function(event) {
|
|
262
|
+
event.preventDefault();
|
|
263
|
+
|
|
264
|
+
if (!node.id) {
|
|
265
|
+
RED.notify("Deploy the config node before testing the WhatsApp session.", "warning");
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
apiRequest("POST", "whatsapp-api/config/" + encodeURIComponent(node.id) + "/test")
|
|
270
|
+
.done(function(response) {
|
|
271
|
+
updateAuthUi(response.status, true);
|
|
272
|
+
})
|
|
273
|
+
.fail(showError);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
$("#whatsapp-api-disconnect").on("click", function(event) {
|
|
277
|
+
event.preventDefault();
|
|
278
|
+
|
|
279
|
+
if (!node.id) {
|
|
280
|
+
updateAuthUi({ label: "Disconnected", state: "disconnected" }, false);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
apiRequest("POST", "whatsapp-api/config/" + encodeURIComponent(node.id) + "/disconnect")
|
|
285
|
+
.done(function(response) {
|
|
286
|
+
updateAuthUi(response.status, true);
|
|
287
|
+
})
|
|
288
|
+
.fail(showError);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
updateAuthUi({ label: "Disconnected", state: "disconnected" }, Boolean(node.id));
|
|
292
|
+
pollRuntimeStatus();
|
|
293
|
+
startPolling();
|
|
294
|
+
|
|
295
|
+
this._whatsappApiCleanup = function() {
|
|
296
|
+
stopPolling();
|
|
297
|
+
};
|
|
298
|
+
},
|
|
299
|
+
oneditsave: function() {
|
|
300
|
+
if (this._whatsappApiCleanup) {
|
|
301
|
+
this._whatsappApiCleanup();
|
|
302
|
+
this._whatsappApiCleanup = null;
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
oneditcancel: function() {
|
|
306
|
+
if (this._whatsappApiCleanup) {
|
|
307
|
+
this._whatsappApiCleanup();
|
|
308
|
+
this._whatsappApiCleanup = null;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
registerSimpleNode("whatsapp-api-in", {
|
|
314
|
+
defaults: {
|
|
315
|
+
account: { required: true, type: "whatsapp-api-config" },
|
|
316
|
+
includeRaw: { value: false },
|
|
317
|
+
name: { value: "" }
|
|
318
|
+
},
|
|
319
|
+
icon: "font-awesome/fa-inbox",
|
|
320
|
+
inputs: 0,
|
|
321
|
+
label: function() {
|
|
322
|
+
return this.name || "whatsapp-api in";
|
|
323
|
+
},
|
|
324
|
+
outputs: 1
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
registerSimpleNode("whatsapp-api-send", {
|
|
328
|
+
defaults: {
|
|
329
|
+
account: { required: true, type: "whatsapp-api-config" },
|
|
330
|
+
name: { value: "" },
|
|
331
|
+
peer: { value: "" }
|
|
332
|
+
},
|
|
333
|
+
icon: "font-awesome/fa-paper-plane",
|
|
334
|
+
inputs: 1,
|
|
335
|
+
label: function() {
|
|
336
|
+
return this.name || "whatsapp-api send";
|
|
337
|
+
},
|
|
338
|
+
outputs: 1
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
registerSimpleNode("whatsapp-api-history", {
|
|
342
|
+
defaults: {
|
|
343
|
+
account: { required: true, type: "whatsapp-api-config" },
|
|
344
|
+
includeRaw: { value: false },
|
|
345
|
+
limit: { value: 10, validate: RED.validators.number() },
|
|
346
|
+
name: { value: "" },
|
|
347
|
+
peer: { value: "" },
|
|
348
|
+
unreadOnly: { value: false }
|
|
349
|
+
},
|
|
350
|
+
icon: "font-awesome/fa-history",
|
|
351
|
+
inputs: 1,
|
|
352
|
+
label: function() {
|
|
353
|
+
return this.name || "whatsapp-api history";
|
|
354
|
+
},
|
|
355
|
+
outputs: 1
|
|
356
|
+
});
|
|
357
|
+
})();
|
|
358
|
+
</script>
|