@theotherwillembotha/node-red-whatsapp 0.0.55 → 0.4.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 +33 -21
- package/build/GenerateNodes.d.ts +2 -0
- package/build/GenerateNodes.d.ts.map +1 -0
- package/build/GenerateNodes.js +5 -7
- package/build/Nodes.html +1522 -538
- package/build/Nodes.js +52261 -14
- package/build/Plugins.html +37 -0
- package/build/Plugins.js +45828 -55
- package/build/index.d.ts +7 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +1 -0
- package/build/runtime/NodeManagerRuntime.js +140 -0
- package/build/whatsapp/node/WhatsappAccountConfigNode.d.ts +14 -0
- package/build/whatsapp/node/WhatsappAccountConfigNode.d.ts.map +1 -0
- package/build/whatsapp/node/WhatsappAccountConfigNode.js +7 -2
- package/build/whatsapp/node/WhatsappDynamicSendMessageNode.d.ts +19 -0
- package/build/whatsapp/node/WhatsappDynamicSendMessageNode.d.ts.map +1 -0
- package/build/whatsapp/node/WhatsappDynamicSendMessageNode.js +112 -0
- package/build/whatsapp/node/WhatsappGroupConfigNode.d.ts +16 -0
- package/build/whatsapp/node/WhatsappGroupConfigNode.d.ts.map +1 -0
- package/build/whatsapp/node/WhatsappGroupConfigNode.js +19 -8
- package/build/whatsapp/node/WhatsappReceiveMessageNode.d.ts +20 -0
- package/build/whatsapp/node/WhatsappReceiveMessageNode.d.ts.map +1 -0
- package/build/whatsapp/node/WhatsappReceiveMessageNode.js +2 -2
- package/build/whatsapp/node/WhatsappSendMessageNode.d.ts +17 -0
- package/build/whatsapp/node/WhatsappSendMessageNode.d.ts.map +1 -0
- package/build/whatsapp/node/WhatsappSendMessageNode.js +55 -33
- package/build/whatsapp/service/WhatsappClient.d.ts +189 -0
- package/build/whatsapp/service/WhatsappClient.d.ts.map +1 -0
- package/build/whatsapp/service/WhatsappClient.js +140 -14
- package/build/whatsapp/service/WhatsappService.d.ts +26 -0
- package/build/whatsapp/service/WhatsappService.d.ts.map +1 -0
- package/build/whatsapp/service/WhatsappService.js +85 -23
- package/build/whatsapp/service/WhatsappStore.d.ts +127 -0
- package/build/whatsapp/service/WhatsappStore.d.ts.map +1 -0
- package/build/whatsapp/service/WhatsappStore.js +10 -7
- package/package.json +11 -8
package/build/Nodes.html
CHANGED
|
@@ -7,173 +7,418 @@
|
|
|
7
7
|
You have been warned.
|
|
8
8
|
|
|
9
9
|
-->
|
|
10
|
-
|
|
11
10
|
<!-- WhatsappAccountConfigNode -->
|
|
11
|
+
|
|
12
|
+
<style>
|
|
13
|
+
.theotherwillembotha-compact-table li {
|
|
14
|
+
padding-top: 4px;
|
|
15
|
+
padding-bottom: 4px;
|
|
16
|
+
}
|
|
17
|
+
</style>
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
<script type="text/javascript">
|
|
21
|
+
// a simple client for interfaceing with the WhatsappService on the backend. Note: this needs to be moved to its own file.
|
|
22
|
+
let waService = {
|
|
23
|
+
unlinkAccount : function(nodeID, localConnectionId){
|
|
24
|
+
return new Promise((resolve, fail) => {
|
|
25
|
+
|
|
26
|
+
let request = JSON.stringify({
|
|
27
|
+
nodeID: nodeID,
|
|
28
|
+
localConnectionId: localConnectionId
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
$.ajax({
|
|
32
|
+
url: "whatsapp/unlinkaccount",
|
|
33
|
+
type: "POST",
|
|
34
|
+
data: request,
|
|
35
|
+
contentType: "application/json; charset=utf-8",
|
|
36
|
+
success: (response) => {
|
|
37
|
+
resolve(response);
|
|
38
|
+
},
|
|
39
|
+
error: (jqXHR, textStatus, errorThrown) => {
|
|
40
|
+
fail(errorThrown);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
getConnectionDetails:function(nodeID, localConnectionId){
|
|
47
|
+
return new Promise((resolve, fail) => {
|
|
48
|
+
let request = JSON.stringify({
|
|
49
|
+
nodeID: nodeID,
|
|
50
|
+
localConnectionId: localConnectionId
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
$.ajax({
|
|
54
|
+
url: "whatsapp/connectiondetails",
|
|
55
|
+
type: "POST",
|
|
56
|
+
data: request,
|
|
57
|
+
contentType: "application/json; charset=utf-8",
|
|
58
|
+
success: (response) => {
|
|
59
|
+
resolve(response);
|
|
60
|
+
},
|
|
61
|
+
error: (jqXHR, textStatus, errorThrown) => {
|
|
62
|
+
fail(errorThrown);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
createGroup:function(nodeID, localConnectionId, name){
|
|
69
|
+
console.log("create group! " + name);
|
|
70
|
+
|
|
71
|
+
return new Promise((resolve, fail) => {
|
|
72
|
+
let request = JSON.stringify({
|
|
73
|
+
nodeID: nodeID,
|
|
74
|
+
localConnectionId: localConnectionId,
|
|
75
|
+
groupName: name
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
$.ajax({
|
|
79
|
+
url: "whatsapp/creategroup",
|
|
80
|
+
type: "POST",
|
|
81
|
+
data: request,
|
|
82
|
+
contentType: "application/json; charset=utf-8",
|
|
83
|
+
success: (response) => {
|
|
84
|
+
resolve(response);
|
|
85
|
+
},
|
|
86
|
+
error: (jqXHR, textStatus, errorThrown) => {
|
|
87
|
+
fail(errorThrown);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
getGroups:function(nodeID, localConnectionId){
|
|
94
|
+
return new Promise((resolve, fail) => {
|
|
95
|
+
let request = JSON.stringify({
|
|
96
|
+
nodeID: nodeID,
|
|
97
|
+
localConnectionId: localConnectionId
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
$.ajax({
|
|
101
|
+
url: "whatsapp/getgroups",
|
|
102
|
+
type: "POST",
|
|
103
|
+
data: request,
|
|
104
|
+
contentType: "application/json; charset=utf-8",
|
|
105
|
+
success: (response) => {
|
|
106
|
+
resolve(response);
|
|
107
|
+
},
|
|
108
|
+
error: (jqXHR, textStatus, errorThrown) => {
|
|
109
|
+
fail(errorThrown);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
addUserToGroup:function(nodeID, localConnectionId, userId, groupId, role){
|
|
116
|
+
return new Promise((resolve, fail) => {
|
|
117
|
+
let request = JSON.stringify({
|
|
118
|
+
nodeID: nodeID,
|
|
119
|
+
localConnectionId: localConnectionId,
|
|
120
|
+
userId: userId,
|
|
121
|
+
groupId: groupId,
|
|
122
|
+
role:role
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
$.ajax({
|
|
126
|
+
url: "whatsapp/groupadduser",
|
|
127
|
+
type: "POST",
|
|
128
|
+
data: request,
|
|
129
|
+
contentType: "application/json; charset=utf-8",
|
|
130
|
+
success: (response) => {
|
|
131
|
+
resolve(response);
|
|
132
|
+
},
|
|
133
|
+
error: (jqXHR, textStatus, errorThrown) => {
|
|
134
|
+
fail(errorThrown);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
updateUserInGroup:function(nodeID, localConnectionId, userId, groupId, role){
|
|
141
|
+
console.log("update user in group.");
|
|
142
|
+
return new Promise((resolve, fail) => {
|
|
143
|
+
let request = JSON.stringify({
|
|
144
|
+
nodeID: nodeID,
|
|
145
|
+
localConnectionId: localConnectionId,
|
|
146
|
+
userId: userId,
|
|
147
|
+
groupId: groupId,
|
|
148
|
+
role:role
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
$.ajax({
|
|
152
|
+
url: "whatsapp/groupupdateuser",
|
|
153
|
+
type: "POST",
|
|
154
|
+
data: request,
|
|
155
|
+
contentType: "application/json; charset=utf-8",
|
|
156
|
+
success: (response) => {
|
|
157
|
+
resolve(response);
|
|
158
|
+
},
|
|
159
|
+
error: (jqXHR, textStatus, errorThrown) => {
|
|
160
|
+
fail(errorThrown);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
removeUserFromGroup:function(nodeID, localConnectionId, userId, groupId){
|
|
168
|
+
console.log("removeUserFromGroup", nodeID, localConnectionId, userId, groupId);
|
|
169
|
+
return new Promise((resolve, fail) => {
|
|
170
|
+
let request = JSON.stringify({
|
|
171
|
+
nodeID: nodeID,
|
|
172
|
+
localConnectionId: localConnectionId,
|
|
173
|
+
userId: userId,
|
|
174
|
+
groupId: groupId
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
$.ajax({
|
|
178
|
+
url: "whatsapp/groupremoveuser",
|
|
179
|
+
type: "POST",
|
|
180
|
+
data: request,
|
|
181
|
+
contentType: "application/json; charset=utf-8",
|
|
182
|
+
success: (response) => {
|
|
183
|
+
resolve(response);
|
|
184
|
+
},
|
|
185
|
+
error: (jqXHR, textStatus, errorThrown) => {
|
|
186
|
+
fail(errorThrown);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
getAccounts: function() {
|
|
193
|
+
return new Promise((resolve, fail) => {
|
|
194
|
+
$.ajax({
|
|
195
|
+
url: "whatsapp/accounts",
|
|
196
|
+
type: "GET",
|
|
197
|
+
success: (response) => { resolve(response); },
|
|
198
|
+
error: (jqXHR, textStatus, errorThrown) => { fail(errorThrown); }
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
},
|
|
202
|
+
}
|
|
203
|
+
</script>
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
<!-- WhatsappGroupConfigNode -->
|
|
207
|
+
|
|
208
|
+
<style>
|
|
209
|
+
|
|
210
|
+
</style>
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
<!-- WhatsappSendMessageNode -->
|
|
214
|
+
|
|
215
|
+
<style>
|
|
216
|
+
.wa-send-action-fields .form-row { margin-bottom: 3px; }
|
|
217
|
+
</style>
|
|
218
|
+
<script>
|
|
219
|
+
var WA_SEND_TYPES = {
|
|
220
|
+
text: {
|
|
221
|
+
label: "Text",
|
|
222
|
+
icon: "fa-font",
|
|
223
|
+
fields: [
|
|
224
|
+
{ key: "value", label: "Text", types: ["msg", "flow", "global", "str"] }
|
|
225
|
+
]
|
|
226
|
+
},
|
|
227
|
+
image: {
|
|
228
|
+
label: "Image",
|
|
229
|
+
icon: "fa-image",
|
|
230
|
+
fields: [
|
|
231
|
+
{ key: "value", label: "Image", types: ["msg", "flow", "global"] }
|
|
232
|
+
]
|
|
233
|
+
},
|
|
234
|
+
video: {
|
|
235
|
+
label: "Video",
|
|
236
|
+
icon: "fa-video-camera",
|
|
237
|
+
fields: [
|
|
238
|
+
{ key: "value", label: "Video", types: ["msg", "flow", "global"] }
|
|
239
|
+
]
|
|
240
|
+
},
|
|
241
|
+
document: {
|
|
242
|
+
label: "Document",
|
|
243
|
+
icon: "fa-file",
|
|
244
|
+
fields: [
|
|
245
|
+
{ key: "value", label: "File", types: ["msg", "flow", "global"] },
|
|
246
|
+
{ key: "documentName", label: "Name", types: ["msg", "flow", "global", "str"] },
|
|
247
|
+
{ key: "documentMimetype", label: "Mime Type", types: ["msg", "flow", "global", "str"] }
|
|
248
|
+
]
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
</script>
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
<!-- logger -->
|
|
255
|
+
|
|
12
256
|
<style>
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
257
|
+
.editorgroupborder {
|
|
258
|
+
border-width:1px;
|
|
259
|
+
border-style:solid;
|
|
260
|
+
border-color:lightgray;
|
|
261
|
+
padding: 3px;
|
|
262
|
+
margin-top: 2px;
|
|
263
|
+
margin-bottom: 2px;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.editorsectionheading {
|
|
267
|
+
font-weight: 600;
|
|
268
|
+
margin-bottom:1px !important;
|
|
269
|
+
margin-top:6px !important;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.nomargin {
|
|
273
|
+
margin-bottom:1px !important;
|
|
274
|
+
margin-top:1px !important;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.slidecontainer {
|
|
278
|
+
width: 100%;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.slider {
|
|
282
|
+
-webkit-appearance: none;
|
|
283
|
+
appearance: none;
|
|
284
|
+
width: 100%;
|
|
285
|
+
height: 25px;
|
|
286
|
+
background: #d3d3d3;
|
|
287
|
+
outline: none;
|
|
288
|
+
opacity: 0.7;
|
|
289
|
+
-webkit-transition: .2s;
|
|
290
|
+
transition: opacity .2s;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.slider:hover {
|
|
294
|
+
opacity: 1;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.slider::-webkit-slider-thumb {
|
|
298
|
+
-webkit-appearance: none;
|
|
299
|
+
appearance: none;
|
|
300
|
+
width: 25px;
|
|
301
|
+
height: 25px;
|
|
302
|
+
background: #04AA6D;
|
|
303
|
+
cursor: pointer;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.slider::-moz-range-thumb {
|
|
307
|
+
width: 25px;
|
|
308
|
+
height: 25px;
|
|
309
|
+
background: #04AA6D;
|
|
310
|
+
cursor: pointer;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.towb_editorlabel {
|
|
314
|
+
width: 120px !important;
|
|
315
|
+
}
|
|
316
|
+
input.towb_editorfield, select.towb_editorfield, textarea.towb_editorfield {
|
|
317
|
+
width: 70% !important;
|
|
318
|
+
}
|
|
319
|
+
input.towb_editorfield_short, select.towb_editorfield_short {
|
|
320
|
+
width: 30% !important;
|
|
16
321
|
}
|
|
17
322
|
|
|
18
323
|
</style>
|
|
324
|
+
|
|
19
325
|
<script type="text/javascript">
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
let request = JSON.stringify({
|
|
66
|
-
nodeID: nodeID,
|
|
67
|
-
localConnectionId: localConnectionId,
|
|
68
|
-
groupName: name
|
|
69
|
-
});
|
|
70
|
-
$.ajax({
|
|
71
|
-
url: "whatsapp/creategroup",
|
|
72
|
-
type: "POST",
|
|
73
|
-
data: request,
|
|
74
|
-
contentType: "application/json; charset=utf-8",
|
|
75
|
-
success: (response) => {
|
|
76
|
-
resolve(response);
|
|
77
|
-
},
|
|
78
|
-
error: (jqXHR, textStatus, errorThrown) => {
|
|
79
|
-
fail(errorThrown);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
},
|
|
84
|
-
getGroups: function(nodeID, localConnectionId) {
|
|
85
|
-
return new Promise((resolve, fail) => {
|
|
86
|
-
let request = JSON.stringify({
|
|
87
|
-
nodeID: nodeID,
|
|
88
|
-
localConnectionId: localConnectionId
|
|
89
|
-
});
|
|
90
|
-
$.ajax({
|
|
91
|
-
url: "whatsapp/getgroups",
|
|
92
|
-
type: "POST",
|
|
93
|
-
data: request,
|
|
94
|
-
contentType: "application/json; charset=utf-8",
|
|
95
|
-
success: (response) => {
|
|
96
|
-
resolve(response);
|
|
97
|
-
},
|
|
98
|
-
error: (jqXHR, textStatus, errorThrown) => {
|
|
99
|
-
fail(errorThrown);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
},
|
|
104
|
-
addUserToGroup: function(nodeID, localConnectionId, userId, groupId, role) {
|
|
105
|
-
return new Promise((resolve, fail) => {
|
|
106
|
-
let request = JSON.stringify({
|
|
107
|
-
nodeID: nodeID,
|
|
108
|
-
localConnectionId: localConnectionId,
|
|
109
|
-
userId: userId,
|
|
110
|
-
groupId: groupId,
|
|
111
|
-
role: role
|
|
112
|
-
});
|
|
113
|
-
$.ajax({
|
|
114
|
-
url: "whatsapp/groupadduser",
|
|
115
|
-
type: "POST",
|
|
116
|
-
data: request,
|
|
117
|
-
contentType: "application/json; charset=utf-8",
|
|
118
|
-
success: (response) => {
|
|
119
|
-
resolve(response);
|
|
326
|
+
|
|
327
|
+
// CSS
|
|
328
|
+
dropdownStyles = `
|
|
329
|
+
.loggertype-dropdown {
|
|
330
|
+
position: absolute;
|
|
331
|
+
background: white;
|
|
332
|
+
border: 1px solid #ccc;
|
|
333
|
+
border-radius: 4px;
|
|
334
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
|
335
|
+
max-height: 150px;
|
|
336
|
+
overflow-y: auto;
|
|
337
|
+
z-index: 1000;
|
|
338
|
+
min-width: 150px;
|
|
339
|
+
}
|
|
340
|
+
.loggertype-dropdown .dropdown-item {
|
|
341
|
+
padding: 6px 12px;
|
|
342
|
+
cursor: pointer;
|
|
343
|
+
}
|
|
344
|
+
.loggertype-dropdown .dropdown-item:hover {
|
|
345
|
+
background: #f0f0f0;
|
|
346
|
+
}
|
|
347
|
+
`;
|
|
348
|
+
$('<style>').text(dropdownStyles).appendTo('head');
|
|
349
|
+
|
|
350
|
+
</script>
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
<!-- WhatsappDynamicSendMessageNode -->
|
|
356
|
+
|
|
357
|
+
<style>
|
|
358
|
+
.wa-dynamic-send-action-fields .form-row { margin-bottom: 3px; }
|
|
359
|
+
</style>
|
|
360
|
+
<script>
|
|
361
|
+
// WA_SEND_TYPES is defined by WhatsappSendMessageNode.html (included in the same page).
|
|
362
|
+
// Guard against load-order variation with a var declaration — var re-declarations are safe.
|
|
363
|
+
if (typeof WA_SEND_TYPES === "undefined") {
|
|
364
|
+
var WA_SEND_TYPES = {
|
|
365
|
+
text: {
|
|
366
|
+
label: "Text",
|
|
367
|
+
icon: "fa-font",
|
|
368
|
+
fields: [
|
|
369
|
+
{ key: "value", label: "Text", types: ["msg", "flow", "global", "str"] }
|
|
370
|
+
]
|
|
120
371
|
},
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
updateUserInGroup: function(nodeID, localConnectionId, userId, groupId, role) {
|
|
128
|
-
console.log("update user in group.");
|
|
129
|
-
return new Promise((resolve, fail) => {
|
|
130
|
-
let request = JSON.stringify({
|
|
131
|
-
nodeID: nodeID,
|
|
132
|
-
localConnectionId: localConnectionId,
|
|
133
|
-
userId: userId,
|
|
134
|
-
groupId: groupId,
|
|
135
|
-
role: role
|
|
136
|
-
});
|
|
137
|
-
$.ajax({
|
|
138
|
-
url: "whatsapp/groupupdateuser",
|
|
139
|
-
type: "POST",
|
|
140
|
-
data: request,
|
|
141
|
-
contentType: "application/json; charset=utf-8",
|
|
142
|
-
success: (response) => {
|
|
143
|
-
resolve(response);
|
|
372
|
+
image: {
|
|
373
|
+
label: "Image",
|
|
374
|
+
icon: "fa-image",
|
|
375
|
+
fields: [
|
|
376
|
+
{ key: "value", label: "Image", types: ["msg", "flow", "global"] }
|
|
377
|
+
]
|
|
144
378
|
},
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
removeUserFromGroup: function(nodeID, localConnectionId, userId, groupId) {
|
|
152
|
-
console.log("removeUserFromGroup", nodeID, localConnectionId, userId, groupId);
|
|
153
|
-
return new Promise((resolve, fail) => {
|
|
154
|
-
let request = JSON.stringify({
|
|
155
|
-
nodeID: nodeID,
|
|
156
|
-
localConnectionId: localConnectionId,
|
|
157
|
-
userId: userId,
|
|
158
|
-
groupId: groupId
|
|
159
|
-
});
|
|
160
|
-
$.ajax({
|
|
161
|
-
url: "whatsapp/groupremoveuser",
|
|
162
|
-
type: "POST",
|
|
163
|
-
data: request,
|
|
164
|
-
contentType: "application/json; charset=utf-8",
|
|
165
|
-
success: (response) => {
|
|
166
|
-
resolve(response);
|
|
379
|
+
video: {
|
|
380
|
+
label: "Video",
|
|
381
|
+
icon: "fa-video-camera",
|
|
382
|
+
fields: [
|
|
383
|
+
{ key: "value", label: "Video", types: ["msg", "flow", "global"] }
|
|
384
|
+
]
|
|
167
385
|
},
|
|
168
|
-
|
|
169
|
-
|
|
386
|
+
document: {
|
|
387
|
+
label: "Document",
|
|
388
|
+
icon: "fa-file",
|
|
389
|
+
fields: [
|
|
390
|
+
{ key: "value", label: "File", types: ["msg", "flow", "global"] },
|
|
391
|
+
{ key: "documentName", label: "Name", types: ["msg", "flow", "global", "str"] },
|
|
392
|
+
{ key: "documentMimetype", label: "Mime Type", types: ["msg", "flow", "global", "str"] }
|
|
393
|
+
]
|
|
170
394
|
}
|
|
171
|
-
|
|
172
|
-
});
|
|
173
|
-
},
|
|
395
|
+
};
|
|
174
396
|
}
|
|
397
|
+
</script>
|
|
175
398
|
|
|
176
|
-
|
|
399
|
+
|
|
400
|
+
<!-- WhatsappReceiveMessageNode -->
|
|
401
|
+
|
|
402
|
+
<style>
|
|
403
|
+
</style>
|
|
404
|
+
<script>
|
|
405
|
+
const WA_ACCEPT_FIELDS = [
|
|
406
|
+
{ id: "Text", icon: "fa-font", label: "Text", defaultEnabled: true },
|
|
407
|
+
{ id: "ExtendedText", icon: "fa-align-left", label: "Extended Text", defaultEnabled: true },
|
|
408
|
+
{ id: "Image", icon: "fa-image", label: "Image", defaultEnabled: true },
|
|
409
|
+
{ id: "Video", icon: "fa-video-camera", label: "Video", defaultEnabled: true },
|
|
410
|
+
{ id: "Album", icon: "fa-th-large", label: "Album", defaultEnabled: true },
|
|
411
|
+
{ id: "Document", icon: "fa-file", label: "Document", defaultEnabled: false },
|
|
412
|
+
{ id: "Contact", icon: "fa-address-card", label: "Contact", defaultEnabled: false },
|
|
413
|
+
{ id: "Template", icon: "fa-th-list", label: "Template", defaultEnabled: false },
|
|
414
|
+
{ id: "Location", icon: "fa-map-marker", label: "Location", defaultEnabled: false },
|
|
415
|
+
{ id: "Event", icon: "fa-calendar", label: "Event", defaultEnabled: false },
|
|
416
|
+
{ id: "EventResponse", icon: "fa-calendar-check-o", label: "Event Response", defaultEnabled: false },
|
|
417
|
+
{ id: "Sticker", icon: "fa-sticky-note-o", label: "Sticker", defaultEnabled: false },
|
|
418
|
+
];
|
|
419
|
+
</script>
|
|
420
|
+
|
|
421
|
+
|
|
177
422
|
<script type="text/javascript">
|
|
178
423
|
RED.nodes.registerType('WhatsappAccountConfigNode', {
|
|
179
424
|
category: 'config',
|
|
@@ -199,8 +444,9 @@
|
|
|
199
444
|
{
|
|
200
445
|
let node = this;
|
|
201
446
|
let $btn = $("#wa-account-link-btn");
|
|
447
|
+
let isLinked = document.getElementById("node-config-input-accountLinked").checked;
|
|
202
448
|
// if the account is already linked, show the Unlink state
|
|
203
|
-
if (
|
|
449
|
+
if (isLinked) {
|
|
204
450
|
$btn.html('<i class="fa fa-unlink"></i> Unlink Account');
|
|
205
451
|
}
|
|
206
452
|
// if we don't have a local connectionId for this node, generate one.
|
|
@@ -208,10 +454,44 @@
|
|
|
208
454
|
$("#node-config-input-localConnectionId").val(crypto.randomUUID());
|
|
209
455
|
}
|
|
210
456
|
let localConnectionId = $("#node-config-input-localConnectionId").val();
|
|
211
|
-
|
|
457
|
+
// --- Claim Account section ---
|
|
458
|
+
// Only offer claiming if this node is not already linked.
|
|
459
|
+
if (!isLinked) {
|
|
460
|
+
waService.getAccounts().then(function(accounts) {
|
|
461
|
+
let unclaimed = accounts.filter(function(a) {
|
|
462
|
+
return !a.claimed;
|
|
463
|
+
});
|
|
464
|
+
if (unclaimed.length === 0) return;
|
|
465
|
+
let $claimSelect = $("#wa-claim-select");
|
|
466
|
+
$claimSelect.empty();
|
|
467
|
+
unclaimed.forEach(function(account) {
|
|
468
|
+
let label = account.name && account.phoneNumber ? account.name + " (+" + account.phoneNumber + ")" : account.name || account.phoneNumber || account.key;
|
|
469
|
+
if (!account.connected) label += " \u26a0 disconnected";
|
|
470
|
+
$('<option>').val(account.key).text(label).appendTo($claimSelect);
|
|
471
|
+
});
|
|
472
|
+
$("#wa-claim-section").show();
|
|
473
|
+
$("#wa-or-separator").show();
|
|
474
|
+
}).catch(function() {
|
|
475
|
+
/* backend not ready — ignore */ });
|
|
476
|
+
}
|
|
477
|
+
$("#wa-claim-btn").on("click", function() {
|
|
478
|
+
let claimedKey = $("#wa-claim-select").val();
|
|
479
|
+
if (!claimedKey) return;
|
|
480
|
+
// adopt the claimed account's localConnectionId and mark as linked
|
|
481
|
+
$("#node-config-input-localConnectionId").val(claimedKey);
|
|
482
|
+
$("#node-config-input-accountLinked").prop("checked", true);
|
|
483
|
+
$("#wa-claim-section").hide();
|
|
484
|
+
$("#wa-or-separator").hide();
|
|
485
|
+
$btn.html('<i class="fa fa-unlink"></i> Unlink Account');
|
|
486
|
+
$("#wa-account-qrcode").html("<p>\u2714 Account claimed. Deploy to activate.</p>");
|
|
487
|
+
});
|
|
488
|
+
// --- Link Account button ---
|
|
489
|
+
$btn.on("click", function() {
|
|
212
490
|
$btn.prop("disabled", true);
|
|
491
|
+
// re-read localConnectionId in case it was overridden by a claim
|
|
492
|
+
localConnectionId = $("#node-config-input-localConnectionId").val();
|
|
213
493
|
if (document.getElementById("node-config-input-accountLinked").checked) {
|
|
214
|
-
waService.unlinkAccount(node.id, localConnectionId).then(response
|
|
494
|
+
waService.unlinkAccount(node.id, localConnectionId).then(function(response) {
|
|
215
495
|
$btn.prop("disabled", false);
|
|
216
496
|
$btn.html('<i class="fa fa-link"></i> Link Account');
|
|
217
497
|
document.getElementById("node-config-input-accountLinked").checked = false;
|
|
@@ -283,6 +563,21 @@
|
|
|
283
563
|
<input type="checkbox" id="node-config-input-accountLinked">
|
|
284
564
|
</div>
|
|
285
565
|
|
|
566
|
+
<div class="form-row" id="wa-claim-section" style="display:none">
|
|
567
|
+
<label><i class="fa fa-mobile"></i> Claim</label>
|
|
568
|
+
<div style="width:70%; display:inline-flex; align-items:center;">
|
|
569
|
+
<select id="wa-claim-select" style="flex-grow:1; min-width:0;"></select>
|
|
570
|
+
<button type="button" id="wa-claim-btn" class="red-ui-button" style="margin-left:8px; flex-shrink:0;">
|
|
571
|
+
<i class="fa fa-check"></i> Claim
|
|
572
|
+
</button>
|
|
573
|
+
</div>
|
|
574
|
+
</div>
|
|
575
|
+
|
|
576
|
+
<div class="form-row" id="wa-or-separator" style="display:none">
|
|
577
|
+
<label></label>
|
|
578
|
+
<span style="color:#aaa; font-style:italic;">— or link a new account —</span>
|
|
579
|
+
</div>
|
|
580
|
+
|
|
286
581
|
<div class="form-row">
|
|
287
582
|
<label></label>
|
|
288
583
|
<button type="button" id="wa-account-link-btn" class="red-ui-button">
|
|
@@ -318,9 +613,7 @@ To unlink the device, open the editor and click **Unlink Account**, then remove
|
|
|
318
613
|
|
|
319
614
|
Credentials are persisted to `/data/whatsapp/<localConnectionId>/` if the `/data` directory exists (standard Node-RED Docker image), otherwise to `./whatsapp/<localConnectionId>/` relative to the Node-RED working directory.
|
|
320
615
|
</script>
|
|
321
|
-
|
|
322
|
-
<style>
|
|
323
|
-
</style>
|
|
616
|
+
|
|
324
617
|
<script type="text/javascript">
|
|
325
618
|
RED.nodes.registerType('WhatsappGroupConfigNode', {
|
|
326
619
|
category: 'config',
|
|
@@ -591,8 +884,9 @@ Credentials are persisted to `/data/whatsapp/<localConnectionId>/` if the `/data
|
|
|
591
884
|
options: []
|
|
592
885
|
}]
|
|
593
886
|
});
|
|
594
|
-
// set initial button state
|
|
887
|
+
// set initial button state and load groups for the already-selected account
|
|
595
888
|
setCreateButtonEnabled(accountConfigInput.val());
|
|
889
|
+
updateGroupList(accountConfigInput.val());
|
|
596
890
|
// show inline create section
|
|
597
891
|
$("#wa-group-new-btn").on("click", () => {
|
|
598
892
|
$("#wa-group-new-row").hide();
|
|
@@ -705,8 +999,6 @@ Credentials are persisted to `/data/whatsapp/<localConnectionId>/` if the `/data
|
|
|
705
999
|
</div>
|
|
706
1000
|
</div>
|
|
707
1001
|
|
|
708
|
-
|
|
709
|
-
|
|
710
1002
|
</div>
|
|
711
1003
|
</script>
|
|
712
1004
|
|
|
@@ -725,119 +1017,7 @@ References a WhatsApp group within a linked account. Used as the target for Send
|
|
|
725
1017
|
|
|
726
1018
|
Click **Create New Group** to create a new WhatsApp group from within Node-RED. The new group will appear in the group dropdown once created.
|
|
727
1019
|
</script>
|
|
728
|
-
<!-- WhatsappSendMessageNode -->
|
|
729
|
-
<style>
|
|
730
|
-
</style>
|
|
731
|
-
<script>
|
|
732
|
-
const WA_SEND_FIELDS = [{
|
|
733
|
-
id: "text",
|
|
734
|
-
icon: "fa-font",
|
|
735
|
-
label: "Send Text",
|
|
736
|
-
types: ["msg", "flow", "global", "str"]
|
|
737
|
-
}, {
|
|
738
|
-
id: "image",
|
|
739
|
-
icon: "fa-image",
|
|
740
|
-
label: "Send Image",
|
|
741
|
-
types: ["msg", "flow", "global"]
|
|
742
|
-
}, ];
|
|
743
|
-
|
|
744
|
-
</script>
|
|
745
|
-
<!-- logger -->
|
|
746
|
-
<style>
|
|
747
|
-
.editorgroupborder {
|
|
748
|
-
border-width: 1px;
|
|
749
|
-
border-style: solid;
|
|
750
|
-
border-color: lightgray;
|
|
751
|
-
padding: 3px;
|
|
752
|
-
margin-top: 2px;
|
|
753
|
-
margin-bottom: 2px;
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
.editorsectionheading {
|
|
757
|
-
font-weight: 600;
|
|
758
|
-
margin-bottom: 1px !important;
|
|
759
|
-
margin-top: 6px !important;
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
.nomargin {
|
|
763
|
-
margin-bottom: 1px !important;
|
|
764
|
-
margin-top: 1px !important;
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
.slidecontainer {
|
|
768
|
-
width: 100%;
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
.slider {
|
|
772
|
-
-webkit-appearance: none;
|
|
773
|
-
appearance: none;
|
|
774
|
-
width: 100%;
|
|
775
|
-
height: 25px;
|
|
776
|
-
background: #d3d3d3;
|
|
777
|
-
outline: none;
|
|
778
|
-
opacity: 0.7;
|
|
779
|
-
-webkit-transition: .2s;
|
|
780
|
-
transition: opacity .2s;
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
.slider:hover {
|
|
784
|
-
opacity: 1;
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
.slider::-webkit-slider-thumb {
|
|
788
|
-
-webkit-appearance: none;
|
|
789
|
-
appearance: none;
|
|
790
|
-
width: 25px;
|
|
791
|
-
height: 25px;
|
|
792
|
-
background: #04AA6D;
|
|
793
|
-
cursor: pointer;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
.slider::-moz-range-thumb {
|
|
797
|
-
width: 25px;
|
|
798
|
-
height: 25px;
|
|
799
|
-
background: #04AA6D;
|
|
800
|
-
cursor: pointer;
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
.towb_editorlabel {
|
|
804
|
-
width: 120px !important;
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
.towb_editorfield {
|
|
808
|
-
width: 70% !important;
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
.towb_editorfield_short {
|
|
812
|
-
width: 30% !important;
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
</style>
|
|
816
|
-
<script type="text/javascript">
|
|
817
|
-
// CSS
|
|
818
|
-
dropdownStyles = `
|
|
819
|
-
.loggertype-dropdown {
|
|
820
|
-
position: absolute;
|
|
821
|
-
background: white;
|
|
822
|
-
border: 1px solid #ccc;
|
|
823
|
-
border-radius: 4px;
|
|
824
|
-
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
|
825
|
-
max-height: 150px;
|
|
826
|
-
overflow-y: auto;
|
|
827
|
-
z-index: 1000;
|
|
828
|
-
min-width: 150px;
|
|
829
|
-
}
|
|
830
|
-
.loggertype-dropdown .dropdown-item {
|
|
831
|
-
padding: 6px 12px;
|
|
832
|
-
cursor: pointer;
|
|
833
|
-
}
|
|
834
|
-
.loggertype-dropdown .dropdown-item:hover {
|
|
835
|
-
background: #f0f0f0;
|
|
836
|
-
}
|
|
837
|
-
`;
|
|
838
|
-
$('<style>').text(dropdownStyles).appendTo('head');
|
|
839
1020
|
|
|
840
|
-
</script>
|
|
841
1021
|
<script type="text/javascript">
|
|
842
1022
|
RED.nodes.registerType('WhatsappSendMessageNode', {
|
|
843
1023
|
category: 'whatsapp',
|
|
@@ -865,10 +1045,8 @@ Click **Create New Group** to create a new WhatsApp group from within Node-RED.
|
|
|
865
1045
|
required: false,
|
|
866
1046
|
validate: function(value) {
|
|
867
1047
|
try {
|
|
868
|
-
|
|
869
|
-
return
|
|
870
|
-
return !p.enabled || (p.value !== undefined && p.value !== "");
|
|
871
|
-
});
|
|
1048
|
+
JSON.parse(value || "[]");
|
|
1049
|
+
return true;
|
|
872
1050
|
} catch (e) {
|
|
873
1051
|
return false;
|
|
874
1052
|
}
|
|
@@ -894,59 +1072,91 @@ Click **Create New Group** to create a new WhatsApp group from within Node-RED.
|
|
|
894
1072
|
required: true
|
|
895
1073
|
},
|
|
896
1074
|
metricsReference: {
|
|
897
|
-
|
|
898
|
-
|
|
1075
|
+
required: false,
|
|
1076
|
+
value: '',
|
|
1077
|
+
type: 'DelegatedConfigReferenceNode'
|
|
899
1078
|
},
|
|
900
1079
|
},
|
|
901
1080
|
oneditprepare: function() {
|
|
902
1081
|
// **** WhatsappSendMessageNode **** //
|
|
903
1082
|
{
|
|
904
1083
|
let node = this;
|
|
905
|
-
let
|
|
1084
|
+
let actionList = $("#wa-send-actions-list");
|
|
1085
|
+
actionList.editableList({
|
|
1086
|
+
sortable: true,
|
|
1087
|
+
removable: true,
|
|
1088
|
+
height: "auto",
|
|
1089
|
+
scrollOnAdd: true,
|
|
1090
|
+
addButton: "Add Message",
|
|
1091
|
+
addItem: function(container, i, action) {
|
|
1092
|
+
action = action || {};
|
|
1093
|
+
let type = action.type || "text";
|
|
1094
|
+
container.css({
|
|
1095
|
+
padding: "6px 4px"
|
|
1096
|
+
});
|
|
1097
|
+
// ── Type row ──────────────────────────────────────────────────────
|
|
1098
|
+
let typeRow = $('<div style="display:flex; align-items:center; gap:8px; margin-bottom:5px;">');
|
|
1099
|
+
let iconEl = $('<i style="width:14px; text-align:center; flex-shrink:0;">').addClass("fa " + (WA_SEND_TYPES[type] ? WA_SEND_TYPES[type].icon : "fa-comment"));
|
|
1100
|
+
let typeSelect = $('<select class="wa-send-action-type" style="flex:0 0 auto;">');
|
|
1101
|
+
Object.keys(WA_SEND_TYPES).forEach(function(key) {
|
|
1102
|
+
typeSelect.append($("<option>").val(key).text(WA_SEND_TYPES[key].label));
|
|
1103
|
+
});
|
|
1104
|
+
typeSelect.val(type);
|
|
1105
|
+
typeRow.append(iconEl).append(typeSelect);
|
|
1106
|
+
container.append(typeRow);
|
|
1107
|
+
// ── Fields container ──────────────────────────────────────────────
|
|
1108
|
+
let fieldsContainer = $('<div class="wa-send-action-fields">');
|
|
1109
|
+
container.append(fieldsContainer);
|
|
1110
|
+
let renderFields = function(selectedType) {
|
|
1111
|
+
fieldsContainer.empty();
|
|
1112
|
+
let typeDef = WA_SEND_TYPES[selectedType];
|
|
1113
|
+
if (!typeDef) return;
|
|
1114
|
+
iconEl.removeClass().addClass("fa " + typeDef.icon);
|
|
1115
|
+
typeDef.fields.forEach(function(field) {
|
|
1116
|
+
let fieldRow = $('<div class="form-row nomargin" style="display:flex; align-items:center; gap:6px;">');
|
|
1117
|
+
fieldRow.append($('<label style="width:70px; margin:0; font-size:0.85em; flex-shrink:0;">').text(field.label));
|
|
1118
|
+
let input = $('<input type="text">').addClass("wa-send-field-" + field.key);
|
|
1119
|
+
fieldRow.append(input);
|
|
1120
|
+
fieldsContainer.append(fieldRow);
|
|
1121
|
+
input.typedInput({
|
|
1122
|
+
types: field.types
|
|
1123
|
+
}).typedInput("type", action[field.key + "Type"] || field.types[0]).typedInput("value", action[field.key] || "");
|
|
1124
|
+
fieldRow.find(".red-ui-typedInput-container").css({
|
|
1125
|
+
flex: "1",
|
|
1126
|
+
"min-width": "0",
|
|
1127
|
+
width: ""
|
|
1128
|
+
});
|
|
1129
|
+
});
|
|
1130
|
+
};
|
|
1131
|
+
renderFields(type);
|
|
1132
|
+
typeSelect.on("change", function() {
|
|
1133
|
+
// Reset saved values when the type changes so stale data isn't carried over.
|
|
1134
|
+
action = {
|
|
1135
|
+
type: $(this).val()
|
|
1136
|
+
};
|
|
1137
|
+
renderFields($(this).val());
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
});
|
|
1141
|
+
let actions = [];
|
|
906
1142
|
try {
|
|
907
|
-
|
|
1143
|
+
actions = JSON.parse(node.payloads || "[]");
|
|
908
1144
|
} catch (e) {}
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
}) || {
|
|
913
|
-
enabled: false,
|
|
914
|
-
type: field.types[0],
|
|
915
|
-
value: ""
|
|
916
|
-
};
|
|
917
|
-
let row = $('<div class="form-row nomargin">' + '<label style="width:120px;"><i class="fa ' + field.icon + '"></i> ' + field.label + '</label>' + '<div id="wa-send-row-' + field.id + '" style="display:inline-flex; align-items:center; gap:6px; vertical-align:middle; width:calc(100% - 128px);">' + '<input type="checkbox" id="wa-send-enabled-' + field.id + '" style="width:auto; margin:0; flex-shrink:0;">' + '<input type="text" id="wa-send-value-' + field.id + '">' + '</div>' + '</div>');
|
|
918
|
-
$("#wa-send-payload-rows").append(row);
|
|
919
|
-
$("#wa-send-value-" + field.id).typedInput({
|
|
920
|
-
types: field.types
|
|
921
|
-
}).typedInput("type", saved.type).typedInput("value", saved.value);
|
|
922
|
-
// typedInput inserts its container as a sibling of the (now hidden) input, making it
|
|
923
|
-
// a direct flex child — apply flex:1 so it expands to fill the remaining row width.
|
|
924
|
-
$("#wa-send-row-" + field.id + " .red-ui-typedInput-container").css({
|
|
925
|
-
"flex": "1",
|
|
926
|
-
"min-width": "0",
|
|
927
|
-
"width": ""
|
|
928
|
-
});
|
|
929
|
-
$("#wa-send-enabled-" + field.id).prop("checked", saved.enabled);
|
|
930
|
-
// live validation: highlight the typedInput when checkbox is checked but value is empty.
|
|
931
|
-
var checkFieldValid = function() {
|
|
932
|
-
var enabled = $("#wa-send-enabled-" + field.id).prop("checked");
|
|
933
|
-
var value = $("#wa-send-value-" + field.id).typedInput("value");
|
|
934
|
-
var invalid = enabled && (!value || value === "");
|
|
935
|
-
$("#wa-send-row-" + field.id + " .red-ui-typedInput-container").toggleClass("input-error", invalid);
|
|
936
|
-
};
|
|
937
|
-
$("#wa-send-enabled-" + field.id).on("change", checkFieldValid);
|
|
938
|
-
$("#wa-send-value-" + field.id).on("change", checkFieldValid);
|
|
939
|
-
checkFieldValid();
|
|
940
|
-
});
|
|
1145
|
+
if (actions.length > 0) {
|
|
1146
|
+
actionList.editableList("addItems", actions);
|
|
1147
|
+
}
|
|
941
1148
|
}
|
|
942
1149
|
// **** logger **** //
|
|
943
1150
|
{
|
|
944
1151
|
let node = this;
|
|
1152
|
+
let templateConfig = {};
|
|
945
1153
|
// controls.
|
|
946
1154
|
let logEnabled = $("#node-input-logEnabled");
|
|
947
1155
|
let logTemplateEnabled = $("#node-input-logTemplateOverrideEnabled");
|
|
948
1156
|
let logTemplateEnabledSection = $("#section_logTemplateOverrideEnabled");
|
|
949
1157
|
let logEnabledSection = $("#section_logEnabled");
|
|
1158
|
+
let loggerSection = $("#section_loggerconfig");
|
|
1159
|
+
loggerSection.hide();
|
|
950
1160
|
// functions.
|
|
951
1161
|
let getLoggerTypes = async function() {
|
|
952
1162
|
let types = [];
|
|
@@ -1070,43 +1280,788 @@ Click **Create New Group** to create a new WhatsApp group from within Node-RED.
|
|
|
1070
1280
|
loggerEditButton.removeClass("disabled")
|
|
1071
1281
|
}
|
|
1072
1282
|
});
|
|
1073
|
-
//
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1283
|
+
// Show the logger section only if at least one logger type is registered.
|
|
1284
|
+
// If nodered_logging (or any other logger provider) is not installed, the section stays hidden.
|
|
1285
|
+
getLoggerTypes().then(loggerTypes => {
|
|
1286
|
+
if (loggerTypes.length === 0) return;
|
|
1287
|
+
loggerSection.show();
|
|
1288
|
+
updateLoggerSelectorList().then(() => {
|
|
1289
|
+
loggerSelector.val(node.logger);
|
|
1290
|
+
loggerSelector.trigger('change');
|
|
1291
|
+
});
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
// **** metrics **** //
|
|
1295
|
+
{
|
|
1296
|
+
let node = this;
|
|
1297
|
+
let templateConfig = {};
|
|
1298
|
+
let metricsEnabled = $("#node-input-metricsEnabled");
|
|
1299
|
+
let metricsSection = $("#section_metricsconfig");
|
|
1300
|
+
let metricsContainer = metricsEnabled.closest(".editorgroupborder");
|
|
1301
|
+
let metricsEnabledSection = metricsContainer.find("#section_metricsEnabled");
|
|
1302
|
+
let metricsEnableCheck = metricsContainer.find(".metricsEnableCheck");
|
|
1303
|
+
metricsSection.hide();
|
|
1304
|
+
if (templateConfig.showBorder === false) {
|
|
1305
|
+
metricsSection.find(".editorsectionheading").hide();
|
|
1306
|
+
metricsSection.find(".editorgroupborder").css({
|
|
1307
|
+
border: "none",
|
|
1308
|
+
padding: 0,
|
|
1309
|
+
margin: 0
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
if (templateConfig.showEnable === false) {
|
|
1313
|
+
metricsEnableCheck.hide();
|
|
1314
|
+
metricsEnabledSection.show();
|
|
1315
|
+
}
|
|
1316
|
+
let getMetricsProviderTypes = async function() {
|
|
1317
|
+
let types = [];
|
|
1318
|
+
await $.ajax({
|
|
1319
|
+
url: "nodetypeservice/find?tag=MetricsProvider",
|
|
1320
|
+
type: "GET",
|
|
1321
|
+
contentType: "application/json; charset=utf-8",
|
|
1322
|
+
success: (response) => {
|
|
1323
|
+
types = response;
|
|
1324
|
+
},
|
|
1325
|
+
error: (jqXHR, textStatus, errorThrown) => {
|
|
1326
|
+
console.log(jqXHR, textStatus, errorThrown);
|
|
1327
|
+
},
|
|
1328
|
+
});
|
|
1329
|
+
return types;
|
|
1330
|
+
};
|
|
1331
|
+
let getMetricsProviders = async () => {
|
|
1332
|
+
let providers = [];
|
|
1333
|
+
let providerTypes = (await getMetricsProviderTypes()).map(t => t.id);
|
|
1334
|
+
RED.nodes.eachConfig(cfg => {
|
|
1335
|
+
if (providerTypes.includes(cfg.type)) {
|
|
1336
|
+
providers.push({
|
|
1337
|
+
id: cfg.id,
|
|
1338
|
+
name: cfg.label(),
|
|
1339
|
+
type: cfg.type
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
});
|
|
1343
|
+
return providers;
|
|
1344
|
+
};
|
|
1345
|
+
let updateMetricsSelectorList = async () => {
|
|
1346
|
+
metricsSelector.empty();
|
|
1347
|
+
(await getMetricsProviders()).forEach(provider => {
|
|
1348
|
+
$('<option value="' + provider.id + '">' + provider.name + '</option>').appendTo(metricsSelector);
|
|
1349
|
+
});
|
|
1350
|
+
$('<option value="_ADD_">none</option>').appendTo(metricsSelector);
|
|
1351
|
+
};
|
|
1352
|
+
metricsEnabled.on('change', function() {
|
|
1353
|
+
if (metricsEnabled.prop("checked")) {
|
|
1354
|
+
metricsEnabledSection.show();
|
|
1355
|
+
} else {
|
|
1356
|
+
metricsEnabledSection.hide();
|
|
1357
|
+
}
|
|
1358
|
+
});
|
|
1359
|
+
let metricsSelector = metricsContainer.find("#metrics-selector");
|
|
1360
|
+
let metricsEditButton = metricsContainer.find("#metrics-edit-btn");
|
|
1361
|
+
let metricsAddButton = metricsContainer.find("#metrics-add-btn");
|
|
1362
|
+
metricsEditButton.on('click', async function(event) {
|
|
1363
|
+
event.stopPropagation();
|
|
1364
|
+
if (metricsEditButton.hasClass("disabled")) return;
|
|
1365
|
+
let selectedProvider = (await getMetricsProviders()).find(p => p.id === metricsSelector.val());
|
|
1366
|
+
if (selectedProvider) {
|
|
1367
|
+
RED.editor.editConfig("#metrics-selector", selectedProvider.type, selectedProvider.id);
|
|
1368
|
+
}
|
|
1369
|
+
});
|
|
1370
|
+
metricsAddButton.on('click', function(event) {
|
|
1371
|
+
event.stopPropagation();
|
|
1372
|
+
let $btn = $(event.currentTarget);
|
|
1373
|
+
let btnOffset = $btn.offset();
|
|
1374
|
+
let btnH = $btn.outerHeight();
|
|
1375
|
+
let btnW = $btn.outerWidth();
|
|
1376
|
+
$('.metricstype-dropdown').remove();
|
|
1377
|
+
getMetricsProviderTypes().then(providerTypes => {
|
|
1378
|
+
let $dropdown = $('<div class="metricstype-dropdown red-ui-editor"></div>').css({
|
|
1379
|
+
position: 'absolute',
|
|
1380
|
+
zIndex: 1000
|
|
1381
|
+
});
|
|
1382
|
+
providerTypes.forEach(providerType => {
|
|
1383
|
+
$('<div class="dropdown-item"></div>').text(providerType.name).data('value', providerType.id).css({
|
|
1384
|
+
padding: '4px 8px',
|
|
1385
|
+
cursor: 'pointer'
|
|
1386
|
+
}).appendTo($dropdown);
|
|
1387
|
+
});
|
|
1388
|
+
$('body').append($dropdown);
|
|
1389
|
+
$dropdown.css({
|
|
1390
|
+
top: btnOffset.top + btnH,
|
|
1391
|
+
left: Math.max(0, btnOffset.left + btnW - $dropdown.outerWidth())
|
|
1392
|
+
});
|
|
1393
|
+
$(document).on('mousedown.metricsdropdown', function(e) {
|
|
1394
|
+
if (!$(e.target).closest('.metricstype-dropdown').length) {
|
|
1395
|
+
$dropdown.remove();
|
|
1396
|
+
$(document).off('mousedown.metricsdropdown');
|
|
1397
|
+
}
|
|
1398
|
+
});
|
|
1399
|
+
$dropdown.on('click', '.dropdown-item', function(e) {
|
|
1400
|
+
e.stopPropagation();
|
|
1401
|
+
const selectedValue = $(this).data('value');
|
|
1402
|
+
$dropdown.remove();
|
|
1403
|
+
RED.editor.editConfig("#metrics-selector", selectedValue, "_ADD_");
|
|
1404
|
+
});
|
|
1405
|
+
});
|
|
1406
|
+
});
|
|
1407
|
+
metricsSelector.on("focus", updateMetricsSelectorList);
|
|
1408
|
+
metricsSelector.on('change', function() {
|
|
1409
|
+
if ("_ADD_" === metricsSelector.val()) {
|
|
1410
|
+
metricsEditButton.addClass("disabled");
|
|
1411
|
+
} else {
|
|
1412
|
+
metricsEditButton.removeClass("disabled");
|
|
1413
|
+
}
|
|
1414
|
+
});
|
|
1415
|
+
// Show the metrics section only if at least one metrics provider type is registered.
|
|
1416
|
+
// If nodered_prometheus (or any other provider) is not installed, the section stays hidden.
|
|
1417
|
+
getMetricsProviderTypes().then(providerTypes => {
|
|
1418
|
+
if (providerTypes.length === 0) return;
|
|
1419
|
+
metricsSection.show();
|
|
1420
|
+
updateMetricsSelectorList().then(() => {
|
|
1421
|
+
metricsSelector.val(node.metricsReference);
|
|
1422
|
+
metricsSelector.trigger('change');
|
|
1423
|
+
});
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
},
|
|
1427
|
+
oneditsave: function() {
|
|
1428
|
+
// **** WhatsappSendMessageNode **** //
|
|
1429
|
+
{
|
|
1430
|
+
let actions = [];
|
|
1431
|
+
$("#wa-send-actions-list").editableList("items").each(function() {
|
|
1432
|
+
let container = $(this);
|
|
1433
|
+
let type = container.find(".wa-send-action-type").val();
|
|
1434
|
+
let typeDef = WA_SEND_TYPES[type];
|
|
1435
|
+
if (!typeDef) return;
|
|
1436
|
+
let action = {
|
|
1437
|
+
type: type
|
|
1438
|
+
};
|
|
1439
|
+
typeDef.fields.forEach(function(field) {
|
|
1440
|
+
let input = container.find(".wa-send-field-" + field.key);
|
|
1441
|
+
if (input.length) {
|
|
1442
|
+
action[field.key] = input.typedInput("value");
|
|
1443
|
+
action[field.key + "Type"] = input.typedInput("type");
|
|
1444
|
+
}
|
|
1445
|
+
});
|
|
1446
|
+
actions.push(action);
|
|
1447
|
+
});
|
|
1448
|
+
$("#node-input-payloads").val(JSON.stringify(actions));
|
|
1449
|
+
}
|
|
1450
|
+
// **** logger **** //
|
|
1451
|
+
{
|
|
1452
|
+
let node = this;
|
|
1453
|
+
let templateConfig = {};
|
|
1454
|
+
node = this;
|
|
1455
|
+
var selectedLogger = $("#logger-selector").val();
|
|
1456
|
+
node.logger = (selectedLogger && selectedLogger !== "_ADD_") ? selectedLogger : '';
|
|
1457
|
+
node.logTemplateOverride = node.logTemplateOverrideEditor.getValue();
|
|
1458
|
+
node.logTemplateOverrideEditor.destroy();
|
|
1459
|
+
delete node.logTemplateOverrideEditor;
|
|
1460
|
+
}
|
|
1461
|
+
// **** metrics **** //
|
|
1462
|
+
{
|
|
1463
|
+
let node = this;
|
|
1464
|
+
let templateConfig = {};
|
|
1465
|
+
node = this;
|
|
1466
|
+
var selectedProvider = $("#metrics-selector").val();
|
|
1467
|
+
node.metricsReference = (selectedProvider && selectedProvider !== "_ADD_") ? selectedProvider : '';
|
|
1468
|
+
if (templateConfig.showEnable === false) {
|
|
1469
|
+
node.metricsEnabled = !!node.metricsReference;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
},
|
|
1473
|
+
oneditcancel: function() {},
|
|
1474
|
+
oneditdelete: function() {},
|
|
1475
|
+
});
|
|
1476
|
+
|
|
1477
|
+
</script>
|
|
1478
|
+
|
|
1479
|
+
|
|
1480
|
+
<script type="text/html" data-template-name='WhatsappSendMessageNode'>
|
|
1481
|
+
<div id='section_WhatsappSendMessageNode'>
|
|
1482
|
+
<div class="form-row">
|
|
1483
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
1484
|
+
<input type="text" id="node-input-name" />
|
|
1485
|
+
</div>
|
|
1486
|
+
|
|
1487
|
+
<div class="form-row nomargin">
|
|
1488
|
+
<label for="node-input-groupConfig" style="width:120px;"><i class="fa fa-users"></i> Group</label>
|
|
1489
|
+
<input type="text" id="node-input-groupConfig" placeholder="config">
|
|
1490
|
+
</div>
|
|
1491
|
+
|
|
1492
|
+
<div class="form-row editorsectionheading">
|
|
1493
|
+
<i class="w-16 fa fa-paper-plane"></i> <span>Messages</span>
|
|
1494
|
+
</div>
|
|
1495
|
+
|
|
1496
|
+
<div class="editorgroupborder">
|
|
1497
|
+
<ol id="wa-send-actions-list"></ol>
|
|
1498
|
+
</div>
|
|
1499
|
+
|
|
1500
|
+
<input type="hidden" id="node-input-payloads">
|
|
1501
|
+
|
|
1502
|
+
|
|
1503
|
+
</div>
|
|
1504
|
+
|
|
1505
|
+
<div id='section_logger'>
|
|
1506
|
+
<div id="section_loggerconfig">
|
|
1507
|
+
<div class="form-row editorsectionheading">
|
|
1508
|
+
<i class="w-16 fa fa-eye"></i> <span>Logging</span>
|
|
1509
|
+
</div>
|
|
1510
|
+
<div class="editorgroupborder">
|
|
1511
|
+
<div class="form-row nomargin logEnableCheck">
|
|
1512
|
+
<label class="towb_editorlabel"> </label>
|
|
1513
|
+
<input class="towb_checkbox" type="checkbox" id="node-input-logEnabled" placeholder="logEnabled" value="false">
|
|
1514
|
+
<label class="towb_checkboxlabel" for="node-input-logEnabled"><span>Enable Logging</span></label>
|
|
1515
|
+
</div>
|
|
1516
|
+
|
|
1517
|
+
<div id="section_logEnabled">
|
|
1518
|
+
<div class="form-row nomargin">
|
|
1519
|
+
<label class="towb_editorlabel" for="logger-selector">Logger</label>
|
|
1520
|
+
<div style="width: 70%; display: inline-flex;">
|
|
1521
|
+
<select id="logger-selector" style="flex-grow: 1;" class="">
|
|
1522
|
+
<option value="_ADD_">none</option>
|
|
1523
|
+
</select>
|
|
1524
|
+
<a id="logger-edit-btn" class="red-ui-button disabled" style="margin-left: 10px;">
|
|
1525
|
+
<i class="fa fa-pencil"></i>
|
|
1526
|
+
</a>
|
|
1527
|
+
<a id="logger-add-btn" class="red-ui-button" style="margin-left: 10px;">
|
|
1528
|
+
<i class="fa fa-plus"></i>
|
|
1529
|
+
</a>
|
|
1530
|
+
</div>
|
|
1531
|
+
</div>
|
|
1532
|
+
|
|
1533
|
+
<div class="form-row nomargin">
|
|
1534
|
+
<label class="towb_editorlabel"> </label>
|
|
1535
|
+
<input class="towb_checkbox" type="checkbox" id="node-input-logTemplateOverrideEnabled" placeholder="" value="false">
|
|
1536
|
+
<label class="towb_checkboxlabel" for="node-input-logTemplateOverrideEnabled"><span>Override Template</span></label>
|
|
1537
|
+
</div>
|
|
1538
|
+
|
|
1539
|
+
<div id="section_logTemplateOverrideEnabled">
|
|
1540
|
+
<div class="form-row">
|
|
1541
|
+
<label class="towb_editorlabel" for="node-input-logTemplateOverrideEditor"><i class="fa fa-code"></i> Template</label>
|
|
1542
|
+
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-logTemplateOverrideEditor"></div>
|
|
1543
|
+
</div>
|
|
1544
|
+
</div>
|
|
1545
|
+
</div>
|
|
1546
|
+
</div>
|
|
1547
|
+
</div>
|
|
1548
|
+
|
|
1549
|
+
</div>
|
|
1550
|
+
|
|
1551
|
+
<div id='section_metrics'>
|
|
1552
|
+
<div id="section_metricsconfig">
|
|
1553
|
+
<div class="form-row editorsectionheading">
|
|
1554
|
+
<i class="w-16 fa fa-eye"></i> <span>Metrics</span>
|
|
1555
|
+
</div>
|
|
1556
|
+
<div class="editorgroupborder">
|
|
1557
|
+
<div class="form-row nomargin metricsEnableCheck">
|
|
1558
|
+
<label class="towb_editorlabel"> </label>
|
|
1559
|
+
<input class="towb_checkbox" type="checkbox" id="node-input-metricsEnabled" value="false" />
|
|
1560
|
+
<label class="towb_checkboxlabel" for="node-input-metricsEnabled"><span>Enable Metrics</span></label>
|
|
1561
|
+
</div>
|
|
1562
|
+
|
|
1563
|
+
<div id="section_metricsEnabled">
|
|
1564
|
+
<div class="form-row nomargin">
|
|
1565
|
+
<label class="towb_editorlabel" for="metrics-selector">Metric Provider</label>
|
|
1566
|
+
<div style="width: 70%; display: inline-flex;">
|
|
1567
|
+
<select id="metrics-selector" style="flex-grow: 1; min-width: 0;">
|
|
1568
|
+
<option value="_ADD_">none</option>
|
|
1569
|
+
</select>
|
|
1570
|
+
<a id="metrics-edit-btn" class="red-ui-button disabled" style="margin-left: 10px; flex-shrink: 0;">
|
|
1571
|
+
<i class="fa fa-pencil"></i>
|
|
1572
|
+
</a>
|
|
1573
|
+
<a id="metrics-add-btn" class="red-ui-button" style="margin-left: 10px; flex-shrink: 0;">
|
|
1574
|
+
<i class="fa fa-plus"></i>
|
|
1575
|
+
</a>
|
|
1576
|
+
</div>
|
|
1577
|
+
</div>
|
|
1578
|
+
</div>
|
|
1579
|
+
</div>
|
|
1580
|
+
</div>
|
|
1581
|
+
|
|
1582
|
+
</div>
|
|
1583
|
+
</script>
|
|
1584
|
+
|
|
1585
|
+
<script type="text/markdown" data-help-name='WhatsappSendMessageNode'>
|
|
1586
|
+
> **Early development** — this package is still maturing. Some features may be incomplete and diagnostic log output is intentionally verbose for now.
|
|
1587
|
+
|
|
1588
|
+
Sends one or more messages to a WhatsApp group when triggered.
|
|
1589
|
+
|
|
1590
|
+
### Properties
|
|
1591
|
+
|
|
1592
|
+
: *name* (string) : Display label for this node.
|
|
1593
|
+
: *group* (config) : The WhatsApp Group config node to send to.
|
|
1594
|
+
|
|
1595
|
+
### Messages
|
|
1596
|
+
|
|
1597
|
+
Each row in the **Messages** list is sent as a separate WhatsApp message, in order, when the node is triggered. Rows can be reordered by dragging and removed with the delete button.
|
|
1598
|
+
|
|
1599
|
+
Select the message type from the dropdown, then fill in the value fields:
|
|
1600
|
+
|
|
1601
|
+
- **Text** — a single text value (`msg`, `flow`, `global`, `str` with Handlebars support for `str`)
|
|
1602
|
+
- **Image** — a Buffer containing the image data (`msg`, `flow`, `global`)
|
|
1603
|
+
- **Video** — a Buffer containing the video data (`msg`, `flow`, `global`)
|
|
1604
|
+
- **Document** — three fields: the file Buffer, an optional filename, and an optional MIME type (defaults to `application/octet-stream`)
|
|
1605
|
+
|
|
1606
|
+
### Inputs
|
|
1607
|
+
|
|
1608
|
+
: *msg* : Trigger message. `msg`-type field values are resolved against this message.
|
|
1609
|
+
|
|
1610
|
+
### Logging
|
|
1611
|
+
: *enable logging* (boolean) : if checked, then the node will produce logging output to the specified logger.
|
|
1612
|
+
: *logger* (logconfig) : the endppoint that the node will log events to.
|
|
1613
|
+
: *override template* (logconfig) : if checked, a custom logging template can be provided for the log message.
|
|
1614
|
+
: *logger template* (mustache) : a template in mustache format to generate a message for logging purposes (see LoggerConfig node for more information)
|
|
1615
|
+
|
|
1616
|
+
### Metrics
|
|
1617
|
+
: *enable metrics* (boolean) : if checked, then the node will produce metrics output to the specified metrics provider.
|
|
1618
|
+
: *metric provider* (metricsconfig) : the metrics backend that this node will report to.
|
|
1619
|
+
</script>
|
|
1620
|
+
|
|
1621
|
+
<script type="text/javascript">
|
|
1622
|
+
RED.nodes.registerType('WhatsappDynamicSendMessageNode', {
|
|
1623
|
+
category: 'whatsapp',
|
|
1624
|
+
icon: 'whatsapp.png',
|
|
1625
|
+
color: '#E6FFDA',
|
|
1626
|
+
label: function() {
|
|
1627
|
+
return this.name
|
|
1628
|
+
},
|
|
1629
|
+
paletteLabel: 'WhatsApp Dynamic Send',
|
|
1630
|
+
inputs: 1,
|
|
1631
|
+
inputLabels: (i) => 'message',
|
|
1632
|
+
outputs: 0,
|
|
1633
|
+
outputLabels: (i) => [][i],
|
|
1634
|
+
defaults: {
|
|
1635
|
+
name: {
|
|
1636
|
+
value: 'Dynamic Send WhatsApp Message',
|
|
1637
|
+
required: true
|
|
1638
|
+
},
|
|
1639
|
+
accountConfig: {
|
|
1640
|
+
type: 'WhatsappAccountConfigNode',
|
|
1641
|
+
required: true
|
|
1642
|
+
},
|
|
1643
|
+
recipient: {
|
|
1644
|
+
value: 'payload.sender.id'
|
|
1645
|
+
},
|
|
1646
|
+
recipientType: {
|
|
1647
|
+
value: 'msg'
|
|
1648
|
+
},
|
|
1649
|
+
payloads: {
|
|
1650
|
+
value: '[]',
|
|
1651
|
+
required: false,
|
|
1652
|
+
validate: function(value) {
|
|
1653
|
+
try {
|
|
1654
|
+
JSON.parse(value || "[]");
|
|
1655
|
+
return true;
|
|
1656
|
+
} catch (e) {
|
|
1657
|
+
return false;
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
},
|
|
1661
|
+
logEnabled: {
|
|
1662
|
+
value: false,
|
|
1663
|
+
required: true
|
|
1664
|
+
},
|
|
1665
|
+
logger: {
|
|
1666
|
+
required: false,
|
|
1667
|
+
value: '',
|
|
1668
|
+
type: 'DelegatedConfigReferenceNode'
|
|
1669
|
+
},
|
|
1670
|
+
logTemplateOverrideEnabled: {
|
|
1671
|
+
value: false
|
|
1672
|
+
},
|
|
1673
|
+
logTemplateOverride: {
|
|
1674
|
+
value: 'message:{{msg}}'
|
|
1675
|
+
},
|
|
1676
|
+
metricsEnabled: {
|
|
1677
|
+
value: false,
|
|
1678
|
+
required: true
|
|
1679
|
+
},
|
|
1680
|
+
metricsReference: {
|
|
1681
|
+
required: false,
|
|
1682
|
+
value: '',
|
|
1683
|
+
type: 'DelegatedConfigReferenceNode'
|
|
1684
|
+
},
|
|
1685
|
+
},
|
|
1686
|
+
oneditprepare: function() {
|
|
1687
|
+
// **** WhatsappDynamicSendMessageNode **** //
|
|
1688
|
+
{
|
|
1689
|
+
let node = this;
|
|
1690
|
+
// ── Recipient typed input ─────────────────────────────────────────────────
|
|
1691
|
+
$("#node-input-recipient").typedInput({
|
|
1692
|
+
types: ["msg", "flow", "global", "str"],
|
|
1693
|
+
typeField: "#node-input-recipientType"
|
|
1694
|
+
});
|
|
1695
|
+
// ── Messages editableList ─────────────────────────────────────────────────
|
|
1696
|
+
let actionList = $("#wa-dynamic-send-actions-list");
|
|
1697
|
+
actionList.editableList({
|
|
1698
|
+
sortable: true,
|
|
1699
|
+
removable: true,
|
|
1700
|
+
height: "auto",
|
|
1701
|
+
scrollOnAdd: true,
|
|
1702
|
+
addButton: "Add Message",
|
|
1703
|
+
addItem: function(container, i, action) {
|
|
1704
|
+
action = action || {};
|
|
1705
|
+
let type = action.type || "text";
|
|
1706
|
+
container.css({
|
|
1707
|
+
padding: "6px 4px"
|
|
1708
|
+
});
|
|
1709
|
+
// Type row
|
|
1710
|
+
let typeRow = $('<div style="display:flex; align-items:center; gap:8px; margin-bottom:5px;">');
|
|
1711
|
+
let iconEl = $('<i style="width:14px; text-align:center; flex-shrink:0;">').addClass("fa " + (WA_SEND_TYPES[type] ? WA_SEND_TYPES[type].icon : "fa-comment"));
|
|
1712
|
+
let typeSelect = $('<select class="wa-dynamic-send-action-type" style="flex:0 0 auto;">');
|
|
1713
|
+
Object.keys(WA_SEND_TYPES).forEach(function(key) {
|
|
1714
|
+
typeSelect.append($("<option>").val(key).text(WA_SEND_TYPES[key].label));
|
|
1715
|
+
});
|
|
1716
|
+
typeSelect.val(type);
|
|
1717
|
+
typeRow.append(iconEl).append(typeSelect);
|
|
1718
|
+
container.append(typeRow);
|
|
1719
|
+
// Fields container
|
|
1720
|
+
let fieldsContainer = $('<div class="wa-dynamic-send-action-fields">');
|
|
1721
|
+
container.append(fieldsContainer);
|
|
1722
|
+
let renderFields = function(selectedType) {
|
|
1723
|
+
fieldsContainer.empty();
|
|
1724
|
+
let typeDef = WA_SEND_TYPES[selectedType];
|
|
1725
|
+
if (!typeDef) return;
|
|
1726
|
+
iconEl.removeClass().addClass("fa " + typeDef.icon);
|
|
1727
|
+
typeDef.fields.forEach(function(field) {
|
|
1728
|
+
let fieldRow = $('<div class="form-row nomargin" style="display:flex; align-items:center; gap:6px;">');
|
|
1729
|
+
fieldRow.append($('<label style="width:70px; margin:0; font-size:0.85em; flex-shrink:0;">').text(field.label));
|
|
1730
|
+
let input = $('<input type="text">').addClass("wa-dynamic-send-field-" + field.key);
|
|
1731
|
+
fieldRow.append(input);
|
|
1732
|
+
fieldsContainer.append(fieldRow);
|
|
1733
|
+
input.typedInput({
|
|
1734
|
+
types: field.types
|
|
1735
|
+
}).typedInput("type", action[field.key + "Type"] || field.types[0]).typedInput("value", action[field.key] || "");
|
|
1736
|
+
fieldRow.find(".red-ui-typedInput-container").css({
|
|
1737
|
+
flex: "1",
|
|
1738
|
+
"min-width": "0",
|
|
1739
|
+
width: ""
|
|
1740
|
+
});
|
|
1741
|
+
});
|
|
1742
|
+
};
|
|
1743
|
+
renderFields(type);
|
|
1744
|
+
typeSelect.on("change", function() {
|
|
1745
|
+
action = {
|
|
1746
|
+
type: $(this).val()
|
|
1747
|
+
};
|
|
1748
|
+
renderFields($(this).val());
|
|
1749
|
+
});
|
|
1750
|
+
}
|
|
1751
|
+
});
|
|
1752
|
+
let actions = [];
|
|
1753
|
+
try {
|
|
1754
|
+
actions = JSON.parse(node.payloads || "[]");
|
|
1755
|
+
} catch (e) {}
|
|
1756
|
+
if (actions.length > 0) {
|
|
1757
|
+
actionList.editableList("addItems", actions);
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
// **** logger **** //
|
|
1761
|
+
{
|
|
1762
|
+
let node = this;
|
|
1763
|
+
let templateConfig = {};
|
|
1764
|
+
// controls.
|
|
1765
|
+
let logEnabled = $("#node-input-logEnabled");
|
|
1766
|
+
let logTemplateEnabled = $("#node-input-logTemplateOverrideEnabled");
|
|
1767
|
+
let logTemplateEnabledSection = $("#section_logTemplateOverrideEnabled");
|
|
1768
|
+
let logEnabledSection = $("#section_logEnabled");
|
|
1769
|
+
let loggerSection = $("#section_loggerconfig");
|
|
1770
|
+
loggerSection.hide();
|
|
1771
|
+
// functions.
|
|
1772
|
+
let getLoggerTypes = async function() {
|
|
1773
|
+
let types = [];
|
|
1774
|
+
await $.ajax({
|
|
1775
|
+
url: "nodetypeservice/find?tag=LoggerType",
|
|
1776
|
+
type: "GET",
|
|
1777
|
+
contentType: "application/json; charset=utf-8",
|
|
1778
|
+
data: JSON.stringify({
|
|
1779
|
+
id: node.id
|
|
1780
|
+
}),
|
|
1781
|
+
success: (response) => {
|
|
1782
|
+
types = response;
|
|
1783
|
+
},
|
|
1784
|
+
error: (jqXHR, textStatus, errorThrown) => {
|
|
1785
|
+
console.log(jqXHR, textStatus, errorThrown);
|
|
1786
|
+
},
|
|
1787
|
+
});
|
|
1788
|
+
return types;
|
|
1789
|
+
}
|
|
1790
|
+
let getLoggers = async () => {
|
|
1791
|
+
let loggers = [];
|
|
1792
|
+
// get the list of types again.
|
|
1793
|
+
let loggerTypes = (await getLoggerTypes()).map(type => type.id);
|
|
1794
|
+
// add all of the known loggers.
|
|
1795
|
+
RED.nodes.eachConfig(cfg => {
|
|
1796
|
+
if (loggerTypes.includes(cfg.type)) {
|
|
1797
|
+
loggers.push({
|
|
1798
|
+
id: cfg.id,
|
|
1799
|
+
name: cfg.label(),
|
|
1800
|
+
type: cfg.type
|
|
1801
|
+
});
|
|
1802
|
+
}
|
|
1803
|
+
});
|
|
1804
|
+
return loggers;
|
|
1805
|
+
};
|
|
1806
|
+
let updateLoggerSelectorList = async () => {
|
|
1807
|
+
loggerSelector.empty();
|
|
1808
|
+
(await getLoggers()).forEach(logger => {
|
|
1809
|
+
$('<option value="' + logger.id + '">' + logger.name + '</option>').appendTo(loggerSelector);
|
|
1810
|
+
});
|
|
1811
|
+
$('<option value="_ADD_">none</option>').appendTo(loggerSelector);
|
|
1812
|
+
}
|
|
1813
|
+
logEnabled.on('change', function() {
|
|
1814
|
+
if (logEnabled.prop("checked")) {
|
|
1815
|
+
logEnabledSection.show();
|
|
1816
|
+
node._def.defaults.logger.required = true;
|
|
1817
|
+
} else {
|
|
1818
|
+
logEnabledSection.hide()
|
|
1819
|
+
node._def.defaults.logger.required = false;
|
|
1820
|
+
}
|
|
1821
|
+
});
|
|
1822
|
+
logTemplateEnabled.on('change', function() {
|
|
1823
|
+
if (logTemplateEnabled.prop("checked")) {
|
|
1824
|
+
logTemplateEnabledSection.show();
|
|
1825
|
+
node._def.defaults.logTemplateOverride.required = true;
|
|
1826
|
+
} else {
|
|
1827
|
+
logTemplateEnabledSection.hide()
|
|
1828
|
+
node._def.defaults.logTemplateOverride.required = false;
|
|
1829
|
+
}
|
|
1830
|
+
});
|
|
1831
|
+
node.logTemplateOverrideEditor = RED.editor.createEditor({
|
|
1832
|
+
id: 'node-input-logTemplateOverrideEditor',
|
|
1833
|
+
mode: 'ace/mode/handlebars',
|
|
1834
|
+
value: node.logTemplateOverride
|
|
1835
|
+
});
|
|
1836
|
+
let loggerSelector = $("#logger-selector");
|
|
1837
|
+
let loggerEditButton = $("#logger-edit-btn");
|
|
1838
|
+
let loggerAddButton = $("#logger-add-btn");
|
|
1839
|
+
loggerEditButton.on('click', async function(event) {
|
|
1840
|
+
event.stopPropagation();
|
|
1841
|
+
if (loggerEditButton.hasClass("disabled")) {
|
|
1842
|
+
return;
|
|
1843
|
+
}
|
|
1844
|
+
// figure out what type of logger this is.
|
|
1845
|
+
let selectedLogger = (await getLoggers()).find(logger => logger.id === loggerSelector.val());
|
|
1846
|
+
RED.editor.editConfig("#logger-selector", selectedLogger.type, selectedLogger.id);
|
|
1847
|
+
});
|
|
1848
|
+
loggerAddButton.on('click', function(event) {
|
|
1849
|
+
event.stopPropagation();
|
|
1850
|
+
let $btn = $(event.target);
|
|
1851
|
+
// Remove any existing dropdown
|
|
1852
|
+
$('.loggertype-dropdown').remove();
|
|
1853
|
+
getLoggerTypes().then(loggerTypes => {
|
|
1854
|
+
let $dropdown = $('<div class="loggertype-dropdown red-ui-editor"></div>');
|
|
1855
|
+
loggerTypes.forEach(loggerType => {
|
|
1856
|
+
$('<div class="dropdown-item"></div>').text(loggerType.name).data('value', loggerType.id).css({
|
|
1857
|
+
padding: '4px 8px',
|
|
1858
|
+
cursor: 'pointer'
|
|
1859
|
+
}).appendTo($dropdown);
|
|
1860
|
+
});
|
|
1861
|
+
// Append first so outerWidth() is measurable, then position so the
|
|
1862
|
+
// right edge of the dropdown aligns with the right edge of the button.
|
|
1863
|
+
$('body').append($dropdown);
|
|
1864
|
+
$dropdown.css({
|
|
1865
|
+
top: $btn.offset().top + $btn.outerHeight(),
|
|
1866
|
+
left: $btn.offset().left + $btn.outerWidth() - $dropdown.outerWidth()
|
|
1867
|
+
});
|
|
1868
|
+
$(document).on('mousedown.dropdown', function(e) {
|
|
1869
|
+
if (!$(e.target).closest('.loggertype-dropdown').length) {
|
|
1870
|
+
$dropdown.remove();
|
|
1871
|
+
$(document).off('mousedown.dropdown'); // Clean up the listener
|
|
1872
|
+
}
|
|
1873
|
+
});
|
|
1874
|
+
// Handle item selection
|
|
1875
|
+
$dropdown.on('click', '.dropdown-item', function(e) {
|
|
1876
|
+
e.stopPropagation();
|
|
1877
|
+
const selectedValue = $(this).data('value');
|
|
1878
|
+
const selectedText = $(this).text();
|
|
1879
|
+
// Do whatever you need with the selection
|
|
1880
|
+
console.log('Selected:', selectedValue, selectedText);
|
|
1881
|
+
$dropdown.remove();
|
|
1882
|
+
RED.editor.editConfig("#logger-selector", selectedValue, "_ADD_");
|
|
1883
|
+
});
|
|
1884
|
+
})
|
|
1885
|
+
});
|
|
1886
|
+
loggerSelector.on("focus", updateLoggerSelectorList);
|
|
1887
|
+
loggerSelector.on('change', function(event) {
|
|
1888
|
+
if ("_ADD_" === loggerSelector.val()) {
|
|
1889
|
+
loggerEditButton.addClass("disabled")
|
|
1890
|
+
} else {
|
|
1891
|
+
loggerEditButton.removeClass("disabled")
|
|
1892
|
+
}
|
|
1893
|
+
});
|
|
1894
|
+
// Show the logger section only if at least one logger type is registered.
|
|
1895
|
+
// If nodered_logging (or any other logger provider) is not installed, the section stays hidden.
|
|
1896
|
+
getLoggerTypes().then(loggerTypes => {
|
|
1897
|
+
if (loggerTypes.length === 0) return;
|
|
1898
|
+
loggerSection.show();
|
|
1899
|
+
updateLoggerSelectorList().then(() => {
|
|
1900
|
+
loggerSelector.val(node.logger);
|
|
1901
|
+
loggerSelector.trigger('change');
|
|
1902
|
+
});
|
|
1077
1903
|
});
|
|
1078
1904
|
}
|
|
1079
1905
|
// **** metrics **** //
|
|
1080
1906
|
{
|
|
1081
1907
|
let node = this;
|
|
1908
|
+
let templateConfig = {};
|
|
1082
1909
|
let metricsEnabled = $("#node-input-metricsEnabled");
|
|
1083
|
-
let
|
|
1910
|
+
let metricsSection = $("#section_metricsconfig");
|
|
1911
|
+
let metricsContainer = metricsEnabled.closest(".editorgroupborder");
|
|
1912
|
+
let metricsEnabledSection = metricsContainer.find("#section_metricsEnabled");
|
|
1913
|
+
let metricsEnableCheck = metricsContainer.find(".metricsEnableCheck");
|
|
1914
|
+
metricsSection.hide();
|
|
1915
|
+
if (templateConfig.showBorder === false) {
|
|
1916
|
+
metricsSection.find(".editorsectionheading").hide();
|
|
1917
|
+
metricsSection.find(".editorgroupborder").css({
|
|
1918
|
+
border: "none",
|
|
1919
|
+
padding: 0,
|
|
1920
|
+
margin: 0
|
|
1921
|
+
});
|
|
1922
|
+
}
|
|
1923
|
+
if (templateConfig.showEnable === false) {
|
|
1924
|
+
metricsEnableCheck.hide();
|
|
1925
|
+
metricsEnabledSection.show();
|
|
1926
|
+
}
|
|
1927
|
+
let getMetricsProviderTypes = async function() {
|
|
1928
|
+
let types = [];
|
|
1929
|
+
await $.ajax({
|
|
1930
|
+
url: "nodetypeservice/find?tag=MetricsProvider",
|
|
1931
|
+
type: "GET",
|
|
1932
|
+
contentType: "application/json; charset=utf-8",
|
|
1933
|
+
success: (response) => {
|
|
1934
|
+
types = response;
|
|
1935
|
+
},
|
|
1936
|
+
error: (jqXHR, textStatus, errorThrown) => {
|
|
1937
|
+
console.log(jqXHR, textStatus, errorThrown);
|
|
1938
|
+
},
|
|
1939
|
+
});
|
|
1940
|
+
return types;
|
|
1941
|
+
};
|
|
1942
|
+
let getMetricsProviders = async () => {
|
|
1943
|
+
let providers = [];
|
|
1944
|
+
let providerTypes = (await getMetricsProviderTypes()).map(t => t.id);
|
|
1945
|
+
RED.nodes.eachConfig(cfg => {
|
|
1946
|
+
if (providerTypes.includes(cfg.type)) {
|
|
1947
|
+
providers.push({
|
|
1948
|
+
id: cfg.id,
|
|
1949
|
+
name: cfg.label(),
|
|
1950
|
+
type: cfg.type
|
|
1951
|
+
});
|
|
1952
|
+
}
|
|
1953
|
+
});
|
|
1954
|
+
return providers;
|
|
1955
|
+
};
|
|
1956
|
+
let updateMetricsSelectorList = async () => {
|
|
1957
|
+
metricsSelector.empty();
|
|
1958
|
+
(await getMetricsProviders()).forEach(provider => {
|
|
1959
|
+
$('<option value="' + provider.id + '">' + provider.name + '</option>').appendTo(metricsSelector);
|
|
1960
|
+
});
|
|
1961
|
+
$('<option value="_ADD_">none</option>').appendTo(metricsSelector);
|
|
1962
|
+
};
|
|
1084
1963
|
metricsEnabled.on('change', function() {
|
|
1085
1964
|
if (metricsEnabled.prop("checked")) {
|
|
1086
1965
|
metricsEnabledSection.show();
|
|
1087
|
-
node._def.defaults.metricsReference.required = true;
|
|
1088
1966
|
} else {
|
|
1089
|
-
metricsEnabledSection.hide()
|
|
1090
|
-
|
|
1967
|
+
metricsEnabledSection.hide();
|
|
1968
|
+
}
|
|
1969
|
+
});
|
|
1970
|
+
let metricsSelector = metricsContainer.find("#metrics-selector");
|
|
1971
|
+
let metricsEditButton = metricsContainer.find("#metrics-edit-btn");
|
|
1972
|
+
let metricsAddButton = metricsContainer.find("#metrics-add-btn");
|
|
1973
|
+
metricsEditButton.on('click', async function(event) {
|
|
1974
|
+
event.stopPropagation();
|
|
1975
|
+
if (metricsEditButton.hasClass("disabled")) return;
|
|
1976
|
+
let selectedProvider = (await getMetricsProviders()).find(p => p.id === metricsSelector.val());
|
|
1977
|
+
if (selectedProvider) {
|
|
1978
|
+
RED.editor.editConfig("#metrics-selector", selectedProvider.type, selectedProvider.id);
|
|
1979
|
+
}
|
|
1980
|
+
});
|
|
1981
|
+
metricsAddButton.on('click', function(event) {
|
|
1982
|
+
event.stopPropagation();
|
|
1983
|
+
let $btn = $(event.currentTarget);
|
|
1984
|
+
let btnOffset = $btn.offset();
|
|
1985
|
+
let btnH = $btn.outerHeight();
|
|
1986
|
+
let btnW = $btn.outerWidth();
|
|
1987
|
+
$('.metricstype-dropdown').remove();
|
|
1988
|
+
getMetricsProviderTypes().then(providerTypes => {
|
|
1989
|
+
let $dropdown = $('<div class="metricstype-dropdown red-ui-editor"></div>').css({
|
|
1990
|
+
position: 'absolute',
|
|
1991
|
+
zIndex: 1000
|
|
1992
|
+
});
|
|
1993
|
+
providerTypes.forEach(providerType => {
|
|
1994
|
+
$('<div class="dropdown-item"></div>').text(providerType.name).data('value', providerType.id).css({
|
|
1995
|
+
padding: '4px 8px',
|
|
1996
|
+
cursor: 'pointer'
|
|
1997
|
+
}).appendTo($dropdown);
|
|
1998
|
+
});
|
|
1999
|
+
$('body').append($dropdown);
|
|
2000
|
+
$dropdown.css({
|
|
2001
|
+
top: btnOffset.top + btnH,
|
|
2002
|
+
left: Math.max(0, btnOffset.left + btnW - $dropdown.outerWidth())
|
|
2003
|
+
});
|
|
2004
|
+
$(document).on('mousedown.metricsdropdown', function(e) {
|
|
2005
|
+
if (!$(e.target).closest('.metricstype-dropdown').length) {
|
|
2006
|
+
$dropdown.remove();
|
|
2007
|
+
$(document).off('mousedown.metricsdropdown');
|
|
2008
|
+
}
|
|
2009
|
+
});
|
|
2010
|
+
$dropdown.on('click', '.dropdown-item', function(e) {
|
|
2011
|
+
e.stopPropagation();
|
|
2012
|
+
const selectedValue = $(this).data('value');
|
|
2013
|
+
$dropdown.remove();
|
|
2014
|
+
RED.editor.editConfig("#metrics-selector", selectedValue, "_ADD_");
|
|
2015
|
+
});
|
|
2016
|
+
});
|
|
2017
|
+
});
|
|
2018
|
+
metricsSelector.on("focus", updateMetricsSelectorList);
|
|
2019
|
+
metricsSelector.on('change', function() {
|
|
2020
|
+
if ("_ADD_" === metricsSelector.val()) {
|
|
2021
|
+
metricsEditButton.addClass("disabled");
|
|
2022
|
+
} else {
|
|
2023
|
+
metricsEditButton.removeClass("disabled");
|
|
1091
2024
|
}
|
|
1092
2025
|
});
|
|
2026
|
+
// Show the metrics section only if at least one metrics provider type is registered.
|
|
2027
|
+
// If nodered_prometheus (or any other provider) is not installed, the section stays hidden.
|
|
2028
|
+
getMetricsProviderTypes().then(providerTypes => {
|
|
2029
|
+
if (providerTypes.length === 0) return;
|
|
2030
|
+
metricsSection.show();
|
|
2031
|
+
updateMetricsSelectorList().then(() => {
|
|
2032
|
+
metricsSelector.val(node.metricsReference);
|
|
2033
|
+
metricsSelector.trigger('change');
|
|
2034
|
+
});
|
|
2035
|
+
});
|
|
1093
2036
|
}
|
|
1094
2037
|
},
|
|
1095
2038
|
oneditsave: function() {
|
|
1096
|
-
// ****
|
|
2039
|
+
// **** WhatsappDynamicSendMessageNode **** //
|
|
1097
2040
|
{
|
|
1098
|
-
let
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
2041
|
+
let actions = [];
|
|
2042
|
+
$("#wa-dynamic-send-actions-list").editableList("items").each(function() {
|
|
2043
|
+
let container = $(this);
|
|
2044
|
+
let type = container.find(".wa-dynamic-send-action-type").val();
|
|
2045
|
+
let typeDef = WA_SEND_TYPES[type];
|
|
2046
|
+
if (!typeDef) return;
|
|
2047
|
+
let action = {
|
|
2048
|
+
type: type
|
|
1104
2049
|
};
|
|
2050
|
+
typeDef.fields.forEach(function(field) {
|
|
2051
|
+
let input = container.find(".wa-dynamic-send-field-" + field.key);
|
|
2052
|
+
if (input.length) {
|
|
2053
|
+
action[field.key] = input.typedInput("value");
|
|
2054
|
+
action[field.key + "Type"] = input.typedInput("type");
|
|
2055
|
+
}
|
|
2056
|
+
});
|
|
2057
|
+
actions.push(action);
|
|
1105
2058
|
});
|
|
1106
|
-
$("#node-input-payloads").val(JSON.stringify(
|
|
2059
|
+
$("#node-input-payloads").val(JSON.stringify(actions));
|
|
1107
2060
|
}
|
|
1108
2061
|
// **** logger **** //
|
|
1109
2062
|
{
|
|
2063
|
+
let node = this;
|
|
2064
|
+
let templateConfig = {};
|
|
1110
2065
|
node = this;
|
|
1111
2066
|
var selectedLogger = $("#logger-selector").val();
|
|
1112
2067
|
node.logger = (selectedLogger && selectedLogger !== "_ADD_") ? selectedLogger : '';
|
|
@@ -1115,7 +2070,16 @@ Click **Create New Group** to create a new WhatsApp group from within Node-RED.
|
|
|
1115
2070
|
delete node.logTemplateOverrideEditor;
|
|
1116
2071
|
}
|
|
1117
2072
|
// **** metrics **** //
|
|
1118
|
-
{
|
|
2073
|
+
{
|
|
2074
|
+
let node = this;
|
|
2075
|
+
let templateConfig = {};
|
|
2076
|
+
node = this;
|
|
2077
|
+
var selectedProvider = $("#metrics-selector").val();
|
|
2078
|
+
node.metricsReference = (selectedProvider && selectedProvider !== "_ADD_") ? selectedProvider : '';
|
|
2079
|
+
if (templateConfig.showEnable === false) {
|
|
2080
|
+
node.metricsEnabled = !!node.metricsReference;
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
1119
2083
|
},
|
|
1120
2084
|
oneditcancel: function() {},
|
|
1121
2085
|
oneditdelete: function() {},
|
|
@@ -1124,23 +2088,36 @@ Click **Create New Group** to create a new WhatsApp group from within Node-RED.
|
|
|
1124
2088
|
</script>
|
|
1125
2089
|
|
|
1126
2090
|
|
|
1127
|
-
<script type="text/html" data-template-name='
|
|
1128
|
-
<div id='
|
|
2091
|
+
<script type="text/html" data-template-name='WhatsappDynamicSendMessageNode'>
|
|
2092
|
+
<div id='section_WhatsappDynamicSendMessageNode'>
|
|
1129
2093
|
<div class="form-row">
|
|
1130
2094
|
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
1131
2095
|
<input type="text" id="node-input-name" />
|
|
1132
2096
|
</div>
|
|
1133
2097
|
|
|
1134
|
-
<div class="form-row
|
|
1135
|
-
<
|
|
1136
|
-
|
|
2098
|
+
<div class="form-row editorsectionheading">
|
|
2099
|
+
<i class="w-16 fa fa-envelope"></i> <span>WhatsApp Account</span>
|
|
2100
|
+
</div>
|
|
2101
|
+
|
|
2102
|
+
<div class="editorgroupborder">
|
|
2103
|
+
<div class="form-row nomargin">
|
|
2104
|
+
<label for="node-input-accountConfig" style="width:120px;"><i class="fa fa-user"></i> Account</label>
|
|
2105
|
+
<input type="text" id="node-input-accountConfig" placeholder="config">
|
|
2106
|
+
</div>
|
|
2107
|
+
|
|
2108
|
+
<div class="form-row nomargin">
|
|
2109
|
+
<label for="node-input-recipient" style="width:120px;"><i class="fa fa-paper-plane"></i> Recipient</label>
|
|
2110
|
+
<input type="text" id="node-input-recipient" placeholder="payload.sender.id">
|
|
2111
|
+
<input type="hidden" id="node-input-recipientType">
|
|
2112
|
+
</div>
|
|
1137
2113
|
</div>
|
|
1138
2114
|
|
|
1139
2115
|
<div class="form-row editorsectionheading">
|
|
1140
|
-
<i class="w-16 fa fa-paper-plane"></i> <span>
|
|
2116
|
+
<i class="w-16 fa fa-paper-plane"></i> <span>Messages</span>
|
|
1141
2117
|
</div>
|
|
1142
2118
|
|
|
1143
|
-
<div class="editorgroupborder"
|
|
2119
|
+
<div class="editorgroupborder">
|
|
2120
|
+
<ol id="wa-dynamic-send-actions-list"></ol>
|
|
1144
2121
|
</div>
|
|
1145
2122
|
|
|
1146
2123
|
<input type="hidden" id="node-input-payloads">
|
|
@@ -1155,13 +2132,14 @@ Click **Create New Group** to create a new WhatsApp group from within Node-RED.
|
|
|
1155
2132
|
</div>
|
|
1156
2133
|
<div class="editorgroupborder">
|
|
1157
2134
|
<div class="form-row nomargin logEnableCheck">
|
|
1158
|
-
<
|
|
1159
|
-
<
|
|
2135
|
+
<label class="towb_editorlabel"> </label>
|
|
2136
|
+
<input class="towb_checkbox" type="checkbox" id="node-input-logEnabled" placeholder="logEnabled" value="false">
|
|
2137
|
+
<label class="towb_checkboxlabel" for="node-input-logEnabled"><span>Enable Logging</span></label>
|
|
1160
2138
|
</div>
|
|
1161
2139
|
|
|
1162
2140
|
<div id="section_logEnabled">
|
|
1163
2141
|
<div class="form-row nomargin">
|
|
1164
|
-
<label for="logger-selector">Logger</label>
|
|
2142
|
+
<label class="towb_editorlabel" for="logger-selector">Logger</label>
|
|
1165
2143
|
<div style="width: 70%; display: inline-flex;">
|
|
1166
2144
|
<select id="logger-selector" style="flex-grow: 1;" class="">
|
|
1167
2145
|
<option value="_ADD_">none</option>
|
|
@@ -1176,8 +2154,9 @@ Click **Create New Group** to create a new WhatsApp group from within Node-RED.
|
|
|
1176
2154
|
</div>
|
|
1177
2155
|
|
|
1178
2156
|
<div class="form-row nomargin">
|
|
1179
|
-
<
|
|
1180
|
-
<
|
|
2157
|
+
<label class="towb_editorlabel"> </label>
|
|
2158
|
+
<input class="towb_checkbox" type="checkbox" id="node-input-logTemplateOverrideEnabled" placeholder="" value="false">
|
|
2159
|
+
<label class="towb_checkboxlabel" for="node-input-logTemplateOverrideEnabled"><span>Override Template</span></label>
|
|
1181
2160
|
</div>
|
|
1182
2161
|
|
|
1183
2162
|
<div id="section_logTemplateOverrideEnabled">
|
|
@@ -1199,14 +2178,25 @@ Click **Create New Group** to create a new WhatsApp group from within Node-RED.
|
|
|
1199
2178
|
</div>
|
|
1200
2179
|
<div class="editorgroupborder">
|
|
1201
2180
|
<div class="form-row nomargin metricsEnableCheck">
|
|
1202
|
-
<
|
|
1203
|
-
<
|
|
2181
|
+
<label class="towb_editorlabel"> </label>
|
|
2182
|
+
<input class="towb_checkbox" type="checkbox" id="node-input-metricsEnabled" value="false" />
|
|
2183
|
+
<label class="towb_checkboxlabel" for="node-input-metricsEnabled"><span>Enable Metrics</span></label>
|
|
1204
2184
|
</div>
|
|
1205
2185
|
|
|
1206
2186
|
<div id="section_metricsEnabled">
|
|
1207
2187
|
<div class="form-row nomargin">
|
|
1208
|
-
<label class="towb_editorlabel" for="
|
|
1209
|
-
<
|
|
2188
|
+
<label class="towb_editorlabel" for="metrics-selector">Metric Provider</label>
|
|
2189
|
+
<div style="width: 70%; display: inline-flex;">
|
|
2190
|
+
<select id="metrics-selector" style="flex-grow: 1; min-width: 0;">
|
|
2191
|
+
<option value="_ADD_">none</option>
|
|
2192
|
+
</select>
|
|
2193
|
+
<a id="metrics-edit-btn" class="red-ui-button disabled" style="margin-left: 10px; flex-shrink: 0;">
|
|
2194
|
+
<i class="fa fa-pencil"></i>
|
|
2195
|
+
</a>
|
|
2196
|
+
<a id="metrics-add-btn" class="red-ui-button" style="margin-left: 10px; flex-shrink: 0;">
|
|
2197
|
+
<i class="fa fa-plus"></i>
|
|
2198
|
+
</a>
|
|
2199
|
+
</div>
|
|
1210
2200
|
</div>
|
|
1211
2201
|
</div>
|
|
1212
2202
|
</div>
|
|
@@ -1215,26 +2205,28 @@ Click **Create New Group** to create a new WhatsApp group from within Node-RED.
|
|
|
1215
2205
|
</div>
|
|
1216
2206
|
</script>
|
|
1217
2207
|
|
|
1218
|
-
<script type="text/markdown" data-help-name='
|
|
2208
|
+
<script type="text/markdown" data-help-name='WhatsappDynamicSendMessageNode'>
|
|
1219
2209
|
> **Early development** — this package is still maturing. Some features may be incomplete and diagnostic log output is intentionally verbose for now.
|
|
1220
2210
|
|
|
1221
|
-
Sends a
|
|
2211
|
+
Sends one or more WhatsApp messages directly to a recipient resolved at runtime — for example, replying privately to the sender of a group message.
|
|
1222
2212
|
|
|
1223
2213
|
### Properties
|
|
1224
2214
|
|
|
1225
|
-
: *name*
|
|
1226
|
-
: *
|
|
2215
|
+
: *name* (string) : Display label for this node.
|
|
2216
|
+
: *account* (config) : The WhatsApp Account config node to send from.
|
|
2217
|
+
: *recipient* (typed) : The target JID resolved from the incoming message, flow, global context, or a static string. Defaults to `msg.payload.sender.id` — the JID of whoever sent the triggering message.
|
|
1227
2218
|
|
|
1228
|
-
###
|
|
2219
|
+
### LID addressing
|
|
1229
2220
|
|
|
1230
|
-
|
|
2221
|
+
If the recipient JID ends with `@lid` (newer WhatsApp clients), the node automatically resolves it to the corresponding phone-number JID via the contact store before sending.
|
|
1231
2222
|
|
|
1232
|
-
|
|
1233
|
-
|
|
2223
|
+
### Messages
|
|
2224
|
+
|
|
2225
|
+
Each row in the **Messages** list is sent as a separate WhatsApp message, in order. Supports Text, Image, Video, and Document (with optional filename and MIME type).
|
|
1234
2226
|
|
|
1235
2227
|
### Inputs
|
|
1236
2228
|
|
|
1237
|
-
: *msg* : Trigger message.
|
|
2229
|
+
: *msg* : Trigger message. `msg`-type field values and the recipient are resolved against this message.
|
|
1238
2230
|
|
|
1239
2231
|
### Logging
|
|
1240
2232
|
: *enable logging* (boolean) : if checked, then the node will produce logging output to the specified logger.
|
|
@@ -1244,166 +2236,9 @@ Each field has a checkbox to enable it and a typed value. Supported value types
|
|
|
1244
2236
|
|
|
1245
2237
|
### Metrics
|
|
1246
2238
|
: *enable metrics* (boolean) : if checked, then the node will produce metrics output to the specified metrics provider.
|
|
1247
|
-
: *metric
|
|
1248
|
-
</script>
|
|
1249
|
-
<!-- WhatsappReceiveMessageNode -->
|
|
1250
|
-
<style>
|
|
1251
|
-
</style>
|
|
1252
|
-
<script>
|
|
1253
|
-
const WA_ACCEPT_FIELDS = [{
|
|
1254
|
-
id: "Text",
|
|
1255
|
-
icon: "fa-font",
|
|
1256
|
-
label: "Text",
|
|
1257
|
-
defaultEnabled: true
|
|
1258
|
-
}, {
|
|
1259
|
-
id: "ExtendedText",
|
|
1260
|
-
icon: "fa-align-left",
|
|
1261
|
-
label: "Extended Text",
|
|
1262
|
-
defaultEnabled: true
|
|
1263
|
-
}, {
|
|
1264
|
-
id: "Image",
|
|
1265
|
-
icon: "fa-image",
|
|
1266
|
-
label: "Image",
|
|
1267
|
-
defaultEnabled: true
|
|
1268
|
-
}, {
|
|
1269
|
-
id: "Video",
|
|
1270
|
-
icon: "fa-video-camera",
|
|
1271
|
-
label: "Video",
|
|
1272
|
-
defaultEnabled: true
|
|
1273
|
-
}, {
|
|
1274
|
-
id: "Album",
|
|
1275
|
-
icon: "fa-th-large",
|
|
1276
|
-
label: "Album",
|
|
1277
|
-
defaultEnabled: true
|
|
1278
|
-
}, {
|
|
1279
|
-
id: "Document",
|
|
1280
|
-
icon: "fa-file",
|
|
1281
|
-
label: "Document",
|
|
1282
|
-
defaultEnabled: false
|
|
1283
|
-
}, {
|
|
1284
|
-
id: "Contact",
|
|
1285
|
-
icon: "fa-address-card",
|
|
1286
|
-
label: "Contact",
|
|
1287
|
-
defaultEnabled: false
|
|
1288
|
-
}, {
|
|
1289
|
-
id: "Template",
|
|
1290
|
-
icon: "fa-th-list",
|
|
1291
|
-
label: "Template",
|
|
1292
|
-
defaultEnabled: false
|
|
1293
|
-
}, {
|
|
1294
|
-
id: "Interactive",
|
|
1295
|
-
icon: "fa-hand-o-up",
|
|
1296
|
-
label: "Interactive",
|
|
1297
|
-
defaultEnabled: false
|
|
1298
|
-
}, {
|
|
1299
|
-
id: "Location",
|
|
1300
|
-
icon: "fa-map-marker",
|
|
1301
|
-
label: "Location",
|
|
1302
|
-
defaultEnabled: false
|
|
1303
|
-
}, {
|
|
1304
|
-
id: "LiveLocation",
|
|
1305
|
-
icon: "fa-location-arrow",
|
|
1306
|
-
label: "Live Location",
|
|
1307
|
-
defaultEnabled: false
|
|
1308
|
-
}, ];
|
|
1309
|
-
|
|
2239
|
+
: *metric provider* (metricsconfig) : the metrics backend that this node will report to.
|
|
1310
2240
|
</script>
|
|
1311
|
-
<!-- logger -->
|
|
1312
|
-
<style>
|
|
1313
|
-
.editorgroupborder {
|
|
1314
|
-
border-width: 1px;
|
|
1315
|
-
border-style: solid;
|
|
1316
|
-
border-color: lightgray;
|
|
1317
|
-
padding: 3px;
|
|
1318
|
-
margin-top: 2px;
|
|
1319
|
-
margin-bottom: 2px;
|
|
1320
|
-
}
|
|
1321
|
-
|
|
1322
|
-
.editorsectionheading {
|
|
1323
|
-
font-weight: 600;
|
|
1324
|
-
margin-bottom: 1px !important;
|
|
1325
|
-
margin-top: 6px !important;
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
.nomargin {
|
|
1329
|
-
margin-bottom: 1px !important;
|
|
1330
|
-
margin-top: 1px !important;
|
|
1331
|
-
}
|
|
1332
|
-
|
|
1333
|
-
.slidecontainer {
|
|
1334
|
-
width: 100%;
|
|
1335
|
-
}
|
|
1336
|
-
|
|
1337
|
-
.slider {
|
|
1338
|
-
-webkit-appearance: none;
|
|
1339
|
-
appearance: none;
|
|
1340
|
-
width: 100%;
|
|
1341
|
-
height: 25px;
|
|
1342
|
-
background: #d3d3d3;
|
|
1343
|
-
outline: none;
|
|
1344
|
-
opacity: 0.7;
|
|
1345
|
-
-webkit-transition: .2s;
|
|
1346
|
-
transition: opacity .2s;
|
|
1347
|
-
}
|
|
1348
|
-
|
|
1349
|
-
.slider:hover {
|
|
1350
|
-
opacity: 1;
|
|
1351
|
-
}
|
|
1352
|
-
|
|
1353
|
-
.slider::-webkit-slider-thumb {
|
|
1354
|
-
-webkit-appearance: none;
|
|
1355
|
-
appearance: none;
|
|
1356
|
-
width: 25px;
|
|
1357
|
-
height: 25px;
|
|
1358
|
-
background: #04AA6D;
|
|
1359
|
-
cursor: pointer;
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
.slider::-moz-range-thumb {
|
|
1363
|
-
width: 25px;
|
|
1364
|
-
height: 25px;
|
|
1365
|
-
background: #04AA6D;
|
|
1366
|
-
cursor: pointer;
|
|
1367
|
-
}
|
|
1368
|
-
|
|
1369
|
-
.towb_editorlabel {
|
|
1370
|
-
width: 120px !important;
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
|
-
.towb_editorfield {
|
|
1374
|
-
width: 70% !important;
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
.towb_editorfield_short {
|
|
1378
|
-
width: 30% !important;
|
|
1379
|
-
}
|
|
1380
|
-
|
|
1381
|
-
</style>
|
|
1382
|
-
<script type="text/javascript">
|
|
1383
|
-
// CSS
|
|
1384
|
-
dropdownStyles = `
|
|
1385
|
-
.loggertype-dropdown {
|
|
1386
|
-
position: absolute;
|
|
1387
|
-
background: white;
|
|
1388
|
-
border: 1px solid #ccc;
|
|
1389
|
-
border-radius: 4px;
|
|
1390
|
-
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
|
1391
|
-
max-height: 150px;
|
|
1392
|
-
overflow-y: auto;
|
|
1393
|
-
z-index: 1000;
|
|
1394
|
-
min-width: 150px;
|
|
1395
|
-
}
|
|
1396
|
-
.loggertype-dropdown .dropdown-item {
|
|
1397
|
-
padding: 6px 12px;
|
|
1398
|
-
cursor: pointer;
|
|
1399
|
-
}
|
|
1400
|
-
.loggertype-dropdown .dropdown-item:hover {
|
|
1401
|
-
background: #f0f0f0;
|
|
1402
|
-
}
|
|
1403
|
-
`;
|
|
1404
|
-
$('<style>').text(dropdownStyles).appendTo('head');
|
|
1405
2241
|
|
|
1406
|
-
</script>
|
|
1407
2242
|
<script type="text/javascript">
|
|
1408
2243
|
RED.nodes.registerType('WhatsappReceiveMessageNode', {
|
|
1409
2244
|
category: 'whatsapp',
|
|
@@ -1458,8 +2293,9 @@ Each field has a checkbox to enable it and a typed value. Supported value types
|
|
|
1458
2293
|
required: true
|
|
1459
2294
|
},
|
|
1460
2295
|
metricsReference: {
|
|
1461
|
-
|
|
1462
|
-
|
|
2296
|
+
required: false,
|
|
2297
|
+
value: '',
|
|
2298
|
+
type: 'DelegatedConfigReferenceNode'
|
|
1463
2299
|
},
|
|
1464
2300
|
},
|
|
1465
2301
|
oneditprepare: function() {
|
|
@@ -1490,11 +2326,14 @@ Each field has a checkbox to enable it and a typed value. Supported value types
|
|
|
1490
2326
|
// **** logger **** //
|
|
1491
2327
|
{
|
|
1492
2328
|
let node = this;
|
|
2329
|
+
let templateConfig = {};
|
|
1493
2330
|
// controls.
|
|
1494
2331
|
let logEnabled = $("#node-input-logEnabled");
|
|
1495
2332
|
let logTemplateEnabled = $("#node-input-logTemplateOverrideEnabled");
|
|
1496
2333
|
let logTemplateEnabledSection = $("#section_logTemplateOverrideEnabled");
|
|
1497
2334
|
let logEnabledSection = $("#section_logEnabled");
|
|
2335
|
+
let loggerSection = $("#section_loggerconfig");
|
|
2336
|
+
loggerSection.hide();
|
|
1498
2337
|
// functions.
|
|
1499
2338
|
let getLoggerTypes = async function() {
|
|
1500
2339
|
let types = [];
|
|
@@ -1618,26 +2457,148 @@ Each field has a checkbox to enable it and a typed value. Supported value types
|
|
|
1618
2457
|
loggerEditButton.removeClass("disabled")
|
|
1619
2458
|
}
|
|
1620
2459
|
});
|
|
1621
|
-
//
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
2460
|
+
// Show the logger section only if at least one logger type is registered.
|
|
2461
|
+
// If nodered_logging (or any other logger provider) is not installed, the section stays hidden.
|
|
2462
|
+
getLoggerTypes().then(loggerTypes => {
|
|
2463
|
+
if (loggerTypes.length === 0) return;
|
|
2464
|
+
loggerSection.show();
|
|
2465
|
+
updateLoggerSelectorList().then(() => {
|
|
2466
|
+
loggerSelector.val(node.logger);
|
|
2467
|
+
loggerSelector.trigger('change');
|
|
2468
|
+
});
|
|
1625
2469
|
});
|
|
1626
2470
|
}
|
|
1627
2471
|
// **** metrics **** //
|
|
1628
2472
|
{
|
|
1629
2473
|
let node = this;
|
|
2474
|
+
let templateConfig = {};
|
|
1630
2475
|
let metricsEnabled = $("#node-input-metricsEnabled");
|
|
1631
|
-
let
|
|
2476
|
+
let metricsSection = $("#section_metricsconfig");
|
|
2477
|
+
let metricsContainer = metricsEnabled.closest(".editorgroupborder");
|
|
2478
|
+
let metricsEnabledSection = metricsContainer.find("#section_metricsEnabled");
|
|
2479
|
+
let metricsEnableCheck = metricsContainer.find(".metricsEnableCheck");
|
|
2480
|
+
metricsSection.hide();
|
|
2481
|
+
if (templateConfig.showBorder === false) {
|
|
2482
|
+
metricsSection.find(".editorsectionheading").hide();
|
|
2483
|
+
metricsSection.find(".editorgroupborder").css({
|
|
2484
|
+
border: "none",
|
|
2485
|
+
padding: 0,
|
|
2486
|
+
margin: 0
|
|
2487
|
+
});
|
|
2488
|
+
}
|
|
2489
|
+
if (templateConfig.showEnable === false) {
|
|
2490
|
+
metricsEnableCheck.hide();
|
|
2491
|
+
metricsEnabledSection.show();
|
|
2492
|
+
}
|
|
2493
|
+
let getMetricsProviderTypes = async function() {
|
|
2494
|
+
let types = [];
|
|
2495
|
+
await $.ajax({
|
|
2496
|
+
url: "nodetypeservice/find?tag=MetricsProvider",
|
|
2497
|
+
type: "GET",
|
|
2498
|
+
contentType: "application/json; charset=utf-8",
|
|
2499
|
+
success: (response) => {
|
|
2500
|
+
types = response;
|
|
2501
|
+
},
|
|
2502
|
+
error: (jqXHR, textStatus, errorThrown) => {
|
|
2503
|
+
console.log(jqXHR, textStatus, errorThrown);
|
|
2504
|
+
},
|
|
2505
|
+
});
|
|
2506
|
+
return types;
|
|
2507
|
+
};
|
|
2508
|
+
let getMetricsProviders = async () => {
|
|
2509
|
+
let providers = [];
|
|
2510
|
+
let providerTypes = (await getMetricsProviderTypes()).map(t => t.id);
|
|
2511
|
+
RED.nodes.eachConfig(cfg => {
|
|
2512
|
+
if (providerTypes.includes(cfg.type)) {
|
|
2513
|
+
providers.push({
|
|
2514
|
+
id: cfg.id,
|
|
2515
|
+
name: cfg.label(),
|
|
2516
|
+
type: cfg.type
|
|
2517
|
+
});
|
|
2518
|
+
}
|
|
2519
|
+
});
|
|
2520
|
+
return providers;
|
|
2521
|
+
};
|
|
2522
|
+
let updateMetricsSelectorList = async () => {
|
|
2523
|
+
metricsSelector.empty();
|
|
2524
|
+
(await getMetricsProviders()).forEach(provider => {
|
|
2525
|
+
$('<option value="' + provider.id + '">' + provider.name + '</option>').appendTo(metricsSelector);
|
|
2526
|
+
});
|
|
2527
|
+
$('<option value="_ADD_">none</option>').appendTo(metricsSelector);
|
|
2528
|
+
};
|
|
1632
2529
|
metricsEnabled.on('change', function() {
|
|
1633
2530
|
if (metricsEnabled.prop("checked")) {
|
|
1634
2531
|
metricsEnabledSection.show();
|
|
1635
|
-
node._def.defaults.metricsReference.required = true;
|
|
1636
2532
|
} else {
|
|
1637
|
-
metricsEnabledSection.hide()
|
|
1638
|
-
|
|
2533
|
+
metricsEnabledSection.hide();
|
|
2534
|
+
}
|
|
2535
|
+
});
|
|
2536
|
+
let metricsSelector = metricsContainer.find("#metrics-selector");
|
|
2537
|
+
let metricsEditButton = metricsContainer.find("#metrics-edit-btn");
|
|
2538
|
+
let metricsAddButton = metricsContainer.find("#metrics-add-btn");
|
|
2539
|
+
metricsEditButton.on('click', async function(event) {
|
|
2540
|
+
event.stopPropagation();
|
|
2541
|
+
if (metricsEditButton.hasClass("disabled")) return;
|
|
2542
|
+
let selectedProvider = (await getMetricsProviders()).find(p => p.id === metricsSelector.val());
|
|
2543
|
+
if (selectedProvider) {
|
|
2544
|
+
RED.editor.editConfig("#metrics-selector", selectedProvider.type, selectedProvider.id);
|
|
2545
|
+
}
|
|
2546
|
+
});
|
|
2547
|
+
metricsAddButton.on('click', function(event) {
|
|
2548
|
+
event.stopPropagation();
|
|
2549
|
+
let $btn = $(event.currentTarget);
|
|
2550
|
+
let btnOffset = $btn.offset();
|
|
2551
|
+
let btnH = $btn.outerHeight();
|
|
2552
|
+
let btnW = $btn.outerWidth();
|
|
2553
|
+
$('.metricstype-dropdown').remove();
|
|
2554
|
+
getMetricsProviderTypes().then(providerTypes => {
|
|
2555
|
+
let $dropdown = $('<div class="metricstype-dropdown red-ui-editor"></div>').css({
|
|
2556
|
+
position: 'absolute',
|
|
2557
|
+
zIndex: 1000
|
|
2558
|
+
});
|
|
2559
|
+
providerTypes.forEach(providerType => {
|
|
2560
|
+
$('<div class="dropdown-item"></div>').text(providerType.name).data('value', providerType.id).css({
|
|
2561
|
+
padding: '4px 8px',
|
|
2562
|
+
cursor: 'pointer'
|
|
2563
|
+
}).appendTo($dropdown);
|
|
2564
|
+
});
|
|
2565
|
+
$('body').append($dropdown);
|
|
2566
|
+
$dropdown.css({
|
|
2567
|
+
top: btnOffset.top + btnH,
|
|
2568
|
+
left: Math.max(0, btnOffset.left + btnW - $dropdown.outerWidth())
|
|
2569
|
+
});
|
|
2570
|
+
$(document).on('mousedown.metricsdropdown', function(e) {
|
|
2571
|
+
if (!$(e.target).closest('.metricstype-dropdown').length) {
|
|
2572
|
+
$dropdown.remove();
|
|
2573
|
+
$(document).off('mousedown.metricsdropdown');
|
|
2574
|
+
}
|
|
2575
|
+
});
|
|
2576
|
+
$dropdown.on('click', '.dropdown-item', function(e) {
|
|
2577
|
+
e.stopPropagation();
|
|
2578
|
+
const selectedValue = $(this).data('value');
|
|
2579
|
+
$dropdown.remove();
|
|
2580
|
+
RED.editor.editConfig("#metrics-selector", selectedValue, "_ADD_");
|
|
2581
|
+
});
|
|
2582
|
+
});
|
|
2583
|
+
});
|
|
2584
|
+
metricsSelector.on("focus", updateMetricsSelectorList);
|
|
2585
|
+
metricsSelector.on('change', function() {
|
|
2586
|
+
if ("_ADD_" === metricsSelector.val()) {
|
|
2587
|
+
metricsEditButton.addClass("disabled");
|
|
2588
|
+
} else {
|
|
2589
|
+
metricsEditButton.removeClass("disabled");
|
|
1639
2590
|
}
|
|
1640
2591
|
});
|
|
2592
|
+
// Show the metrics section only if at least one metrics provider type is registered.
|
|
2593
|
+
// If nodered_prometheus (or any other provider) is not installed, the section stays hidden.
|
|
2594
|
+
getMetricsProviderTypes().then(providerTypes => {
|
|
2595
|
+
if (providerTypes.length === 0) return;
|
|
2596
|
+
metricsSection.show();
|
|
2597
|
+
updateMetricsSelectorList().then(() => {
|
|
2598
|
+
metricsSelector.val(node.metricsReference);
|
|
2599
|
+
metricsSelector.trigger('change');
|
|
2600
|
+
});
|
|
2601
|
+
});
|
|
1641
2602
|
}
|
|
1642
2603
|
},
|
|
1643
2604
|
oneditsave: function() {
|
|
@@ -1653,6 +2614,8 @@ Each field has a checkbox to enable it and a typed value. Supported value types
|
|
|
1653
2614
|
}
|
|
1654
2615
|
// **** logger **** //
|
|
1655
2616
|
{
|
|
2617
|
+
let node = this;
|
|
2618
|
+
let templateConfig = {};
|
|
1656
2619
|
node = this;
|
|
1657
2620
|
var selectedLogger = $("#logger-selector").val();
|
|
1658
2621
|
node.logger = (selectedLogger && selectedLogger !== "_ADD_") ? selectedLogger : '';
|
|
@@ -1661,7 +2624,16 @@ Each field has a checkbox to enable it and a typed value. Supported value types
|
|
|
1661
2624
|
delete node.logTemplateOverrideEditor;
|
|
1662
2625
|
}
|
|
1663
2626
|
// **** metrics **** //
|
|
1664
|
-
{
|
|
2627
|
+
{
|
|
2628
|
+
let node = this;
|
|
2629
|
+
let templateConfig = {};
|
|
2630
|
+
node = this;
|
|
2631
|
+
var selectedProvider = $("#metrics-selector").val();
|
|
2632
|
+
node.metricsReference = (selectedProvider && selectedProvider !== "_ADD_") ? selectedProvider : '';
|
|
2633
|
+
if (templateConfig.showEnable === false) {
|
|
2634
|
+
node.metricsEnabled = !!node.metricsReference;
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
1665
2637
|
},
|
|
1666
2638
|
oneditcancel: function() {},
|
|
1667
2639
|
oneditdelete: function() {},
|
|
@@ -1720,13 +2692,14 @@ Each field has a checkbox to enable it and a typed value. Supported value types
|
|
|
1720
2692
|
</div>
|
|
1721
2693
|
<div class="editorgroupborder">
|
|
1722
2694
|
<div class="form-row nomargin logEnableCheck">
|
|
1723
|
-
<
|
|
1724
|
-
<
|
|
2695
|
+
<label class="towb_editorlabel"> </label>
|
|
2696
|
+
<input class="towb_checkbox" type="checkbox" id="node-input-logEnabled" placeholder="logEnabled" value="false">
|
|
2697
|
+
<label class="towb_checkboxlabel" for="node-input-logEnabled"><span>Enable Logging</span></label>
|
|
1725
2698
|
</div>
|
|
1726
2699
|
|
|
1727
2700
|
<div id="section_logEnabled">
|
|
1728
2701
|
<div class="form-row nomargin">
|
|
1729
|
-
<label for="logger-selector">Logger</label>
|
|
2702
|
+
<label class="towb_editorlabel" for="logger-selector">Logger</label>
|
|
1730
2703
|
<div style="width: 70%; display: inline-flex;">
|
|
1731
2704
|
<select id="logger-selector" style="flex-grow: 1;" class="">
|
|
1732
2705
|
<option value="_ADD_">none</option>
|
|
@@ -1741,8 +2714,9 @@ Each field has a checkbox to enable it and a typed value. Supported value types
|
|
|
1741
2714
|
</div>
|
|
1742
2715
|
|
|
1743
2716
|
<div class="form-row nomargin">
|
|
1744
|
-
<
|
|
1745
|
-
<
|
|
2717
|
+
<label class="towb_editorlabel"> </label>
|
|
2718
|
+
<input class="towb_checkbox" type="checkbox" id="node-input-logTemplateOverrideEnabled" placeholder="" value="false">
|
|
2719
|
+
<label class="towb_checkboxlabel" for="node-input-logTemplateOverrideEnabled"><span>Override Template</span></label>
|
|
1746
2720
|
</div>
|
|
1747
2721
|
|
|
1748
2722
|
<div id="section_logTemplateOverrideEnabled">
|
|
@@ -1764,14 +2738,25 @@ Each field has a checkbox to enable it and a typed value. Supported value types
|
|
|
1764
2738
|
</div>
|
|
1765
2739
|
<div class="editorgroupborder">
|
|
1766
2740
|
<div class="form-row nomargin metricsEnableCheck">
|
|
1767
|
-
<
|
|
1768
|
-
<
|
|
2741
|
+
<label class="towb_editorlabel"> </label>
|
|
2742
|
+
<input class="towb_checkbox" type="checkbox" id="node-input-metricsEnabled" value="false" />
|
|
2743
|
+
<label class="towb_checkboxlabel" for="node-input-metricsEnabled"><span>Enable Metrics</span></label>
|
|
1769
2744
|
</div>
|
|
1770
2745
|
|
|
1771
2746
|
<div id="section_metricsEnabled">
|
|
1772
2747
|
<div class="form-row nomargin">
|
|
1773
|
-
<label class="towb_editorlabel" for="
|
|
1774
|
-
<
|
|
2748
|
+
<label class="towb_editorlabel" for="metrics-selector">Metric Provider</label>
|
|
2749
|
+
<div style="width: 70%; display: inline-flex;">
|
|
2750
|
+
<select id="metrics-selector" style="flex-grow: 1; min-width: 0;">
|
|
2751
|
+
<option value="_ADD_">none</option>
|
|
2752
|
+
</select>
|
|
2753
|
+
<a id="metrics-edit-btn" class="red-ui-button disabled" style="margin-left: 10px; flex-shrink: 0;">
|
|
2754
|
+
<i class="fa fa-pencil"></i>
|
|
2755
|
+
</a>
|
|
2756
|
+
<a id="metrics-add-btn" class="red-ui-button" style="margin-left: 10px; flex-shrink: 0;">
|
|
2757
|
+
<i class="fa fa-plus"></i>
|
|
2758
|
+
</a>
|
|
2759
|
+
</div>
|
|
1775
2760
|
</div>
|
|
1776
2761
|
</div>
|
|
1777
2762
|
</div>
|
|
@@ -1808,7 +2793,6 @@ Each message type can be individually enabled or disabled. By default Text, Exte
|
|
|
1808
2793
|
|
|
1809
2794
|
### Metrics
|
|
1810
2795
|
: *enable metrics* (boolean) : if checked, then the node will produce metrics output to the specified metrics provider.
|
|
1811
|
-
: *metric
|
|
2796
|
+
: *metric provider* (metricsconfig) : the metrics backend that this node will report to.
|
|
1812
2797
|
</script>
|
|
1813
|
-
|
|
1814
2798
|
|