mirror of
https://github.com/lingble/chatwoot.git
synced 2025-12-25 23:27:15 +00:00
## Description This implementation adds support for the `media_name` parameter for WhatsApp document templates, resolving the issue where documents appear as "untitled" when sent via templates. **Problem solved:** Documents sent via WhatsApp templates always appeared as "untitled" because Chatwoot didn't process the `filename` field required by the WhatsApp API. **Solution:** Added support for the `media_name` parameter that maps to the WhatsApp API's `filename` field. ## Type of change - [x] New feature (non-breaking change which adds functionality) - [x] This change requires a documentation update ## How Has This Been Tested? Created and executed **7 comprehensive test scenarios**: 1. ✅ Document without `media_name` (backward compatibility) 2. ✅ Document with valid `media_name` 3. ✅ Document with blank `media_name` 4. ✅ Document with null `media_name` 5. ✅ Image with `media_name` (ignored as expected) 6. ✅ Video with `media_name` (ignored as expected) 7. ✅ Blank URL (returns nil appropriately) **All tests passed** and confirmed **100% backward compatibility**. ## Technical Implementation **Backend Changes:** - `PopulateTemplateParametersService`: Added `media_name` parameter support - `TemplateProcessorService`: Pass `media_name` to parameter builder - `WhatsappCloudService`: Updated documentation with `media_name` example **Frontend Changes:** - `WhatsAppTemplateParser.vue`: Added UI field for document filename input - `templateHelper.js`: Include `media_name` for document templates - `whatsappTemplates.json`: Added translation key for document name placeholder **Key Features:** - 🔄 **100% Backward Compatible** - Existing templates continue working - 📝 **Document Filename Support** - Users can specify custom filenames - 🎯 **Document-Only Feature** - Only affects document media types - ✅ **Comprehensive Testing** - All edge cases covered ## Expected Behavior **Before:** ```ruby # All documents appear as "untitled" { type: 'document', document: { link: 'https://example.com/document.pdf' } } ``` **After:** ```ruby # With media_name - displays custom filename { type: 'document', document: { link: 'https://example.com/document.pdf', filename: 'Invoice_2025.pdf' } } # Without media_name - works as before { type: 'document', document: { link: 'https://example.com/document.pdf' } } ``` ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
129 lines
3.8 KiB
Ruby
129 lines
3.8 KiB
Ruby
class Whatsapp::TemplateProcessorService
|
|
pattr_initialize [:channel!, :template_params, :message]
|
|
|
|
def call
|
|
return [nil, nil, nil, nil] if template_params.blank?
|
|
|
|
process_template_with_params
|
|
end
|
|
|
|
private
|
|
|
|
def process_template_with_params
|
|
[
|
|
template_params['name'],
|
|
template_params['namespace'],
|
|
template_params['language'],
|
|
processed_templates_params
|
|
]
|
|
end
|
|
|
|
def find_template
|
|
channel.message_templates.find do |t|
|
|
t['name'] == template_params['name'] && t['language'] == template_params['language'] && t['status']&.downcase == 'approved'
|
|
end
|
|
end
|
|
|
|
def processed_templates_params
|
|
template = find_template
|
|
return if template.blank?
|
|
|
|
# Convert legacy format to enhanced format before processing
|
|
converter = Whatsapp::TemplateParameterConverterService.new(template_params, template)
|
|
normalized_params = converter.normalize_to_enhanced
|
|
|
|
process_enhanced_template_params(template, normalized_params['processed_params'])
|
|
end
|
|
|
|
def process_enhanced_template_params(template, processed_params = nil)
|
|
processed_params ||= template_params['processed_params']
|
|
components = []
|
|
|
|
components.concat(process_header_components(processed_params))
|
|
components.concat(process_body_components(processed_params, template))
|
|
components.concat(process_footer_components(processed_params))
|
|
components.concat(process_button_components(processed_params))
|
|
|
|
@template_params = components
|
|
end
|
|
|
|
def process_header_components(processed_params)
|
|
return [] if processed_params['header'].blank?
|
|
|
|
header_params = build_header_params(processed_params['header'])
|
|
header_params.present? ? [{ type: 'header', parameters: header_params }] : []
|
|
end
|
|
|
|
def build_header_params(header_data)
|
|
header_params = []
|
|
header_data.each do |key, value|
|
|
next if value.blank?
|
|
|
|
if media_url_with_type?(key, header_data)
|
|
media_name = header_data['media_name']
|
|
media_param = parameter_builder.build_media_parameter(value, header_data['media_type'], media_name)
|
|
header_params << media_param if media_param
|
|
elsif key != 'media_type' && key != 'media_name'
|
|
header_params << parameter_builder.build_parameter(value)
|
|
end
|
|
end
|
|
header_params
|
|
end
|
|
|
|
def media_url_with_type?(key, header_data)
|
|
key == 'media_url' && header_data['media_type'].present?
|
|
end
|
|
|
|
def process_body_components(processed_params, template)
|
|
return [] if processed_params['body'].blank?
|
|
|
|
body_params = processed_params['body'].filter_map do |key, value|
|
|
next if value.blank?
|
|
|
|
parameter_format = template['parameter_format']
|
|
if parameter_format == 'NAMED'
|
|
parameter_builder.build_named_parameter(key, value)
|
|
else
|
|
parameter_builder.build_parameter(value)
|
|
end
|
|
end
|
|
|
|
body_params.present? ? [{ type: 'body', parameters: body_params }] : []
|
|
end
|
|
|
|
def process_footer_components(processed_params)
|
|
return [] if processed_params['footer'].blank?
|
|
|
|
footer_params = processed_params['footer'].filter_map do |_, value|
|
|
next if value.blank?
|
|
|
|
parameter_builder.build_parameter(value)
|
|
end
|
|
|
|
footer_params.present? ? [{ type: 'footer', parameters: footer_params }] : []
|
|
end
|
|
|
|
def process_button_components(processed_params)
|
|
return [] if processed_params['buttons'].blank?
|
|
|
|
button_params = processed_params['buttons'].filter_map.with_index do |button, index|
|
|
next if button.blank?
|
|
|
|
if button['type'] == 'url' || button['parameter'].present?
|
|
{
|
|
type: 'button',
|
|
sub_type: button['type'] || 'url',
|
|
index: index,
|
|
parameters: [parameter_builder.build_button_parameter(button)]
|
|
}
|
|
end
|
|
end
|
|
|
|
button_params.compact
|
|
end
|
|
|
|
def parameter_builder
|
|
@parameter_builder ||= Whatsapp::PopulateTemplateParametersService.new
|
|
end
|
|
end
|