n8n-nodes-digitalsac 0.2.1 → 0.2.5

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
@@ -53,19 +53,53 @@ Configure as credenciais Digitalsac com a URL base e seu Bearer Token:
53
53
 
54
54
  ### Validar Data
55
55
  1. Selecione a operação **Validar Data**
56
- 2. No campo **Dados (JSON)**, insira os dados no formato JSON
56
+ 2. No campo **Dados (JSON)**, insira os dados no formato:
57
+ ```json
58
+ {
59
+ "data": "string com a data a ser validada"
60
+ }
61
+ ```
57
62
 
58
63
  ### Listar Filas/Atendentes
59
64
  1. Selecione a operação **Listar Filas** ou **Listar Atendentes**
60
65
  2. Não é necessário configurar parâmetros adicionais
61
66
 
62
- ### Transferir para Fila/Atendente
63
- 1. Selecione a operação **Transferir para Fila** ou **Transferir para Atendente**
64
- 2. No campo **Dados (JSON)**, insira os dados no formato JSON
67
+ ### Transferir para Fila
68
+ 1. Selecione a operação **Transferir para Fila**
69
+ 2. No campo **Dados (JSON)**, insira os dados no formato:
70
+ ```json
71
+ {
72
+ "ticketId": 0,
73
+ "queueId": 0
74
+ }
75
+ ```
76
+ Onde:
77
+ - `ticketId`: ID do ticket a ser transferido
78
+ - `queueId`: ID da fila de destino
79
+
80
+ ### Transferir para Atendente
81
+ 1. Selecione a operação **Transferir para Atendente**
82
+ 2. No campo **Dados (JSON)**, insira os dados no formato:
83
+ ```json
84
+ {
85
+ "ticketId": 0,
86
+ "userId": 0
87
+ }
88
+ ```
89
+ Onde:
90
+ - `ticketId`: ID do ticket a ser transferido
91
+ - `userId`: ID do atendente de destino
65
92
 
66
93
  ### Fechar Ticket
67
94
  1. Selecione a operação **Fechar Ticket**
68
- 2. No campo **Dados (JSON)**, insira os dados no formato JSON
95
+ 2. No campo **Dados (JSON)**, insira os dados no formato:
96
+ ```json
97
+ {
98
+ "ticketId": 0
99
+ }
100
+ ```
101
+ Onde:
102
+ - `ticketId`: ID do ticket a ser fechado
69
103
 
70
104
  ### Enviar Mensagem de Texto
71
105
  1. Selecione a operação **Enviar Mensagem**
@@ -94,6 +128,38 @@ Configure as credenciais Digitalsac com a URL base e seu Bearer Token:
94
128
  ```
95
129
  Onde `data` é o nome da propriedade binária que contém o arquivo.
96
130
 
131
+ ## Exemplos de Respostas da API
132
+
133
+ ### Transferir para Fila
134
+ **Resposta de sucesso:**
135
+ ```json
136
+ {
137
+ "status": 0
138
+ }
139
+ ```
140
+
141
+ ### Transferir para Atendente
142
+ **Resposta de sucesso:**
143
+ ```json
144
+ {
145
+ "status": 0
146
+ }
147
+ ```
148
+
149
+ ### Fechar Ticket
150
+ **Resposta de sucesso:**
151
+ ```json
152
+ "string"
153
+ ```
154
+
155
+ ### Validar Data
156
+ **Resposta de sucesso:**
157
+ ```json
158
+ {
159
+ "status": 0
160
+ }
161
+ ```
162
+
97
163
  ## Exemplo de Fluxo
98
164
 
99
165
  ### Enviar PDF para um contato
@@ -111,6 +177,28 @@ Onde `data` é o nome da propriedade binária que contém o arquivo.
111
177
  }
112
178
  ```
113
179
 
180
+ ### Transferir ticket para uma fila específica
181
+ 1. Adicione um nó **Digitalsac**
182
+ - Operação: **Transferir para Fila**
183
+ - Dados (JSON):
184
+ ```json
185
+ {
186
+ "ticketId": 123,
187
+ "queueId": 5
188
+ }
189
+ ```
190
+
191
+ ### Transferir ticket para um atendente específico
192
+ 1. Adicione um nó **Digitalsac**
193
+ - Operação: **Transferir para Atendente**
194
+ - Dados (JSON):
195
+ ```json
196
+ {
197
+ "ticketId": 123,
198
+ "userId": 10
199
+ }
200
+ ```
201
+
114
202
  ## Suporte
115
203
 
116
204
  Para suporte, entre em contato com [contato@digitalsac.io](mailto:contato@digitalsac.io).
