evolution_api 1.0.0 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f9d422931f741f23c7b75790231e4922e80a4da988ca81efe6ad655cb802f6e8
4
- data.tar.gz: 7afc22ac6c06add8f996dd52e05e6df8ac36369e76a985eb31afc3ada1c32fc6
3
+ metadata.gz: bb44daa0ea45acd5e886118b118b5f59ee0a4d377269e1eeeb44bc817cd2e25c
4
+ data.tar.gz: d7005e170ef810ad634437cda1d8f8302c6b55ad2710fee66e1a2e3877b9f71f
5
5
  SHA512:
6
- metadata.gz: 5cbbedbfbe2392247e5cb0eb1b8009185a88f9623b1c174b0c2f7d2e52106830c2b42b15633551e8203dd5459d4df198d4d77c5221a616ac367c588983c7bf98
7
- data.tar.gz: 06ae0b0533e58315d95b38b25d83335332dd869b37511735e4809f8d4ceb38c57b251d1ea66e66ecd93d9a863c03e79dc8faa50ad9a7b9f6a0d21c914685f875
6
+ metadata.gz: af4025a664be1f58d9c1181dc14aab7c5255acf2e48e805146e99a4feb817a5770e57fe8ed3a2e227f023a308a074f86b2b027a7cd028c28be47913fe86986a4
7
+ data.tar.gz: b3b285afe6028f5321426b9341f544edd1b2d907be05e4f0f6432009935b40e7f24cc21e5c32835582933c04fece155ab966004cc186440ddd3f34137340e587
data/README.md CHANGED
@@ -39,6 +39,427 @@ Ou instale diretamente:
39
39
  gem install evolution_api
