feat(wuzapi): add send_buttons, send_list, send_url_button methods

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Rodribm10 2026-04-15 01:28:00 -03:00
parent a4472b80b9
commit 7a203ccb6d
2 changed files with 114 additions and 0 deletions

View File

@ -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

View File

@ -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