@@ -49,19 +49,19 @@ class Digitalsac {
49
49
  operation: ['validateWhatsapp', 'validateCpf', 'sendMessage'],
50
50
  },
51
51
  },
52
- description: 'Número, CPF ou UUID da conexão (conforme operação)',
52
+ description: 'Número, CPF ou UUID da mensagem (conforme operação)',
53
53
  },
54
54
  {
55
55
  displayName: 'Dados (JSON)',
56
56
  name: 'bodyData',
57
57
  type: 'json',
58
- default: '{\n "body": "Mensagem a ser enviada",\n "number": "5511999999999",\n "externalKey": "chave_opcional"\n // Para enviar arquivo, adicione: "binaryPropertyName": "data"\n}',
58
+ default: '{"body": "Mensagem de teste", "number": "5511999999999", "externalKey": "chave123"}',
59
59
  displayOptions: {
60
60
  show: {
61
61
  operation: ['validateDate', 'transferQueue', 'transferAgent', 'closeTicket', 'sendMessage'],
62
62
  },
63
63
  },
64
- description: 'Para enviar mensagem: {"body": "texto", "number": "5511999999999", "externalKey": "opcional"}. Para enviar arquivo, adicione "binaryPropertyName": "data" (onde "data" é o nome da propriedade binária)',
64
+ description: 'Dados no formato JSON',
65
65
  },
66
66
  ],
67
67
  };
