Files
Sivin VargheseandGitHub 2a30e7b082 fix: render agent variables in automation messages (#14338)
# Pull Request Template

## Description

This PR fixes an issue where agent variables like
`{{agent.name}}`,`{{agent.first_name}}`, `{{agent.last_name}}`, and
`{{agent.email}}` were not rendering in automation messages.
In automation, these either showed blank or returned `Liquid error:
internal`, while the same variables worked fine in macros.

**Cause**
Automation messages are created without a sender, so agent data was
missing during variable rendering. This also caused errors in name
handling, and `email` was not defined at all.

**Solution**
* Handle missing agent data safely to avoid errors
* Add support for `{{agent.email}}`
* Fallback to conversation assignee when sender is not present


Fixes
https://linear.app/chatwoot/issue/CW-6979/template-variables-not-working-in-automated-messages

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?

### Screenshots

**Automation**
<img width="759" height="284" alt="image"
src="https://github.com/user-attachments/assets/61a877b7-4984-4a7f-bbef-b8c510dcbdfe"
/>

**Before**
<img width="404" height="105" alt="image"
src="https://github.com/user-attachments/assets/da665ce8-137d-4249-8ee5-a1acc11391db"
/>


**After**
<img width="564" height="132" alt="image"
src="https://github.com/user-attachments/assets/6a80d67c-49c8-4658-b782-ae4acbc77256"
/>



## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [ ] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] 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
- [ ] Any dependent changes have been merged and published in downstream
modules
2026-05-04 13:25:40 +05:30

97 lines
2.6 KiB
Ruby

module Liquidable
extend ActiveSupport::Concern
included do
before_create :process_liquid_in_content
before_create :process_liquid_in_template_params
end
private
def message_drops
{
'contact' => ContactDrop.new(conversation.contact),
'agent' => UserDrop.new(sender || conversation.assignee),
'conversation' => ConversationDrop.new(conversation),
'inbox' => InboxDrop.new(inbox),
'account' => AccountDrop.new(conversation.account)
}
end
def liquid_processable_message?
content.present? && (message_type == 'outgoing' || message_type == 'template')
end
def process_liquid_in_content
return unless liquid_processable_message?
template = Liquid::Template.parse(modified_liquid_content)
self.content = template.render(message_drops)
rescue Liquid::Error
# If there is an error in the liquid syntax, we don't want to process it
end
def modified_liquid_content
# This regex is used to match the code blocks in the content
# We don't want to process liquid in code blocks
content.gsub(/`(.*?)`/m, '{% raw %}`\\1`{% endraw %}')
end
def process_liquid_in_template_params
return unless template_params_present? && liquid_processable_template_params?
processed_params = process_liquid_in_hash(template_params_data['processed_params'])
# Update the additional_attributes with processed template_params
self.additional_attributes = additional_attributes.merge(
'template_params' => template_params_data.merge('processed_params' => processed_params)
)
rescue Liquid::Error
# If there is an error in the liquid syntax, we don't want to process it
end
def template_params_present?
additional_attributes&.dig('template_params', 'processed_params').present?
end
def liquid_processable_template_params?
message_type == 'outgoing' || message_type == 'template'
end
def template_params_data
additional_attributes['template_params']
end
def process_liquid_in_hash(hash)
return hash unless hash.is_a?(Hash)
hash.transform_values { |value| process_liquid_value(value) }
end
def process_liquid_value(value)
case value
when String
process_liquid_string(value)
when Hash
process_liquid_in_hash(value)
when Array
process_liquid_array(value)
else
value
end
end
def process_liquid_array(array)
array.map { |item| process_liquid_value(item) }
end
def process_liquid_string(string)
return string if string.blank?
template = Liquid::Template.parse(string)
template.render(message_drops)
rescue Liquid::Error
string
end
end