n8n-nodes-digitalsac 0.2.0 → 0.2.4

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
@@ -1,6 +1,6 @@
1
1
  # n8n-nodes-digitalsac
2
2
 
3
- Este pacote adiciona um nó personalizado ao n8n para interagir com a API do Digitalsac.
3
+ Este pacote adiciona um nó personalizado ao n8n para interagir com a API do Digitalsac Izing Pro.
4
4
 
5
5
  ## Funcionalidades
6
6
 
@@ -12,13 +12,193 @@ Este pacote adiciona um nó personalizado ao n8n para interagir com a API do Dig
12
12
  - Transferir para Fila
13
13
  - Transferir para Atendente
14
14
  - Fechar Ticket
15
+ - Enviar Mensagem (texto e arquivos)
15
16
 
16
17
  ## Instalação
17
18
 
19
+ ### Via interface do n8n
20
+ 1. Vá para **Configurações > Community Nodes**
21
+ 2. Digite `n8n-nodes-digitalsac` na caixa de pesquisa
22
+ 3. Clique em **Instalar**
23
+
24
+ ### Via linha de comando
18
25
  ```bash
26
+ npm install -g n8n-nodes-digitalsac
27
+ ```
28
+
29
+ Ou se você estiver usando o n8n com um diretório específico:
30
+ ```bash
31
+ cd ~/.n8n
19
32
  npm install n8n-nodes-digitalsac
20
33
  ```
21
34
 
22
35
  ## Autenticação
23
36
 
