n8n-nodes-digitalsac 0.4.4 → 0.5.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
@@ -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.0)
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:
@@ -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.0",
4
4
  "description": "Izing Pro Digitalsac",
5
5
  "keywords": [
6
6
  "n8n",