feat: Add call-to-action template support for Twilio (#13179)
Fixes https://linear.app/chatwoot/issue/CW-6228/add-call-to-action-template-support-for-twilio-whatsapp-templates Adds support for Twilio WhatsApp call-to-action templates, enabling customers to use URL button templates with variable inputs. <img width="2982" height="1388" alt="CleanShot 2026-01-05 at 16 25 55@2x" src="https://github.com/user-attachments/assets/7cf332f5-3f3e-4ffb-a461-71c60a0156c8" />
This commit is contained in:
@@ -41,6 +41,9 @@ const getTemplateType = template => {
|
||||
if (template.template_type === TWILIO_CONTENT_TEMPLATE_TYPES.QUICK_REPLY) {
|
||||
return t('CONTENT_TEMPLATES.PICKER.TYPES.QUICK_REPLY');
|
||||
}
|
||||
if (template.template_type === TWILIO_CONTENT_TEMPLATE_TYPES.CALL_TO_ACTION) {
|
||||
return t('CONTENT_TEMPLATES.PICKER.TYPES.CALL_TO_ACTION');
|
||||
}
|
||||
return t('CONTENT_TEMPLATES.PICKER.TYPES.TEXT');
|
||||
};
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"TYPES": {
|
||||
"MEDIA": "Media",
|
||||
"QUICK_REPLY": "Quick Reply",
|
||||
"CALL_TO_ACTION": "Call to Action",
|
||||
"TEXT": "Text"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -164,4 +164,5 @@ export const TWILIO_CONTENT_TEMPLATE_TYPES = {
|
||||
TEXT: 'text',
|
||||
MEDIA: 'media',
|
||||
QUICK_REPLY: 'quick_reply',
|
||||
CALL_TO_ACTION: 'call_to_action',
|
||||
};
|
||||
|
||||
@@ -23,8 +23,8 @@ class Twilio::TemplateProcessorService
|
||||
|
||||
def build_content_variables(template)
|
||||
case template['template_type']
|
||||
when 'text', 'quick_reply'
|
||||
convert_text_template(template_params) # Text and quick reply templates use body variables
|
||||
when 'text', 'quick_reply', 'call_to_action'
|
||||
convert_text_template(template_params) # Text, quick reply and call-to-action templates use body variables
|
||||
when 'media'
|
||||
convert_media_template(template_params)
|
||||
else
|
||||
|
||||
@@ -63,6 +63,8 @@ class Twilio::TemplateSyncService
|
||||
'media'
|
||||
elsif template_types.include?('twilio/quick-reply')
|
||||
'quick_reply'
|
||||
elsif template_types.include?('twilio/call-to-action')
|
||||
'call_to_action'
|
||||
elsif template_types.include?('twilio/catalog')
|
||||
'catalog'
|
||||
else
|
||||
@@ -107,6 +109,8 @@ class Twilio::TemplateSyncService
|
||||
template_types['twilio/media']['body']
|
||||
elsif template_types['twilio/quick-reply']
|
||||
template_types['twilio/quick-reply']['body']
|
||||
elsif template_types['twilio/call-to-action']
|
||||
template_types['twilio/call-to-action']['body']
|
||||
elsif template_types['twilio/catalog']
|
||||
template_types['twilio/catalog']['body']
|
||||
else
|
||||
|
||||
@@ -81,7 +81,29 @@ RSpec.describe Twilio::TemplateSyncService do
|
||||
)
|
||||
end
|
||||
|
||||
let(:templates) { [text_template, media_template, quick_reply_template, catalog_template] }
|
||||
let(:call_to_action_template) do
|
||||
instance_double(
|
||||
Twilio::REST::Content::V1::ContentInstance,
|
||||
sid: 'HX444555666',
|
||||
friendly_name: 'payment_reminder',
|
||||
language: 'en',
|
||||
date_created: Time.current,
|
||||
date_updated: Time.current,
|
||||
variables: {},
|
||||
types: {
|
||||
'twilio/call-to-action' => {
|
||||
'body' => 'Hello, this is a gentle reminder regarding your RVA Astrology course fee.' \
|
||||
'\n\n• Vignana Course: ₹3,000\n• Panditha Course: ₹6,000' \
|
||||
'\n\nThe payment is due on {{date}}.\nKindly complete the payment at your convenience',
|
||||
'actions' => [
|
||||
{ 'id' => 'make_payment', 'title' => 'Make Payment', 'url' => 'https://example.com/payment' }
|
||||
]
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
let(:templates) { [text_template, media_template, quick_reply_template, catalog_template, call_to_action_template] }
|
||||
|
||||
before do
|
||||
allow(twilio_channel).to receive(:send).and_call_original
|
||||
@@ -104,7 +126,7 @@ RSpec.describe Twilio::TemplateSyncService do
|
||||
twilio_channel.reload
|
||||
expect(twilio_channel.content_templates).to be_present
|
||||
expect(twilio_channel.content_templates['templates']).to be_an(Array)
|
||||
expect(twilio_channel.content_templates['templates'].size).to eq(4)
|
||||
expect(twilio_channel.content_templates['templates'].size).to eq(5)
|
||||
expect(twilio_channel.content_templates_last_updated).to be_within(1.second).of(Time.current)
|
||||
end
|
||||
end
|
||||
@@ -172,6 +194,32 @@ RSpec.describe Twilio::TemplateSyncService do
|
||||
)
|
||||
end
|
||||
|
||||
it 'correctly formats call-to-action templates with variables' do
|
||||
sync_service.call
|
||||
|
||||
twilio_channel.reload
|
||||
call_to_action_data = twilio_channel.content_templates['templates'].find do |t|
|
||||
t['friendly_name'] == 'payment_reminder'
|
||||
end
|
||||
|
||||
expect(call_to_action_data).to include(
|
||||
'content_sid' => 'HX444555666',
|
||||
'friendly_name' => 'payment_reminder',
|
||||
'language' => 'en',
|
||||
'status' => 'approved',
|
||||
'template_type' => 'call_to_action',
|
||||
'media_type' => nil,
|
||||
'variables' => {},
|
||||
'category' => 'utility'
|
||||
)
|
||||
|
||||
expected_body = 'Hello, this is a gentle reminder regarding your RVA Astrology course fee.' \
|
||||
'\n\n• Vignana Course: ₹3,000\n• Panditha Course: ₹6,000' \
|
||||
'\n\nThe payment is due on {{date}}.\nKindly complete the payment at your convenience'
|
||||
expect(call_to_action_data['body']).to eq(expected_body)
|
||||
expect(call_to_action_data['body']).to match(/{{date}}/)
|
||||
end
|
||||
|
||||
it 'categorizes marketing templates correctly' do
|
||||
marketing_template = instance_double(
|
||||
Twilio::REST::Content::V1::ContentInstance,
|
||||
|
||||
Reference in New Issue
Block a user