24
- Configure as credenciais Digitalsac com a URL base e seu Bearer Token.
37
+ Configure as credenciais Digitalsac com a URL base e seu Bearer Token:
38
+ 1. Vá para **Credenciais > Criar Nova Credencial**
39
+ 2. Selecione **Izing Pro Digitalsac API**
40
+ 3. Preencha:
41
+ - **API Base URL**: URL da sua instalação do Digitalsac (ex: https://seudominio.digitalsac.com.br)
42
+ - **Bearer Token**: Seu token de autenticação
43
+
44
+ ## Como Usar
45
+
46
+ ### Validar WhatsApp
47
+ 1. Selecione a operação **Validar WhatsApp**
48
+ 2. No campo **Parâmetro**, insira o número de telefone a ser validado
49
+
50
+ ### Validar CPF
51
+ 1. Selecione a operação **Validar CPF**
52
+ 2. No campo **Parâmetro**, insira o CPF a ser validado
53
+
54
+ ### Validar Data
55
+ 1. Selecione a operação **Validar Data**
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
+ ```
62
+
63
+ ### Listar Filas/Atendentes
64
+ 1. Selecione a operação **Listar Filas** ou **Listar Atendentes**
65
+ 2. Não é necessário configurar parâmetros adicionais
66
+
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
92
+
93
+ ### Fechar Ticket
94
+ 1. Selecione a operação **Fechar Ticket**
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
103
+
104
+ ### Enviar Mensagem de Texto
105
+ 1. Selecione a operação **Enviar Mensagem**
106
+ 2. No campo **Parâmetro**, insira o UUID da conexão
107
+ 3. No campo **Dados (JSON)**, insira os dados no formato:
108
+ ```json
109
+ {
110
+ "body": "Sua mensagem aqui",
111
+ "number": "5511999999999",
112
+ "externalKey": "chave_opcional"
113
+ }
114
+ ```
115
+
116
+ ### Enviar Arquivo
117
+ 1. Conecte um nó que forneça dados binários (ex: HTTP Request, Read Binary File)
118
+ 2. Selecione a operação **Enviar Mensagem**
119
+ 3. No campo **Parâmetro**, insira o UUID da conexão
120
+ 4. No campo **Dados (JSON)**, insira os dados no formato:
121
+ ```json
122
+ {
123
+ "body": "Mensagem que acompanha o arquivo",
124
+ "number": "5511999999999",
125
+ "externalKey": "chave_opcional",
126
+ "binaryPropertyName": "data"
127
+ }
128
+ ```
129
+ Onde `data` é o nome da propriedade binária que contém o arquivo.
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
+
163
+ ## Exemplo de Fluxo
164
+
165
+ ### Enviar PDF para um contato
166
+ 1. Adicione um nó **Read Binary File**
167
+ - Configure para ler um arquivo PDF
168
+ 2. Conecte ao nó **Digitalsac**
169
+ - Operação: **Enviar Mensagem**
170
+ - Parâmetro: `seu-uuid-de-conexão`
171
+ - Dados (JSON):
172
+ ```json
173
+ {
174
+ "body": "Segue o PDF solicitado",
175
+ "number": "5511999999999",
176
+ "binaryPropertyName": "data"
177
+ }
178
+ ```
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
+
202
+ ## Suporte
203
+
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,32 @@ 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
+ body = JSON.parse(this.getNodeParameter('bodyData', i));
98
+ headers['Content-Type'] = 'application/json';
99
+ options = {
100
+ method,
101
+ headers,
102
+ body: JSON.stringify(body),
103
+ uri: `${baseUrl}${url}`,
104
+ json: true,
105
+ };
106
106
  break;
107
107
  case 'listQueues':
108
108
  url = '/typebot/listar_filas';
@@ -113,116 +113,76 @@ class Digitalsac {
113
113
  case 'transferQueue':
114
114
  url = '/typebot/transferir_para_fila';
115
115
  method = 'POST';
116
- body = this.getNodeParameter('bodyData', i);
116
+ body = JSON.parse(this.getNodeParameter('bodyData', i));
117
+ headers['Content-Type'] = 'application/json';
118
+ options = {
119
+ method,
120
+ headers,
121
+ body: JSON.stringify(body),
122
+ uri: `${baseUrl}${url}`,
123
+ json: true,
124
+ };
117
125
  break;
118
126
  case 'transferAgent':
119
127
  url = '/typebot/transferir_para_atendente';
120
128
  method = 'POST';
121
- body = this.getNodeParameter('bodyData', i);
129
+ body = JSON.parse(this.getNodeParameter('bodyData', i));
130
+ headers['Content-Type'] = 'application/json';
131
+ options = {
132
+ method,
133
+ headers,
134
+ body: JSON.stringify(body),
135
+ uri: `${baseUrl}${url}`,
136
+ json: true,
137
+ };
122
138
  break;
123
139
  case 'closeTicket':
124
140
  url = '/typebot/fechar_ticket';
125
141
  method = 'POST';
126
- body = this.getNodeParameter('bodyData', i);
142
+ body = JSON.parse(this.getNodeParameter('bodyData', i));
143
+ headers['Content-Type'] = 'application/json';
144
+ options = {
145
+ method,
146
+ headers,
147
+ body: JSON.stringify(body),
148
+ uri: `${baseUrl}${url}`,
149
+ json: true,
150
+ };
127
151
  break;
128
152
  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}`;
153
+ url = `/v1/api/external/${param}`;
132
154
  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
- try {
144
- // Tentar fazer parse do JSON
145
- if (typeof messageData === 'string') {
146
- messageBody = JSON.parse(messageData);
147
- }
148
- else {
149
- messageBody = messageData;
150
- }
151
- }
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
- };
181
- }
155
+ body = JSON.parse(this.getNodeParameter('bodyData', i));
156
+ headers['Content-Type'] = 'application/json';
157
+ options = {
158
+ method,
159
+ headers,
160
+ body: JSON.stringify(body),
161
+ uri: `${baseUrl}${url}`,
162
+ json: true,
163
+ };
182
164
  break;
