n8n-nodes-digitalsac 0.4.4 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,6 +13,10 @@ Este pacote adiciona um nó personalizado ao n8n para interagir com a API do Dig
13
13
  - Transferir para Atendente
14
14
  - Fechar Ticket
15
15
  - Enviar Mensagem (texto e arquivos)
16
+ - **Enviar Botões Interativos**
17
+ - **Enviar Listas**
18
+ - **Enviar Mídia com Caption**
19
+ - **Enviar Arquivo Base64**
16
20
  - Listar Tags
17
21
  - Vincular Tag
18
22
  - Criar Tag
@@ -46,6 +50,33 @@ cd ~/.n8n
46
50
  npm install n8n-nodes-digitalsac
47
51
  ```
48
52
 
53
+ ## ✨ Novas Funcionalidades (v0.5.1)
54
+
55
+ ### 🔘 Enviar Botões Interativos
56
+ Envie mensagens com botões clicáveis:
57
+ - **Resposta Rápida**: Botão que responde instantaneamente
58
+ - **URL**: Botão que abre links
59
+ - **Copiar**: Botão que copia texto
60
+ - **Ligar**: Botão que inicia chamada
61
+
62
+ ### 📋 Enviar Listas
63
+ Crie menus organizados com:
64
+ - Múltiplas seções
65
+ - Opções clicáveis
66
+ - Descrições detalhadas
67
+
68
+ ### 🖼️ Enviar Mídia com Caption
69
+ Envie arquivos com legendas personalizadas:
70
+ - Upload direto de arquivos
71
+ - Caption obrigatório
72
+ - Suporte a imagens, PDFs, vídeos
73
+
74
+ ### 📁 Enviar Base64
75
+ Envie arquivos via base64:
76
+ - Sem necessidade de upload
77
+ - Caption opcional
78
+ - Ideal para integração com APIs
79
+
49
80
  ## Autenticação
50
81
 
51
82
  Configure as credenciais Digitalsac com a URL base e seu Bearer Token:
@@ -134,6 +165,151 @@ Onde:
134
165
  - **Sem arquivo**: Envia como JSON (texto)
135
166
  - **Com arquivo**: Envia como FormData (arquivo + texto)
136
167
 
168
+ ### 🔘 Enviar Botões Interativos
169
+ 1. Selecione a operação **Enviar Botões**
170
+ 2. No campo **Parâmetro**, insira o UUID da conexão
171
+ 3. Preencha os campos básicos:
172
+ - **Título**: "Escolha uma opção"
173
+ - **Corpo da Mensagem**: "Clique em uma das opções abaixo:"
174
+ - **Número de Telefone**: "5511999999999"
175
+ - **Chave Externa**: "btn_001"
176
+ 4. No campo **Botões (JSON)**, configure os botões:
177
+
178
+ ```json
179
+ [
180
+ {
181
+ "tipo": {"label": "Resposta Rápida", "value": "quick_reply"},
182
+ "display_text": "✅ Sim",
183
+ "conteudo": "sim"
184
+ },
185
+ {
186
+ "tipo": {"label": "Resposta Rápida", "value": "quick_reply"},
187
+ "display_text": "❌ Não",
188
+ "conteudo": "nao"
189
+ },
190
+ {
191
+ "tipo": {"label": "URL", "value": "url"},
192
+ "display_text": "🌐 Visitar Site",
193
+ "conteudo": "https://www.digitalsac.com.br"
194
+ },
195
+ {
196
+ "tipo": {"label": "Copiar", "value": "copy"},
197
+ "display_text": "📋 Copiar Código",
198
+ "conteudo": "PROMO2024"
199
+ },
200
+ {
201
+ "tipo": {"label": "Ligar", "value": "call"},
202
+ "display_text": "📞 Ligar Agora",
203
+ "conteudo": "5511999999999"
204
+ }
205
+ ]
206
+ ```
207
+
208
+ **Tipos de botão disponíveis:**
209
+ - `quick_reply`: Resposta rápida (o texto vai para o chat)
210
+ - `url`: Abre um link no navegador
211
+ - `copy`: Copia texto para área de transferência
212
+ - `call`: Inicia uma chamada telefônica
213
+
214
+ ### 📋 Enviar Lista
215
+ 1. Selecione a operação **Enviar Lista**
216
+ 2. No campo **Parâmetro**, insira o UUID da conexão
217
+ 3. Preencha os campos básicos:
218
+ - **Título**: "Menu de Opções"
219
+ - **Texto**: "Escolha uma categoria:"
220
+ - **Texto do Botão**: "Ver Opções"
221
+ - **Rodapé**: "Powered by DigitalSac"
222
+ - **Número de Telefone**: "5511999999999"
223
+ - **Chave Externa**: "list_001"
224
+ 4. No campo **Seções (JSON)**, configure as seções:
225
+
226
+ ```json
227
+ [
228
+ {
229
+ "title": "🛍️ Produtos",
230
+ "lines": [
231
+ {
232
+ "title": "Smartphone Premium",
233
+ "description": "iPhone 15 Pro Max 256GB",
234
+ "rowId": "prod_iphone"
235
+ },
236
+ {
237
+ "title": "Notebook Gamer",
238
+ "description": "Dell Alienware com RTX 4090",
239
+ "rowId": "prod_notebook"
240
+ }
241
+ ]
242
+ },
243
+ {
244
+ "title": "🛠️ Serviços",
245
+ "lines": [
246
+ {
247
+ "title": "Suporte Técnico",
248
+ "description": "Assistência técnica especializada",
249
+ "rowId": "serv_suporte"
250
+ },
251
+ {
252
+ "title": "Consultoria",
253
+ "description": "Consultoria personalizada",
254
+ "rowId": "serv_consultoria"
255
+ }
256
+ ]
257
+ },
258
+ {
259
+ "title": "📞 Contato",
260
+ "lines": [
261
+ {
262
+ "title": "Falar com Vendedor",
263
+ "description": "Atendimento comercial",
264
+ "rowId": "cont_vendas"
265
+ }
266
+ ]
267
+ }
268
+ ]
269
+ ```
270
+
271
+ ### 🖼️ Enviar Mídia com Caption
272
+ 1. **Conecte um nó com arquivo** (ex: HTTP Request, Read Binary File, Google Drive)
273
+ 2. Conecte ao nó **Digitalsac**
274
+ 3. Selecione a operação **Enviar Mídia com Caption**
275
+ 4. No campo **Parâmetro**, insira o UUID da conexão
276
+ 5. Preencha os campos:
277
+ - **Caption**: "Esta é uma imagem importante do produto"
278
+ - **Número de Telefone**: "5511999999999"
279
+ - **Chave Externa**: "media_001"
280
+
281
+ **Tipos de arquivo suportados:**
282
+ - 🖼️ Imagens: JPG, PNG, GIF
283
+ - 📄 Documentos: PDF, DOC, DOCX
284
+ - 🎥 Vídeos: MP4, AVI
285
+ - 🎵 Áudio: MP3, WAV
286
+
287
+ ### 📁 Enviar Base64
288
+ 1. Selecione a operação **Enviar Base64**
289
+ 2. No campo **Parâmetro**, insira o UUID da conexão
290
+ 3. Preencha os campos:
291
+ - **Caption (Opcional)**: "Documento enviado via API"
292
+ - **Número de Telefone**: "5511999999999"
293
+ - **Arquivo Base64**: Cole o arquivo codificado em base64
294
+ - **Tipo MIME**: "application/pdf" (ou conforme o arquivo)
295
+ - **Nome do Arquivo**: "documento.pdf"
296
+ - **Chave Externa**: "base64_001"
297
+
298
+ **Exemplo de uso com código base64:**
299
+ ```json
300
+ {
301
+ "caption": "Relatório mensal de vendas",
302
+ "mediaBase64": "JVBERi0xLjQKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwovUGFnZXMgMiAwIFIKPj4KZW5kb2JqCjIgMCBvYmoKPDwKL1R5cGUgL1BhZ2VzCi9LaWRzIFszIDAgUl0KL0NvdW50IDEKPD4KZW5kb2JqCjMgMCBvYmoKPDwKL1R5cGUgL1BhZ2UKL1BhcmVudCAyIDAgUgovTWVkaWFCb3ggWzAgMCA2MTIgNzkyXQo+PgplbmRvYmoKeHJlZgowIDQKMDAwMDAwMDAwMCA2NTUzNSBmCjAwMDAwMDAwMDkgMDAwMDAgbgowMDAwMDAwMDc0IDAwMDAwIG4KMDAwMDAwMDEyMCAwMDAwMCBuCnRyYWlsZXIKPDwKL1NpemUgNAovUm9vdCAxIDAgUgo+PgpzdGFydHhyZWYKMTc5CiUlRU9G",
303
+ "mimeType": "application/pdf",
304
+ "fileName": "relatorio.pdf"
305
+ }
306
+ ```
307
+
308
+ **Dica**: Para converter arquivo para base64:
309
+ - **Linux/Mac**: `base64 arquivo.pdf`
310
+ - **Windows**: Use PowerShell: `[Convert]::ToBase64String([IO.File]::ReadAllBytes("arquivo.pdf"))`
311
+ - **Online**: Use conversores como base64encode.org
312
+
137
313
  ### Listar Tags
138
314
  1. Selecione a operação **Listar Tags**
139
315
  2. Não é necessário configurar parâmetros adicionais
@@ -457,6 +633,74 @@ Gera um link para download do arquivo .ics (calendário) de um agendamento espec
457
633
  - Pode ser importado em qualquer aplicativo de calendário (Google Calendar, Outlook, Apple Calendar, etc.)
458
634
  - É útil para integração com sistemas externos ou envio para clientes
459
635
 
636
+ ## 💡 Casos de Uso Práticos
637
+
638
+ ### 🤖 Bot de Atendimento Interativo
639
+ Combine as novas operações para criar um fluxo completo:
640
+
641
+ 1. **Enviar Botões** → Menu inicial com opções
642
+ 2. **Enviar Lista** → Catálogo de produtos/serviços
643
+ 3. **Enviar Mídia** → Imagens dos produtos
644
+ 4. **Enviar Base64** → Contratos/documentos
645
+
646
+ ### 🛒 E-commerce Automation
647
+ ```
648
+ Trigger (Webhook)
649
+
650
+ Enviar Botões (Confirmar pedido?)
651
+
652
+ Enviar Lista (Formas de pagamento)
653
+
654
+ Enviar Base64 (Contrato PDF)
655
+
656
+ Enviar Mídia (Comprovante)
657
+ ```
658
+
659
+ ### 📊 Relatórios Automatizados
660
+ ```
661
+ Scheduler (Diário)
662
+
663
+ HTTP Request (Buscar dados)
664
+
665
+ Code (Gerar gráfico base64)
666
+
667
+ Enviar Base64 (Relatório visual)
668
+ ```
669
+
670
+ ### 🎯 Marketing Campaigns
671
+ ```
672
+ Database (Lista clientes)
673
+
674
+ Loop (Para cada cliente)
675
+
676
+ Enviar Botões (CTA personalizado)
677
+
678
+ Webhook (Capturar resposta)
679
+ ```
680
+
681
+ ### 🔄 Workflow Exemplo Completo
682
+ **Cenário**: Venda de produto com confirmação interativa
683
+
684
+ ```
685
+ 1. Webhook (Novo lead)
686
+
687
+ 2. Enviar Botões ("Interesse em comprar?")
688
+
689
+ 3. IF (Resposta = "Sim")
690
+
691
+ 4. Enviar Lista (Catálogo produtos)
692
+
693
+ 5. HTTP Request (Buscar detalhes do produto)
694
+
695
+ 6. Enviar Mídia (Foto do produto)
696
+
697
+ 7. Enviar Botões ("Fechar pedido?")
698
+
699
+ 8. Enviar Base64 (Contrato PDF)
700
+
701
+ 9. Webhook (Notificar vendedor)
702
+ ```
703
+
460
704
  ## Suporte
461
705
 
462
706
  Para suporte, entre em contato com [contato@digitalsac.io](mailto:contato@digitalsac.io).
@@ -36,6 +36,10 @@ class Digitalsac {
36
36
  { name: 'Transferir para Atendente', value: 'transferAgent' },
37
37
  { name: 'Fechar Ticket', value: 'closeTicket' },
38
38
  { name: 'Enviar Mensagem', value: 'sendMessage' },
39
+ { name: 'Enviar Botões', value: 'sendButtons' },
40
+ { name: 'Enviar Lista', value: 'sendList' },
41
+ { name: 'Enviar Mídia com Caption', value: 'sendMediaCaption' },
42
+ { name: 'Enviar Base64', value: 'sendBase64' },
39
43
  { name: 'Listar Tags', value: 'listTags' },
40
44
  { name: 'Vincular Tag', value: 'linkTag' },
41
45
  { name: 'Criar Tag', value: 'createTag' },
@@ -61,10 +65,10 @@ class Digitalsac {
61
65
  default: '',
62
66
  displayOptions: {
63
67
  show: {
64
- operation: ['validateWhatsapp', 'validateCpf', 'sendMessage'],
68
+ operation: ['validateWhatsapp', 'validateCpf', 'sendMessage', 'sendButtons', 'sendList', 'sendMediaCaption', 'sendBase64'],
65
69
  },
66
70
  },
67
- description: 'Número, CPF ou UUID da mensagem (conforme operação)',
71
+ description: 'Número, CPF ou UUID da conexão (conforme operação)',
68
72
  },
69
73
  {
70
74
  displayName: 'User ID',
@@ -109,11 +113,219 @@ class Digitalsac {
109
113
  default: 'Digitalsac123',
110
114
  displayOptions: {
111
115
  show: {
112
- operation: ['sendMessage'],
116
+ operation: ['sendMessage', 'sendButtons', 'sendList', 'sendMediaCaption', 'sendBase64'],
113
117
  },
114
118
  },
115
119
  description: 'Identificador único opcional para a mensagem',
116
120
  },
121
+ // Campos para Enviar Botões
122
+ {
123
+ displayName: 'Título',
124
+ name: 'buttonTitle',
125
+ type: 'string',
126
+ default: 'Escolha uma opção',
127
+ displayOptions: {
128
+ show: {
129
+ operation: ['sendButtons'],
130
+ },
131
+ },
132
+ description: 'Título do conjunto de botões',
133
+ },
134
+ {
135
+ displayName: 'Corpo da Mensagem',
136
+ name: 'buttonBody',
137
+ type: 'string',
138
+ default: 'Clique em uma das opções abaixo:',
139
+ displayOptions: {
140
+ show: {
141
+ operation: ['sendButtons'],
142
+ },
143
+ },
144
+ description: 'Corpo da mensagem com botões',
145
+ },
146
+ {
147
+ displayName: 'Número de Telefone',
148
+ name: 'buttonPhoneNumber',
149
+ type: 'string',
150
+ default: '5511999999999',
151
+ displayOptions: {
152
+ show: {
153
+ operation: ['sendButtons'],
154
+ },
155
+ },
156
+ description: 'Número de telefone no formato DDI+DDD+Número',
157
+ },
158
+ {
159
+ displayName: 'Botões (JSON)',
160
+ name: 'buttonsData',
161
+ type: 'json',
162
+ 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]',
163
+ displayOptions: {
164
+ show: {
165
+ operation: ['sendButtons'],
166
+ },
167
+ },
168
+ description: 'Array de botões em formato JSON',
169
+ },
170
+ // Campos para Enviar Lista
171
+ {
172
+ displayName: 'Título',
173
+ name: 'listTitle',
174
+ type: 'string',
175
+ default: 'Menu de Opções',
176
+ displayOptions: {
177
+ show: {
178
+ operation: ['sendList'],
179
+ },
180
+ },
181
+ description: 'Título da lista',
182
+ },
183
+ {
184
+ displayName: 'Texto',
185
+ name: 'listText',
186
+ type: 'string',
187
+ default: 'Escolha uma categoria:',
188
+ displayOptions: {
189
+ show: {
190
+ operation: ['sendList'],
191
+ },
192
+ },
193
+ description: 'Texto da lista',
194
+ },
195
+ {
196
+ displayName: 'Texto do Botão',
197
+ name: 'listButtonText',
198
+ type: 'string',
199
+ default: 'Ver Opções',
200
+ displayOptions: {
201
+ show: {
202
+ operation: ['sendList'],
203
+ },
204
+ },
205
+ description: 'Texto do botão para abrir a lista',
206
+ },
207
+ {
208
+ displayName: 'Rodapé',
209
+ name: 'listFooter',
210
+ type: 'string',
211
+ default: 'Powered by DigitalSac',
212
+ displayOptions: {
213
+ show: {
214
+ operation: ['sendList'],
215
+ },
216
+ },
217
+ description: 'Rodapé da lista',
218
+ },
219
+ {
220
+ displayName: 'Número de Telefone',
221
+ name: 'listPhoneNumber',
222
+ type: 'string',
223
+ default: '5511999999999',
224
+ displayOptions: {
225
+ show: {
226
+ operation: ['sendList'],
227
+ },
228
+ },
229
+ description: 'Número de telefone no formato DDI+DDD+Número',
230
+ },
231
+ {
232
+ displayName: 'Seções (JSON)',
233
+ name: 'sectionsData',
234
+ type: 'json',
235
+ default: '[\n {\n "title": "Produtos",\n "lines": [\n {\n "title": "Produto A",\n "description": "Descrição do produto A",\n "rowId": "prod_a"\n }\n ]\n }\n]',
236
+ displayOptions: {
237
+ show: {
238
+ operation: ['sendList'],
239
+ },
240
+ },
241
+ description: 'Array de seções da lista em formato JSON',
242
+ },
243
+ // Campos para Enviar Mídia com Caption
244
+ {
245
+ displayName: 'Caption',
246
+ name: 'mediaCaption',
247
+ type: 'string',
248
+ default: 'Arquivo enviado via API',
249
+ displayOptions: {
250
+ show: {
251
+ operation: ['sendMediaCaption'],
252
+ },
253
+ },
254
+ description: 'Legenda do arquivo',
255
+ },
256
+ {
257
+ displayName: 'Número de Telefone',
258
+ name: 'mediaCaptionPhoneNumber',
259
+ type: 'string',
260
+ default: '5511999999999',
261
+ displayOptions: {
262
+ show: {
263
+ operation: ['sendMediaCaption'],
264
+ },
265
+ },
266
+ description: 'Número de telefone no formato DDI+DDD+Número',
267
+ },
268
+ // Campos para Enviar Base64
269
+ {
270
+ displayName: 'Caption (Opcional)',
271
+ name: 'base64Caption',
272
+ type: 'string',
273
+ default: '',
274
+ displayOptions: {
275
+ show: {
276
+ operation: ['sendBase64'],
277
+ },
278
+ },
279
+ description: 'Legenda opcional do arquivo',
280
+ },
281
+ {
282
+ displayName: 'Número de Telefone',
283
+ name: 'base64PhoneNumber',
284
+ type: 'string',
285
+ default: '5511999999999',
286
+ displayOptions: {
287
+ show: {
288
+ operation: ['sendBase64'],
289
+ },
290
+ },
291
+ description: 'Número de telefone no formato DDI+DDD+Número',
292
+ },
293
+ {
294
+ displayName: 'Arquivo Base64',
295
+ name: 'mediaBase64',
296
+ type: 'string',
297
+ default: '',
298
+ displayOptions: {
299
+ show: {
300
+ operation: ['sendBase64'],
301
+ },
302
+ },
303
+ description: 'Arquivo codificado em base64',
304
+ },
305
+ {
306
+ displayName: 'Tipo MIME',
307
+ name: 'mimeType',
308
+ type: 'string',
309
+ default: 'image/png',
310
+ displayOptions: {
311
+ show: {
312
+ operation: ['sendBase64'],
313
+ },
314
+ },
315
+ description: 'Tipo MIME do arquivo (ex: image/png, application/pdf)',
316
+ },
317
+ {
318
+ displayName: 'Nome do Arquivo',
319
+ name: 'fileName',
320
+ type: 'string',
321
+ default: 'arquivo.png',
322
+ displayOptions: {
323
+ show: {
324
+ operation: ['sendBase64'],
325
+ },
326
+ },
327
+ description: 'Nome do arquivo com extensão',
328
+ },
117
329
  {
118
330
  displayName: 'Nome da Tag',
119
331
  name: 'tagName',
@@ -785,6 +997,177 @@ class Digitalsac {
785
997
  const calendarScheduleId = this.getNodeParameter('calendarScheduleId', i);
786
998
  url = `/typebot/calendar-link?scheduleId=${calendarScheduleId}`;
787
999
  break;
1000
+ case 'sendButtons':
1001
+ // Validar se o UUID foi fornecido
1002
+ if (!param || param.trim() === '') {
1003
+ throw new Error('UUID da conexão é obrigatório para enviar botões. Preencha o campo "Parâmetro" com o UUID da conexão.');
1004
+ }
1005
+ url = `/v1/api/external/${param}/send-buttons`;
1006
+ method = 'POST';
1007
+ const buttonTitle = this.getNodeParameter('buttonTitle', i);
1008
+ const buttonBody = this.getNodeParameter('buttonBody', i);
1009
+ const buttonPhoneNumber = this.getNodeParameter('buttonPhoneNumber', i);
1010
+ const buttonExternalKey = this.getNodeParameter('externalKey', i);
1011
+ const buttonsData = this.getNodeParameter('buttonsData', i);
1012
+ let parsedButtons;
1013
+ try {
1014
+ parsedButtons = JSON.parse(buttonsData);
1015
+ }
1016
+ catch (error) {
1017
+ throw new Error('Erro ao fazer parse do JSON dos botões. Verifique a sintaxe.');
1018
+ }
1019
+ body = {
1020
+ title: buttonTitle,
1021
+ body: buttonBody,
1022
+ number: buttonPhoneNumber,
1023
+ extraButtons: parsedButtons,
1024
+ externalKey: buttonExternalKey
1025
+ };
1026
+ headers['Content-Type'] = 'application/json';
1027
+ options = {
1028
+ method,
1029
+ headers,
1030
+ body,
1031
+ uri: `${baseUrl}${url}`,
1032
+ json: true,
1033
+ };
1034
+ break;
1035
+ case 'sendList':
1036
+ // Validar se o UUID foi fornecido
1037
+ if (!param || param.trim() === '') {
1038
+ throw new Error('UUID da conexão é obrigatório para enviar lista. Preencha o campo "Parâmetro" com o UUID da conexão.');
1039
+ }
1040
+ url = `/v1/api/external/${param}/send-list`;
1041
+ method = 'POST';
1042
+ const listTitle = this.getNodeParameter('listTitle', i);
1043
+ const listText = this.getNodeParameter('listText', i);
1044
+ const listButtonText = this.getNodeParameter('listButtonText', i);
1045
+ const listFooter = this.getNodeParameter('listFooter', i);
1046
+ const listPhoneNumber = this.getNodeParameter('listPhoneNumber', i);
1047
+ const listExternalKey = this.getNodeParameter('externalKey', i);
1048
+ const sectionsData = this.getNodeParameter('sectionsData', i);
1049
+ let parsedSections;
1050
+ try {
1051
+ parsedSections = JSON.parse(sectionsData);
1052
+ }
1053
+ catch (error) {
1054
+ throw new Error('Erro ao fazer parse do JSON das seções. Verifique a sintaxe.');
1055
+ }
1056
+ body = {
1057
+ title: listTitle,
1058
+ text: listText,
1059
+ buttonText: listButtonText,
1060
+ footer: listFooter,
1061
+ number: listPhoneNumber,
1062
+ sections: parsedSections,
1063
+ externalKey: listExternalKey
1064
+ };
1065
+ headers['Content-Type'] = 'application/json';
1066
+ options = {
1067
+ method,
1068
+ headers,
1069
+ body,
1070
+ uri: `${baseUrl}${url}`,
1071
+ json: true,
1072
+ };
1073
+ break;
1074
+ case 'sendMediaCaption':
1075
+ // Validar se o UUID foi fornecido
1076
+ if (!param || param.trim() === '') {
1077
+ throw new Error('UUID da conexão é obrigatório para enviar mídia. Preencha o campo "Parâmetro" com o UUID da conexão.');
1078
+ }
1079
+ url = `/v1/api/external/${param}/send-media-caption`;
1080
+ method = 'POST';
1081
+ const mediaCaption = this.getNodeParameter('mediaCaption', i);
1082
+ const mediaCaptionPhoneNumber = this.getNodeParameter('mediaCaptionPhoneNumber', i);
1083
+ const mediaCaptionExternalKey = this.getNodeParameter('externalKey', i);
1084
+ // Verificar se há dados binários
1085
+ let hasBinaryDataCaption = false;
1086
+ let binaryDataCaption;
1087
+ let binaryFileNameCaption;
1088
+ let binaryContentTypeCaption;
1089
+ if (items[i].binary) {
1090
+ const binary = items[i].binary;
1091
+ const binaryKeys = Object.keys(binary);
1092
+ if (binaryKeys.length > 0) {
1093
+ const binaryPropertyName = binaryKeys[0];
1094
+ hasBinaryDataCaption = true;
1095
+ const binaryProperty = binary[binaryPropertyName];
1096
+ binaryDataCaption = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
1097
+ binaryFileNameCaption = binaryProperty.fileName || 'file';
1098
+ binaryContentTypeCaption = binaryProperty.mimeType;
1099
+ }
1100
+ }
1101
+ if (!hasBinaryDataCaption || !binaryDataCaption) {
1102
+ throw new Error('Arquivo binário é obrigatório para enviar mídia com caption. Conecte um nó com arquivo binário antes deste.');
1103
+ }
1104
+ // Enviar como FormData
1105
+ const formDataCaption = {
1106
+ caption: mediaCaption,
1107
+ number: mediaCaptionPhoneNumber,
1108
+ externalKey: mediaCaptionExternalKey,
1109
+ media: {
1110
+ value: binaryDataCaption,
1111
+ options: {
1112
+ filename: binaryFileNameCaption,
1113
+ contentType: binaryContentTypeCaption,
1114
+ },
1115
+ },
1116
+ };
1117
+ options = {
1118
+ method,
1119
+ headers: {
1120
+ Authorization: `Bearer ${token}`,
1121
+ Accept: 'application/json',
1122
+ },
1123
+ formData: formDataCaption,
1124
+ uri: `${baseUrl}${url}`,
1125
+ json: true,
1126
+ };
1127
+ break;
1128
+ case 'sendBase64':
1129
+ // Validar se o UUID foi fornecido
1130
+ if (!param || param.trim() === '') {
1131
+ throw new Error('UUID da conexão é obrigatório para enviar base64. Preencha o campo "Parâmetro" com o UUID da conexão.');
1132
+ }
1133
+ url = `/v1/api/external/${param}/send-base64-media`;
1134
+ method = 'POST';
1135
+ const base64Caption = this.getNodeParameter('base64Caption', i);
1136
+ const base64PhoneNumber = this.getNodeParameter('base64PhoneNumber', i);
1137
+ const base64ExternalKey = this.getNodeParameter('externalKey', i);
1138
+ const mediaBase64 = this.getNodeParameter('mediaBase64', i);
1139
+ const mimeType = this.getNodeParameter('mimeType', i);
1140
+ const fileName = this.getNodeParameter('fileName', i);
1141
+ // Validar campos obrigatórios
1142
+ if (!mediaBase64 || mediaBase64.trim() === '') {
1143
+ throw new Error('Arquivo Base64 é obrigatório.');
1144
+ }
1145
+ if (!mimeType || mimeType.trim() === '') {
1146
+ throw new Error('Tipo MIME é obrigatório.');
1147
+ }
1148
+ if (!fileName || fileName.trim() === '') {
1149
+ throw new Error('Nome do arquivo é obrigatório.');
1150
+ }
1151
+ const base64Body = {
1152
+ number: base64PhoneNumber,
1153
+ mediaBase64: mediaBase64,
1154
+ mimeType: mimeType,
1155
+ fileName: fileName,
1156
+ externalKey: base64ExternalKey
1157
+ };
1158
+ // Adicionar caption apenas se fornecido
1159
+ if (base64Caption && base64Caption.trim() !== '') {
1160
+ base64Body.caption = base64Caption;
1161
+ }
1162
+ headers['Content-Type'] = 'application/json';
1163
+ options = {
1164
+ method,
1165
+ headers,
1166
+ body: base64Body,
1167
+ uri: `${baseUrl}${url}`,
1168
+ json: true,
1169
+ };
1170
+ break;
788
1171
  }
789
1172
  // Se as opções não foram definidas no switch, defina-as aqui para operações GET
790
1173
  if (!options.method) {
@@ -43,6 +43,10 @@ export class Digitalsac implements INodeType {
43
43
  { name: 'Transferir para Atendente', value: 'transferAgent' },
44
44
  { name: 'Fechar Ticket', value: 'closeTicket' },
45
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' },
46
50
  { name: 'Listar Tags', value: 'listTags' },
47
51
  { name: 'Vincular Tag', value: 'linkTag' },
48
52
  { name: 'Criar Tag', value: 'createTag' },
@@ -68,10 +72,10 @@ export class Digitalsac implements INodeType {
68
72
  default: '',
69
73
  displayOptions: {
70
74
  show: {
71
- operation: ['validateWhatsapp', 'validateCpf', 'sendMessage'],
75
+ operation: ['validateWhatsapp', 'validateCpf', 'sendMessage', 'sendButtons', 'sendList', 'sendMediaCaption', 'sendBase64'],
72
76
  },
73
77
  },
74
- description: 'Número, CPF ou UUID da mensagem (conforme operação)',
78
+ description: 'Número, CPF ou UUID da conexão (conforme operação)',
75
79
  },
76
80
  {
77
81
  displayName: 'User ID',
@@ -116,11 +120,219 @@ export class Digitalsac implements INodeType {
116
120
  default: 'Digitalsac123',
117
121
  displayOptions: {
118
122
  show: {
119
- operation: ['sendMessage'],
123
+ operation: ['sendMessage', 'sendButtons', 'sendList', 'sendMediaCaption', 'sendBase64'],
120
124
  },
121
125
  },
122
126
  description: 'Identificador único opcional para a mensagem',
123
127
  },
128
+ // Campos para Enviar Botões
129
+ {
130
+ displayName: 'Título',
131
+ name: 'buttonTitle',
132
+ type: 'string',
133
+ default: 'Escolha uma opção',
134
+ displayOptions: {
135
+ show: {
136
+ operation: ['sendButtons'],
137
+ },
138
+ },
139
+ description: 'Título do conjunto de botões',
140
+ },
141
+ {
142
+ displayName: 'Corpo da Mensagem',
143
+ name: 'buttonBody',
144
+ type: 'string',
145
+ default: 'Clique em uma das opções abaixo:',
146
+ displayOptions: {
147
+ show: {
148
+ operation: ['sendButtons'],
149
+ },
150
+ },
151
+ description: 'Corpo da mensagem com botões',
152
+ },
153
+ {
154
+ displayName: 'Número de Telefone',
155
+ name: 'buttonPhoneNumber',
156
+ type: 'string',
157
+ default: '5511999999999',
158
+ displayOptions: {
159
+ show: {
160
+ operation: ['sendButtons'],
161
+ },
162
+ },
163
+ description: 'Número de telefone no formato DDI+DDD+Número',
164
+ },
165
+ {
166
+ displayName: 'Botões (JSON)',
167
+ name: 'buttonsData',
168
+ type: 'json',
169
+ 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]',
170
+ displayOptions: {
171
+ show: {
172
+ operation: ['sendButtons'],
173
+ },
174
+ },
175
+ description: 'Array de botões em formato JSON',
176
+ },
177
+ // Campos para Enviar Lista
178
+ {
179
+ displayName: 'Título',
180
+ name: 'listTitle',
181
+ type: 'string',
182
+ default: 'Menu de Opções',
183
+ displayOptions: {
184
+ show: {
185
+ operation: ['sendList'],
186
+ },
187
+ },
188
+ description: 'Título da lista',
189
+ },
190
+ {
191
+ displayName: 'Texto',
192
+ name: 'listText',
193
+ type: 'string',
194
+ default: 'Escolha uma categoria:',
195
+ displayOptions: {
196
+ show: {
197
+ operation: ['sendList'],
198
+ },
199
+ },
200
+ description: 'Texto da lista',
201
+ },
202
+ {
203
+ displayName: 'Texto do Botão',
204
+ name: 'listButtonText',
205
+ type: 'string',
206
+ default: 'Ver Opções',
207
+ displayOptions: {
208
+ show: {
209
+ operation: ['sendList'],
210
+ },
211
+ },
212
+ description: 'Texto do botão para abrir a lista',
213
+ },
214
+ {
215
+ displayName: 'Rodapé',
216
+ name: 'listFooter',
217
+ type: 'string',
218
+ default: 'Powered by DigitalSac',
219
+ displayOptions: {
220
+ show: {
221
+ operation: ['sendList'],
222
+ },
223
+ },
224
+ description: 'Rodapé da lista',
225
+ },
226
+ {
227
+ displayName: 'Número de Telefone',
228
+ name: 'listPhoneNumber',
229
+ type: 'string',
230
+ default: '5511999999999',
231
+ displayOptions: {
232
+ show: {
233
+ operation: ['sendList'],
234
+ },
235
+ },
236
+ description: 'Número de telefone no formato DDI+DDD+Número',
237
+ },
238
+ {
239
+ displayName: 'Seções (JSON)',
240
+ name: 'sectionsData',
241
+ type: 'json',
242
+ default: '[\n {\n "title": "Produtos",\n "lines": [\n {\n "title": "Produto A",\n "description": "Descrição do produto A",\n "rowId": "prod_a"\n }\n ]\n }\n]',
243
+ displayOptions: {
244
+ show: {
245
+ operation: ['sendList'],
246
+ },
247
+ },
248
+ description: 'Array de seções da lista em formato JSON',
249
+ },
250
+ // Campos para Enviar Mídia com Caption
251
+ {
252
+ displayName: 'Caption',
253
+ name: 'mediaCaption',
254
+ type: 'string',
255
+ default: 'Arquivo enviado via API',
256
+ displayOptions: {
257
+ show: {
258
+ operation: ['sendMediaCaption'],
259
+ },
260
+ },
261
+ description: 'Legenda do arquivo',
262
+ },
263
+ {
264
+ displayName: 'Número de Telefone',
265
+ name: 'mediaCaptionPhoneNumber',
266
+ type: 'string',
267
+ default: '5511999999999',
268
+ displayOptions: {
269
+ show: {
270
+ operation: ['sendMediaCaption'],
271
+ },
272
+ },
273
+ description: 'Número de telefone no formato DDI+DDD+Número',
274
+ },
275
+ // Campos para Enviar Base64
276
+ {
277
+ displayName: 'Caption (Opcional)',
278
+ name: 'base64Caption',
279
+ type: 'string',
280
+ default: '',
281
+ displayOptions: {
282
+ show: {
283
+ operation: ['sendBase64'],
284
+ },
285
+ },
286
+ description: 'Legenda opcional do arquivo',
287
+ },
288
+ {
289
+ displayName: 'Número de Telefone',
290
+ name: 'base64PhoneNumber',
291
+ type: 'string',
292
+ default: '5511999999999',
293
+ displayOptions: {
294
+ show: {
295
+ operation: ['sendBase64'],
296
+ },
297
+ },
298
+ description: 'Número de telefone no formato DDI+DDD+Número',
299
+ },
300
+ {
301
+ displayName: 'Arquivo Base64',
302
+ name: 'mediaBase64',
303
+ type: 'string',
304
+ default: '',
305
+ displayOptions: {
306
+ show: {
307
+ operation: ['sendBase64'],
308
+ },
309
+ },
310
+ description: 'Arquivo codificado em base64',
311
+ },
312
+ {
313
+ displayName: 'Tipo MIME',
314
+ name: 'mimeType',
315
+ type: 'string',
316
+ default: 'image/png',
317
+ displayOptions: {
318
+ show: {
319
+ operation: ['sendBase64'],
320
+ },
321
+ },
322
+ description: 'Tipo MIME do arquivo (ex: image/png, application/pdf)',
323
+ },
324
+ {
325
+ displayName: 'Nome do Arquivo',
326
+ name: 'fileName',
327
+ type: 'string',
328
+ default: 'arquivo.png',
329
+ displayOptions: {
330
+ show: {
331
+ operation: ['sendBase64'],
332
+ },
333
+ },
334
+ description: 'Nome do arquivo com extensão',
335
+ },
124
336
  {
125
337
  displayName: 'Nome da Tag',
126
338
  name: 'tagName',
@@ -817,6 +1029,199 @@ export class Digitalsac implements INodeType {
817
1029
 
818
1030
  url = `/typebot/calendar-link?scheduleId=${calendarScheduleId}`;
819
1031
  break;
1032
+ case 'sendButtons':
1033
+ // Validar se o UUID foi fornecido
1034
+ if (!param || param.trim() === '') {
1035
+ throw new Error('UUID da conexão é obrigatório para enviar botões. Preencha o campo "Parâmetro" com o UUID da conexão.');
1036
+ }
1037
+
1038
+ url = `/v1/api/external/${param}/send-buttons`;
1039
+ method = 'POST';
1040
+
1041
+ const buttonTitle = this.getNodeParameter('buttonTitle', i) as string;
1042
+ const buttonBody = this.getNodeParameter('buttonBody', i) as string;
1043
+ const buttonPhoneNumber = this.getNodeParameter('buttonPhoneNumber', i) as string;
1044
+ const buttonExternalKey = this.getNodeParameter('externalKey', i) as string;
1045
+ const buttonsData = this.getNodeParameter('buttonsData', i) as string;
1046
+
1047
+ let parsedButtons;
1048
+ try {
1049
+ parsedButtons = JSON.parse(buttonsData);
1050
+ } catch (error) {
1051
+ throw new Error('Erro ao fazer parse do JSON dos botões. Verifique a sintaxe.');
1052
+ }
1053
+
1054
+ body = {
1055
+ title: buttonTitle,
1056
+ body: buttonBody,
1057
+ number: buttonPhoneNumber,
1058
+ extraButtons: parsedButtons,
1059
+ externalKey: buttonExternalKey
1060
+ };
1061
+
1062
+ headers['Content-Type'] = 'application/json';
1063
+ options = {
1064
+ method,
1065
+ headers,
1066
+ body,
1067
+ uri: `${baseUrl}${url}`,
1068
+ json: true,
1069
+ };
1070
+ break;
1071
+ case 'sendList':
1072
+ // Validar se o UUID foi fornecido
1073
+ if (!param || param.trim() === '') {
1074
+ throw new Error('UUID da conexão é obrigatório para enviar lista. Preencha o campo "Parâmetro" com o UUID da conexão.');
1075
+ }
1076
+
1077
+ url = `/v1/api/external/${param}/send-list`;
1078
+ method = 'POST';
1079
+
1080
+ const listTitle = this.getNodeParameter('listTitle', i) as string;
1081
+ const listText = this.getNodeParameter('listText', i) as string;
1082
+ const listButtonText = this.getNodeParameter('listButtonText', i) as string;
1083
+ const listFooter = this.getNodeParameter('listFooter', i) as string;
1084
+ const listPhoneNumber = this.getNodeParameter('listPhoneNumber', i) as string;
1085
+ const listExternalKey = this.getNodeParameter('externalKey', i) as string;
1086
+ const sectionsData = this.getNodeParameter('sectionsData', i) as string;
1087
+
1088
+ let parsedSections;
1089
+ try {
1090
+ parsedSections = JSON.parse(sectionsData);
1091
+ } catch (error) {
1092
+ throw new Error('Erro ao fazer parse do JSON das seções. Verifique a sintaxe.');
1093
+ }
1094
+
1095
+ body = {
1096
+ title: listTitle,
1097
+ text: listText,
1098
+ buttonText: listButtonText,
1099
+ footer: listFooter,
1100
+ number: listPhoneNumber,
1101
+ sections: parsedSections,
1102
+ externalKey: listExternalKey
1103
+ };
1104
+
1105
+ headers['Content-Type'] = 'application/json';
1106
+ options = {
1107
+ method,
1108
+ headers,
1109
+ body,
1110
+ uri: `${baseUrl}${url}`,
1111
+ json: true,
1112
+ };
1113
+ break;
1114
+ case 'sendMediaCaption':
1115
+ // Validar se o UUID foi fornecido
1116
+ if (!param || param.trim() === '') {
1117
+ throw new Error('UUID da conexão é obrigatório para enviar mídia. Preencha o campo "Parâmetro" com o UUID da conexão.');
1118
+ }
1119
+
1120
+ url = `/v1/api/external/${param}/send-media-caption`;
1121
+ method = 'POST';
1122
+
1123
+ const mediaCaption = this.getNodeParameter('mediaCaption', i) as string;
1124
+ const mediaCaptionPhoneNumber = this.getNodeParameter('mediaCaptionPhoneNumber', i) as string;
1125
+ const mediaCaptionExternalKey = this.getNodeParameter('externalKey', i) as string;
1126
+
1127
+ // Verificar se há dados binários
1128
+ let hasBinaryDataCaption = false;
1129
+ let binaryDataCaption: Buffer | undefined;
1130
+ let binaryFileNameCaption: string | undefined;
1131
+ let binaryContentTypeCaption: string | undefined;
1132
+
1133
+ if (items[i].binary) {
1134
+ const binary = items[i].binary as IBinaryKeyData;
1135
+ const binaryKeys = Object.keys(binary);
1136
+ if (binaryKeys.length > 0) {
1137
+ const binaryPropertyName = binaryKeys[0];
1138
+ hasBinaryDataCaption = true;
1139
+
1140
+ const binaryProperty = binary[binaryPropertyName];
1141
+ binaryDataCaption = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
1142
+ binaryFileNameCaption = binaryProperty.fileName || 'file';
1143
+ binaryContentTypeCaption = binaryProperty.mimeType;
1144
+ }
1145
+ }
1146
+
1147
+ if (!hasBinaryDataCaption || !binaryDataCaption) {
1148
+ throw new Error('Arquivo binário é obrigatório para enviar mídia com caption. Conecte um nó com arquivo binário antes deste.');
1149
+ }
1150
+
1151
+ // Enviar como FormData
1152
+ const formDataCaption: IDataObject = {
1153
+ caption: mediaCaption,
1154
+ number: mediaCaptionPhoneNumber,
1155
+ externalKey: mediaCaptionExternalKey,
1156
+ media: {
1157
+ value: binaryDataCaption,
1158
+ options: {
1159
+ filename: binaryFileNameCaption,
1160
+ contentType: binaryContentTypeCaption,
1161
+ },
1162
+ },
1163
+ };
1164
+
1165
+ options = {
1166
+ method,
1167
+ headers: {
1168
+ Authorization: `Bearer ${token}`,
1169
+ Accept: 'application/json',
1170
+ },
1171
+ formData: formDataCaption,
1172
+ uri: `${baseUrl}${url}`,
1173
+ json: true,
1174
+ };
1175
+ break;
1176
+ case 'sendBase64':
1177
+ // Validar se o UUID foi fornecido
1178
+ if (!param || param.trim() === '') {
1179
+ throw new Error('UUID da conexão é obrigatório para enviar base64. Preencha o campo "Parâmetro" com o UUID da conexão.');
1180
+ }
1181
+
1182
+ url = `/v1/api/external/${param}/send-base64-media`;
1183
+ method = 'POST';
1184
+
1185
+ const base64Caption = this.getNodeParameter('base64Caption', i) as string;
1186
+ const base64PhoneNumber = this.getNodeParameter('base64PhoneNumber', i) as string;
1187
+ const base64ExternalKey = this.getNodeParameter('externalKey', i) as string;
1188
+ const mediaBase64 = this.getNodeParameter('mediaBase64', i) as string;
1189
+ const mimeType = this.getNodeParameter('mimeType', i) as string;
1190
+ const fileName = this.getNodeParameter('fileName', i) as string;
1191
+
1192
+ // Validar campos obrigatórios
1193
+ if (!mediaBase64 || mediaBase64.trim() === '') {
1194
+ throw new Error('Arquivo Base64 é obrigatório.');
1195
+ }
1196
+ if (!mimeType || mimeType.trim() === '') {
1197
+ throw new Error('Tipo MIME é obrigatório.');
1198
+ }
1199
+ if (!fileName || fileName.trim() === '') {
1200
+ throw new Error('Nome do arquivo é obrigatório.');
1201
+ }
1202
+
1203
+ const base64Body: IDataObject = {
1204
+ number: base64PhoneNumber,
1205
+ mediaBase64: mediaBase64,
1206
+ mimeType: mimeType,
1207
+ fileName: fileName,
1208
+ externalKey: base64ExternalKey
1209
+ };
1210
+
1211
+ // Adicionar caption apenas se fornecido
1212
+ if (base64Caption && base64Caption.trim() !== '') {
1213
+ base64Body.caption = base64Caption;
1214
+ }
1215
+
1216
+ headers['Content-Type'] = 'application/json';
1217
+ options = {
1218
+ method,
1219
+ headers,
1220
+ body: base64Body,
1221
+ uri: `${baseUrl}${url}`,
1222
+ json: true,
1223
+ };
1224
+ break;
820
1225
  }
821
1226
 
822
1227
  // 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.4.4",
3
+ "version": "0.5.1",
4
4
  "description": "Izing Pro Digitalsac",
5
5
  "keywords": [
6
6
  "n8n",