n8n-nodes-digitalsac 0.2.9 → 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 CHANGED
@@ -20,6 +20,12 @@ Este pacote adiciona um nó personalizado ao n8n para interagir com a API do Dig
20
20
  - Vincular Kanban
21
21
  - Listar Carteiras
22
22
  - Vincular Carteira
23
+ - **Agendamento:**
24
+ - Listar Serviços
25
+ - Listar Usuários Disponíveis
26
+ - Listar Horários Disponíveis
27
+ - Criar Agendamento
28
+ - Cancelar Agendamento
23
29
 
24
30
  ## Instalação
25
31
 
@@ -110,30 +116,22 @@ Onde:
110
116
 
111
117
  ### Enviar Mensagem de Texto
112
118
  1. Selecione a operação **Enviar Mensagem**
113
- 2. No campo **Parâmetro**, insira o UUID da conexão
114
- 3. No campo **Dados (JSON)**, insira os dados no formato:
115
- ```json
116
- {
117
- "body": "Sua mensagem aqui",
118
- "number": "5511999999999",
119
- "externalKey": "chave_opcional"
120
- }
121
- ```
119
+ 2. No campo **Parâmetro**, insira o UUID da conexão (ex: 999ab3a2-9f1f-4ffb-969a-bfb72234ece1)
120
+ 3. No campo **Corpo da Mensagem**, insira o texto da mensagem
121
+ 4. No campo **Número de Telefone**, insira o número no formato DDI+DDD+Número (ex: 5511999999999)
122
+ 5. No campo **Chave Externa**, insira um identificador único opcional
122
123
 
123
124
  ### Enviar Arquivo
124
- 1. Conecte um nó que forneça dados binários (ex: HTTP Request, Read Binary File)
125
- 2. Selecione a operação **Enviar Mensagem**
126
- 3. No campo **Parâmetro**, insira o UUID da conexão
127
- 4. No campo **Dados (JSON)**, insira os dados no formato:
128
- ```json
129
- {
130
- "body": "Mensagem que acompanha o arquivo",
131
- "number": "5511999999999",
132
- "externalKey": "chave_opcional",
133
- "binaryPropertyName": "data"
134
- }
135
- ```
136
- Onde `data` é o nome da propriedade binária que contém o arquivo.
125
+ 1. Conecte um nó que forneça dados binários (ex: **HTTP Request**, **Read Binary File**, **Google Drive**)
126
+ 2. Conecte ao **Digitalsac**
127
+ 3. Selecione a operação **Enviar Mensagem**
128
+ 4. No campo **Parâmetro**, insira o UUID da conexão
129
+ 5. Preencha os demais campos normalmente
130
+ 6. O nó detectará automaticamente o arquivo binário e enviará via FormData
131
+
132
+ **Nota**: O nó detecta automaticamente se há dados binários conectados e escolhe o método correto:
133
+ - **Sem arquivo**: Envia como JSON (texto)
134
+ - **Com arquivo**: Envia como FormData (arquivo + texto)
137
135
 
138
136
  ### Listar Tags
139
137
  1. Selecione a operação **Listar Tags**
@@ -263,15 +261,10 @@ Onde:
263
261
  - Configure para ler um arquivo PDF
264
262
  2. Conecte ao nó **Digitalsac**
265
263
  - Operação: **Enviar Mensagem**
266
- - Parâmetro: `seu-uuid-de-conexão`
267
- - Dados (JSON):
268
- ```json
269
- {
270
- "body": "Segue o PDF solicitado",
271
- "number": "5511999999999",
272
- "binaryPropertyName": "data"
273
- }
274
- ```
264
+ - Parâmetro: `999ab3a2-9f1f-4ffb-969a-bfb72234ece1` (seu UUID de conexão)
265
+ - Corpo da Mensagem: `Segue o PDF solicitado`
266
+ - Número de Telefone: `5511999999999`
267
+ - Chave Externa: `pdf_documento_123`
275
268
 