183
165
  }
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;
166
+ // Se as opções não foram definidas no switch, defina-as aqui para operações GET
167
+ if (!options.method) {
168
+ options = {
169
+ method,
170
+ headers,
171
+ uri: `${baseUrl}${url}`,
172
+ json: true,
173
+ };
194
174
  }
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;
213
- }
214
- // Mesclar opções adicionais
215
- Object.assign(requestOptions, options);
216
175
  try {
217
- responseData = await this.helpers.request(requestOptions);
176
+ responseData = await this.helpers.request(options);
218
177
  returnData.push({ json: responseData });
219
178
  }
220
179
  catch (error) {
221
- if (this.continueOnFail()) {
180
+ if (error.response) {
181
+ returnData.push({ json: { error: error.response.body } });
182
+ }
183
+ else {
222
184
  returnData.push({ json: { error: error.message } });
223
- continue;
224
185
  }
225
- throw error;
226
186
  }
227
187
  }
228
188
  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,201 @@
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
+ body = JSON.parse(this.getNodeParameter('bodyData', i) as string);
108
+ headers['Content-Type'] = 'application/json';
109
+ options = {
110
+ method,
111
+ headers,
112
+ body: JSON.stringify(body),
113
+ uri: `${baseUrl}${url}`,
114
+ json: true,
115
+ };
116
+ break;
117
+ case 'listQueues':
118
+ url = '/typebot/listar_filas';
119
+ break;
120
+ case 'listAgents':
121
+ url = '/typebot/listar_atendentes';
122
+ break;
123
+ case 'transferQueue':
124
+ url = '/typebot/transferir_para_fila';
125
+ method = 'POST';
126
+ body = JSON.parse(this.getNodeParameter('bodyData', i) as string);
127
+ headers['Content-Type'] = 'application/json';
128
+ options = {
129
+ method,
130
+ headers,
131
+ body: JSON.stringify(body),
132
+ uri: `${baseUrl}${url}`,
133
+ json: true,
134
+ };
135
+ break;
136
+ case 'transferAgent':
137
+ url = '/typebot/transferir_para_atendente';
138
+ method = 'POST';
139
+ body = JSON.parse(this.getNodeParameter('bodyData', i) as string);
140
+ headers['Content-Type'] = 'application/json';
141
+ options = {
142
+ method,
143
+ headers,
144
+ body: JSON.stringify(body),
145
+ uri: `${baseUrl}${url}`,
146
+ json: true,
147
+ };
148
+ break;
149
+ case 'closeTicket':
150
+ url = '/typebot/fechar_ticket';
151
+ method = 'POST';
152
+ body = JSON.parse(this.getNodeParameter('bodyData', i) as string);
153
+ headers['Content-Type'] = 'application/json';
154
+ options = {
155
+ method,
156
+ headers,
157
+ body: JSON.stringify(body),
158
+ uri: `${baseUrl}${url}`,
159
+ json: true,
160
+ };
161
+ break;
162
+ case 'sendMessage':
163
+ url = `/v1/api/external/${param}`;
164
+ method = 'POST';
165
+ body = JSON.parse(this.getNodeParameter('bodyData', i) as string);
166
+ headers['Content-Type'] = 'application/json';
167
+ options = {
168
+ method,
169
+ headers,
170
+ body: JSON.stringify(body),
171
+ uri: `${baseUrl}${url}`,
172
+ json: true,
173
+ };
174
+ break;
175
+ }
176
+
177
+ // Se as opções não foram definidas no switch, defina-as aqui para operações GET
178
+ if (!options.method) {
179
+ options = {
180
+ method,
181
+ headers,
182
+ uri: `${baseUrl}${url}`,
183
+ json: true,
184
+ };
185
+ }
186
+
187
+ try {
188
+ responseData = await this.helpers.request(options);
189
+ returnData.push({ json: responseData });
190
+ } catch (error: any) {
191
+ if (error.response) {
192
+ returnData.push({ json: { error: error.response.body } });
193
+ } else {
194
+ returnData.push({ json: { error: error.message } });
195
+ }
196
+ }
197
+ }
198
+
199
+ return [returnData];
200
+ }
201
+ }