Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
729a551571 |
@@ -89,7 +89,7 @@ gem 'wisper', '2.0.0'
|
||||
##--- gems for channels ---##
|
||||
gem 'facebook-messenger'
|
||||
gem 'line-bot-api'
|
||||
gem 'twilio-ruby'
|
||||
gem 'twilio-ruby', '~> 5.66'
|
||||
# twitty will handle subscription of twitter account events
|
||||
# gem 'twitty', git: 'https://github.com/chatwoot/twitty'
|
||||
gem 'twitty', '~> 0.1.5'
|
||||
|
||||
+8
-10
@@ -252,10 +252,8 @@ GEM
|
||||
railties (>= 5.0.0)
|
||||
faker (3.2.0)
|
||||
i18n (>= 1.8.11, < 2)
|
||||
faraday (2.13.1)
|
||||
faraday-net_http (>= 2.0, < 3.5)
|
||||
json
|
||||
logger
|
||||
faraday (2.9.0)
|
||||
faraday-net_http (>= 2.0, < 3.2)
|
||||
faraday-follow_redirects (0.3.0)
|
||||
faraday (>= 1, < 3)
|
||||
faraday-mashify (0.1.1)
|
||||
@@ -263,8 +261,8 @@ GEM
|
||||
hashie
|
||||
faraday-multipart (1.0.4)
|
||||
multipart-post (~> 2)
|
||||
faraday-net_http (3.4.0)
|
||||
net-http (>= 0.5.0)
|
||||
faraday-net_http (3.1.0)
|
||||
net-http
|
||||
faraday-net_http_persistent (2.1.0)
|
||||
faraday (~> 2.5)
|
||||
net-http-persistent (~> 4.0)
|
||||
@@ -423,7 +421,7 @@ GEM
|
||||
judoscale-sidekiq (1.8.2)
|
||||
judoscale-ruby (= 1.8.2)
|
||||
sidekiq (>= 5.0)
|
||||
jwt (2.10.1)
|
||||
jwt (2.8.1)
|
||||
base64
|
||||
kaminari (1.2.2)
|
||||
activesupport (>= 4.1.0)
|
||||
@@ -505,7 +503,7 @@ GEM
|
||||
mutex_m (0.3.0)
|
||||
neighbor (0.2.3)
|
||||
activerecord (>= 5.2)
|
||||
net-http (0.6.0)
|
||||
net-http (0.4.1)
|
||||
uri
|
||||
net-http-persistent (4.0.2)
|
||||
connection_pool (~> 2.2)
|
||||
@@ -851,7 +849,7 @@ GEM
|
||||
i18n
|
||||
timeout (0.4.3)
|
||||
trailblazer-option (0.1.2)
|
||||
twilio-ruby (7.6.0)
|
||||
twilio-ruby (5.77.0)
|
||||
faraday (>= 0.9, < 3.0)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
nokogiri (>= 1.6, < 2.0)
|
||||
@@ -1043,7 +1041,7 @@ DEPENDENCIES
|
||||
telephone_number
|
||||
test-prof
|
||||
time_diff
|
||||
twilio-ruby
|
||||
twilio-ruby (~> 5.66)
|
||||
twitty (~> 0.1.5)
|
||||
tzinfo-data
|
||||
uglifier
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
3.4.3
|
||||
3.4.2
|
||||
|
||||
@@ -127,27 +127,12 @@ export const IFrameHelper = {
|
||||
window.onmessage = e => {
|
||||
if (
|
||||
typeof e.data !== 'string' ||
|
||||
e.data.indexOf('chatwoot-widget:') !== 0
|
||||
e.data.indexOf('chatwoot-widget:') !== 0 ||
|
||||
e.origin !== window.location.origin
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate origin - allow messages from either:
|
||||
// 1. The widget origin (widget -> parent communication)
|
||||
// 2. The current page origin (same-origin messages)
|
||||
const widgetOrigin = window.$chatwoot?.baseUrl
|
||||
? new URL(window.$chatwoot.baseUrl).origin
|
||||
: null;
|
||||
const parentOrigin = window.location.origin;
|
||||
|
||||
const isValidOrigin =
|
||||
e.origin === parentOrigin ||
|
||||
(widgetOrigin && e.origin === widgetOrigin);
|
||||
|
||||
if (!isValidOrigin) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message = JSON.parse(e.data.replace('chatwoot-widget:', ''));
|
||||
if (typeof IFrameHelper.events[message.event] === 'function') {
|
||||
IFrameHelper.events[message.event](message);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Table name: assignment_policies
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# assignment_order :integer default("round_robin"), not null
|
||||
# assignment_order :integer default(0), not null
|
||||
# conversation_priority :integer default("earliest_created"), not null
|
||||
# description :text
|
||||
# enabled :boolean default(TRUE), not null
|
||||
|
||||
@@ -3,7 +3,6 @@ module Liquidable
|
||||
|
||||
included do
|
||||
before_create :process_liquid_in_content
|
||||
before_create :process_liquid_in_template_params
|
||||
end
|
||||
|
||||
private
|
||||
@@ -36,61 +35,4 @@ module Liquidable
|
||||
# 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
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# idx_notifications_performance (user_id,account_id,snoozed_until,read_at)
|
||||
# index_notifications_on_account_id (account_id)
|
||||
# index_notifications_on_last_activity_at (last_activity_at)
|
||||
# index_notifications_on_user_id (user_id)
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
shared: &shared
|
||||
version: '4.5.2'
|
||||
version: '4.5.0'
|
||||
|
||||
development:
|
||||
<<: *shared
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class AddTemplateParamsToCampaigns < ActiveRecord::Migration[7.1]
|
||||
def change
|
||||
add_column :campaigns, :template_params, :jsonb
|
||||
add_column :campaigns, :template_params, :jsonb, default: {}, null: false
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
class AddFeatureCitationToAssistantConfig < ActiveRecord::Migration[7.1]
|
||||
def up
|
||||
return unless ChatwootApp.enterprise?
|
||||
|
||||
Captain::Assistant.find_each do |assistant|
|
||||
assistant.update!(
|
||||
config: assistant.config.merge('feature_citation' => true)
|
||||
@@ -10,8 +8,6 @@ class AddFeatureCitationToAssistantConfig < ActiveRecord::Migration[7.1]
|
||||
end
|
||||
|
||||
def down
|
||||
return unless ChatwootApp.enterprise?
|
||||
|
||||
Captain::Assistant.find_each do |assistant|
|
||||
config = assistant.config.dup
|
||||
config.delete('feature_citation')
|
||||
|
||||
+15
-15
@@ -2,7 +2,7 @@
|
||||
|
||||
# Description: Install and manage a Chatwoot installation.
|
||||
# OS: Ubuntu 20.04 LTS, 22.04 LTS, 24.04 LTS
|
||||
# Script Version: 3.4.3
|
||||
# Script Version: 3.4.2
|
||||
# Run this script as root
|
||||
|
||||
set -eu -o errexit -o pipefail -o noclobber -o nounset
|
||||
@@ -19,7 +19,7 @@ fi
|
||||
# option --output/-o requires 1 argument
|
||||
LONGOPTS=console,debug,help,install,Install:,logs:,restart,ssl,upgrade,Upgrade:,webserver,version,web-only,worker-only,convert:
|
||||
OPTIONS=cdhiI:l:rsuU:wvWK
|
||||
CWCTL_VERSION="3.4.3"
|
||||
CWCTL_VERSION="3.3.0"
|
||||
pg_pass=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 15 ; echo '')
|
||||
CHATWOOT_HUB_URL="https://hub.2.chatwoot.com/events"
|
||||
|
||||
@@ -430,7 +430,7 @@ function configure_systemd_services() {
|
||||
|
||||
if [ "$DEPLOYMENT_TYPE" == "web" ]; then
|
||||
echo "Setting up web-only deployment"
|
||||
|
||||
|
||||
# Stop and disable existing services if converting
|
||||
if [ "$existing_full_deployment" = true ]; then
|
||||
echo "Converting from full deployment to web-only"
|
||||
@@ -449,14 +449,14 @@ function configure_systemd_services() {
|
||||
|
||||
cp /home/chatwoot/chatwoot/deployment/chatwoot-web.1.service /etc/systemd/system/chatwoot-web.1.service
|
||||
cp /home/chatwoot/chatwoot/deployment/chatwoot-web.target /etc/systemd/system/chatwoot-web.target
|
||||
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable chatwoot-web.target
|
||||
systemctl start chatwoot-web.target
|
||||
|
||||
|
||||
elif [ "$DEPLOYMENT_TYPE" == "worker" ]; then
|
||||
echo "Setting up worker-only deployment"
|
||||
|
||||
|
||||
# Stop and disable existing services if converting
|
||||
if [ "$existing_full_deployment" = true ]; then
|
||||
echo "Converting from full deployment to worker-only"
|
||||
@@ -475,14 +475,14 @@ function configure_systemd_services() {
|
||||
|
||||
cp /home/chatwoot/chatwoot/deployment/chatwoot-worker.1.service /etc/systemd/system/chatwoot-worker.1.service
|
||||
cp /home/chatwoot/chatwoot/deployment/chatwoot-worker.target /etc/systemd/system/chatwoot-worker.target
|
||||
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable chatwoot-worker.target
|
||||
systemctl start chatwoot-worker.target
|
||||
|
||||
|
||||
else
|
||||
echo "Setting up full deployment (web + worker)"
|
||||
|
||||
|
||||
# Stop existing specialized deployments if converting back to full
|
||||
if [ -f "/etc/systemd/system/chatwoot-web.target" ]; then
|
||||
echo "Converting from web-only to full deployment"
|
||||
@@ -494,7 +494,7 @@ function configure_systemd_services() {
|
||||
systemctl stop chatwoot-worker.target || true
|
||||
systemctl disable chatwoot-worker.target || true
|
||||
fi
|
||||
|
||||
|
||||
cp /home/chatwoot/chatwoot/deployment/chatwoot-web.1.service /etc/systemd/system/chatwoot-web.1.service
|
||||
cp /home/chatwoot/chatwoot/deployment/chatwoot-worker.1.service /etc/systemd/system/chatwoot-worker.1.service
|
||||
cp /home/chatwoot/chatwoot/deployment/chatwoot.target /etc/systemd/system/chatwoot.target
|
||||
@@ -538,7 +538,7 @@ function setup_ssl() {
|
||||
cd chatwoot
|
||||
sed -i "s/http:\/\/0.0.0.0:3000/https:\/\/$domain_name/g" .env
|
||||
EOF
|
||||
|
||||
|
||||
# Restart the appropriate chatwoot target
|
||||
if [ -f "/etc/systemd/system/chatwoot-web.target" ]; then
|
||||
systemctl restart chatwoot-web.target
|
||||
@@ -1005,7 +1005,7 @@ EOF
|
||||
upgrade_redis
|
||||
upgrade_node
|
||||
get_pnpm
|
||||
|
||||
|
||||
sudo -i -u chatwoot << EOF
|
||||
|
||||
# Navigate to the Chatwoot directory
|
||||
@@ -1098,16 +1098,16 @@ function restart() {
|
||||
##############################################################################
|
||||
function convert_deployment() {
|
||||
echo "Converting Chatwoot deployment to: $DEPLOYMENT_TYPE"
|
||||
|
||||
|
||||
# Check if Chatwoot is installed
|
||||
if [ ! -d "/home/chatwoot/chatwoot" ]; then
|
||||
echo "Chatwoot installation not found. Use --install first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
# Run the systemd service configuration which handles conversion logic
|
||||
configure_systemd_services
|
||||
|
||||
|
||||
echo "Deployment converted successfully to: $DEPLOYMENT_TYPE"
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@chatwoot/chatwoot",
|
||||
"version": "4.5.2",
|
||||
"version": "4.5.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"eslint": "eslint app/**/*.{js,vue}",
|
||||
|
||||
@@ -69,159 +69,4 @@ shared_examples_for 'liqudable' do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when liquid is present in template_params' do
|
||||
let(:contact) do
|
||||
create(:contact, name: 'john', email: 'john@example.com', phone_number: '+912883', custom_attributes: { customer_type: 'platinum' })
|
||||
end
|
||||
let(:conversation) { create(:conversation, id: 1, contact: contact, custom_attributes: { priority: 'high' }) }
|
||||
|
||||
context 'when message is outgoing with template_params' do
|
||||
let(:message) { build(:message, conversation: conversation, message_type: 'outgoing') }
|
||||
|
||||
it 'replaces liquid variables in template_params body' do
|
||||
message.additional_attributes = {
|
||||
'template_params' => {
|
||||
'name' => 'greet',
|
||||
'category' => 'MARKETING',
|
||||
'language' => 'en',
|
||||
'processed_params' => {
|
||||
'body' => {
|
||||
'customer_name' => '{{contact.name}}',
|
||||
'customer_email' => '{{contact.email}}'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
message.save!
|
||||
|
||||
body_params = message.additional_attributes['template_params']['processed_params']['body']
|
||||
expect(body_params['customer_name']).to eq 'John'
|
||||
expect(body_params['customer_email']).to eq 'john@example.com'
|
||||
end
|
||||
|
||||
it 'replaces liquid variables in nested template_params' do
|
||||
message.additional_attributes = {
|
||||
'template_params' => {
|
||||
'name' => 'test_template',
|
||||
'processed_params' => {
|
||||
'header' => {
|
||||
'media_url' => 'https://example.com/{{contact.name}}.jpg'
|
||||
},
|
||||
'body' => {
|
||||
'customer_name' => '{{contact.name}}',
|
||||
'priority' => '{{conversation.custom_attribute.priority}}'
|
||||
},
|
||||
'footer' => {
|
||||
'company' => '{{account.name}}'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
message.save!
|
||||
|
||||
processed = message.additional_attributes['template_params']['processed_params']
|
||||
expect(processed['header']['media_url']).to eq 'https://example.com/John.jpg'
|
||||
expect(processed['body']['customer_name']).to eq 'John'
|
||||
expect(processed['body']['priority']).to eq 'high'
|
||||
expect(processed['footer']['company']).to eq conversation.account.name
|
||||
end
|
||||
|
||||
it 'handles arrays in template_params' do
|
||||
message.additional_attributes = {
|
||||
'template_params' => {
|
||||
'name' => 'test_template',
|
||||
'processed_params' => {
|
||||
'buttons' => [
|
||||
{ 'type' => 'url', 'parameter' => 'https://example.com/{{contact.name}}' },
|
||||
{ 'type' => 'text', 'parameter' => 'Hello {{contact.name}}' }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
message.save!
|
||||
|
||||
buttons = message.additional_attributes['template_params']['processed_params']['buttons']
|
||||
expect(buttons[0]['parameter']).to eq 'https://example.com/John'
|
||||
expect(buttons[1]['parameter']).to eq 'Hello John'
|
||||
end
|
||||
|
||||
it 'handles custom attributes in template_params' do
|
||||
message.additional_attributes = {
|
||||
'template_params' => {
|
||||
'name' => 'test_template',
|
||||
'processed_params' => {
|
||||
'body' => {
|
||||
'customer_type' => '{{contact.custom_attribute.customer_type}}',
|
||||
'priority' => '{{conversation.custom_attribute.priority}}'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
message.save!
|
||||
|
||||
body_params = message.additional_attributes['template_params']['processed_params']['body']
|
||||
expect(body_params['customer_type']).to eq 'platinum'
|
||||
expect(body_params['priority']).to eq 'high'
|
||||
end
|
||||
|
||||
it 'handles missing email with default filter in template_params' do
|
||||
contact.update!(email: nil)
|
||||
message.additional_attributes = {
|
||||
'template_params' => {
|
||||
'name' => 'test_template',
|
||||
'processed_params' => {
|
||||
'body' => {
|
||||
'customer_email' => '{{ contact.email | default: "no-email@example.com" }}'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
message.save!
|
||||
|
||||
body_params = message.additional_attributes['template_params']['processed_params']['body']
|
||||
expect(body_params['customer_email']).to eq 'no-email@example.com'
|
||||
end
|
||||
|
||||
it 'handles broken liquid syntax in template_params gracefully' do
|
||||
message.additional_attributes = {
|
||||
'template_params' => {
|
||||
'name' => 'test_template',
|
||||
'processed_params' => {
|
||||
'body' => {
|
||||
'broken_liquid' => '{{contact.name} {{invalid}}'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
message.save!
|
||||
|
||||
body_params = message.additional_attributes['template_params']['processed_params']['body']
|
||||
expect(body_params['broken_liquid']).to eq '{{contact.name} {{invalid}}'
|
||||
end
|
||||
|
||||
it 'does not process template_params when message is incoming' do
|
||||
incoming_message = build(:message, conversation: conversation, message_type: 'incoming')
|
||||
incoming_message.additional_attributes = {
|
||||
'template_params' => {
|
||||
'name' => 'test_template',
|
||||
'processed_params' => {
|
||||
'body' => {
|
||||
'customer_name' => '{{contact.name}}'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
incoming_message.save!
|
||||
|
||||
body_params = incoming_message.additional_attributes['template_params']['processed_params']['body']
|
||||
expect(body_params['customer_name']).to eq '{{contact.name}}'
|
||||
end
|
||||
|
||||
it 'does not process template_params when not present' do
|
||||
message.additional_attributes = { 'other_data' => 'test' }
|
||||
expect { message.save! }.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user