diff --git a/app/services/wuzapi/client.rb b/app/services/wuzapi/client.rb index 3e49d5f38..f5c4d6837 100644 --- a/app/services/wuzapi/client.rb +++ b/app/services/wuzapi/client.rb @@ -79,6 +79,38 @@ class Wuzapi::Client # rubocop:disable Metrics/ClassLength ) end + # Sends quick-reply buttons (up to 3 by WhatsApp limit). + def send_buttons(user_token, phone_number, text, buttons) + payload = { + Phone: phone_number, + Text: text, + Buttons: buttons.map { |b| { DisplayText: b[:text] || b['text'] } } + } + request(:post, '/chat/sendbuttons', payload, user_auth_headers(user_token)) + end + + # Sends a list menu (sections with rows). + def send_list(user_token, phone_number, text:, button_text:, sections:, footer: nil) + payload = { + Phone: phone_number, + Text: text, + ButtonText: button_text, + FooterText: footer, + Sections: build_list_sections(sections) + }.compact + request(:post, '/chat/sendlist', payload, user_auth_headers(user_token)) + end + + # Sends a single button that opens a URL when clicked. + def send_url_button(user_token, phone_number, text:, button_text:, url:) + payload = { + Phone: phone_number, + Text: text, + Buttons: [{ Type: 'url', DisplayText: button_text, Url: url }] + } + request(:post, '/chat/sendbuttons', payload, user_auth_headers(user_token)) + end + def send_chat_presence(user_token, phone_number, state, media = nil) # State: "composing" or "paused" # Media: "audio" (optional) @@ -401,4 +433,19 @@ class Wuzapi::Client # rubocop:disable Metrics/ClassLength def raise_authentication_error(response) raise AuthenticationError, "Authentication failed: #{response.code} #{response.body}" end + + def build_list_sections(sections) + sections.map do |s| + { + Title: s[:title] || s['title'], + Rows: Array(s[:rows] || s['rows']).map do |r| + { + Title: r[:title] || r['title'], + Description: r[:description] || r['description'], + RowId: r[:row_id] || r['row_id'] + } + end + } + end + end end diff --git a/spec/services/wuzapi/client_interactive_spec.rb b/spec/services/wuzapi/client_interactive_spec.rb new file mode 100644 index 000000000..2e04f3c9f --- /dev/null +++ b/spec/services/wuzapi/client_interactive_spec.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Wuzapi::Client do + let(:base_url) { 'https://wuzapi.test' } + let(:client) { described_class.new(base_url) } + let(:user_token) { 'tok' } + let(:phone) { '5561999999999' } + + describe '#send_buttons' do + it 'POSTs to /chat/sendbuttons with buttons payload' do + stub = stub_request(:post, "#{base_url}/chat/sendbuttons") + .with( + headers: { 'Token' => user_token }, + body: hash_including( + Phone: phone, + Text: 'Curtiu?', + Buttons: [ + { DisplayText: 'Sim' }, + { DisplayText: 'Não' } + ] + ) + ) + .to_return(status: 200, body: { Id: 'msg-1' }.to_json, headers: { 'Content-Type' => 'application/json' }) + + response = client.send_buttons( + user_token, phone, 'Curtiu?', + [{ text: 'Sim' }, { text: 'Não' }] + ) + expect(response).to be_a(Hash) + expect(stub).to have_been_requested + end + end + + describe '#send_list' do + it 'POSTs to /chat/sendlist with sections' do + stub = stub_request(:post, "#{base_url}/chat/sendlist") + .with(headers: { 'Token' => user_token }) + .to_return(status: 200, body: { Id: 'msg-2' }.to_json, headers: { 'Content-Type' => 'application/json' }) + + response = client.send_list( + user_token, phone, + text: 'Cardápio', + button_text: 'Ver opções', + sections: [{ title: 'Bebidas', rows: [{ title: 'Água', row_id: 'agua' }] }] + ) + expect(response).to be_a(Hash) + expect(stub).to have_been_requested + end + end + + describe '#send_url_button' do + it 'POSTs to /chat/sendbuttons with URL button' do + stub = stub_request(:post, "#{base_url}/chat/sendbuttons") + .with(body: hash_including('Buttons' => array_including(hash_including('Type' => 'url')))) + .to_return(status: 200, body: { Id: 'msg-3' }.to_json, headers: { 'Content-Type' => 'application/json' }) + + response = client.send_url_button(user_token, phone, + text: 'Avalie no Google', + button_text: 'Avaliar', + url: 'https://g.page/r/XYZ') + expect(response).to be_a(Hash) + expect(stub).to have_been_requested + end + end +end