40
40
  ```
41
41
 
42
+ ## 🚂 Integração com Rails
43
+
44
+ ### Instalação em Projetos Rails
45
+
46
+ 1. **Adicione a gem ao seu Gemfile:**
47
+
48
+ ```ruby
49
+ # Gemfile
50
+ gem 'evolution_api'
51
+ ```
52
+
53
+ 2. **Execute o bundle install:**
54
+
55
+ ```bash
56
+ bundle install
57
+ ```
58
+
59
+ 3. **Configure a gem no initializer:**
60
+
61
+ ```ruby
62
+ # config/initializers/evolution_api.rb
63
+ EvolutionApi.configure do |config|
64
+ config.base_url = Rails.application.credentials.evolution_api[:base_url] || "http://localhost:8080"
65
+ config.api_key = Rails.application.credentials.evolution_api[:api_key]
66
+ config.timeout = 30
67
+ config.retry_attempts = 3
68
+ config.retry_delay = 1
69
+ end
70
+ ```
71
+
72
+ 4. **Configure as credenciais (Rails 5.2+):**
73
+
74
+ ```bash
75
+ rails credentials:edit
76
+ ```
77
+
78
+ Adicione no arquivo de credenciais:
79
+
80
+ ```yaml
81
+ evolution_api:
82
+ base_url: "https://sua-evolution-api.com"
83
+ api_key: "sua_api_key_aqui"
84
+ ```
85
+
86
+ ### Exemplo de Controller Rails
87
+
88
+ ```ruby
89
+ # app/controllers/whatsapp_controller.rb
90
+ class WhatsAppController < ApplicationController
91
+ before_action :set_client
92
+
93
+ def send_message
94
+ begin
95
+ response = @client.send_text_message(
96
+ params[:instance_name],
97
+ params[:phone_number],
98
+ params[:message]
99
+ )
100
+
101
+ render json: { success: true, data: response }
102
+ rescue EvolutionApi::Error => e
103
+ render json: { success: false, error: e.message }, status: :unprocessable_entity
104
+ end
105
+ end
106
+
107
+ def list_instances
108
+ instances = @client.list_instances
109
+ render json: { instances: instances }
110
+ end
111
+
112
+ def create_instance
113
+ response = @client.create_instance(params[:instance_name], {
114
+ qrcode: true,
115
+ webhook: webhook_url
116
+ })
117
+
118
+ render json: { success: true, data: response }
119
+ end
120
+
121
+ private
122
+
123
+ def set_client
124
+ @client = EvolutionApi.client
125
+ end
126
+
127
+ def webhook_url
128
+ "#{request.base_url}/webhooks/whatsapp"
129
+ end
130
+ end
131
+ ```
132
+
133
+ ### Exemplo de Model Rails
134
+
135
+ ```ruby
136
+ # app/models/whatsapp_message.rb
137
+ class WhatsAppMessage < ApplicationRecord
138
+ validates :instance_name, presence: true
139
+ validates :phone_number, presence: true
140
+ validates :message_type, presence: true, inclusion: { in: %w[text image audio video document] }
141
+ validates :content, presence: true
142
+
143
+ after_create :send_to_whatsapp
144
+
145
+ private
146
+
147
+ def send_to_whatsapp
148
+ client = EvolutionApi.client
149
+
150
+ case message_type
151
+ when 'text'
152
+ client.send_text_message(instance_name, phone_number, content)
153
+ when 'image'
154
+ client.send_image_message(instance_name, phone_number, content, caption)
155
+ when 'audio'
156
+ client.send_audio_message(instance_name, phone_number, content)
157
+ when 'video'
158
+ client.send_video_message(instance_name, phone_number, content, caption)
159
+ when 'document'
160
+ client.send_document_message(instance_name, phone_number, content, caption)
161
+ end
162
+ rescue EvolutionApi::Error => e
163
+ update(status: 'failed', error_message: e.message)
164
+ end
165
+ end
166
+ ```
167
+
168
+ ### Exemplo de Service Object
169
+
170
+ ```ruby
171
+ # app/services/whatsapp_service.rb
172
+ class WhatsAppService
173
+ def initialize(instance_name = nil)
174
+ @client = EvolutionApi.client
175
+ @instance_name = instance_name || Rails.application.credentials.evolution_api[:default_instance]
176
+ end
177
+
178
+ def send_bulk_messages(phone_numbers, message)
179
+ results = []
180
+
181
+ phone_numbers.each do |phone|
182
+ begin
183
+ response = @client.send_text_message(@instance_name, phone, message)
184
+ results << { phone: phone, success: true, response: response }
185
+ rescue EvolutionApi::Error => e
186
+ results << { phone: phone, success: false, error: e.message }
187
+ end
188
+ end
189
+
190
+ results
191
+ end
192
+
193
+ def broadcast_message(message, options = {})
194
+ contacts = @client.get_contacts(@instance_name)
195
+
196
+ contacts.each do |contact|
197
+ next if options[:exclude_numbers]&.include?(contact['id'])
198
+
199
+ @client.send_text_message(@instance_name, contact['id'], message)
200
+ sleep(options[:delay] || 1) # Evita rate limiting
201
+ end
202
+ end
203
+
204
+ def instance_status
205
+ @client.get_instance(@instance_name)
206
+ end
207
+
208
+ def is_connected?
209
+ status = instance_status
210
+ status['status'] == 'open'
211
+ end
212
+ end
213
+ ```
214
+
215
+ ### Exemplo de Job para Processamento Assíncrono
216
+
217
+ ```ruby
218
+ # app/jobs/whatsapp_message_job.rb
219
+ class WhatsAppMessageJob < ApplicationJob
220
+ queue_as :whatsapp
221
+
222
+ def perform(instance_name, phone_number, message, message_type = 'text')
223
+ client = EvolutionApi.client
224
+
225
+ case message_type
226
+ when 'text'
227
+ client.send_text_message(instance_name, phone_number, message)
228
+ when 'image'
229
+ client.send_image_message(instance_name, phone_number, message[:url], message[:caption])
230
+ when 'audio'
231
+ client.send_audio_message(instance_name, phone_number, message[:url])
232
+ when 'video'
233
+ client.send_video_message(instance_name, phone_number, message[:url], message[:caption])
234
+ when 'document'
235
+ client.send_document_message(instance_name, phone_number, message[:url], message[:caption])
236
+ end
237
+ rescue EvolutionApi::Error => e
238
+ Rails.logger.error "WhatsApp message failed: #{e.message}"
239
+ raise e
240
+ end
241
+ end
242
+ ```
243
+
244
+ ### Exemplo de Webhook Controller
245
+
246
+ ```ruby
247
+ # app/controllers/webhooks/whatsapp_controller.rb
248
+ class Webhooks::WhatsappController < ApplicationController
249
+ skip_before_action :verify_authenticity_token
250
+
251
+ def receive
252
+ case params[:event]
253
+ when 'connection.update'
254
+ handle_connection_update
255
+ when 'message.upsert'
256
+ handle_message_upsert
257
+ when 'qr.update'
258
+ handle_qr_update
259
+ end
260
+
261
+ head :ok
262
+ end
263
+
264
+ private
265
+
266
+ def handle_connection_update
267
+ instance_name = params[:instance]
268
+ status = params[:data][:status]
269
+
270
+ Rails.logger.info "WhatsApp instance #{instance_name} status: #{status}"
271
+
272
+ # Atualizar status no banco de dados
273
+ instance = WhatsAppInstance.find_by(name: instance_name)
274
+ instance&.update(status: status)
275
+ end
276
+
277
+ def handle_message_upsert
278
+ message_data = params[:data]
279
+ instance_name = params[:instance]
280
+
281
+ # Processar mensagem recebida
282
+ message = Message.create!(
283
+ instance_name: instance_name,
284
+ phone_number: message_data[:key][:remoteJid],
285
+ message_type: detect_message_type(message_data[:message]),
286
+ content: extract_message_content(message_data[:message]),
287
+ from_me: message_data[:key][:fromMe],
288
+ timestamp: Time.at(message_data[:messageTimestamp])
289
+ )
290
+
291
+ # Processar automaticamente se necessário
292
+ AutoReplyService.new(message).process if should_auto_reply?(message)
293
+ end
294
+
295
+ def handle_qr_update
296
+ instance_name = params[:instance]
297
+ qr_code = params[:data][:qrcode]
298
+
299
+ # Salvar QR code para exibição
300
+ Rails.cache.write("whatsapp_qr_#{instance_name}", qr_code, expires_in: 2.minutes)
301
+ end
302
+
303
+ def detect_message_type(message)
304
+ return 'text' if message[:conversation] || message[:extendedTextMessage]
305
+ return 'image' if message[:imageMessage]
306
+ return 'audio' if message[:audioMessage]
307
+ return 'video' if message[:videoMessage]
308
+ return 'document' if message[:documentMessage]
309
+ return 'location' if message[:locationMessage]
310
+ return 'contact' if message[:contactMessage]
311
+ 'unknown'
312
+ end
313
+
314
+ def extract_message_content(message)
315
+ return message[:conversation] if message[:conversation]
316
+ return message[:extendedTextMessage][:text] if message[:extendedTextMessage]
317
+ return message[:imageMessage][:url] if message[:imageMessage]
318
+ return message[:audioMessage][:url] if message[:audioMessage]
319
+ return message[:videoMessage][:url] if message[:videoMessage]
320
+ return message[:documentMessage][:url] if message[:documentMessage]
321
+ nil
322
+ end
323
+
324
+ def should_auto_reply?(message)
325
+ !message.from_me && message.message_type == 'text'
326
+ end
327
+ end
328
+ ```
329
+
330
+ ### Configuração de Rotas
331
+
332
+ ```ruby
333
+ # config/routes.rb
334
+ Rails.application.routes.draw do
335
+ # Rotas para WhatsApp
336
+ resources :whatsapp, only: [:index] do
337
+ collection do
338
+ post :send_message
339
+ get :list_instances
340
+ post :create_instance
341
+ get :qr_code/:instance_name, action: :qr_code, as: :qr_code
342
+ end
343
+ end
344
+
345
+ # Webhook para receber mensagens
346
+ post 'webhooks/whatsapp', to: 'webhooks/whatsapp#receive'
347
+ end
348
+ ```
349
+
350
+ ### Exemplo de View
351
+
352
+ ```erb
353
+ <!-- app/views/whatsapp/index.html.erb -->
354
+ <div class="whatsapp-dashboard">
355
+ <h1>WhatsApp Dashboard</h1>
356
+
357
+ <div class="instances">
358
+ <h2>Instâncias</h2>
359
+ <div id="instances-list">
360
+ <!-- Será preenchido via JavaScript -->
361
+ </div>
362
+
363
+ <button onclick="createInstance()">Nova Instância</button>
364
+ </div>
365
+
366
+ <div class="qr-code" id="qr-code">
367
+ <!-- QR Code será exibido aqui -->
368
+ </div>
369
+
370
+ <div class="send-message">
371
+ <h2>Enviar Mensagem</h2>
372
+ <form id="message-form">
373
+ <select name="instance_name" required>
374
+ <option value="">Selecione uma instância</option>
375
+ </select>
376
+
377
+ <input type="tel" name="phone_number" placeholder="Número (ex: 5511999999999)" required>
378
+
379
+ <textarea name="message" placeholder="Mensagem" required></textarea>
380
+
381
+ <button type="submit">Enviar</button>
382
+ </form>
383
+ </div>
384
+ </div>
385
+
386
+ <script>
387
+ document.addEventListener('DOMContentLoaded', function() {
388
+ loadInstances();
389
+ setupMessageForm();
390
+ });
391
+
392
+ function loadInstances() {
393
+ fetch('/whatsapp/list_instances')
394
+ .then(response => response.json())
395
+ .then(data => {
396
+ const instancesList = document.getElementById('instances-list');
397
+ const instanceSelect = document.querySelector('select[name="instance_name"]');
398
+
399
+ data.instances.forEach(instance => {
400
+ // Atualizar lista de instâncias
401
+ instancesList.innerHTML += `
402
+ <div class="instance">
403
+ <strong>${instance.instance}</strong>
404
+ <span class="status ${instance.status}">${instance.status}</span>
405
+ </div>
406
+ `;
407
+
408
+ // Atualizar select
409
+ instanceSelect.innerHTML += `
410
+ <option value="${instance.instance}">${instance.instance} (${instance.status})</option>
411
+ `;
412
+ });
413
+ });
414
+ }
415
+
416
+ function setupMessageForm() {
417
+ document.getElementById('message-form').addEventListener('submit', function(e) {
418
+ e.preventDefault();
419
+
420
+ const formData = new FormData(this);
421
+
422
+ fetch('/whatsapp/send_message', {
423
+ method: 'POST',
424
+ body: formData
425
+ })
426
+ .then(response => response.json())
427
+ .then(data => {
428
+ if (data.success) {
429
+ alert('Mensagem enviada com sucesso!');
430
+ this.reset();
431
+ } else {
432
+ alert('Erro ao enviar mensagem: ' + data.error);
433
+ }
434
+ });
435
+ });
436
+ }
437
+
438
+ function createInstance() {
439
+ const instanceName = prompt('Nome da instância:');
440
+ if (!instanceName) return;
441
+
442
+ fetch('/whatsapp/create_instance', {
443
+ method: 'POST',
444
+ headers: {
445
+ 'Content-Type': 'application/json',
446
+ 'X-CSRF-Token': document.querySelector('[name="csrf-token"]').content
447
+ },
448
+ body: JSON.stringify({ instance_name: instanceName })
449
+ })
450
+ .then(response => response.json())
451
+ .then(data => {
452
+ if (data.success) {
453
+ alert('Instância criada! Verifique o QR Code.');
454
+ loadInstances();
455
+ } else {
456
+ alert('Erro ao criar instância: ' + data.error);
457
+ }
458
+ });
459
+ }
460
+ </script>
461
+ ```
462
+
42
463
  ## ⚙️ Configuração
43
464
 
44
465
  ### Configuração Básica
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EvolutionApi
4
- VERSION = '1.0.0'
4
+ VERSION = '1.1.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: evolution_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evolution API Ruby Client