276
269
  ### Transferir ticket para uma fila específica
277
270
  1. Adicione um nó **Digitalsac**
@@ -336,6 +329,85 @@ Onde:
336
329
  }
337
330
  ```
338
331
 
332
+ ## Funcionalidades de Agendamento
333
+
334
+ ### Listar Serviços
335
+ Lista todos os serviços disponíveis para agendamento.
336
+ 1. Selecione a operação **Listar Serviços**
337
+ 2. (Opcional) No campo **ID do Usuário**, insira o ID do usuário para filtrar apenas os serviços que ele atende
338
+
339
+ **Retorno exemplo:**
340
+ ```json
341
+ {
342
+ "servicos": "*1* - Consulta (30min - R$ 100,00)\n*2* - Retorno (15min - R$ 50,00)"
343
+ }
344
+ ```
345
+
346
+ ### Listar Usuários Disponíveis
347
+ Lista os usuários/atendentes disponíveis para um serviço em uma data específica.
348
+ 1. Selecione a operação **Listar Usuários Disponíveis**
349
+ 2. Preencha:
350
+ - **ID do Serviço**: ID do serviço desejado
351
+ - **Data**: Data no formato YYYY-MM-DD (ex: 2025-08-07)
352
+ - **Horário** (opcional): Horário específico no formato HH:mm (ex: 09:00)
353
+
354
+ **Retorno exemplo:**
355
+ ```json
356
+ {
357
+ "usuarios": "*28* - João Silva\n*29* - Maria Santos"
358
+ }
359
+ ```
360
+
361
+ ### Listar Horários Disponíveis
362
+ Lista os horários disponíveis para um serviço e usuário em uma data específica.
363
+ 1. Selecione a operação **Listar Horários Disponíveis**
364
+ 2. Preencha:
365
+ - **ID do Serviço**: ID do serviço desejado
366
+ - **ID do Usuário**: ID do atendente/usuário
367
+ - **Data**: Data no formato YYYY-MM-DD
368
+
369
+ **Retorno exemplo:**
370
+ ```json
371
+ {
372
+ "horarios": "*1* - 09:00\n*2* - 09:30\n*3* - 10:00\n*4* - 10:30"
373
+ }
374
+ ```
375
+
376
+ ### Criar Agendamento
377
+ Cria um novo agendamento no sistema.
378
+ 1. Selecione a operação **Criar Agendamento**
379
+ 2. Preencha:
380
+ - **ID do Serviço**: ID do serviço
381
+ - **ID do Usuário**: ID do atendente/usuário
382
+ - **Data**: Data do agendamento (YYYY-MM-DD)
383
+ - **Horário**: Horário do agendamento (HH:mm)
384
+ - **Nome do Contato**: Nome do cliente
385
+ - **Telefone do Contato**: Telefone do cliente (formato: 5511999999999)
386
+ - **Observações** (opcional): Notas sobre o agendamento
387
+
388
+ **Retorno exemplo:**
389
+ ```json
390
+ {
391
+ "status": 0,
392
+ "mensagem": "Agendamento criado com sucesso",
393
+ "scheduleId": 123
394
+ }
395
+ ```
396
+
397
+ ### Cancelar Agendamento
398
+ Cancela um agendamento existente.
399
+ 1. Selecione a operação **Cancelar Agendamento**
400
+ 2. Preencha:
401
+ - **ID do Agendamento**: ID do agendamento a ser cancelado
402
+
403
+ **Retorno exemplo:**
404
+ ```json
405
+ {
406
+ "status": 0,
407
+ "mensagem": "Agendamento cancelado com sucesso"
408
+ }
409
+ ```
410
+
339
411
  ## Suporte
340
412
 
341
413
  Para suporte, entre em contato com [contato@digitalsac.io](mailto:contato@digitalsac.io).
@@ -43,6 +43,12 @@ class Digitalsac {
43
43
  { name: 'Vincular Kanban', value: 'linkKanban' },
44
44
  { name: 'Listar Carteiras', value: 'listCarteiras' },
45
45
  { name: 'Vincular Carteira', value: 'linkCarteira' },
46
+ // Agendamento
47
+ { name: 'Listar Serviços', value: 'listServices' },
48
+ { name: 'Listar Usuários Disponíveis', value: 'listAvailableUsers' },
49
+ { name: 'Listar Horários Disponíveis', value: 'listAvailableSlots' },
50
+ { name: 'Criar Agendamento', value: 'createSchedule' },
51
+ { name: 'Cancelar Agendamento', value: 'cancelSchedule' },
46
52
  ],
47
53
  default: 'validateWhatsapp',
48
54
  },
@@ -214,6 +220,118 @@ class Digitalsac {
214
220
  },
215
221
  description: 'ID do ticket e ID do usuário da carteira',
216
222
  },
223
+ // Campos para Agendamento
224
+ {
225
+ displayName: 'ID do Usuário',
226
+ name: 'scheduleUserId',
227
+ type: 'number',
228
+ default: 0,
229
+ displayOptions: {
230
+ show: {
231
+ operation: ['listServices'],
232
+ },
233
+ },
234
+ description: 'ID do usuário para filtrar serviços (opcional)',
235
+ },
236
+ {
237
+ displayName: 'ID do Serviço',
238
+ name: 'serviceId',
239
+ type: 'number',
240
+ default: 1,
241
+ displayOptions: {
242
+ show: {
243
+ operation: ['listAvailableUsers', 'listAvailableSlots', 'createSchedule'],
244
+ },
245
+ },
246
+ description: 'ID do serviço',
247
+ },
248
+ {
249
+ displayName: 'Data',
250
+ name: 'scheduleDate',
251
+ type: 'string',
252
+ default: '',
253
+ placeholder: '2025-08-07',
254
+ displayOptions: {
255
+ show: {
256
+ operation: ['listAvailableUsers', 'listAvailableSlots', 'createSchedule'],
257
+ },
258
+ },
259
+ description: 'Data no formato YYYY-MM-DD',
260
+ },
261
+ {
262
+ displayName: 'Horário',
263
+ name: 'scheduleTime',
264
+ type: 'string',
265
+ default: '',
266
+ placeholder: '09:00',
267
+ displayOptions: {
268
+ show: {
269
+ operation: ['listAvailableUsers', 'createSchedule'],
270
+ },
271
+ },
272
+ description: 'Horário no formato HH:mm (opcional para listar usuários)',
273
+ },
274
+ {
275
+ displayName: 'ID do Usuário',
276
+ name: 'scheduleAttendantId',
277
+ type: 'number',
278
+ default: 1,
279
+ displayOptions: {
280
+ show: {
281
+ operation: ['listAvailableSlots', 'createSchedule'],
282
+ },
283
+ },
284
+ description: 'ID do atendente/usuário',
285
+ },
286
+ {
287
+ displayName: 'Nome do Contato',
288
+ name: 'contactName',
289
+ type: 'string',
290
+ default: '',
291
+ displayOptions: {
292
+ show: {
293
+ operation: ['createSchedule'],
294
+ },
295
+ },
296
+ description: 'Nome do cliente/contato',
297
+ },
298
+ {
299
+ displayName: 'Telefone do Contato',
300
+ name: 'contactPhone',
301
+ type: 'string',
302
+ default: '',
303
+ placeholder: '5511999999999',
304
+ displayOptions: {
305
+ show: {
306
+ operation: ['createSchedule'],
307
+ },
308
+ },
309
+ description: 'Telefone do cliente/contato',
310
+ },
311
+ {
312
+ displayName: 'Observações',
313
+ name: 'scheduleNotes',
314
+ type: 'string',
315
+ default: '',
316
+ displayOptions: {
317
+ show: {
318
+ operation: ['createSchedule'],
319
+ },
320
+ },
321
+ description: 'Observações sobre o agendamento (opcional)',
322
+ },
323
+ {
324
+ displayName: 'ID do Agendamento',
325
+ name: 'scheduleId',
326
+ type: 'number',
327
+ default: 0,
328
+ displayOptions: {
329
+ show: {
330
+ operation: ['cancelSchedule'],
331
+ },
332
+ },
333
+ description: 'ID do agendamento a ser cancelado',
334
+ },
217
335
  ],
218
336
  };
219
337
  }
@@ -321,26 +439,75 @@ class Digitalsac {
321
439
  };
322
440
  break;
323
441
  case 'sendMessage':
442
+ // Validar se o UUID foi fornecido
443
+ if (!param || param.trim() === '') {
444
+ throw new Error('UUID da conexão é obrigatório para enviar mensagem. Preencha o campo "Parâmetro" com o UUID da conexão.');
445
+ }
324
446
  url = `/v1/api/external/${param}`;
325
447
  method = 'POST';
326
448
  // Usar campos separados em vez de JSON
327
449
  const messageBody = this.getNodeParameter('messageBody', i);
328
450
  const phoneNumber = this.getNodeParameter('phoneNumber', i);
329
451
  const externalKey = this.getNodeParameter('externalKey', i);
330
- // Criar o objeto diretamente
331
- body = {
332
- body: messageBody,
333
- number: phoneNumber,
334
- externalKey: externalKey
335
- };
336
- headers['Content-Type'] = 'application/json';
337
- options = {
338
- method,
339
- headers,
340
- body,
341
- uri: `${baseUrl}${url}`,
342
- json: true,
343
- };
452
+ // Verificar se dados binários (para envio de arquivo)
453
+ let hasBinaryData = false;
454
+ let binaryData;
455
+ let binaryFileName;
456
+ let binaryContentType;
457
+ if (items[i].binary) {
458
+ const binary = items[i].binary;
459
+ // Procurar por qualquer propriedade binária disponível
460
+ const binaryKeys = Object.keys(binary);
461
+ if (binaryKeys.length > 0) {
462
+ const binaryPropertyName = binaryKeys[0]; // Usar a primeira propriedade binária encontrada
463
+ hasBinaryData = true;
464
+ const binaryProperty = binary[binaryPropertyName];
465
+ binaryData = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
466
+ binaryFileName = binaryProperty.fileName || 'file';
467
+ binaryContentType = binaryProperty.mimeType;
468
+ }
469
+ }
470
+ if (hasBinaryData && binaryData) {
471
+ // Enviar como FormData (para arquivos)
472
+ const formData = {
473
+ body: messageBody,
474
+ number: phoneNumber,
475
+ externalKey: externalKey,
476
+ media: {
477
+ value: binaryData,
478
+ options: {
479
+ filename: binaryFileName,
480
+ contentType: binaryContentType,
481
+ },
482
+ },
483
+ };
484
+ options = {
485
+ method,
486
+ headers: {
487
+ Authorization: `Bearer ${token}`,
488
+ Accept: 'application/json',
489
+ },
490
+ formData: formData,
491
+ uri: `${baseUrl}${url}`,
492
+ json: true,
493
+ };
494
+ }
495
+ else {
496
+ // Enviar como JSON (para texto)
497
+ body = {
498
+ body: messageBody,
499
+ number: phoneNumber,
500
+ externalKey: externalKey
501
+ };
502
+ headers['Content-Type'] = 'application/json';
503
+ options = {
504
+ method,
505
+ headers,
506
+ body,
507
+ uri: `${baseUrl}${url}`,
508
+ json: true,
509
+ };
510
+ }
344
511
  break;
345
512
  case 'listTags':
346
513
  url = '/typebot/listar_tags';
@@ -425,6 +592,75 @@ class Digitalsac {
425
592
  json: true,
426
593
  };
427
594
  break;
595
+ // Casos para Agendamento
596
+ case 'listServices':
597
+ const scheduleUserId = this.getNodeParameter('scheduleUserId', i);
598
+ if (scheduleUserId > 0) {
599
+ url = `/typebot/listar_servicos?userId=${scheduleUserId}`;
600
+ }
601
+ else {
602
+ url = '/typebot/listar_servicos';
603
+ }
604
+ break;
605
+ case 'listAvailableUsers':
606
+ const serviceIdForUsers = this.getNodeParameter('serviceId', i);
607
+ const dateForUsers = this.getNodeParameter('scheduleDate', i);
608
+ const timeForUsers = this.getNodeParameter('scheduleTime', i);
609
+ url = `/typebot/listar_usuarios_disponiveis?serviceId=${serviceIdForUsers}&date=${dateForUsers}`;
610
+ if (timeForUsers) {
611
+ url += `&time=${timeForUsers}`;
612
+ }
613
+ break;
614
+ case 'listAvailableSlots':
615
+ const serviceIdForSlots = this.getNodeParameter('serviceId', i);
616
+ const userIdForSlots = this.getNodeParameter('scheduleAttendantId', i);
617
+ const dateForSlots = this.getNodeParameter('scheduleDate', i);
618
+ url = `/typebot/listar_horarios_disponiveis?serviceId=${serviceIdForSlots}&userId=${userIdForSlots}&date=${dateForSlots}`;
619
+ break;
620
+ case 'createSchedule':
621
+ url = '/typebot/criar_agendamento';
622
+ method = 'POST';
623
+ const serviceIdForCreate = this.getNodeParameter('serviceId', i);
624
+ const userIdForCreate = this.getNodeParameter('scheduleAttendantId', i);
625
+ const dateForCreate = this.getNodeParameter('scheduleDate', i);
626
+ const timeForCreate = this.getNodeParameter('scheduleTime', i);
627
+ const contactNameForCreate = this.getNodeParameter('contactName', i);
628
+ const contactPhoneForCreate = this.getNodeParameter('contactPhone', i);
629
+ const notesForCreate = this.getNodeParameter('scheduleNotes', i);
630
+ body = {
631
+ serviceId: serviceIdForCreate,
632
+ userId: userIdForCreate,
633
+ date: dateForCreate,
634
+ time: timeForCreate,
635
+ contactName: contactNameForCreate,
636
+ contactPhone: contactPhoneForCreate,
637
+ notes: notesForCreate
638
+ };
639
+ headers['Content-Type'] = 'application/json';
640
+ options = {
641
+ method,
642
+ headers,
643
+ body,
644
+ uri: `${baseUrl}${url}`,
645
+ json: true,
646
+ };
647
+ break;
648
+ case 'cancelSchedule':
649
+ url = '/typebot/cancelar_agendamento';
650
+ method = 'POST';
651
+ const scheduleIdForCancel = this.getNodeParameter('scheduleId', i);
652
+ body = {
653
+ scheduleId: scheduleIdForCancel
654
+ };
655
+ headers['Content-Type'] = 'application/json';
656
+ options = {
657
+ method,
658
+ headers,
659
+ body,
660
+ uri: `${baseUrl}${url}`,
661
+ json: true,
662
+ };
663
+ break;
428
664
  }
429
665
  // Se as opções não foram definidas no switch, defina-as aqui para operações GET
430
666
  if (!options.method) {
@@ -4,6 +4,8 @@ import {
4
4
  INodeType,
5
5
  INodeTypeDescription,
6
6
  NodeConnectionType,
7
+ IDataObject,
8
+ IBinaryKeyData,
7
9
  } from 'n8n-workflow';
8
10
 
9
11
  export class Digitalsac implements INodeType {
@@ -48,6 +50,12 @@ export class Digitalsac implements INodeType {
48
50
  { name: 'Vincular Kanban', value: 'linkKanban' },
49
51
  { name: 'Listar Carteiras', value: 'listCarteiras' },
50
52
  { name: 'Vincular Carteira', value: 'linkCarteira' },
53
+ // Agendamento
54
+ { name: 'Listar Serviços', value: 'listServices' },
55
+ { name: 'Listar Usuários Disponíveis', value: 'listAvailableUsers' },
56
+ { name: 'Listar Horários Disponíveis', value: 'listAvailableSlots' },
57
+ { name: 'Criar Agendamento', value: 'createSchedule' },
58
+ { name: 'Cancelar Agendamento', value: 'cancelSchedule' },
51
59
  ],
52
60
  default: 'validateWhatsapp',
53
61
  },
@@ -219,6 +227,118 @@ export class Digitalsac implements INodeType {
219
227
  },
220
228
  description: 'ID do ticket e ID do usuário da carteira',
221
229
  },
230
+ // Campos para Agendamento
231
+ {
232
+ displayName: 'ID do Usuário',
233
+ name: 'scheduleUserId',
234
+ type: 'number',
235
+ default: 0,
236
+ displayOptions: {
237
+ show: {
238
+ operation: ['listServices'],
239
+ },
240
+ },
241
+ description: 'ID do usuário para filtrar serviços (opcional)',
242
+ },
243
+ {
244
+ displayName: 'ID do Serviço',
245
+ name: 'serviceId',
246
+ type: 'number',
247
+ default: 1,
248
+ displayOptions: {
249
+ show: {
250
+ operation: ['listAvailableUsers', 'listAvailableSlots', 'createSchedule'],
251
+ },
252
+ },
253
+ description: 'ID do serviço',
254
+ },
255
+ {
256
+ displayName: 'Data',
257
+ name: 'scheduleDate',
258
+ type: 'string',
259
+ default: '',
260
+ placeholder: '2025-08-07',
261
+ displayOptions: {
262
+ show: {
263
+ operation: ['listAvailableUsers', 'listAvailableSlots', 'createSchedule'],
264
+ },
265
+ },
266
+ description: 'Data no formato YYYY-MM-DD',
267
+ },
268
+ {
269
+ displayName: 'Horário',
270
+ name: 'scheduleTime',
271
+ type: 'string',
272
+ default: '',
273
+ placeholder: '09:00',
274
+ displayOptions: {
275
+ show: {
276
+ operation: ['listAvailableUsers', 'createSchedule'],
277
+ },
278
+ },
279
+ description: 'Horário no formato HH:mm (opcional para listar usuários)',
280
+ },
281
+ {
282
+ displayName: 'ID do Usuário',
283
+ name: 'scheduleAttendantId',
284
+ type: 'number',
285
+ default: 1,
286
+ displayOptions: {
287
+ show: {
288
+ operation: ['listAvailableSlots', 'createSchedule'],
289
+ },
290
+ },
291
+ description: 'ID do atendente/usuário',
292
+ },
293
+ {
294
+ displayName: 'Nome do Contato',
295
+ name: 'contactName',
296
+ type: 'string',
297
+ default: '',
298
+ displayOptions: {
299
+ show: {
300
+ operation: ['createSchedule'],
301
+ },
302
+ },
303
+ description: 'Nome do cliente/contato',
304
+ },
305
+ {
306
+ displayName: 'Telefone do Contato',
307
+ name: 'contactPhone',
308
+ type: 'string',
309
+ default: '',
310
+ placeholder: '5511999999999',
311
+ displayOptions: {
312
+ show: {
313
+ operation: ['createSchedule'],
314
+ },
315
+ },
316
+ description: 'Telefone do cliente/contato',
317
+ },
318
+ {
319
+ displayName: 'Observações',
320
+ name: 'scheduleNotes',
321
+ type: 'string',
322
+ default: '',
323
+ displayOptions: {
324
+ show: {
325
+ operation: ['createSchedule'],
326
+ },
327
+ },
328
+ description: 'Observações sobre o agendamento (opcional)',
329
+ },
330
+ {
331
+ displayName: 'ID do Agendamento',
332
+ name: 'scheduleId',
333
+ type: 'number',
334
+ default: 0,
335
+ displayOptions: {
336
+ show: {
337
+ operation: ['cancelSchedule'],
338
+ },
339
+ },
340
+ description: 'ID do agendamento a ser cancelado',
341
+ },
222
342
  ],
223
343
  };
224
344
 
@@ -327,6 +447,11 @@ export class Digitalsac implements INodeType {
327
447
  };
328
448
  break;
329
449
  case 'sendMessage':
450
+ // Validar se o UUID foi fornecido
451
+ if (!param || param.trim() === '') {
452
+ throw new Error('UUID da conexão é obrigatório para enviar mensagem. Preencha o campo "Parâmetro" com o UUID da conexão.');
453
+ }
454
+
330
455
  url = `/v1/api/external/${param}`;
331
456
  method = 'POST';
332
457
 
@@ -335,21 +460,69 @@ export class Digitalsac implements INodeType {
335
460
  const phoneNumber = this.getNodeParameter('phoneNumber', i) as string;
336
461
  const externalKey = this.getNodeParameter('externalKey', i) as string;
337
462
 
338
- // Criar o objeto diretamente
339
- body = {
340
- body: messageBody,
341
- number: phoneNumber,
342
- externalKey: externalKey
343
- };
463
+ // Verificar se dados binários (para envio de arquivo)
464
+ let hasBinaryData = false;
465
+ let binaryData: Buffer | undefined;
466
+ let binaryFileName: string | undefined;
467
+ let binaryContentType: string | undefined;
344
468
 
345
- headers['Content-Type'] = 'application/json';
346
- options = {
347
- method,
348
- headers,
349
- body,
350
- uri: `${baseUrl}${url}`,
351
- json: true,
352
- };
469
+ if (items[i].binary) {
470
+ const binary = items[i].binary as IBinaryKeyData;
471
+ // Procurar por qualquer propriedade binária disponível
472
+ const binaryKeys = Object.keys(binary);
473
+ if (binaryKeys.length > 0) {
474
+ const binaryPropertyName = binaryKeys[0]; // Usar a primeira propriedade binária encontrada
475
+ hasBinaryData = true;
476
+
477
+ const binaryProperty = binary[binaryPropertyName];
478
+ binaryData = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
479
+ binaryFileName = binaryProperty.fileName || 'file';
480
+ binaryContentType = binaryProperty.mimeType;
481
+ }
482
+ }
483
+
484
+ if (hasBinaryData && binaryData) {
485
+ // Enviar como FormData (para arquivos)
486
+ const formData: IDataObject = {
487
+ body: messageBody,
488
+ number: phoneNumber,
489
+ externalKey: externalKey,
490
+ media: {
491
+ value: binaryData,
492
+ options: {
493
+ filename: binaryFileName,
494
+ contentType: binaryContentType,
495
+ },
496
+ },
497
+ };
498
+
499
+ options = {
500
+ method,
501
+ headers: {
502
+ Authorization: `Bearer ${token}`,
503
+ Accept: 'application/json',
504
+ },
505
+ formData: formData,
506
+ uri: `${baseUrl}${url}`,
507
+ json: true,
508
+ };
509
+ } else {
510
+ // Enviar como JSON (para texto)
511
+ body = {
512
+ body: messageBody,
513
+ number: phoneNumber,
514
+ externalKey: externalKey
515
+ };
516
+
517
+ headers['Content-Type'] = 'application/json';
518
+ options = {
519
+ method,
520
+ headers,
521
+ body,
522
+ uri: `${baseUrl}${url}`,
523
+ json: true,
524
+ };
525
+ }
353
526
  break;
354
527
  case 'listTags':
355
528
  url = '/typebot/listar_tags';
@@ -434,6 +607,87 @@ export class Digitalsac implements INodeType {
434
607
  json: true,
435
608
  };
436
609
  break;
610
+
611
+ // Casos para Agendamento
612
+ case 'listServices':
613
+ const scheduleUserId = this.getNodeParameter('scheduleUserId', i) as number;
614
+ if (scheduleUserId > 0) {
615
+ url = `/typebot/listar_servicos?userId=${scheduleUserId}`;
616
+ } else {
617
+ url = '/typebot/listar_servicos';
618
+ }
619
+ break;
620
+
621
+ case 'listAvailableUsers':
622
+ const serviceIdForUsers = this.getNodeParameter('serviceId', i) as number;
623
+ const dateForUsers = this.getNodeParameter('scheduleDate', i) as string;
624
+ const timeForUsers = this.getNodeParameter('scheduleTime', i) as string;
625
+
626
+ url = `/typebot/listar_usuarios_disponiveis?serviceId=${serviceIdForUsers}&date=${dateForUsers}`;
627
+ if (timeForUsers) {
628
+ url += `&time=${timeForUsers}`;
629
+ }
630
+ break;
631
+
632
+ case 'listAvailableSlots':
633
+ const serviceIdForSlots = this.getNodeParameter('serviceId', i) as number;
634
+ const userIdForSlots = this.getNodeParameter('scheduleAttendantId', i) as number;
635
+ const dateForSlots = this.getNodeParameter('scheduleDate', i) as string;
636
+
637
+ url = `/typebot/listar_horarios_disponiveis?serviceId=${serviceIdForSlots}&userId=${userIdForSlots}&date=${dateForSlots}`;
638
+ break;
639
+
640
+ case 'createSchedule':
641
+ url = '/typebot/criar_agendamento';
642
+ method = 'POST';
643
+
644
+ const serviceIdForCreate = this.getNodeParameter('serviceId', i) as number;
645
+ const userIdForCreate = this.getNodeParameter('scheduleAttendantId', i) as number;
646
+ const dateForCreate = this.getNodeParameter('scheduleDate', i) as string;
647
+ const timeForCreate = this.getNodeParameter('scheduleTime', i) as string;
648
+ const contactNameForCreate = this.getNodeParameter('contactName', i) as string;
649
+ const contactPhoneForCreate = this.getNodeParameter('contactPhone', i) as string;
650
+ const notesForCreate = this.getNodeParameter('scheduleNotes', i) as string;
651
+
652
+ body = {
653
+ serviceId: serviceIdForCreate,
654
+ userId: userIdForCreate,
655
+ date: dateForCreate,
656
+ time: timeForCreate,
657
+ contactName: contactNameForCreate,
658
+ contactPhone: contactPhoneForCreate,
659
+ notes: notesForCreate
660
+ };
661
+
662
+ headers['Content-Type'] = 'application/json';
663
+ options = {
664
+ method,
665
+ headers,
666
+ body,
667
+ uri: `${baseUrl}${url}`,
668
+ json: true,
669
+ };
670
+ break;
671
+
672
+ case 'cancelSchedule':
673
+ url = '/typebot/cancelar_agendamento';
674
+ method = 'POST';
675
+
676
+ const scheduleIdForCancel = this.getNodeParameter('scheduleId', i) as number;
677
+
678
+ body = {
679
+ scheduleId: scheduleIdForCancel
680
+ };
681
+
682
+ headers['Content-Type'] = 'application/json';
683
+ options = {
684
+ method,
685
+ headers,
686
+ body,
687
+ uri: `${baseUrl}${url}`,
688
+ json: true,
689
+ };
690
+ break;
437
691
  }
438
692
 
439
693
  // Se as opções não foram definidas no switch, defina-as aqui para operações GET
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-digitalsac",
3
- "version": "0.2.9",
3
+ "version": "0.4.0",
4
4
  "description": "Izing Pro Digitalsac",
5
5
  "keywords": [
6
6
  "n8n",
@@ -49,7 +49,7 @@
49
49
  "n8n-core": "^1.0.0",
50
50
  "n8n-workflow": "^1.0.0",
51
51
  "prettier": "^2.8.8",
52
- "typescript": "^5.0.0"
52
+ "typescript": "^5.9.2"
53
53
  },
54
54
  "dependencies": {
55
55
  "axios": "^1.9.0"