evolution_api 1.0.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +52 -0
- data/LICENSE.txt +21 -0
- data/README.md +483 -0
- data/bin/test_gem.rb +162 -0
- data/lib/evolution_api/chat.rb +82 -0
- data/lib/evolution_api/client.rb +351 -0
- data/lib/evolution_api/contact.rb +74 -0
- data/lib/evolution_api/errors.rb +95 -0
- data/lib/evolution_api/instance.rb +161 -0
- data/lib/evolution_api/message.rb +249 -0
- data/lib/evolution_api/version.rb +5 -0
- data/lib/evolution_api/webhook.rb +44 -0
- data/lib/evolution_api.rb +86 -0
- metadata +226 -0
data/bin/test_gem.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Script para testar a gem Evolution API localmente
|
5
|
+
# Uso: ruby bin/test_gem.rb
|
6
|
+
|
7
|
+
require 'bundler/setup'
|
8
|
+
require_relative '../lib/evolution_api'
|
9
|
+
|
10
|
+
puts "🧪 Testando Evolution API Ruby Client"
|
11
|
+
puts "=" * 50
|
12
|
+
|
13
|
+
# Configuração para teste
|
14
|
+
EvolutionApi.configure do |config|
|
15
|
+
config.base_url = ENV['EVOLUTION_API_URL'] || 'http://localhost:8080'
|
16
|
+
config.api_key = ENV['EVOLUTION_API_KEY']
|
17
|
+
config.timeout = 10
|
18
|
+
config.retry_attempts = 1
|
19
|
+
config.retry_delay = 0.1
|
20
|
+
end
|
21
|
+
|
22
|
+
client = EvolutionApi.client
|
23
|
+
|
24
|
+
# Teste 1: Verificar se a API está acessível
|
25
|
+
puts "\n1️⃣ Testando conectividade com a API..."
|
26
|
+
begin
|
27
|
+
instances = client.list_instances
|
28
|
+
puts "✅ API acessível! Instâncias encontradas: #{instances.length}"
|
29
|
+
rescue EvolutionApi::ConnectionError => e
|
30
|
+
puts "❌ Erro de conexão: #{e.message}"
|
31
|
+
puts "💡 Verifique se a Evolution API está rodando em #{EvolutionApi.config.base_url}"
|
32
|
+
exit 1
|
33
|
+
rescue EvolutionApi::AuthenticationError => e
|
34
|
+
puts "❌ Erro de autenticação: #{e.message}"
|
35
|
+
puts "💡 Verifique sua API key"
|
36
|
+
exit 1
|
37
|
+
rescue StandardError => e
|
38
|
+
puts "❌ Erro inesperado: #{e.message}"
|
39
|
+
exit 1
|
40
|
+
end
|
41
|
+
|
42
|
+
# Teste 2: Criar uma instância de teste
|
43
|
+
puts "\n2️⃣ Testando criação de instância..."
|
44
|
+
test_instance_name = "test_ruby_#{Time.now.to_i}"
|
45
|
+
|
46
|
+
begin
|
47
|
+
response = client.create_instance(test_instance_name, {
|
48
|
+
qrcode: true,
|
49
|
+
webhook: 'https://example.com/webhook'
|
50
|
+
})
|
51
|
+
puts "✅ Instância criada: #{test_instance_name}"
|
52
|
+
rescue StandardError => e
|
53
|
+
puts "❌ Erro ao criar instância: #{e.message}"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Teste 3: Obter QR Code
|
57
|
+
puts "\n3️⃣ Testando obtenção de QR Code..."
|
58
|
+
begin
|
59
|
+
qr_response = client.get_qr_code(test_instance_name)
|
60
|
+
if qr_response['qrcode']
|
61
|
+
puts "✅ QR Code obtido com sucesso!"
|
62
|
+
puts "📱 QR Code: #{qr_response['qrcode'][0..50]}..."
|
63
|
+
else
|
64
|
+
puts "⚠️ QR Code não disponível (instância pode estar conectada)"
|
65
|
+
end
|
66
|
+
rescue StandardError => e
|
67
|
+
puts "❌ Erro ao obter QR Code: #{e.message}"
|
68
|
+
end
|
69
|
+
|
70
|
+
# Teste 4: Verificar status da instância
|
71
|
+
puts "\n4️⃣ Testando verificação de status..."
|
72
|
+
begin
|
73
|
+
instance_info = client.get_instance(test_instance_name)
|
74
|
+
puts "✅ Status da instância: #{instance_info['status']}"
|
75
|
+
puts "📊 Conectada: #{instance_info['status'] == 'open'}"
|
76
|
+
rescue StandardError => e
|
77
|
+
puts "❌ Erro ao verificar status: #{e.message}"
|
78
|
+
end
|
79
|
+
|
80
|
+
# Teste 5: Testar classes auxiliares
|
81
|
+
puts "\n5️⃣ Testando classes auxiliares..."
|
82
|
+
|
83
|
+
# Teste da classe Instance
|
84
|
+
instance = EvolutionApi::Instance.new(test_instance_name, client)
|
85
|
+
puts "✅ Classe Instance criada"
|
86
|
+
|
87
|
+
# Teste da classe Message
|
88
|
+
sample_message_data = {
|
89
|
+
"id" => "test_id",
|
90
|
+
"key" => {
|
91
|
+
"remoteJid" => "5511999999999@s.whatsapp.net",
|
92
|
+
"fromMe" => false,
|
93
|
+
"id" => "test_message_id"
|
94
|
+
},
|
95
|
+
"message" => {
|
96
|
+
"conversation" => "Teste de mensagem"
|
97
|
+
},
|
98
|
+
"messageTimestamp" => Time.now.to_i,
|
99
|
+
"status" => "received"
|
100
|
+
}
|
101
|
+
|
102
|
+
message = EvolutionApi::Message.new(sample_message_data, test_instance_name)
|
103
|
+
puts "✅ Classe Message criada"
|
104
|
+
puts " Tipo: #{message.type}"
|
105
|
+
puts " Texto: #{message.text}"
|
106
|
+
puts " De: #{message.from}"
|
107
|
+
|
108
|
+
# Teste da classe Chat
|
109
|
+
sample_chat_data = {
|
110
|
+
"id" => "5511999999999@s.whatsapp.net",
|
111
|
+
"name" => "Teste Chat",
|
112
|
+
"unreadCount" => 0,
|
113
|
+
"isGroup" => false,
|
114
|
+
"isReadOnly" => false,
|
115
|
+
"archived" => false,
|
116
|
+
"pinned" => false
|
117
|
+
}
|
118
|
+
|
119
|
+
chat = EvolutionApi::Chat.new(sample_chat_data, test_instance_name)
|
120
|
+
puts "✅ Classe Chat criada"
|
121
|
+
puts " Nome: #{chat.name}"
|
122
|
+
puts " Grupo: #{chat.group?}"
|
123
|
+
|
124
|
+
# Teste da classe Contact
|
125
|
+
sample_contact_data = {
|
126
|
+
"id" => "5511999999999@s.whatsapp.net",
|
127
|
+
"name" => "Teste Contato",
|
128
|
+
"pushName" => "Teste",
|
129
|
+
"verifiedName" => nil,
|
130
|
+
"isBusiness" => false,
|
131
|
+
"isEnterprise" => false,
|
132
|
+
"isHighLevelVerified" => false
|
133
|
+
}
|
134
|
+
|
135
|
+
contact = EvolutionApi::Contact.new(sample_contact_data, test_instance_name)
|
136
|
+
puts "✅ Classe Contact criada"
|
137
|
+
puts " Nome: #{contact.display_name}"
|
138
|
+
puts " Business: #{contact.business?}"
|
139
|
+
|
140
|
+
# Teste 6: Limpeza
|
141
|
+
puts "\n6️⃣ Limpando instância de teste..."
|
142
|
+
begin
|
143
|
+
client.delete_instance(test_instance_name)
|
144
|
+
puts "✅ Instância removida: #{test_instance_name}"
|
145
|
+
rescue StandardError => e
|
146
|
+
puts "⚠️ Erro ao remover instância: #{e.message}"
|
147
|
+
end
|
148
|
+
|
149
|
+
puts "\n🎉 Testes concluídos com sucesso!"
|
150
|
+
puts "\n📋 Resumo:"
|
151
|
+
puts " ✅ Conectividade com API"
|
152
|
+
puts " ✅ Criação de instância"
|
153
|
+
puts " ✅ Obtenção de QR Code"
|
154
|
+
puts " ✅ Verificação de status"
|
155
|
+
puts " ✅ Classes auxiliares"
|
156
|
+
puts " ✅ Limpeza de recursos"
|
157
|
+
|
158
|
+
puts "\n💡 Próximos passos:"
|
159
|
+
puts " 1. Configure suas credenciais da Evolution API"
|
160
|
+
puts " 2. Execute o exemplo básico: ruby examples/basic_usage.rb"
|
161
|
+
puts " 3. Consulte a documentação: https://doc.evolution-api.com/"
|
162
|
+
puts " 4. Veja o README.md para mais exemplos"
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EvolutionApi
|
4
|
+
# Classe para representar chats do WhatsApp
|
5
|
+
class Chat
|
6
|
+
attr_reader :id, :name, :unread_count, :is_group, :is_read_only, :archived, :pinned, :instance_name
|
7
|
+
|
8
|
+
def initialize(data, instance_name = nil)
|
9
|
+
@id = data['id']
|
10
|
+
@name = data['name']
|
11
|
+
@unread_count = data['unreadCount']
|
12
|
+
@is_group = data['isGroup']
|
13
|
+
@is_read_only = data['isReadOnly']
|
14
|
+
@archived = data['archived']
|
15
|
+
@pinned = data['pinned']
|
16
|
+
@instance_name = instance_name
|
17
|
+
end
|
18
|
+
|
19
|
+
# Verifica se é um grupo
|
20
|
+
def group?
|
21
|
+
is_group == true
|
22
|
+
end
|
23
|
+
|
24
|
+
# Verifica se é um chat privado
|
25
|
+
def private?
|
26
|
+
!group?
|
27
|
+
end
|
28
|
+
|
29
|
+
# Verifica se tem mensagens não lidas
|
30
|
+
def unread?
|
31
|
+
unread_count&.positive?
|
32
|
+
end
|
33
|
+
|
34
|
+
# Obtém o número de mensagens não lidas
|
35
|
+
def unread_count
|
36
|
+
@unread_count || 0
|
37
|
+
end
|
38
|
+
|
39
|
+
# Verifica se está arquivado
|
40
|
+
def archived?
|
41
|
+
archived == true
|
42
|
+
end
|
43
|
+
|
44
|
+
# Verifica se está fixado
|
45
|
+
def pinned?
|
46
|
+
pinned == true
|
47
|
+
end
|
48
|
+
|
49
|
+
# Verifica se é somente leitura
|
50
|
+
def read_only?
|
51
|
+
is_read_only == true
|
52
|
+
end
|
53
|
+
|
54
|
+
# Obtém o número do chat (remove sufixos)
|
55
|
+
def number
|
56
|
+
return nil unless id
|
57
|
+
|
58
|
+
id.split('@').first
|
59
|
+
end
|
60
|
+
|
61
|
+
# Converte para hash
|
62
|
+
def to_h
|
63
|
+
{
|
64
|
+
id: id,
|
65
|
+
name: name,
|
66
|
+
number: number,
|
67
|
+
unread_count: unread_count,
|
68
|
+
is_group: group?,
|
69
|
+
is_private: private?,
|
70
|
+
is_read_only: read_only?,
|
71
|
+
archived: archived?,
|
72
|
+
pinned: pinned?,
|
73
|
+
instance_name: instance_name
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
# Converte para JSON
|
78
|
+
def to_json(*args)
|
79
|
+
to_h.to_json(*args)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,351 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EvolutionApi
|
4
|
+
# Cliente principal para interagir com a Evolution API
|
5
|
+
class Client
|
6
|
+
include HTTParty
|
7
|
+
|
8
|
+
attr_reader :config
|
9
|
+
|
10
|
+
def initialize(config = nil)
|
11
|
+
@config = config || EvolutionApi.config
|
12
|
+
setup_http_client
|
13
|
+
end
|
14
|
+
|
15
|
+
# ==================== INSTÂNCIAS ====================
|
16
|
+
|
17
|
+
# Lista todas as instâncias
|
18
|
+
def list_instances
|
19
|
+
get('/instance/fetchInstances')
|
20
|
+
end
|
21
|
+
|
22
|
+
# Cria uma nova instância
|
23
|
+
def create_instance(instance_name, options = {})
|
24
|
+
body = {
|
25
|
+
instanceName: instance_name,
|
26
|
+
qrcode: options[:qrcode] || true,
|
27
|
+
number: options[:number],
|
28
|
+
token: options[:token],
|
29
|
+
webhook: options[:webhook],
|
30
|
+
webhookByEvents: options[:webhook_by_events] || false,
|
31
|
+
webhookBase64: options[:webhook_base64] || false
|
32
|
+
}.compact
|
33
|
+
|
34
|
+
post('/instance/create', body)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Conecta uma instância
|
38
|
+
def connect_instance(instance_name)
|
39
|
+
post("/instance/connect/#{instance_name}")
|
40
|
+
end
|
41
|
+
|
42
|
+
# Desconecta uma instância
|
43
|
+
def disconnect_instance(instance_name)
|
44
|
+
delete("/instance/logout/#{instance_name}")
|
45
|
+
end
|
46
|
+
|
47
|
+
# Remove uma instância
|
48
|
+
def delete_instance(instance_name)
|
49
|
+
delete("/instance/delete/#{instance_name}")
|
50
|
+
end
|
51
|
+
|
52
|
+
# Obtém informações de uma instância
|
53
|
+
def get_instance(instance_name)
|
54
|
+
get("/instance/fetchInstances/#{instance_name}")
|
55
|
+
end
|
56
|
+
|
57
|
+
# Obtém QR Code de uma instância
|
58
|
+
def get_qr_code(instance_name)
|
59
|
+
get("/instance/connect/#{instance_name}")
|
60
|
+
end
|
61
|
+
|
62
|
+
# ==================== MENSAGENS ====================
|
63
|
+
|
64
|
+
# Envia uma mensagem de texto
|
65
|
+
def send_text_message(instance_name, number, text, options = {})
|
66
|
+
body = {
|
67
|
+
number: number,
|
68
|
+
text: text,
|
69
|
+
options: options
|
70
|
+
}
|
71
|
+
|
72
|
+
post("/message/sendText/#{instance_name}", body)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Envia uma mensagem de imagem
|
76
|
+
def send_image_message(instance_name, number, image_url, caption = nil, options = {})
|
77
|
+
body = {
|
78
|
+
number: number,
|
79
|
+
image: image_url,
|
80
|
+
caption: caption,
|
81
|
+
options: options
|
82
|
+
}.compact
|
83
|
+
|
84
|
+
post("/message/sendImage/#{instance_name}", body)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Envia uma mensagem de áudio
|
88
|
+
def send_audio_message(instance_name, number, audio_url, options = {})
|
89
|
+
body = {
|
90
|
+
number: number,
|
91
|
+
audio: audio_url,
|
92
|
+
options: options
|
93
|
+
}
|
94
|
+
|
95
|
+
post("/message/sendAudio/#{instance_name}", body)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Envia uma mensagem de vídeo
|
99
|
+
def send_video_message(instance_name, number, video_url, caption = nil, options = {})
|
100
|
+
body = {
|
101
|
+
number: number,
|
102
|
+
video: video_url,
|
103
|
+
caption: caption,
|
104
|
+
options: options
|
105
|
+
}.compact
|
106
|
+
|
107
|
+
post("/message/sendVideo/#{instance_name}", body)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Envia um documento
|
111
|
+
def send_document_message(instance_name, number, document_url, caption = nil, options = {})
|
112
|
+
body = {
|
113
|
+
number: number,
|
114
|
+
document: document_url,
|
115
|
+
caption: caption,
|
116
|
+
options: options
|
117
|
+
}.compact
|
118
|
+
|
119
|
+
post("/message/sendDocument/#{instance_name}", body)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Envia uma localização
|
123
|
+
def send_location_message(instance_name, number, latitude, longitude, description = nil)
|
124
|
+
body = {
|
125
|
+
number: number,
|
126
|
+
latitude: latitude,
|
127
|
+
longitude: longitude,
|
128
|
+
description: description
|
129
|
+
}.compact
|
130
|
+
|
131
|
+
post("/message/sendLocation/#{instance_name}", body)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Envia uma mensagem de contato
|
135
|
+
def send_contact_message(instance_name, number, contact_number, contact_name)
|
136
|
+
body = {
|
137
|
+
number: number,
|
138
|
+
contacts: [{
|
139
|
+
number: contact_number,
|
140
|
+
name: contact_name
|
141
|
+
}]
|
142
|
+
}
|
143
|
+
|
144
|
+
post("/message/sendContact/#{instance_name}", body)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Envia uma mensagem de botão
|
148
|
+
def send_button_message(instance_name, number, title, description, buttons)
|
149
|
+
body = {
|
150
|
+
number: number,
|
151
|
+
title: title,
|
152
|
+
description: description,
|
153
|
+
buttons: buttons
|
154
|
+
}
|
155
|
+
|
156
|
+
post("/message/sendButton/#{instance_name}", body)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Envia uma lista de opções
|
160
|
+
def send_list_message(instance_name, number, title, description, sections)
|
161
|
+
body = {
|
162
|
+
number: number,
|
163
|
+
title: title,
|
164
|
+
description: description,
|
165
|
+
sections: sections
|
166
|
+
}
|
167
|
+
|
168
|
+
post("/message/sendList/#{instance_name}", body)
|
169
|
+
end
|
170
|
+
|
171
|
+
# ==================== CHAT ====================
|
172
|
+
|
173
|
+
# Obtém chats de uma instância
|
174
|
+
def get_chats(instance_name)
|
175
|
+
get("/chat/findChats/#{instance_name}")
|
176
|
+
end
|
177
|
+
|
178
|
+
# Obtém mensagens de um chat
|
179
|
+
def get_messages(instance_name, number, options = {})
|
180
|
+
params = {
|
181
|
+
limit: options[:limit] || 50,
|
182
|
+
cursor: options[:cursor]
|
183
|
+
}.compact
|
184
|
+
|
185
|
+
get("/chat/findMessages/#{instance_name}/#{number}", params)
|
186
|
+
end
|
187
|
+
|
188
|
+
# Marca mensagens como lidas
|
189
|
+
def mark_messages_as_read(instance_name, number)
|
190
|
+
post("/chat/markMessageAsRead/#{instance_name}", { number: number })
|
191
|
+
end
|
192
|
+
|
193
|
+
# Arquivar chat
|
194
|
+
def archive_chat(instance_name, number)
|
195
|
+
post("/chat/archiveChat/#{instance_name}", { number: number })
|
196
|
+
end
|
197
|
+
|
198
|
+
# Desarquivar chat
|
199
|
+
def unarchive_chat(instance_name, number)
|
200
|
+
post("/chat/unarchiveChat/#{instance_name}", { number: number })
|
201
|
+
end
|
202
|
+
|
203
|
+
# Deletar chat
|
204
|
+
def delete_chat(instance_name, number)
|
205
|
+
delete("/chat/deleteChat/#{instance_name}/#{number}")
|
206
|
+
end
|
207
|
+
|
208
|
+
# ==================== CONTATOS ====================
|
209
|
+
|
210
|
+
# Obtém contatos de uma instância
|
211
|
+
def get_contacts(instance_name)
|
212
|
+
get("/contact/findContacts/#{instance_name}")
|
213
|
+
end
|
214
|
+
|
215
|
+
# Obtém informações de um contato
|
216
|
+
def get_contact(instance_name, number)
|
217
|
+
get("/contact/findContact/#{instance_name}/#{number}")
|
218
|
+
end
|
219
|
+
|
220
|
+
# Verifica se um número existe no WhatsApp
|
221
|
+
def check_number(instance_name, number)
|
222
|
+
post("/contact/checkNumber/#{instance_name}", { number: number })
|
223
|
+
end
|
224
|
+
|
225
|
+
# Bloqueia um contato
|
226
|
+
def block_contact(instance_name, number)
|
227
|
+
post("/contact/blockContact/#{instance_name}", { number: number })
|
228
|
+
end
|
229
|
+
|
230
|
+
# Desbloqueia um contato
|
231
|
+
def unblock_contact(instance_name, number)
|
232
|
+
post("/contact/unblockContact/#{instance_name}", { number: number })
|
233
|
+
end
|
234
|
+
|
235
|
+
# ==================== WEBHOOK ====================
|
236
|
+
|
237
|
+
# Configura webhook para uma instância
|
238
|
+
def set_webhook(instance_name, webhook_url, events = nil)
|
239
|
+
body = {
|
240
|
+
webhook: webhook_url,
|
241
|
+
webhookByEvents: events ? true : false,
|
242
|
+
webhookBase64: false
|
243
|
+
}
|
244
|
+
|
245
|
+
body[:events] = events if events
|
246
|
+
|
247
|
+
post("/webhook/set/#{instance_name}", body)
|
248
|
+
end
|
249
|
+
|
250
|
+
# Obtém configuração de webhook
|
251
|
+
def get_webhook(instance_name)
|
252
|
+
get("/webhook/find/#{instance_name}")
|
253
|
+
end
|
254
|
+
|
255
|
+
# Remove webhook
|
256
|
+
def delete_webhook(instance_name)
|
257
|
+
delete("/webhook/del/#{instance_name}")
|
258
|
+
end
|
259
|
+
|
260
|
+
# ==================== MÉTODOS HTTP ====================
|
261
|
+
|
262
|
+
private
|
263
|
+
|
264
|
+
def setup_http_client
|
265
|
+
self.class.base_uri config.base_url
|
266
|
+
self.class.headers default_headers
|
267
|
+
end
|
268
|
+
|
269
|
+
def default_headers
|
270
|
+
headers = {
|
271
|
+
'Content-Type' => 'application/json',
|
272
|
+
'Accept' => 'application/json'
|
273
|
+
}
|
274
|
+
|
275
|
+
headers['apikey'] = config.api_key if config.api_key
|
276
|
+
headers
|
277
|
+
end
|
278
|
+
|
279
|
+
def get(path, params = {})
|
280
|
+
make_request(:get, path, params: params)
|
281
|
+
end
|
282
|
+
|
283
|
+
def post(path, body = {})
|
284
|
+
make_request(:post, path, body: body)
|
285
|
+
end
|
286
|
+
|
287
|
+
def put(path, body = {})
|
288
|
+
make_request(:put, path, body: body)
|
289
|
+
end
|
290
|
+
|
291
|
+
def delete(path)
|
292
|
+
make_request(:delete, path)
|
293
|
+
end
|
294
|
+
|
295
|
+
def make_request(method, path, options = {})
|
296
|
+
retries = 0
|
297
|
+
begin
|
298
|
+
response = self.class.public_send(method, path, options)
|
299
|
+
handle_response(response)
|
300
|
+
rescue HTTParty::Error => e
|
301
|
+
retries += 1
|
302
|
+
if retries <= config.retry_attempts
|
303
|
+
sleep(config.retry_delay)
|
304
|
+
retry
|
305
|
+
else
|
306
|
+
raise ConnectionError, "Erro de conexão após #{config.retry_attempts} tentativas: #{e.message}"
|
307
|
+
end
|
308
|
+
rescue Net::ReadTimeout, Net::OpenTimeout
|
309
|
+
raise TimeoutError, "Timeout na requisição para #{path}"
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
def handle_response(response)
|
314
|
+
case response.code
|
315
|
+
when 200, 201
|
316
|
+
parse_response(response)
|
317
|
+
when 401
|
318
|
+
raise AuthenticationError.new('Erro de autenticação', response)
|
319
|
+
when 403
|
320
|
+
raise AuthorizationError.new('Acesso negado', response)
|
321
|
+
when 404
|
322
|
+
raise NotFoundError.new('Recurso não encontrado', response)
|
323
|
+
when 422
|
324
|
+
raise ValidationError.new('Erro de validação', response, parse_errors(response))
|
325
|
+
when 429
|
326
|
+
raise RateLimitError.new('Limite de requisições excedido', response)
|
327
|
+
when 500..599
|
328
|
+
raise ServerError.new('Erro interno do servidor', response)
|
329
|
+
else
|
330
|
+
raise Error.new("Erro inesperado: #{response.code}", response, response.code)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
def parse_response(response)
|
335
|
+
return nil if response.body.nil? || response.body.empty?
|
336
|
+
|
337
|
+
JSON.parse(response.body)
|
338
|
+
rescue JSON::ParserError
|
339
|
+
response.body
|
340
|
+
end
|
341
|
+
|
342
|
+
def parse_errors(response)
|
343
|
+
return {} unless response.body
|
344
|
+
|
345
|
+
parsed = JSON.parse(response.body)
|
346
|
+
parsed['errors'] || parsed
|
347
|
+
rescue JSON::ParserError
|
348
|
+
{ 'body' => response.body }
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EvolutionApi
|
4
|
+
# Classe para representar contatos do WhatsApp
|
5
|
+
class Contact
|
6
|
+
attr_reader :id, :name, :push_name, :verified_name, :is_business, :is_enterprise, :is_high_level_verified,
|
7
|
+
:instance_name
|
8
|
+
|
9
|
+
def initialize(data, instance_name = nil)
|
10
|
+
@id = data['id']
|
11
|
+
@name = data['name']
|
12
|
+
@push_name = data['pushName']
|
13
|
+
@verified_name = data['verifiedName']
|
14
|
+
@is_business = data['isBusiness']
|
15
|
+
@is_enterprise = data['isEnterprise']
|
16
|
+
@is_high_level_verified = data['isHighLevelVerified']
|
17
|
+
@instance_name = instance_name
|
18
|
+
end
|
19
|
+
|
20
|
+
# Verifica se é uma conta business
|
21
|
+
def business?
|
22
|
+
is_business == true
|
23
|
+
end
|
24
|
+
|
25
|
+
# Verifica se é uma conta enterprise
|
26
|
+
def enterprise?
|
27
|
+
is_enterprise == true
|
28
|
+
end
|
29
|
+
|
30
|
+
# Verifica se é uma conta verificada de alto nível
|
31
|
+
def high_level_verified?
|
32
|
+
is_high_level_verified == true
|
33
|
+
end
|
34
|
+
|
35
|
+
# Obtém o número do contato (remove sufixos)
|
36
|
+
def number
|
37
|
+
return nil unless id
|
38
|
+
|
39
|
+
id.split('@').first
|
40
|
+
end
|
41
|
+
|
42
|
+
# Obtém o nome de exibição (prioriza nome verificado, depois push name, depois nome)
|
43
|
+
def display_name
|
44
|
+
verified_name || push_name || name || number
|
45
|
+
end
|
46
|
+
|
47
|
+
# Verifica se tem nome verificado
|
48
|
+
def verified?
|
49
|
+
!verified_name.nil? && !verified_name.empty?
|
50
|
+
end
|
51
|
+
|
52
|
+
# Converte para hash
|
53
|
+
def to_h
|
54
|
+
{
|
55
|
+
id: id,
|
56
|
+
number: number,
|
57
|
+
name: name,
|
58
|
+
push_name: push_name,
|
59
|
+
verified_name: verified_name,
|
60
|
+
display_name: display_name,
|
61
|
+
is_business: business?,
|
62
|
+
is_enterprise: enterprise?,
|
63
|
+
is_high_level_verified: high_level_verified?,
|
64
|
+
verified: verified?,
|
65
|
+
instance_name: instance_name
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
# Converte para JSON
|
70
|
+
def to_json(*args)
|
71
|
+
to_h.to_json(*args)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|