n8n-nodes-digitalsac 0.6.2 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +11 -6
- package/dist/digitalsac.svg +3 -0
- package/dist/icons/digitalsac.svg +3 -0
- package/dist/icons/n8n-nodes-digitalsac/dist/nodes/digitalsac/digitalsac.svg +3 -0
- package/dist/nodes/digitalsac.svg +3 -0
- package/index.ts +5 -0
- package/nodes/Digitalsac/Digitalsac.node.ts +1366 -0
- package/nodes/Digitalsac/digitalsac.svg +3 -0
- package/package.json +8 -17
|
@@ -0,0 +1,1366 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IExecuteFunctions,
|
|
3
|
+
INodeExecutionData,
|
|
4
|
+
INodeType,
|
|
5
|
+
INodeTypeDescription,
|
|
6
|
+
NodeConnectionType,
|
|
7
|
+
IDataObject,
|
|
8
|
+
IBinaryKeyData,
|
|
9
|
+
} from 'n8n-workflow';
|
|
10
|
+
|
|
11
|
+
export class Digitalsac implements INodeType {
|
|
12
|
+
description: INodeTypeDescription = {
|
|
13
|
+
displayName: 'Digitalsac Izing Pro',
|
|
14
|
+
name: 'digitalsac',
|
|
15
|
+
icon: 'file:digitalsac.svg',
|
|
16
|
+
group: ['transform'],
|
|
17
|
+
version: 1,
|
|
18
|
+
description: 'Interage com a API do Digitalsac',
|
|
19
|
+
defaults: {
|
|
20
|
+
name: 'Digitalsac',
|
|
21
|
+
},
|
|
22
|
+
inputs: <NodeConnectionType[]>['main'],
|
|
23
|
+
outputs: <NodeConnectionType[]>['main'],
|
|
24
|
+
|
|
25
|
+
credentials: [
|
|
26
|
+
{
|
|
27
|
+
name: 'digitalsacApi',
|
|
28
|
+
required: true,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
properties: [
|
|
32
|
+
{
|
|
33
|
+
displayName: 'Operação',
|
|
34
|
+
name: 'operation',
|
|
35
|
+
type: 'options',
|
|
36
|
+
options: [
|
|
37
|
+
{ name: 'Validar WhatsApp', value: 'validateWhatsapp' },
|
|
38
|
+
{ name: 'Validar CPF', value: 'validateCpf' },
|
|
39
|
+
{ name: 'Validar Data', value: 'validateDate' },
|
|
40
|
+
{ name: 'Listar Filas', value: 'listQueues' },
|
|
41
|
+
{ name: 'Listar Atendentes', value: 'listAgents' },
|
|
42
|
+
{ name: 'Transferir para Fila', value: 'transferQueue' },
|
|
43
|
+
{ name: 'Transferir para Atendente', value: 'transferAgent' },
|
|
44
|
+
{ name: 'Fechar Ticket', value: 'closeTicket' },
|
|
45
|
+
{ name: 'Enviar Mensagem', value: 'sendMessage' },
|
|
46
|
+
{ name: 'Enviar Botões', value: 'sendButtons' },
|
|
47
|
+
{ name: 'Enviar Lista', value: 'sendList' },
|
|
48
|
+
{ name: 'Enviar Mídia com Caption', value: 'sendMediaCaption' },
|
|
49
|
+
{ name: 'Enviar Base64', value: 'sendBase64' },
|
|
50
|
+
{ name: 'Listar Tags', value: 'listTags' },
|
|
51
|
+
{ name: 'Vincular Tag', value: 'linkTag' },
|
|
52
|
+
{ name: 'Criar Tag', value: 'createTag' },
|
|
53
|
+
{ name: 'Listar Kanbans', value: 'listKanbans' },
|
|
54
|
+
{ name: 'Vincular Kanban', value: 'linkKanban' },
|
|
55
|
+
{ name: 'Listar Carteiras', value: 'listCarteiras' },
|
|
56
|
+
{ name: 'Vincular Carteira', value: 'linkCarteira' },
|
|
57
|
+
// Agendamento
|
|
58
|
+
{ name: 'Listar Serviços', value: 'listServices' },
|
|
59
|
+
{ name: 'Listar Usuários Disponíveis', value: 'listAvailableUsers' },
|
|
60
|
+
{ name: 'Listar Horários Disponíveis', value: 'listAvailableSlots' },
|
|
61
|
+
{ name: 'Listar Agendamentos', value: 'listSchedules' },
|
|
62
|
+
{ name: 'Criar Agendamento', value: 'createSchedule' },
|
|
63
|
+
{ name: 'Cancelar Agendamento', value: 'cancelSchedule' },
|
|
64
|
+
{ name: 'Gerar Link do Calendário (.ics)', value: 'calendarLink' },
|
|
65
|
+
// Templates WABA
|
|
66
|
+
{ name: 'Listar Templates WABA', value: 'listWabaTemplates' },
|
|
67
|
+
{ name: 'Enviar Template WABA', value: 'sendWabaTemplate' },
|
|
68
|
+
],
|
|
69
|
+
default: 'validateWhatsapp',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
displayName: 'Parâmetro',
|
|
73
|
+
name: 'param',
|
|
74
|
+
type: 'string',
|
|
75
|
+
default: '',
|
|
76
|
+
displayOptions: {
|
|
77
|
+
show: {
|
|
78
|
+
operation: ['validateWhatsapp', 'validateCpf', 'sendMessage', 'sendButtons', 'sendList', 'sendMediaCaption', 'sendBase64'],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
description: 'Número, CPF ou UUID da conexão (conforme operação)',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
displayName: 'User ID',
|
|
85
|
+
name: 'userId',
|
|
86
|
+
type: 'number',
|
|
87
|
+
default: 1,
|
|
88
|
+
displayOptions: {
|
|
89
|
+
show: {
|
|
90
|
+
operation: ['listKanbans'],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
description: 'ID do usuário para listar kanbans',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
displayName: 'Formato de Resposta',
|
|
97
|
+
name: 'agentsFormat',
|
|
98
|
+
type: 'options',
|
|
99
|
+
options: [
|
|
100
|
+
{ name: 'JSON Completo (Recomendado)', value: 'json' },
|
|
101
|
+
{ name: 'Texto Formatado (Compatibilidade)', value: 'string' },
|
|
102
|
+
],
|
|
103
|
+
default: 'json',
|
|
104
|
+
displayOptions: {
|
|
105
|
+
show: {
|
|
106
|
+
operation: ['listAgents'],
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
description: 'Formato de retorno: JSON completo com todos os campos (id, name, email, phoneNumber, status, profile, isOnline, isActive, isMaster) ou texto formatado para compatibilidade',
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
displayName: 'Corpo da Mensagem',
|
|
113
|
+
name: 'messageBody',
|
|
114
|
+
type: 'string',
|
|
115
|
+
default: 'Mensagem de teste',
|
|
116
|
+
displayOptions: {
|
|
117
|
+
show: {
|
|
118
|
+
operation: ['sendMessage'],
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
description: 'Texto da mensagem a ser enviada',
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
displayName: 'Número de Telefone',
|
|
125
|
+
name: 'phoneNumber',
|
|
126
|
+
type: 'string',
|
|
127
|
+
default: '5511999999999',
|
|
128
|
+
displayOptions: {
|
|
129
|
+
show: {
|
|
130
|
+
operation: ['sendMessage'],
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
description: 'Número de telefone no formato DDI+DDD+Número (ex: 5511999999999)',
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
displayName: 'Chave Externa',
|
|
137
|
+
name: 'externalKey',
|
|
138
|
+
type: 'string',
|
|
139
|
+
default: 'Digitalsac123',
|
|
140
|
+
displayOptions: {
|
|
141
|
+
show: {
|
|
142
|
+
operation: ['sendMessage', 'sendButtons', 'sendList', 'sendMediaCaption', 'sendBase64'],
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
description: 'Identificador único opcional para a mensagem',
|
|
146
|
+
},
|
|
147
|
+
// Campos para Enviar Botões
|
|
148
|
+
{
|
|
149
|
+
displayName: 'Título',
|
|
150
|
+
name: 'buttonTitle',
|
|
151
|
+
type: 'string',
|
|
152
|
+
default: 'Escolha uma opção',
|
|
153
|
+
displayOptions: {
|
|
154
|
+
show: {
|
|
155
|
+
operation: ['sendButtons'],
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
description: 'Título do conjunto de botões',
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
displayName: 'Corpo da Mensagem',
|
|
162
|
+
name: 'buttonBody',
|
|
163
|
+
type: 'string',
|
|
164
|
+
default: 'Clique em uma das opções abaixo:',
|
|
165
|
+
displayOptions: {
|
|
166
|
+
show: {
|
|
167
|
+
operation: ['sendButtons'],
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
description: 'Corpo da mensagem com botões',
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
displayName: 'Número de Telefone',
|
|
174
|
+
name: 'buttonPhoneNumber',
|
|
175
|
+
type: 'string',
|
|
176
|
+
default: '5511999999999',
|
|
177
|
+
displayOptions: {
|
|
178
|
+
show: {
|
|
179
|
+
operation: ['sendButtons'],
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
description: 'Número de telefone no formato DDI+DDD+Número',
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
displayName: 'Botões (JSON)',
|
|
186
|
+
name: 'buttonsData',
|
|
187
|
+
type: 'json',
|
|
188
|
+
default: '[\n {\n "tipo": {"label": "Resposta Rápida", "value": "quick_reply"},\n "display_text": "Sim",\n "conteudo": "sim"\n },\n {\n "tipo": {"label": "URL", "value": "url"},\n "display_text": "Visitar Site",\n "conteudo": "https://exemplo.com"\n }\n]',
|
|
189
|
+
displayOptions: {
|
|
190
|
+
show: {
|
|
191
|
+
operation: ['sendButtons'],
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
description: 'Array de botões em formato JSON',
|
|
195
|
+
},
|
|
196
|
+
// Campos para Enviar Lista
|
|
197
|
+
{
|
|
198
|
+
displayName: 'Título',
|
|
199
|
+
name: 'listTitle',
|
|
200
|
+
type: 'string',
|
|
201
|
+
default: 'Menu de Opções',
|
|
202
|
+
displayOptions: {
|
|
203
|
+
show: {
|
|
204
|
+
operation: ['sendList'],
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
description: 'Título da lista',
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
displayName: 'Texto',
|
|
211
|
+
name: 'listText',
|
|
212
|
+
type: 'string',
|
|
213
|
+
default: 'Escolha uma categoria:',
|
|
214
|
+
displayOptions: {
|
|
215
|
+
show: {
|
|
216
|
+
operation: ['sendList'],
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
description: 'Texto da lista',
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
displayName: 'Texto do Botão',
|
|
223
|
+
name: 'listButtonText',
|
|
224
|
+
type: 'string',
|
|
225
|
+
default: 'Ver Opções',
|
|
226
|
+
displayOptions: {
|
|
227
|
+
show: {
|
|
228
|
+
operation: ['sendList'],
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
description: 'Texto do botão para abrir a lista',
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
displayName: 'Rodapé',
|
|
235
|
+
name: 'listFooter',
|
|
236
|
+
type: 'string',
|
|
237
|
+
default: 'Powered by DigitalSac',
|
|
238
|
+
displayOptions: {
|
|
239
|
+
show: {
|
|
240
|
+
operation: ['sendList'],
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
description: 'Rodapé da lista',
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
displayName: 'Número de Telefone',
|
|
247
|
+
name: 'listPhoneNumber',
|
|
248
|
+
type: 'string',
|
|
249
|
+
default: '5511999999999',
|
|
250
|
+
displayOptions: {
|
|
251
|
+
show: {
|
|
252
|
+
operation: ['sendList'],
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
description: 'Número de telefone no formato DDI+DDD+Número',
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
displayName: 'Seções (JSON)',
|
|
259
|
+
name: 'sectionsData',
|
|
260
|
+
type: 'json',
|
|
261
|
+
default: '[\n {\n "title": "Produtos",\n "lines": [\n {\n "title": "Produto A",\n "description": "Descrição do produto A",\n "rowId": 1\n }\n ]\n }\n]',
|
|
262
|
+
displayOptions: {
|
|
263
|
+
show: {
|
|
264
|
+
operation: ['sendList'],
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
description: 'Array de seções da lista em formato JSON',
|
|
268
|
+
},
|
|
269
|
+
// Campos para Enviar Mídia com Caption
|
|
270
|
+
{
|
|
271
|
+
displayName: 'Caption',
|
|
272
|
+
name: 'mediaCaption',
|
|
273
|
+
type: 'string',
|
|
274
|
+
default: 'Arquivo enviado via API',
|
|
275
|
+
displayOptions: {
|
|
276
|
+
show: {
|
|
277
|
+
operation: ['sendMediaCaption'],
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
description: 'Legenda do arquivo',
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
displayName: 'Número de Telefone',
|
|
284
|
+
name: 'mediaCaptionPhoneNumber',
|
|
285
|
+
type: 'string',
|
|
286
|
+
default: '5511999999999',
|
|
287
|
+
displayOptions: {
|
|
288
|
+
show: {
|
|
289
|
+
operation: ['sendMediaCaption'],
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
description: 'Número de telefone no formato DDI+DDD+Número',
|
|
293
|
+
},
|
|
294
|
+
// Campos para Enviar Base64
|
|
295
|
+
{
|
|
296
|
+
displayName: 'Caption (Opcional)',
|
|
297
|
+
name: 'base64Caption',
|
|
298
|
+
type: 'string',
|
|
299
|
+
default: '',
|
|
300
|
+
displayOptions: {
|
|
301
|
+
show: {
|
|
302
|
+
operation: ['sendBase64'],
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
description: 'Legenda opcional do arquivo',
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
displayName: 'Número de Telefone',
|
|
309
|
+
name: 'base64PhoneNumber',
|
|
310
|
+
type: 'string',
|
|
311
|
+
default: '5511999999999',
|
|
312
|
+
displayOptions: {
|
|
313
|
+
show: {
|
|
314
|
+
operation: ['sendBase64'],
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
description: 'Número de telefone no formato DDI+DDD+Número',
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
displayName: 'Arquivo Base64',
|
|
321
|
+
name: 'mediaBase64',
|
|
322
|
+
type: 'string',
|
|
323
|
+
default: '',
|
|
324
|
+
displayOptions: {
|
|
325
|
+
show: {
|
|
326
|
+
operation: ['sendBase64'],
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
description: 'Arquivo codificado em base64',
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
displayName: 'Tipo MIME',
|
|
333
|
+
name: 'mimeType',
|
|
334
|
+
type: 'string',
|
|
335
|
+
default: 'image/png',
|
|
336
|
+
displayOptions: {
|
|
337
|
+
show: {
|
|
338
|
+
operation: ['sendBase64'],
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
description: 'Tipo MIME do arquivo (ex: image/png, application/pdf)',
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
displayName: 'Nome do Arquivo',
|
|
345
|
+
name: 'fileName',
|
|
346
|
+
type: 'string',
|
|
347
|
+
default: 'arquivo.png',
|
|
348
|
+
displayOptions: {
|
|
349
|
+
show: {
|
|
350
|
+
operation: ['sendBase64'],
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
description: 'Nome do arquivo com extensão',
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
displayName: 'Nome da Tag',
|
|
357
|
+
name: 'tagName',
|
|
358
|
+
type: 'string',
|
|
359
|
+
default: 'Nova Tag',
|
|
360
|
+
displayOptions: {
|
|
361
|
+
show: {
|
|
362
|
+
operation: ['createTag'],
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
description: 'Nome da tag a ser criada',
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
displayName: 'Cor da Tag',
|
|
369
|
+
name: 'tagColor',
|
|
370
|
+
type: 'string',
|
|
371
|
+
default: '#2196F3',
|
|
372
|
+
displayOptions: {
|
|
373
|
+
show: {
|
|
374
|
+
operation: ['createTag'],
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
description: 'Cor da tag em formato hexadecimal (ex: #FF5733, #2196F3, #4CAF50)',
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
displayName: 'Dados (JSON)',
|
|
381
|
+
name: 'bodyData',
|
|
382
|
+
type: 'json',
|
|
383
|
+
default: '{"data": "2024-01-15"}',
|
|
384
|
+
displayOptions: {
|
|
385
|
+
show: {
|
|
386
|
+
operation: ['validateDate'],
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
description: 'Data a ser validada no formato JSON',
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
displayName: 'Dados (JSON)',
|
|
393
|
+
name: 'bodyData',
|
|
394
|
+
type: 'json',
|
|
395
|
+
default: '{"ticketId": 123, "queueId": 1}',
|
|
396
|
+
displayOptions: {
|
|
397
|
+
show: {
|
|
398
|
+
operation: ['transferQueue'],
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
description: 'ID do ticket e ID da fila de destino',
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
displayName: 'Dados (JSON)',
|
|
405
|
+
name: 'bodyData',
|
|
406
|
+
type: 'json',
|
|
407
|
+
default: '{"ticketId": 123, "userId": 1}',
|
|
408
|
+
displayOptions: {
|
|
409
|
+
show: {
|
|
410
|
+
operation: ['transferAgent'],
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
description: 'ID do ticket e ID do atendente de destino',
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
displayName: 'Dados (JSON)',
|
|
417
|
+
name: 'bodyData',
|
|
418
|
+
type: 'json',
|
|
419
|
+
default: '{"ticketId": 123}',
|
|
420
|
+
displayOptions: {
|
|
421
|
+
show: {
|
|
422
|
+
operation: ['closeTicket'],
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
description: 'ID do ticket a ser fechado',
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
displayName: 'Dados (JSON)',
|
|
429
|
+
name: 'bodyData',
|
|
430
|
+
type: 'json',
|
|
431
|
+
default: '{"ticketId": 123, "tagId": 456}',
|
|
432
|
+
displayOptions: {
|
|
433
|
+
show: {
|
|
434
|
+
operation: ['linkTag'],
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
description: 'ID do ticket e ID da tag a ser vinculada',
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
displayName: 'Dados (JSON)',
|
|
441
|
+
name: 'bodyData',
|
|
442
|
+
type: 'json',
|
|
443
|
+
default: '{"ticketId": 123, "kanbanId": 456, "userId": 789}',
|
|
444
|
+
displayOptions: {
|
|
445
|
+
show: {
|
|
446
|
+
operation: ['linkKanban'],
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
description: 'ID do ticket, ID do kanban e ID do usuário',
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
displayName: 'Dados (JSON)',
|
|
453
|
+
name: 'bodyData',
|
|
454
|
+
type: 'json',
|
|
455
|
+
default: '{"ticketId": 123, "userId": 456}',
|
|
456
|
+
displayOptions: {
|
|
457
|
+
show: {
|
|
458
|
+
operation: ['linkCarteira'],
|
|
459
|
+
},
|
|
460
|
+
},
|
|
461
|
+
description: 'ID do ticket e ID do usuário da carteira',
|
|
462
|
+
},
|
|
463
|
+
// Campos para Agendamento
|
|
464
|
+
{
|
|
465
|
+
displayName: 'ID do Usuário',
|
|
466
|
+
name: 'scheduleUserId',
|
|
467
|
+
type: 'number',
|
|
468
|
+
default: 0,
|
|
469
|
+
displayOptions: {
|
|
470
|
+
show: {
|
|
471
|
+
operation: ['listServices'],
|
|
472
|
+
},
|
|
473
|
+
},
|
|
474
|
+
description: 'ID do usuário para filtrar serviços (opcional)',
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
displayName: 'ID do Serviço',
|
|
478
|
+
name: 'serviceId',
|
|
479
|
+
type: 'number',
|
|
480
|
+
default: 1,
|
|
481
|
+
displayOptions: {
|
|
482
|
+
show: {
|
|
483
|
+
operation: ['listAvailableUsers', 'listAvailableSlots', 'createSchedule'],
|
|
484
|
+
},
|
|
485
|
+
},
|
|
486
|
+
description: 'ID do serviço',
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
displayName: 'Data',
|
|
490
|
+
name: 'scheduleDate',
|
|
491
|
+
type: 'string',
|
|
492
|
+
default: '',
|
|
493
|
+
placeholder: '2025-08-07',
|
|
494
|
+
displayOptions: {
|
|
495
|
+
show: {
|
|
496
|
+
operation: ['listAvailableUsers', 'listAvailableSlots', 'createSchedule'],
|
|
497
|
+
},
|
|
498
|
+
},
|
|
499
|
+
description: 'Data no formato YYYY-MM-DD',
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
displayName: 'Horário',
|
|
503
|
+
name: 'scheduleTime',
|
|
504
|
+
type: 'string',
|
|
505
|
+
default: '',
|
|
506
|
+
placeholder: '09:00',
|
|
507
|
+
displayOptions: {
|
|
508
|
+
show: {
|
|
509
|
+
operation: ['listAvailableUsers', 'createSchedule'],
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
description: 'Horário no formato HH:mm (opcional para listar usuários)',
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
displayName: 'ID do Usuário',
|
|
516
|
+
name: 'scheduleAttendantId',
|
|
517
|
+
type: 'number',
|
|
518
|
+
default: 1,
|
|
519
|
+
displayOptions: {
|
|
520
|
+
show: {
|
|
521
|
+
operation: ['listAvailableSlots', 'createSchedule'],
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
description: 'ID do atendente/usuário',
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
displayName: 'Nome do Contato',
|
|
528
|
+
name: 'contactName',
|
|
529
|
+
type: 'string',
|
|
530
|
+
default: '',
|
|
531
|
+
displayOptions: {
|
|
532
|
+
show: {
|
|
533
|
+
operation: ['createSchedule'],
|
|
534
|
+
},
|
|
535
|
+
},
|
|
536
|
+
description: 'Nome do cliente/contato',
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
displayName: 'Telefone do Contato',
|
|
540
|
+
name: 'contactPhone',
|
|
541
|
+
type: 'string',
|
|
542
|
+
default: '',
|
|
543
|
+
placeholder: '5511999999999',
|
|
544
|
+
displayOptions: {
|
|
545
|
+
show: {
|
|
546
|
+
operation: ['createSchedule'],
|
|
547
|
+
},
|
|
548
|
+
},
|
|
549
|
+
description: 'Telefone do cliente/contato',
|
|
550
|
+
},
|
|
551
|
+
{
|
|
552
|
+
displayName: 'Observações',
|
|
553
|
+
name: 'scheduleNotes',
|
|
554
|
+
type: 'string',
|
|
555
|
+
default: '',
|
|
556
|
+
displayOptions: {
|
|
557
|
+
show: {
|
|
558
|
+
operation: ['createSchedule'],
|
|
559
|
+
},
|
|
560
|
+
},
|
|
561
|
+
description: 'Observações sobre o agendamento (opcional)',
|
|
562
|
+
},
|
|
563
|
+
{
|
|
564
|
+
displayName: 'ID da Conexão WhatsApp',
|
|
565
|
+
name: 'whatsappId',
|
|
566
|
+
type: 'number',
|
|
567
|
+
default: 0,
|
|
568
|
+
displayOptions: {
|
|
569
|
+
show: {
|
|
570
|
+
operation: ['createSchedule'],
|
|
571
|
+
},
|
|
572
|
+
},
|
|
573
|
+
description: 'ID da conexão WhatsApp (opcional - usa primeira disponível se não informado)',
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
displayName: 'Mensagem Personalizada',
|
|
577
|
+
name: 'customMessage',
|
|
578
|
+
type: 'string',
|
|
579
|
+
default: '',
|
|
580
|
+
displayOptions: {
|
|
581
|
+
show: {
|
|
582
|
+
operation: ['createSchedule'],
|
|
583
|
+
},
|
|
584
|
+
},
|
|
585
|
+
description: 'Mensagem personalizada do agendamento (opcional)',
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
displayName: 'Lembretes (minutos)',
|
|
589
|
+
name: 'reminders',
|
|
590
|
+
type: 'string',
|
|
591
|
+
default: '60,240',
|
|
592
|
+
placeholder: '60,240,1440',
|
|
593
|
+
displayOptions: {
|
|
594
|
+
show: {
|
|
595
|
+
operation: ['createSchedule'],
|
|
596
|
+
},
|
|
597
|
+
},
|
|
598
|
+
description: 'Lembretes em minutos antes do agendamento (separados por vírgula)',
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
displayName: 'Duração do Intervalo (minutos)',
|
|
602
|
+
name: 'intervalDuration',
|
|
603
|
+
type: 'number',
|
|
604
|
+
default: 30,
|
|
605
|
+
displayOptions: {
|
|
606
|
+
show: {
|
|
607
|
+
operation: ['createSchedule'],
|
|
608
|
+
},
|
|
609
|
+
},
|
|
610
|
+
description: 'Duração do intervalo em minutos',
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
displayName: 'Fechar Ticket',
|
|
614
|
+
name: 'closeTicket',
|
|
615
|
+
type: 'boolean',
|
|
616
|
+
default: false,
|
|
617
|
+
displayOptions: {
|
|
618
|
+
show: {
|
|
619
|
+
operation: ['createSchedule'],
|
|
620
|
+
},
|
|
621
|
+
},
|
|
622
|
+
description: 'Se deve fechar o ticket após criar agendamento',
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
displayName: 'Data',
|
|
626
|
+
name: 'listScheduleDate',
|
|
627
|
+
type: 'string',
|
|
628
|
+
default: '',
|
|
629
|
+
placeholder: '2025-08-08',
|
|
630
|
+
displayOptions: {
|
|
631
|
+
show: {
|
|
632
|
+
operation: ['listSchedules'],
|
|
633
|
+
},
|
|
634
|
+
},
|
|
635
|
+
description: 'Data no formato YYYY-MM-DD para listar agendamentos',
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
displayName: 'User ID (Opcional)',
|
|
639
|
+
name: 'listScheduleUserId',
|
|
640
|
+
type: 'number',
|
|
641
|
+
default: 0,
|
|
642
|
+
displayOptions: {
|
|
643
|
+
show: {
|
|
644
|
+
operation: ['listSchedules'],
|
|
645
|
+
},
|
|
646
|
+
},
|
|
647
|
+
description: 'ID do usuário para filtrar agendamentos (opcional)',
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
displayName: 'ID do Agendamento',
|
|
651
|
+
name: 'scheduleId',
|
|
652
|
+
type: 'number',
|
|
653
|
+
default: 0,
|
|
654
|
+
displayOptions: {
|
|
655
|
+
show: {
|
|
656
|
+
operation: ['cancelSchedule'],
|
|
657
|
+
},
|
|
658
|
+
},
|
|
659
|
+
description: 'ID do agendamento a ser cancelado',
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
displayName: 'ID do Agendamento',
|
|
663
|
+
name: 'calendarScheduleId',
|
|
664
|
+
type: 'number',
|
|
665
|
+
default: 0,
|
|
666
|
+
displayOptions: {
|
|
667
|
+
show: {
|
|
668
|
+
operation: ['calendarLink'],
|
|
669
|
+
},
|
|
670
|
+
},
|
|
671
|
+
description: 'ID do agendamento para gerar o link do calendário',
|
|
672
|
+
},
|
|
673
|
+
// Campos para Templates WABA
|
|
674
|
+
{
|
|
675
|
+
displayName: 'WhatsApp ID',
|
|
676
|
+
name: 'wabaWhatsappId',
|
|
677
|
+
type: 'number',
|
|
678
|
+
default: 1,
|
|
679
|
+
displayOptions: {
|
|
680
|
+
show: {
|
|
681
|
+
operation: ['listWabaTemplates', 'sendWabaTemplate'],
|
|
682
|
+
},
|
|
683
|
+
},
|
|
684
|
+
description: 'ID da conexão WhatsApp WABA',
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
displayName: 'Template ID',
|
|
688
|
+
name: 'wabaTemplateId',
|
|
689
|
+
type: 'number',
|
|
690
|
+
default: 1,
|
|
691
|
+
displayOptions: {
|
|
692
|
+
show: {
|
|
693
|
+
operation: ['sendWabaTemplate'],
|
|
694
|
+
},
|
|
695
|
+
},
|
|
696
|
+
description: 'ID do template no sistema (obtido após importação)',
|
|
697
|
+
},
|
|
698
|
+
{
|
|
699
|
+
displayName: 'Número de Telefone',
|
|
700
|
+
name: 'wabaPhoneNumber',
|
|
701
|
+
type: 'string',
|
|
702
|
+
default: '5511999999999',
|
|
703
|
+
displayOptions: {
|
|
704
|
+
show: {
|
|
705
|
+
operation: ['sendWabaTemplate'],
|
|
706
|
+
},
|
|
707
|
+
},
|
|
708
|
+
description: 'Número do destinatário com DDI (ex: 5511999999999)',
|
|
709
|
+
},
|
|
710
|
+
{
|
|
711
|
+
displayName: 'Parâmetros do Template (JSON)',
|
|
712
|
+
name: 'wabaTemplateParams',
|
|
713
|
+
type: 'json',
|
|
714
|
+
default: '{}',
|
|
715
|
+
displayOptions: {
|
|
716
|
+
show: {
|
|
717
|
+
operation: ['sendWabaTemplate'],
|
|
718
|
+
},
|
|
719
|
+
},
|
|
720
|
+
description: 'Parâmetros customizados para substituir variáveis no template (ex: {"nome_cliente": "João", "numero_pedido": "12345"})',
|
|
721
|
+
},
|
|
722
|
+
],
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
|
726
|
+
const items = this.getInputData();
|
|
727
|
+
const returnData: INodeExecutionData[] = [];
|
|
728
|
+
|
|
729
|
+
const credentials = await this.getCredentials('digitalsacApi');
|
|
730
|
+
const baseUrl = credentials.baseUrl;
|
|
731
|
+
const token = credentials.token;
|
|
732
|
+
|
|
733
|
+
for (let i = 0; i < items.length; i++) {
|
|
734
|
+
const operation = this.getNodeParameter('operation', i) as string;
|
|
735
|
+
let responseData;
|
|
736
|
+
|
|
737
|
+
const headers: Record<string, string> = {
|
|
738
|
+
Authorization: `Bearer ${token}`,
|
|
739
|
+
Accept: 'application/json',
|
|
740
|
+
};
|
|
741
|
+
|
|
742
|
+
let url = '';
|
|
743
|
+
let method: 'GET' | 'POST' = 'GET';
|
|
744
|
+
let body;
|
|
745
|
+
let param = this.getNodeParameter('param', i, '') as string;
|
|
746
|
+
let options: Record<string, any> = {};
|
|
747
|
+
|
|
748
|
+
switch (operation) {
|
|
749
|
+
case 'validateWhatsapp':
|
|
750
|
+
url = `/typebot/whatsappnumber/${param}`;
|
|
751
|
+
break;
|
|
752
|
+
case 'validateCpf':
|
|
753
|
+
url = `/typebot/validate/cpf/${param}`;
|
|
754
|
+
break;
|
|
755
|
+
case 'validateDate':
|
|
756
|
+
url = '/typebot/validate/data';
|
|
757
|
+
method = 'POST';
|
|
758
|
+
try {
|
|
759
|
+
body = JSON.parse(this.getNodeParameter('bodyData', i) as string);
|
|
760
|
+
} catch (e) {
|
|
761
|
+
throw new Error('Formato de JSON inválido para Dados (JSON)');
|
|
762
|
+
}
|
|
763
|
+
headers['Content-Type'] = 'application/json';
|
|
764
|
+
options = {
|
|
765
|
+
method,
|
|
766
|
+
headers,
|
|
767
|
+
body,
|
|
768
|
+
uri: `${baseUrl}${url}`,
|
|
769
|
+
json: true,
|
|
770
|
+
};
|
|
771
|
+
break;
|
|
772
|
+
case 'listQueues':
|
|
773
|
+
url = '/typebot/listar_filas';
|
|
774
|
+
break;
|
|
775
|
+
case 'listAgents':
|
|
776
|
+
const agentsFormat = this.getNodeParameter('agentsFormat', i, 'json') as string;
|
|
777
|
+
url = '/typebot/listar_atendentes';
|
|
778
|
+
if (agentsFormat === 'string') {
|
|
779
|
+
url += '?format=string';
|
|
780
|
+
}
|
|
781
|
+
break;
|
|
782
|
+
case 'transferQueue':
|
|
783
|
+
url = '/typebot/transferir_para_fila';
|
|
784
|
+
method = 'POST';
|
|
785
|
+
try {
|
|
786
|
+
body = JSON.parse(this.getNodeParameter('bodyData', i) as string);
|
|
787
|
+
} catch (e) {
|
|
788
|
+
throw new Error('Formato de JSON inválido para Dados (JSON)');
|
|
789
|
+
}
|
|
790
|
+
headers['Content-Type'] = 'application/json';
|
|
791
|
+
options = {
|
|
792
|
+
method,
|
|
793
|
+
headers,
|
|
794
|
+
body,
|
|
795
|
+
uri: `${baseUrl}${url}`,
|
|
796
|
+
json: true,
|
|
797
|
+
};
|
|
798
|
+
break;
|
|
799
|
+
case 'transferAgent':
|
|
800
|
+
url = '/typebot/transferir_para_atendente';
|
|
801
|
+
method = 'POST';
|
|
802
|
+
try {
|
|
803
|
+
body = JSON.parse(this.getNodeParameter('bodyData', i) as string);
|
|
804
|
+
} catch (e) {
|
|
805
|
+
throw new Error('Formato de JSON inválido para Dados (JSON)');
|
|
806
|
+
}
|
|
807
|
+
headers['Content-Type'] = 'application/json';
|
|
808
|
+
options = {
|
|
809
|
+
method,
|
|
810
|
+
headers,
|
|
811
|
+
body,
|
|
812
|
+
uri: `${baseUrl}${url}`,
|
|
813
|
+
json: true,
|
|
814
|
+
};
|
|
815
|
+
break;
|
|
816
|
+
case 'closeTicket':
|
|
817
|
+
url = '/typebot/fechar_ticket';
|
|
818
|
+
method = 'POST';
|
|
819
|
+
try {
|
|
820
|
+
body = JSON.parse(this.getNodeParameter('bodyData', i) as string);
|
|
821
|
+
} catch (e) {
|
|
822
|
+
throw new Error('Formato de JSON inválido para Dados (JSON)');
|
|
823
|
+
}
|
|
824
|
+
headers['Content-Type'] = 'application/json';
|
|
825
|
+
options = {
|
|
826
|
+
method,
|
|
827
|
+
headers,
|
|
828
|
+
body,
|
|
829
|
+
uri: `${baseUrl}${url}`,
|
|
830
|
+
json: true,
|
|
831
|
+
};
|
|
832
|
+
break;
|
|
833
|
+
case 'sendMessage':
|
|
834
|
+
// Validar se o UUID foi fornecido
|
|
835
|
+
if (!param || param.trim() === '') {
|
|
836
|
+
throw new Error('UUID da conexão é obrigatório para enviar mensagem. Preencha o campo "Parâmetro" com o UUID da conexão.');
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
url = `/v1/api/external/${param}`;
|
|
840
|
+
method = 'POST';
|
|
841
|
+
|
|
842
|
+
// Usar campos separados em vez de JSON
|
|
843
|
+
const messageBody = this.getNodeParameter('messageBody', i) as string;
|
|
844
|
+
const phoneNumber = this.getNodeParameter('phoneNumber', i) as string;
|
|
845
|
+
const externalKey = this.getNodeParameter('externalKey', i) as string;
|
|
846
|
+
|
|
847
|
+
// Verificar se há dados binários (para envio de arquivo)
|
|
848
|
+
let hasBinaryData = false;
|
|
849
|
+
let binaryData: Buffer | undefined;
|
|
850
|
+
let binaryFileName: string | undefined;
|
|
851
|
+
let binaryContentType: string | undefined;
|
|
852
|
+
|
|
853
|
+
if (items[i].binary) {
|
|
854
|
+
const binary = items[i].binary as IBinaryKeyData;
|
|
855
|
+
// Procurar por qualquer propriedade binária disponível
|
|
856
|
+
const binaryKeys = Object.keys(binary);
|
|
857
|
+
if (binaryKeys.length > 0) {
|
|
858
|
+
const binaryPropertyName = binaryKeys[0]; // Usar a primeira propriedade binária encontrada
|
|
859
|
+
hasBinaryData = true;
|
|
860
|
+
|
|
861
|
+
const binaryProperty = binary[binaryPropertyName];
|
|
862
|
+
binaryData = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
|
|
863
|
+
binaryFileName = binaryProperty.fileName || 'file';
|
|
864
|
+
binaryContentType = binaryProperty.mimeType;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
if (hasBinaryData && binaryData) {
|
|
869
|
+
// Enviar como FormData (para arquivos)
|
|
870
|
+
const formData: IDataObject = {
|
|
871
|
+
body: messageBody,
|
|
872
|
+
number: phoneNumber,
|
|
873
|
+
externalKey: externalKey,
|
|
874
|
+
media: {
|
|
875
|
+
value: binaryData,
|
|
876
|
+
options: {
|
|
877
|
+
filename: binaryFileName,
|
|
878
|
+
contentType: binaryContentType,
|
|
879
|
+
},
|
|
880
|
+
},
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
options = {
|
|
884
|
+
method,
|
|
885
|
+
headers: {
|
|
886
|
+
Authorization: `Bearer ${token}`,
|
|
887
|
+
Accept: 'application/json',
|
|
888
|
+
},
|
|
889
|
+
formData: formData,
|
|
890
|
+
uri: `${baseUrl}${url}`,
|
|
891
|
+
json: true,
|
|
892
|
+
};
|
|
893
|
+
} else {
|
|
894
|
+
// Enviar como JSON (para texto)
|
|
895
|
+
body = {
|
|
896
|
+
body: messageBody,
|
|
897
|
+
number: phoneNumber,
|
|
898
|
+
externalKey: externalKey
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
headers['Content-Type'] = 'application/json';
|
|
902
|
+
options = {
|
|
903
|
+
method,
|
|
904
|
+
headers,
|
|
905
|
+
body,
|
|
906
|
+
uri: `${baseUrl}${url}`,
|
|
907
|
+
json: true,
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
break;
|
|
911
|
+
case 'listTags':
|
|
912
|
+
url = '/typebot/listar_tags';
|
|
913
|
+
break;
|
|
914
|
+
case 'linkTag':
|
|
915
|
+
url = '/typebot/vincular_tag';
|
|
916
|
+
method = 'POST';
|
|
917
|
+
try {
|
|
918
|
+
body = JSON.parse(this.getNodeParameter('bodyData', i) as string);
|
|
919
|
+
} catch (e) {
|
|
920
|
+
throw new Error('Formato de JSON inválido para Dados (JSON)');
|
|
921
|
+
}
|
|
922
|
+
headers['Content-Type'] = 'application/json';
|
|
923
|
+
options = {
|
|
924
|
+
method,
|
|
925
|
+
headers,
|
|
926
|
+
body,
|
|
927
|
+
uri: `${baseUrl}${url}`,
|
|
928
|
+
json: true,
|
|
929
|
+
};
|
|
930
|
+
break;
|
|
931
|
+
case 'createTag':
|
|
932
|
+
url = '/typebot/criar_tag';
|
|
933
|
+
method = 'POST';
|
|
934
|
+
|
|
935
|
+
// Usar campos separados para criar tag
|
|
936
|
+
const tagName = this.getNodeParameter('tagName', i) as string;
|
|
937
|
+
const tagColor = this.getNodeParameter('tagColor', i) as string;
|
|
938
|
+
|
|
939
|
+
body = {
|
|
940
|
+
tag: tagName,
|
|
941
|
+
color: tagColor
|
|
942
|
+
};
|
|
943
|
+
|
|
944
|
+
headers['Content-Type'] = 'application/json';
|
|
945
|
+
options = {
|
|
946
|
+
method,
|
|
947
|
+
headers,
|
|
948
|
+
body,
|
|
949
|
+
uri: `${baseUrl}${url}`,
|
|
950
|
+
json: true,
|
|
951
|
+
};
|
|
952
|
+
break;
|
|
953
|
+
case 'listKanbans':
|
|
954
|
+
const userId = this.getNodeParameter('userId', i) as number;
|
|
955
|
+
url = `/typebot/listar_kanbans_v2?userId=${userId}`;
|
|
956
|
+
break;
|
|
957
|
+
case 'linkKanban':
|
|
958
|
+
url = '/typebot/vincular_kanban_v2';
|
|
959
|
+
method = 'POST';
|
|
960
|
+
try {
|
|
961
|
+
body = JSON.parse(this.getNodeParameter('bodyData', i) as string);
|
|
962
|
+
} catch (e) {
|
|
963
|
+
throw new Error('Formato de JSON inválido para Dados (JSON)');
|
|
964
|
+
}
|
|
965
|
+
headers['Content-Type'] = 'application/json';
|
|
966
|
+
options = {
|
|
967
|
+
method,
|
|
968
|
+
headers,
|
|
969
|
+
body,
|
|
970
|
+
uri: `${baseUrl}${url}`,
|
|
971
|
+
json: true,
|
|
972
|
+
};
|
|
973
|
+
break;
|
|
974
|
+
case 'listCarteiras':
|
|
975
|
+
url = '/typebot/listar_carteiras';
|
|
976
|
+
break;
|
|
977
|
+
case 'linkCarteira':
|
|
978
|
+
url = '/typebot/vincular_carteira';
|
|
979
|
+
method = 'POST';
|
|
980
|
+
try {
|
|
981
|
+
body = JSON.parse(this.getNodeParameter('bodyData', i) as string);
|
|
982
|
+
} catch (e) {
|
|
983
|
+
throw new Error('Formato de JSON inválido para Dados (JSON)');
|
|
984
|
+
}
|
|
985
|
+
headers['Content-Type'] = 'application/json';
|
|
986
|
+
options = {
|
|
987
|
+
method,
|
|
988
|
+
headers,
|
|
989
|
+
body,
|
|
990
|
+
uri: `${baseUrl}${url}`,
|
|
991
|
+
json: true,
|
|
992
|
+
};
|
|
993
|
+
break;
|
|
994
|
+
|
|
995
|
+
// Casos para Agendamento
|
|
996
|
+
case 'listServices':
|
|
997
|
+
const scheduleUserId = this.getNodeParameter('scheduleUserId', i) as number;
|
|
998
|
+
if (scheduleUserId > 0) {
|
|
999
|
+
url = `/typebot/listar_servicos?userId=${scheduleUserId}`;
|
|
1000
|
+
} else {
|
|
1001
|
+
url = '/typebot/listar_servicos';
|
|
1002
|
+
}
|
|
1003
|
+
break;
|
|
1004
|
+
|
|
1005
|
+
case 'listAvailableUsers':
|
|
1006
|
+
const serviceIdForUsers = this.getNodeParameter('serviceId', i) as number;
|
|
1007
|
+
const dateForUsers = this.getNodeParameter('scheduleDate', i) as string;
|
|
1008
|
+
const timeForUsers = this.getNodeParameter('scheduleTime', i) as string;
|
|
1009
|
+
|
|
1010
|
+
url = `/typebot/listar_usuarios_disponiveis?serviceId=${serviceIdForUsers}&date=${dateForUsers}`;
|
|
1011
|
+
if (timeForUsers) {
|
|
1012
|
+
url += `&time=${timeForUsers}`;
|
|
1013
|
+
}
|
|
1014
|
+
break;
|
|
1015
|
+
|
|
1016
|
+
case 'listAvailableSlots':
|
|
1017
|
+
const serviceIdForSlots = this.getNodeParameter('serviceId', i) as number;
|
|
1018
|
+
const userIdForSlots = this.getNodeParameter('scheduleAttendantId', i) as number;
|
|
1019
|
+
const dateForSlots = this.getNodeParameter('scheduleDate', i) as string;
|
|
1020
|
+
|
|
1021
|
+
url = `/typebot/listar_horarios_disponiveis?serviceId=${serviceIdForSlots}&userId=${userIdForSlots}&date=${dateForSlots}`;
|
|
1022
|
+
break;
|
|
1023
|
+
|
|
1024
|
+
case 'listSchedules':
|
|
1025
|
+
const dateForList = this.getNodeParameter('listScheduleDate', i) as string;
|
|
1026
|
+
const userIdForList = this.getNodeParameter('listScheduleUserId', i) as number;
|
|
1027
|
+
|
|
1028
|
+
url = `/typebot/listar_agendamentos?date=${dateForList}`;
|
|
1029
|
+
if (userIdForList && userIdForList > 0) {
|
|
1030
|
+
url += `&userId=${userIdForList}`;
|
|
1031
|
+
}
|
|
1032
|
+
break;
|
|
1033
|
+
|
|
1034
|
+
case 'createSchedule':
|
|
1035
|
+
url = '/typebot/criar_agendamento';
|
|
1036
|
+
method = 'POST';
|
|
1037
|
+
|
|
1038
|
+
const serviceIdForCreate = this.getNodeParameter('serviceId', i) as number;
|
|
1039
|
+
const userIdForCreate = this.getNodeParameter('scheduleAttendantId', i) as number;
|
|
1040
|
+
const dateForCreate = this.getNodeParameter('scheduleDate', i) as string;
|
|
1041
|
+
const timeForCreate = this.getNodeParameter('scheduleTime', i) as string;
|
|
1042
|
+
const contactNameForCreate = this.getNodeParameter('contactName', i) as string;
|
|
1043
|
+
const contactPhoneForCreate = this.getNodeParameter('contactPhone', i) as string;
|
|
1044
|
+
const notesForCreate = this.getNodeParameter('scheduleNotes', i) as string;
|
|
1045
|
+
const whatsappIdForCreate = this.getNodeParameter('whatsappId', i) as number;
|
|
1046
|
+
const customMessageForCreate = this.getNodeParameter('customMessage', i) as string;
|
|
1047
|
+
const remindersForCreate = this.getNodeParameter('reminders', i) as string;
|
|
1048
|
+
const intervalDurationForCreate = this.getNodeParameter('intervalDuration', i) as number;
|
|
1049
|
+
const closeTicketForCreate = this.getNodeParameter('closeTicket', i) as boolean;
|
|
1050
|
+
|
|
1051
|
+
// Converter string de lembretes para array
|
|
1052
|
+
const remindersArray = remindersForCreate ? remindersForCreate.split(',').map(r => parseInt(r.trim())) : [60, 240];
|
|
1053
|
+
|
|
1054
|
+
body = {
|
|
1055
|
+
serviceId: serviceIdForCreate,
|
|
1056
|
+
userId: userIdForCreate,
|
|
1057
|
+
date: dateForCreate,
|
|
1058
|
+
time: timeForCreate,
|
|
1059
|
+
contactName: contactNameForCreate,
|
|
1060
|
+
contactPhone: contactPhoneForCreate,
|
|
1061
|
+
notes: notesForCreate,
|
|
1062
|
+
whatsappId: whatsappIdForCreate > 0 ? whatsappIdForCreate : undefined,
|
|
1063
|
+
message: customMessageForCreate || undefined,
|
|
1064
|
+
reminders: remindersArray,
|
|
1065
|
+
intervalDuration: intervalDurationForCreate,
|
|
1066
|
+
closeTicket: closeTicketForCreate
|
|
1067
|
+
};
|
|
1068
|
+
|
|
1069
|
+
headers['Content-Type'] = 'application/json';
|
|
1070
|
+
options = {
|
|
1071
|
+
method,
|
|
1072
|
+
headers,
|
|
1073
|
+
body,
|
|
1074
|
+
uri: `${baseUrl}${url}`,
|
|
1075
|
+
json: true,
|
|
1076
|
+
};
|
|
1077
|
+
break;
|
|
1078
|
+
|
|
1079
|
+
case 'cancelSchedule':
|
|
1080
|
+
url = '/typebot/cancelar_agendamento';
|
|
1081
|
+
method = 'POST';
|
|
1082
|
+
|
|
1083
|
+
const scheduleIdForCancel = this.getNodeParameter('scheduleId', i) as number;
|
|
1084
|
+
|
|
1085
|
+
body = {
|
|
1086
|
+
scheduleId: scheduleIdForCancel
|
|
1087
|
+
};
|
|
1088
|
+
|
|
1089
|
+
headers['Content-Type'] = 'application/json';
|
|
1090
|
+
options = {
|
|
1091
|
+
method,
|
|
1092
|
+
headers,
|
|
1093
|
+
body,
|
|
1094
|
+
uri: `${baseUrl}${url}`,
|
|
1095
|
+
json: true,
|
|
1096
|
+
};
|
|
1097
|
+
break;
|
|
1098
|
+
|
|
1099
|
+
case 'calendarLink':
|
|
1100
|
+
const calendarScheduleId = this.getNodeParameter('calendarScheduleId', i) as number;
|
|
1101
|
+
|
|
1102
|
+
url = `/typebot/calendar-link?scheduleId=${calendarScheduleId}`;
|
|
1103
|
+
break;
|
|
1104
|
+
|
|
1105
|
+
case 'listWabaTemplates':
|
|
1106
|
+
const wabaWhatsappIdForList = this.getNodeParameter('wabaWhatsappId', i) as number;
|
|
1107
|
+
|
|
1108
|
+
url = `/typebot/listar_templates_waba/${wabaWhatsappIdForList}`;
|
|
1109
|
+
method = 'GET';
|
|
1110
|
+
break;
|
|
1111
|
+
|
|
1112
|
+
case 'sendWabaTemplate':
|
|
1113
|
+
url = '/typebot/enviar_template_waba';
|
|
1114
|
+
method = 'POST';
|
|
1115
|
+
|
|
1116
|
+
const wabaTemplateId = this.getNodeParameter('wabaTemplateId', i) as number;
|
|
1117
|
+
const wabaPhoneNumber = this.getNodeParameter('wabaPhoneNumber', i) as string;
|
|
1118
|
+
const wabaWhatsappId = this.getNodeParameter('wabaWhatsappId', i) as number;
|
|
1119
|
+
const wabaTemplateParams = this.getNodeParameter('wabaTemplateParams', i) as string;
|
|
1120
|
+
|
|
1121
|
+
let parsedTemplateParams = {};
|
|
1122
|
+
if (wabaTemplateParams && wabaTemplateParams.trim() !== '' && wabaTemplateParams.trim() !== '{}') {
|
|
1123
|
+
try {
|
|
1124
|
+
parsedTemplateParams = JSON.parse(wabaTemplateParams);
|
|
1125
|
+
} catch (error) {
|
|
1126
|
+
throw new Error('Erro ao fazer parse do JSON dos parâmetros do template. Verifique a sintaxe.');
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
body = {
|
|
1131
|
+
templateId: wabaTemplateId,
|
|
1132
|
+
phoneNumber: wabaPhoneNumber,
|
|
1133
|
+
whatsappId: wabaWhatsappId,
|
|
1134
|
+
templateParams: parsedTemplateParams
|
|
1135
|
+
};
|
|
1136
|
+
|
|
1137
|
+
headers['Content-Type'] = 'application/json';
|
|
1138
|
+
options = {
|
|
1139
|
+
method,
|
|
1140
|
+
headers,
|
|
1141
|
+
body,
|
|
1142
|
+
uri: `${baseUrl}${url}`,
|
|
1143
|
+
json: true,
|
|
1144
|
+
};
|
|
1145
|
+
break;
|
|
1146
|
+
|
|
1147
|
+
case 'sendButtons':
|
|
1148
|
+
// Validar se o UUID foi fornecido
|
|
1149
|
+
if (!param || param.trim() === '') {
|
|
1150
|
+
throw new Error('UUID da conexão é obrigatório para enviar botões. Preencha o campo "Parâmetro" com o UUID da conexão.');
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
url = `/v1/api/external/${param}/send-buttons`;
|
|
1154
|
+
method = 'POST';
|
|
1155
|
+
|
|
1156
|
+
const buttonTitle = this.getNodeParameter('buttonTitle', i) as string;
|
|
1157
|
+
const buttonBody = this.getNodeParameter('buttonBody', i) as string;
|
|
1158
|
+
const buttonPhoneNumber = this.getNodeParameter('buttonPhoneNumber', i) as string;
|
|
1159
|
+
const buttonExternalKey = this.getNodeParameter('externalKey', i) as string;
|
|
1160
|
+
const buttonsData = this.getNodeParameter('buttonsData', i) as string;
|
|
1161
|
+
|
|
1162
|
+
let parsedButtons;
|
|
1163
|
+
try {
|
|
1164
|
+
parsedButtons = JSON.parse(buttonsData);
|
|
1165
|
+
} catch (error) {
|
|
1166
|
+
throw new Error('Erro ao fazer parse do JSON dos botões. Verifique a sintaxe.');
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
body = {
|
|
1170
|
+
title: buttonTitle,
|
|
1171
|
+
body: buttonBody,
|
|
1172
|
+
number: buttonPhoneNumber,
|
|
1173
|
+
extraButtons: parsedButtons,
|
|
1174
|
+
externalKey: buttonExternalKey
|
|
1175
|
+
};
|
|
1176
|
+
|
|
1177
|
+
headers['Content-Type'] = 'application/json';
|
|
1178
|
+
options = {
|
|
1179
|
+
method,
|
|
1180
|
+
headers,
|
|
1181
|
+
body,
|
|
1182
|
+
uri: `${baseUrl}${url}`,
|
|
1183
|
+
json: true,
|
|
1184
|
+
};
|
|
1185
|
+
break;
|
|
1186
|
+
case 'sendList':
|
|
1187
|
+
// Validar se o UUID foi fornecido
|
|
1188
|
+
if (!param || param.trim() === '') {
|
|
1189
|
+
throw new Error('UUID da conexão é obrigatório para enviar lista. Preencha o campo "Parâmetro" com o UUID da conexão.');
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
url = `/v1/api/external/${param}/send-list`;
|
|
1193
|
+
method = 'POST';
|
|
1194
|
+
|
|
1195
|
+
const listTitle = this.getNodeParameter('listTitle', i) as string;
|
|
1196
|
+
const listText = this.getNodeParameter('listText', i) as string;
|
|
1197
|
+
const listButtonText = this.getNodeParameter('listButtonText', i) as string;
|
|
1198
|
+
const listFooter = this.getNodeParameter('listFooter', i) as string;
|
|
1199
|
+
const listPhoneNumber = this.getNodeParameter('listPhoneNumber', i) as string;
|
|
1200
|
+
const listExternalKey = this.getNodeParameter('externalKey', i) as string;
|
|
1201
|
+
const sectionsData = this.getNodeParameter('sectionsData', i) as string;
|
|
1202
|
+
|
|
1203
|
+
let parsedSections;
|
|
1204
|
+
try {
|
|
1205
|
+
parsedSections = JSON.parse(sectionsData);
|
|
1206
|
+
} catch (error) {
|
|
1207
|
+
throw new Error('Erro ao fazer parse do JSON das seções. Verifique a sintaxe.');
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
body = {
|
|
1211
|
+
title: listTitle,
|
|
1212
|
+
text: listText,
|
|
1213
|
+
buttonText: listButtonText,
|
|
1214
|
+
footer: listFooter,
|
|
1215
|
+
number: listPhoneNumber,
|
|
1216
|
+
sections: parsedSections,
|
|
1217
|
+
externalKey: listExternalKey
|
|
1218
|
+
};
|
|
1219
|
+
|
|
1220
|
+
headers['Content-Type'] = 'application/json';
|
|
1221
|
+
options = {
|
|
1222
|
+
method,
|
|
1223
|
+
headers,
|
|
1224
|
+
body,
|
|
1225
|
+
uri: `${baseUrl}${url}`,
|
|
1226
|
+
json: true,
|
|
1227
|
+
};
|
|
1228
|
+
break;
|
|
1229
|
+
case 'sendMediaCaption':
|
|
1230
|
+
// Validar se o UUID foi fornecido
|
|
1231
|
+
if (!param || param.trim() === '') {
|
|
1232
|
+
throw new Error('UUID da conexão é obrigatório para enviar mídia. Preencha o campo "Parâmetro" com o UUID da conexão.');
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
url = `/v1/api/external/${param}/send-media-caption`;
|
|
1236
|
+
method = 'POST';
|
|
1237
|
+
|
|
1238
|
+
const mediaCaption = this.getNodeParameter('mediaCaption', i) as string;
|
|
1239
|
+
const mediaCaptionPhoneNumber = this.getNodeParameter('mediaCaptionPhoneNumber', i) as string;
|
|
1240
|
+
const mediaCaptionExternalKey = this.getNodeParameter('externalKey', i) as string;
|
|
1241
|
+
|
|
1242
|
+
// Verificar se há dados binários
|
|
1243
|
+
let hasBinaryDataCaption = false;
|
|
1244
|
+
let binaryDataCaption: Buffer | undefined;
|
|
1245
|
+
let binaryFileNameCaption: string | undefined;
|
|
1246
|
+
let binaryContentTypeCaption: string | undefined;
|
|
1247
|
+
|
|
1248
|
+
if (items[i].binary) {
|
|
1249
|
+
const binary = items[i].binary as IBinaryKeyData;
|
|
1250
|
+
const binaryKeys = Object.keys(binary);
|
|
1251
|
+
if (binaryKeys.length > 0) {
|
|
1252
|
+
const binaryPropertyName = binaryKeys[0];
|
|
1253
|
+
hasBinaryDataCaption = true;
|
|
1254
|
+
|
|
1255
|
+
const binaryProperty = binary[binaryPropertyName];
|
|
1256
|
+
binaryDataCaption = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
|
|
1257
|
+
binaryFileNameCaption = binaryProperty.fileName || 'file';
|
|
1258
|
+
binaryContentTypeCaption = binaryProperty.mimeType;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
if (!hasBinaryDataCaption || !binaryDataCaption) {
|
|
1263
|
+
throw new Error('Arquivo binário é obrigatório para enviar mídia com caption. Conecte um nó com arquivo binário antes deste.');
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
// Enviar como FormData
|
|
1267
|
+
const formDataCaption: IDataObject = {
|
|
1268
|
+
caption: mediaCaption,
|
|
1269
|
+
number: mediaCaptionPhoneNumber,
|
|
1270
|
+
externalKey: mediaCaptionExternalKey,
|
|
1271
|
+
media: {
|
|
1272
|
+
value: binaryDataCaption,
|
|
1273
|
+
options: {
|
|
1274
|
+
filename: binaryFileNameCaption,
|
|
1275
|
+
contentType: binaryContentTypeCaption,
|
|
1276
|
+
},
|
|
1277
|
+
},
|
|
1278
|
+
};
|
|
1279
|
+
|
|
1280
|
+
options = {
|
|
1281
|
+
method,
|
|
1282
|
+
headers: {
|
|
1283
|
+
Authorization: `Bearer ${token}`,
|
|
1284
|
+
Accept: 'application/json',
|
|
1285
|
+
},
|
|
1286
|
+
formData: formDataCaption,
|
|
1287
|
+
uri: `${baseUrl}${url}`,
|
|
1288
|
+
json: true,
|
|
1289
|
+
};
|
|
1290
|
+
break;
|
|
1291
|
+
case 'sendBase64':
|
|
1292
|
+
// Validar se o UUID foi fornecido
|
|
1293
|
+
if (!param || param.trim() === '') {
|
|
1294
|
+
throw new Error('UUID da conexão é obrigatório para enviar base64. Preencha o campo "Parâmetro" com o UUID da conexão.');
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
url = `/v1/api/external/${param}/send-base64-media`;
|
|
1298
|
+
method = 'POST';
|
|
1299
|
+
|
|
1300
|
+
const base64Caption = this.getNodeParameter('base64Caption', i) as string;
|
|
1301
|
+
const base64PhoneNumber = this.getNodeParameter('base64PhoneNumber', i) as string;
|
|
1302
|
+
const base64ExternalKey = this.getNodeParameter('externalKey', i) as string;
|
|
1303
|
+
const mediaBase64 = this.getNodeParameter('mediaBase64', i) as string;
|
|
1304
|
+
const mimeType = this.getNodeParameter('mimeType', i) as string;
|
|
1305
|
+
const fileName = this.getNodeParameter('fileName', i) as string;
|
|
1306
|
+
|
|
1307
|
+
// Validar campos obrigatórios
|
|
1308
|
+
if (!mediaBase64 || mediaBase64.trim() === '') {
|
|
1309
|
+
throw new Error('Arquivo Base64 é obrigatório.');
|
|
1310
|
+
}
|
|
1311
|
+
if (!mimeType || mimeType.trim() === '') {
|
|
1312
|
+
throw new Error('Tipo MIME é obrigatório.');
|
|
1313
|
+
}
|
|
1314
|
+
if (!fileName || fileName.trim() === '') {
|
|
1315
|
+
throw new Error('Nome do arquivo é obrigatório.');
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
const base64Body: IDataObject = {
|
|
1319
|
+
number: base64PhoneNumber,
|
|
1320
|
+
mediaBase64: mediaBase64,
|
|
1321
|
+
mimeType: mimeType,
|
|
1322
|
+
fileName: fileName,
|
|
1323
|
+
externalKey: base64ExternalKey
|
|
1324
|
+
};
|
|
1325
|
+
|
|
1326
|
+
// Adicionar caption apenas se fornecido
|
|
1327
|
+
if (base64Caption && base64Caption.trim() !== '') {
|
|
1328
|
+
base64Body.caption = base64Caption;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
headers['Content-Type'] = 'application/json';
|
|
1332
|
+
options = {
|
|
1333
|
+
method,
|
|
1334
|
+
headers,
|
|
1335
|
+
body: base64Body,
|
|
1336
|
+
uri: `${baseUrl}${url}`,
|
|
1337
|
+
json: true,
|
|
1338
|
+
};
|
|
1339
|
+
break;
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
// Se as opções não foram definidas no switch, defina-as aqui para operações GET
|
|
1343
|
+
if (!options.method) {
|
|
1344
|
+
options = {
|
|
1345
|
+
method,
|
|
1346
|
+
headers,
|
|
1347
|
+
uri: `${baseUrl}${url}`,
|
|
1348
|
+
json: true,
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
try {
|
|
1353
|
+
responseData = await this.helpers.request(options);
|
|
1354
|
+
returnData.push({ json: responseData });
|
|
1355
|
+
} catch (error: any) {
|
|
1356
|
+
if (error.response) {
|
|
1357
|
+
returnData.push({ json: { error: error.response.body || error.message } });
|
|
1358
|
+
} else {
|
|
1359
|
+
returnData.push({ json: { error: error.message } });
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
return [returnData];
|
|
1365
|
+
}
|
|
1366
|
+
}
|