@@ -77,32 +77,37 @@ class Digitalsac {
77
77
  let responseData;
78
78
  const headers = {
79
79
  Authorization: `Bearer ${token}`,
80
- 'Content-Type': 'application/json',
81
80
  Accept: 'application/json',
82
81
  };
83
82
  let url = '';
84
83
  let method = 'GET';
85
84
  let body;
86
- let param = '';
85
+ let param = this.getNodeParameter('param', i, '');
87
86
  let options = {};
88
- let isFormData = false;
89
- let formDataFields = {};
90
- let binaryData;
91
- let binaryFileName;
92
- let binaryContentType;
93
87
  switch (operation) {
94
88
  case 'validateWhatsapp':
95
- param = this.getNodeParameter('param', i);
96
89
  url = `/typebot/whatsappnumber/${param}`;
97
90
  break;
98
91
  case 'validateCpf':
99
- param = this.getNodeParameter('param', i);
100
92
  url = `/typebot/validate/cpf/${param}`;
101
93
  break;
102
94
  case 'validateDate':
103
95
  url = '/typebot/validate/data';
104
96
  method = 'POST';
105
- body = this.getNodeParameter('bodyData', i);
97
+ try {
98
+ body = JSON.parse(this.getNodeParameter('bodyData', i));
99
+ }
100
+ catch (e) {
101
+ throw new Error('Formato de JSON inválido para Dados (JSON)');
102
+ }
103
+ headers['Content-Type'] = 'application/json';
104
+ options = {
105
+ method,
106
+ headers,
107
+ body,
108
+ uri: `${baseUrl}${url}`,
109
+ json: true,
110
+ };
106
111
  break;
107
112
  case 'listQueues':
108
113
  url = '/typebot/listar_filas';
@@ -113,116 +118,100 @@ class Digitalsac {
113
118
  case 'transferQueue':
114
119
  url = '/typebot/transferir_para_fila';
115
120
  method = 'POST';
116
- body = this.getNodeParameter('bodyData', i);
121
+ try {
122
+ body = JSON.parse(this.getNodeParameter('bodyData', i));
123
+ }
124
+ catch (e) {
125
+ throw new Error('Formato de JSON inválido para Dados (JSON)');
126
+ }
127
+ headers['Content-Type'] = 'application/json';
128
+ options = {
129
+ method,
130
+ headers,
131
+ body,
132
+ uri: `${baseUrl}${url}`,
133
+ json: true,
134
+ };
117
135
  break;
118
136
  case 'transferAgent':
119
137
  url = '/typebot/transferir_para_atendente';
120
138
  method = 'POST';
121
- body = this.getNodeParameter('bodyData', i);
139
+ try {
140
+ body = JSON.parse(this.getNodeParameter('bodyData', i));
141
+ }
142
+ catch (e) {
143
+ throw new Error('Formato de JSON inválido para Dados (JSON)');
144
+ }
145
+ headers['Content-Type'] = 'application/json';
146
+ options = {
147
+ method,
148
+ headers,
149
+ body,
150
+ uri: `${baseUrl}${url}`,
151
+ json: true,
152
+ };
122
153
  break;
123
154
  case 'closeTicket':
124
155
  url = '/typebot/fechar_ticket';
125
156
  method = 'POST';
126
- body = this.getNodeParameter('bodyData', i);
157
+ try {
158
+ body = JSON.parse(this.getNodeParameter('bodyData', i));
159
+ }
160
+ catch (e) {
161
+ throw new Error('Formato de JSON inválido para Dados (JSON)');
162
+ }
163
+ headers['Content-Type'] = 'application/json';
164
+ options = {
165
+ method,
166
+ headers,
167
+ body,
168
+ uri: `${baseUrl}${url}`,
169
+ json: true,
170
+ };
127
171
  break;
128
172
  case 'sendMessage':
129
- // Obter UUID da conexão do campo parâmetro
130
- const uuid = this.getNodeParameter('param', i);
131
- url = `/v1/api/external/${uuid}`;
173
+ url = `/v1/api/external/${param}`;
132
174
  method = 'POST';
133
- // Verificar se há dados binários (para envio de arquivo)
134
- const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i, '');
135
- let hasBinaryData = false;
136
- if (binaryPropertyName && items[i].binary) {
137
- const binary = items[i].binary;
138
- hasBinaryData = binary[binaryPropertyName] !== undefined;
139
- }
140
- // Obter dados do corpo da mensagem
141
- const messageData = this.getNodeParameter('bodyData', i, '{}');
142
- let messageBody = {};
143
175
  try {
144
- // Tentar fazer parse do JSON
145
- if (typeof messageData === 'string') {
146
- messageBody = JSON.parse(messageData);
147
- }
148
- else {
149
- messageBody = messageData;
150
- }
176
+ // Obter o JSON como string e converter para objeto
177
+ const bodyDataStr = this.getNodeParameter('bodyData', i);
178
+ // Remover espaços extras e quebras de linha
179
+ const cleanJson = bodyDataStr.replace(/\s+/g, ' ').trim();
180
+ body = JSON.parse(cleanJson);
151
181
  }
152
- catch (error) {
153
- // Se falhar, usar como está
154
- messageBody = { body: messageData };
155
- }
156
- // Se não tiver dados binários, enviar como JSON normal
157
- if (!hasBinaryData) {
158
- body = messageBody;
159
- }
160
- else {
161
- // Configurar para envio de arquivo
162
- isFormData = true;
163
- formDataFields = messageBody;
164
- // Preparar arquivo binário
165
- if (items[i].binary) {
166
- const binaryKeyData = items[i].binary;
167
- if (binaryKeyData[binaryPropertyName]) {
168
- const binaryProperty = binaryKeyData[binaryPropertyName];
169
- binaryData = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
170
- binaryFileName = binaryProperty.fileName || 'file';
171
- binaryContentType = binaryProperty.mimeType;
172
- }
173
- else {
174
- throw new Error(`Nenhum dado binário encontrado na propriedade "${binaryPropertyName}"`);
175
- }
176
- }
177
- headers['Content-Type'] = 'multipart/form-data';
178
- options = {
179
- formData: true,
180
- };
182
+ catch (e) {
183
+ throw new Error('Formato de JSON inválido para Dados (JSON)');
181
184
  }
185
+ headers['Content-Type'] = 'application/json';
186
+ options = {
187
+ method,
188
+ headers,
189
+ body, // Enviar o objeto diretamente, sem JSON.stringify
190
+ uri: `${baseUrl}${url}`,
191
+ json: true,
192
+ };
182
193
  break;
183
194
  }
184
- // Configurar opções da requisição
185
- const requestOptions = {
186
- method,
187
- headers,
188
- uri: `${baseUrl}${url}`,
189
- json: true,
190
- };
191
- // Adicionar body quando necessário
192
- if (body && !isFormData) {
193
- requestOptions.body = body;
194
- }
195
- // Configurar FormData quando necessário
196
- if (isFormData) {
197
- const formData = {};
198
- // Adicionar campos de texto
199
- Object.entries(formDataFields).forEach(([key, value]) => {
200
- formData[key] = value;
201
- });
202
- // Adicionar arquivo se disponível
203
- if (binaryData && binaryFileName) {
204
- formData.media = {
205
- value: binaryData,
206
- options: {
207
- filename: binaryFileName,
208
- contentType: binaryContentType,
209
- },
210
- };
211
- }
212
- requestOptions.formData = formData;
195
+ // Se as opções não foram definidas no switch, defina-as aqui para operações GET
196
+ if (!options.method) {
197
+ options = {
198
+ method,
199
+ headers,
200
+ uri: `${baseUrl}${url}`,
201
+ json: true,
202
+ };
213
203
  }
214
- // Mesclar opções adicionais
215
- Object.assign(requestOptions, options);
216
204
  try {
217
- responseData = await this.helpers.request(requestOptions);
205
+ responseData = await this.helpers.request(options);
218
206
  returnData.push({ json: responseData });
219
207
  }
220
208
  catch (error) {
221
- if (this.continueOnFail()) {
209
+ if (error.response) {
210
+ returnData.push({ json: { error: error.response.body || error.message } });
211
+ }
212
+ else {
222
213
  returnData.push({ json: { error: error.message } });
223
- continue;
224
214
  }
225
- throw error;
226
215
  }
227
216
  }
228
217
  return [returnData];
package/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { Digitalsac } from './nodes/Digitalsac/Digitalsac.node';
2
+ import { DigitalsacApi } from './credentials/DigitalsacApi.credentials';
3
+
4
+ export const nodes = [Digitalsac];
5
+ export const credentials = [DigitalsacApi];
@@ -0,0 +1,226 @@
1
+ import {
2
+ IExecuteFunctions,
3
+ INodeExecutionData,
4
+ INodeType,
5
+ INodeTypeDescription,
6
+ NodeConnectionType,
7
+ } from 'n8n-workflow';
8
+
9
+ export class Digitalsac implements INodeType {
10
+ description: INodeTypeDescription = {
11
+ displayName: 'Digitalsac Izing Pro',
12
+ name: 'digitalsac',
13
+ icon: 'file:digitalsac.svg',
14
+ group: ['transform'],
15
+ version: 1,
16
+ description: 'Interage com a API do Digitalsac',
17
+ defaults: {
18
+ name: 'Digitalsac',
19
+ },
20
+ inputs: <NodeConnectionType[]>['main'],
21
+ outputs: <NodeConnectionType[]>['main'],
22
+
23
+ credentials: [
24
+ {
25
+ name: 'digitalsacApi',
26
+ required: true,
27
+ },
28
+ ],
29
+ properties: [
30
+ {
31
+ displayName: 'Operação',
32
+ name: 'operation',
33
+ type: 'options',
34
+ options: [
35
+ { name: 'Validar WhatsApp', value: 'validateWhatsapp' },
36
+ { name: 'Validar CPF', value: 'validateCpf' },
37
+ { name: 'Validar Data', value: 'validateDate' },
38
+ { name: 'Listar Filas', value: 'listQueues' },
39
+ { name: 'Listar Atendentes', value: 'listAgents' },
40
+ { name: 'Transferir para Fila', value: 'transferQueue' },
41
+ { name: 'Transferir para Atendente', value: 'transferAgent' },
42
+ { name: 'Fechar Ticket', value: 'closeTicket' },
43
+ { name: 'Enviar Mensagem', value: 'sendMessage' },
44
+ ],
45
+ default: 'validateWhatsapp',
46
+ },
47
+ {
48
+ displayName: 'Parâmetro',
49
+ name: 'param',
50
+ type: 'string',
51
+ default: '',
52
+ displayOptions: {
53
+ show: {
54
+ operation: ['validateWhatsapp', 'validateCpf', 'sendMessage'],
55
+ },
56
+ },
57
+ description: 'Número, CPF ou UUID da mensagem (conforme operação)',
58
+ },
59
+ {
60
+ displayName: 'Dados (JSON)',
61
+ name: 'bodyData',
62
+ type: 'json',
63
+ default: '{"body": "Mensagem de teste", "number": "5511999999999", "externalKey": "chave123"}',
64
+ displayOptions: {
65
+ show: {
66
+ operation: ['validateDate', 'transferQueue', 'transferAgent', 'closeTicket', 'sendMessage'],
67
+ },
68
+ },
69
+ description: 'Dados no formato JSON',
70
+ },
71
+ ],
72
+ };
73
+
74
+ async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
75
+ const items = this.getInputData();
76
+ const returnData: INodeExecutionData[] = [];
77
+
78
+ const credentials = await this.getCredentials('digitalsacApi');
79
+ const baseUrl = credentials.baseUrl;
80
+ const token = credentials.token;
81
+
82
+ for (let i = 0; i < items.length; i++) {
83
+ const operation = this.getNodeParameter('operation', i) as string;
84
+ let responseData;
85
+
86
+ const headers: Record<string, string> = {
87
+ Authorization: `Bearer ${token}`,
88
+ Accept: 'application/json',
89
+ };
90
+
91
+ let url = '';
92
+ let method: 'GET' | 'POST' = 'GET';
93
+ let body;
94
+ let param = this.getNodeParameter('param', i, '') as string;
95
+ let options: Record<string, any> = {};
96
+
97
+ switch (operation) {
98
+ case 'validateWhatsapp':
99
+ url = `/typebot/whatsappnumber/${param}`;
100
+ break;
101
+ case 'validateCpf':
102
+ url = `/typebot/validate/cpf/${param}`;
103
+ break;
104
+ case 'validateDate':
105
+ url = '/typebot/validate/data';
106
+ method = 'POST';
107
+ try {
108
+ body = JSON.parse(this.getNodeParameter('bodyData', i) as string);
109
+ } catch (e) {
110
+ throw new Error('Formato de JSON inválido para Dados (JSON)');
111
+ }
112
+ headers['Content-Type'] = 'application/json';
113
+ options = {
114
+ method,
115
+ headers,
116
+ body,
117
+ uri: `${baseUrl}${url}`,
118
+ json: true,
119
+ };
120
+ break;
121
+ case 'listQueues':
122
+ url = '/typebot/listar_filas';
123
+ break;
124
+ case 'listAgents':
125
+ url = '/typebot/listar_atendentes';
126
+ break;
127
+ case 'transferQueue':
128
+ url = '/typebot/transferir_para_fila';
129
+ method = 'POST';
130
+ try {
131
+ body = JSON.parse(this.getNodeParameter('bodyData', i) as string);
132
+ } catch (e) {
133
+ throw new Error('Formato de JSON inválido para Dados (JSON)');
134
+ }
135
+ headers['Content-Type'] = 'application/json';
136
+ options = {
137
+ method,
138
+ headers,
139
+ body,
140
+ uri: `${baseUrl}${url}`,
141
+ json: true,
142
+ };
143
+ break;
144
+ case 'transferAgent':
145
+ url = '/typebot/transferir_para_atendente';
146
+ method = 'POST';
147
+ try {
148
+ body = JSON.parse(this.getNodeParameter('bodyData', i) as string);
149
+ } catch (e) {
150
+ throw new Error('Formato de JSON inválido para Dados (JSON)');
151
+ }
152
+ headers['Content-Type'] = 'application/json';
153
+ options = {
154
+ method,
155
+ headers,
156
+ body,
157
+ uri: `${baseUrl}${url}`,
158
+ json: true,
159
+ };
160
+ break;
161
+ case 'closeTicket':
162
+ url = '/typebot/fechar_ticket';
163
+ method = 'POST';
164
+ try {
165
+ body = JSON.parse(this.getNodeParameter('bodyData', i) as string);
166
+ } catch (e) {
167
+ throw new Error('Formato de JSON inválido para Dados (JSON)');
168
+ }
169
+ headers['Content-Type'] = 'application/json';
170
+ options = {
171
+ method,
172
+ headers,
173
+ body,
174
+ uri: `${baseUrl}${url}`,
175
+ json: true,
176
+ };
177
+ break;
178
+ case 'sendMessage':
179
+ url = `/v1/api/external/${param}`;
180
+ method = 'POST';
181
+ try {
182
+ // Obter o JSON como string e converter para objeto
183
+ const bodyDataStr = this.getNodeParameter('bodyData', i) as string;
184
+ // Remover espaços extras e quebras de linha
185
+ const cleanJson = bodyDataStr.replace(/\s+/g, ' ').trim();
186
+ body = JSON.parse(cleanJson);
187
+ } catch (e) {
188
+ throw new Error('Formato de JSON inválido para Dados (JSON)');
189
+ }
190
+
191
+ headers['Content-Type'] = 'application/json';
192
+ options = {
193
+ method,
194
+ headers,
195
+ body, // Enviar o objeto diretamente, sem JSON.stringify
196
+ uri: `${baseUrl}${url}`,
197
+ json: true,
198
+ };
199
+ break;
200
+ }
201
+
202
+ // Se as opções não foram definidas no switch, defina-as aqui para operações GET
203
+ if (!options.method) {
204
+ options = {
205
+ method,
206
+ headers,
207
+ uri: `${baseUrl}${url}`,
208
+ json: true,
209
+ };
210
+ }
211
+
212
+ try {
213
+ responseData = await this.helpers.request(options);
214
+ returnData.push({ json: responseData });
215
+ } catch (error: any) {
216
+ if (error.response) {
217
+ returnData.push({ json: { error: error.response.body || error.message } });
218
+ } else {
219
+ returnData.push({ json: { error: error.message } });
220
+ }
221
+ }
222
+ }
223
+
224
+ return [returnData];
225
+ }
226
+ }