From f7f687ce5374525652f4186095aad73df7dad356 Mon Sep 17 00:00:00 2001 From: Pranav Date: Fri, 21 Jun 2024 17:28:48 -0700 Subject: [PATCH 1/6] fix: Update notification payload (#9666) - Fix notification payload to avoid argument error. --- app/services/notification/push_notification_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/notification/push_notification_service.rb b/app/services/notification/push_notification_service.rb index 263800d4a..9878107c1 100644 --- a/app/services/notification/push_notification_service.rb +++ b/app/services/notification/push_notification_service.rb @@ -66,7 +66,7 @@ class Notification::PushNotificationService def send_browser_push(subscription) return unless can_send_browser_push?(subscription) - WebPush.payload_send(browser_push_payload(subscription)) + WebPush.payload_send(**browser_push_payload(subscription)) Rails.logger.info("Browser push sent to #{user.email} with title #{push_message[:title]}") rescue WebPush::ExpiredSubscription, WebPush::InvalidSubscription, WebPush::Unauthorized => e Rails.logger.info "WebPush subscription expired: #{e.message}" From 96f4f50d2deff8a09f312e860bf891ad4d454c36 Mon Sep 17 00:00:00 2001 From: Clairton Rodrigo Heinzen Date: Wed, 26 Jun 2024 16:40:36 -0300 Subject: [PATCH 2/6] feat: Add the ability to un-assign teams using automation (#9668) Co-authored-by: Pranav --- .../dashboard/helper/automationHelper.js | 6 +-- app/models/concerns/json_schema_validator.rb | 1 - app/services/action_service.rb | 6 ++- spec/factories/conversations.rb | 12 +++++ spec/services/action_service_spec.rb | 44 +++++++++++++++++-- 5 files changed, 60 insertions(+), 9 deletions(-) diff --git a/app/javascript/dashboard/helper/automationHelper.js b/app/javascript/dashboard/helper/automationHelper.js index 665e8faf6..33e920edc 100644 --- a/app/javascript/dashboard/helper/automationHelper.js +++ b/app/javascript/dashboard/helper/automationHelper.js @@ -121,7 +121,7 @@ export const generateConditionOptions = (options, key = 'id') => { }; // Add the "None" option to the agent list -export const agentList = agents => [ +export const addNoneToList = agents => [ { id: 'nil', name: 'None', @@ -137,8 +137,8 @@ export const getActionOptions = ({ type, }) => { const actionsMap = { - assign_agent: agentList(agents), - assign_team: teams, + assign_agent: addNoneToList(agents), + assign_team: addNoneToList(teams), send_email_to_team: teams, add_label: generateConditionOptions(labels, 'title'), remove_label: generateConditionOptions(labels, 'title'), diff --git a/app/models/concerns/json_schema_validator.rb b/app/models/concerns/json_schema_validator.rb index 4ae94df12..808564be9 100644 --- a/app/models/concerns/json_schema_validator.rb +++ b/app/models/concerns/json_schema_validator.rb @@ -48,7 +48,6 @@ class JsonSchemaValidator < ActiveModel::Validator # Add validation errors to the record with a formatted statement validation_errors.each do |error| - # byebug format_and_append_error(error, record) end end diff --git a/app/services/action_service.rb b/app/services/action_service.rb index 79f1234a0..4f33a302b 100644 --- a/app/services/action_service.rb +++ b/app/services/action_service.rb @@ -50,7 +50,11 @@ class ActionService end def assign_team(team_ids = []) - return unassign_team if team_ids[0]&.zero? + # FIXME: The explicit checks for zero or nil (string) is bad. Move + # this to a separate unassign action. + should_unassign = team_ids.blank? || %w[nil 0].include?(team_ids[0].to_s) + return @conversation.update!(team_id: nil) if should_unassign + # check if team belongs to account only if team_id is present # if team_id is nil, then it means that the team is being unassigned return unless !team_ids[0].nil? && team_belongs_to_account?(team_ids) diff --git a/spec/factories/conversations.rb b/spec/factories/conversations.rb index 6f2bc34ef..c552e6c30 100644 --- a/spec/factories/conversations.rb +++ b/spec/factories/conversations.rb @@ -16,5 +16,17 @@ FactoryBot.define do conversation.contact ||= create(:contact, :with_email, account: conversation.account) conversation.contact_inbox ||= create(:contact_inbox, contact: conversation.contact, inbox: conversation.inbox) end + + trait :with_team do + after(:build) do |conversation| + conversation.team ||= create(:team, account: conversation.account) + end + end + + trait :with_assignee do + after(:build) do |conversation| + conversation.assignee ||= create(:user, account: conversation.account, role: :agent) + end + end end end diff --git a/spec/services/action_service_spec.rb b/spec/services/action_service_spec.rb index bdc9c88ff..c28761844 100644 --- a/spec/services/action_service_spec.rb +++ b/spec/services/action_service_spec.rb @@ -31,18 +31,54 @@ describe ActionService do describe '#assign_agent' do let(:agent) { create(:user, account: account, role: :agent) } - let(:conversation) { create(:conversation, account: account) } let(:inbox_member) { create(:inbox_member, inbox: conversation.inbox, user: agent) } + let(:conversation) { create(:conversation, :with_assignee, account: account) } let(:action_service) { described_class.new(conversation) } it 'unassigns the conversation if agent id is nil' do action_service.assign_agent(['nil']) expect(conversation.reload.assignee).to be_nil end + end - it 'unassigns the team if team_id is nil' do - action_service.assign_team([nil]) - expect(conversation.reload.team).to be_nil + describe '#assign_team' do + let(:agent) { create(:user, account: account, role: :agent) } + let(:inbox_member) { create(:inbox_member, inbox: conversation.inbox, user: agent) } + let(:team) { create(:team, name: 'ConversationTeam', account: account) } + let(:conversation) { create(:conversation, :with_team, account: account) } + let(:action_service) { described_class.new(conversation) } + + context 'when team_id is not present' do + it 'unassign the if team_id is "nil"' do + expect do + action_service.assign_team(['nil']) + end.not_to raise_error + expect(conversation.reload.team).to be_nil + end + + it 'unassign the if team_id is 0' do + expect do + action_service.assign_team([0]) + end.not_to raise_error + expect(conversation.reload.team).to be_nil + end + end + + context 'when team_id is present' do + it 'assign the team if the team is part of the account' do + original_team = conversation.team + expect do + action_service.assign_team([team.id]) + end.to change { conversation.reload.team }.from(original_team) + end + + it 'does not assign the team if the team is part of the account' do + original_team = conversation.team + invalid_team_id = 999_999_999 + expect do + action_service.assign_team([invalid_team_id]) + end.not_to change { conversation.reload.team }.from(original_team) + end end end end From 73c4180e6498c44995deb5b4db26b0651d569f2c Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Wed, 26 Jun 2024 12:46:21 -0700 Subject: [PATCH 3/6] chore(snyk): Upgrade sentry-rails & sentry-sidekiq to 5.18.0 (#9686) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit

This PR was automatically created by Snyk using the credentials of a real user.


![snyk-top-banner](https://github.com/andygongea/OWASP-Benchmark/assets/818805/c518c423-16fe-447e-b67f-ad5a49b5d123) ### Snyk has created this PR to fix 1 vulnerabilities in the rubygems dependencies of this project. #### Snyk changed the following file(s): - `Gemfile` - `Gemfile.lock` #### Vulnerabilities that will be fixed with an upgrade: | | Issue | Score | :-------------------------:|:-------------------------|:------------------------- ![medium severity](https://res.cloudinary.com/snyk/image/upload/w_20,h_20/v1561977819/icon/m.png 'medium severity') | Web Cache Poisoning
[SNYK-RUBY-RACK-1061917](https://snyk.io/vuln/SNYK-RUBY-RACK-1061917) |   **616**   --- > [!IMPORTANT] > > - Check the changes in this PR to ensure they won't cause issues with your project. > - Max score is 1000. Note that the real score may have changed since the PR was raised. > - This PR was automatically created by Snyk using the credentials of a real user. --- **Note:** _You are seeing this because you or someone else with access to this repository has authorized Snyk to open fix PRs._ For more information: 🧐 [View latest project report](https://app.snyk.io/org/chatwoot/project/b7197bbd-6200-4f23-931d-c39928584360?utm_source=github&utm_medium=referral&page=fix-pr) 📜 [Customise PR templates](https://docs.snyk.io/scan-using-snyk/pull-requests/snyk-fix-pull-or-merge-requests/customize-pr-templates) 🛠 [Adjust project settings](https://app.snyk.io/org/chatwoot/project/b7197bbd-6200-4f23-931d-c39928584360?utm_source=github&utm_medium=referral&page=fix-pr/settings) 📚 [Read about Snyk's upgrade logic](https://support.snyk.io/hc/en-us/articles/360003891078-Snyk-patches-to-fix-vulnerabilities) --- **Learn how to fix vulnerabilities with free interactive lessons:** 🦉 [Learn about vulnerability in an interactive lesson of Snyk Learn.](https://learn.snyk.io/?loc=fix-pr) [//]: # 'snyk:metadata:{"customTemplate":{"variablesUsed":[],"fieldsUsed":[]},"dependencies":[{"name":"sentry-rails","from":"5.17.3","to":"5.18.0"},{"name":"sentry-sidekiq","from":"5.17.3","to":"5.18.0"}],"env":"prod","issuesToFix":[{"exploit_maturity":"Proof of Concept","id":"SNYK-RUBY-RACK-1061917","priority_score":616,"priority_score_factors":[{"type":"exploit","label":"Proof of Concept","score":107},{"type":"fixability","label":true,"score":214},{"type":"cvssScore","label":"5.9","score":295},{"type":"scoreVersion","label":"v1","score":1}],"severity":"medium","title":"Web Cache Poisoning"},{"exploit_maturity":"Proof of Concept","id":"SNYK-RUBY-RACK-1061917","priority_score":616,"priority_score_factors":[{"type":"exploit","label":"Proof of Concept","score":107},{"type":"fixability","label":true,"score":214},{"type":"cvssScore","label":"5.9","score":295},{"type":"scoreVersion","label":"v1","score":1}],"severity":"medium","title":"Web Cache Poisoning"},{"exploit_maturity":"Proof of Concept","id":"SNYK-RUBY-RACK-1061917","priority_score":616,"priority_score_factors":[{"type":"exploit","label":"Proof of Concept","score":107},{"type":"fixability","label":true,"score":214},{"type":"cvssScore","label":"5.9","score":295},{"type":"scoreVersion","label":"v1","score":1}],"severity":"medium","title":"Web Cache Poisoning"}],"prId":"a3fcec38-ff99-4d64-ae69-545ad067aff5","prPublicId":"a3fcec38-ff99-4d64-ae69-545ad067aff5","packageManager":"rubygems","priorityScoreList":[616],"projectPublicId":"b7197bbd-6200-4f23-931d-c39928584360","projectUrl":"https://app.snyk.io/org/chatwoot/project/b7197bbd-6200-4f23-931d-c39928584360?utm_source=github&utm_medium=referral&page=fix-pr","prType":"fix","templateFieldSources":{"branchName":"default","commitMessage":"default","description":"default","title":"default"},"templateVariants":["priorityScore"],"type":"auto","upgrade":["SNYK-RUBY-RACK-1061917"],"vulns":["SNYK-RUBY-RACK-1061917"],"patch":[],"isBreakingChange":false,"remediationStrategy":"vuln"}' Co-authored-by: snyk-bot Co-authored-by: Pranav --- Gemfile | 4 ++-- Gemfile.lock | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Gemfile b/Gemfile index 43f255074..5e1e5917f 100644 --- a/Gemfile +++ b/Gemfile @@ -111,9 +111,9 @@ gem 'elastic-apm', require: false gem 'newrelic_rpm', require: false gem 'newrelic-sidekiq-metrics', '>= 1.6.2', require: false gem 'scout_apm', require: false -gem 'sentry-rails', '>= 5.14.0', require: false +gem 'sentry-rails', '>= 5.18.0', require: false gem 'sentry-ruby', require: false -gem 'sentry-sidekiq', '>= 5.15.0', require: false +gem 'sentry-sidekiq', '>= 5.18.0', require: false ##-- background job processing --## gem 'sidekiq', '>= 7.2.4' diff --git a/Gemfile.lock b/Gemfile.lock index bbb1aa063..04a8fb534 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -150,7 +150,7 @@ GEM statsd-ruby (~> 1.1) base64 (0.2.0) bcrypt (3.1.20) - bigdecimal (3.1.7) + bigdecimal (3.1.8) bindex (0.8.1) bootsnap (1.16.0) msgpack (~> 1.2) @@ -603,7 +603,7 @@ GEM ffi (~> 1.0) redis (5.0.6) redis-client (>= 0.9.0) - redis-client (0.22.1) + redis-client (0.22.2) connection_pool redis-namespace (1.10.0) redis (>= 4) @@ -703,14 +703,14 @@ GEM activesupport (>= 4) selectize-rails (0.12.6) semantic_range (3.0.0) - sentry-rails (5.17.3) + sentry-rails (5.18.0) railties (>= 5.0) - sentry-ruby (~> 5.17.3) - sentry-ruby (5.17.3) + sentry-ruby (~> 5.18.0) + sentry-ruby (5.18.0) bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) - sentry-sidekiq (5.17.3) - sentry-ruby (~> 5.17.3) + sentry-sidekiq (5.18.0) + sentry-ruby (~> 5.18.0) sidekiq (>= 3.0) sexp_processor (4.17.0) shoulda-matchers (5.3.0) @@ -931,9 +931,9 @@ DEPENDENCIES scout_apm scss_lint seed_dump - sentry-rails (>= 5.14.0) + sentry-rails (>= 5.18.0) sentry-ruby - sentry-sidekiq (>= 5.15.0) + sentry-sidekiq (>= 5.18.0) shoulda-matchers sidekiq (>= 7.2.4) sidekiq-cron (>= 1.12.0) From b2de6843f68fdb71917b7ea3e8767cb95b1bb0bc Mon Sep 17 00:00:00 2001 From: What are you searching? Date: Thu, 27 Jun 2024 02:54:57 +0700 Subject: [PATCH 4/6] chore()Update Translate vi.yml (#9656) --- config/locales/vi.yml | 80 +++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/config/locales/vi.yml b/config/locales/vi.yml index 189184d4d..8fb3e4507 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -31,7 +31,7 @@ vi: disposable_email: Chúng tôi không cho phép các email dùng một lần invalid_email: Bạn đã nhập một email không hợp lệ email_already_exists: "Bạn đã đăng ký một tài khoản với %{email}" - invalid_params: 'Invalid, please check the signup paramters and try again' + invalid_params: 'Không hợp lệ, vui lòng kiểm tra thông số đăng ký và thử lại' failed: Đăng ký thât bại data_import: data_type: @@ -51,7 +51,7 @@ vi: dyte: invalid_message_type: "Loại tin nhắn không hợp lệ. Hành động không được phép" slack: - invalid_channel_id: "Invalid slack channel. Please try again" + invalid_channel_id: "Kênh chùng không hợp lệ. Vui lòng thử lại" inboxes: imap: socket_error: Vui lòng kiểm tra kết nối mạng, địa chỉ IMAP và thử lại. @@ -63,43 +63,43 @@ vi: name: không nên bắt đầu hoặc kết thúc bằng các ký hiệu và không nên có kí tự < > / \ @. custom_filters: number_of_records: Đã đạt giới hạn. Số lượng tuỳ chọn lọc tối đa cho mỗi mỗi người dùng mỗi tài khoản là 50. - invalid_attribute: Invalid attribute key - [%{key}]. The key should be one of [%{allowed_keys}] or a custom attribute defined in the account. - invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}]. - invalid_value: Invalid value. The values provided for %{attribute_name} are invalid + invalid_attribute: Khóa thuộc tính không hợp lệ - [%{key}]. Chìa khóa phải là một trong [%{allowed_keys}] hoặc thuộc tính tùy chỉnh được xác định trong tài khoản. + invalid_operator: Toán tử không hợp lệ. Các toán tử được phép cho %{attribute_name} là [%{allowed_keys}]. + invalid_value: Giá trị không hợp lệ. Các giá trị được cung cấp cho %{attribute_name} không hợp lệ reports: period: Thời gian báo cáo từ %{since} đến %{until} utc_warning: Báo cáo đã được tạo với múi giờ UTC agent_csv: agent_name: Tên tổng đài viên - conversations_count: Assigned conversations - avg_first_response_time: Avg first response time + conversations_count: Cuộc trò chuyện được chỉ định + avg_first_response_time: Thời gian phản hồi đầu tiên trung bình avg_resolution_time: Avg resolution time resolution_count: Số lượng giải quyết - avg_customer_waiting_time: Avg customer waiting time + avg_customer_waiting_time: Thời gian chờ đợi trung bình của khách hàng inbox_csv: inbox_name: Tên kênh inbox_type: Kiểu kênh conversations_count: Số hội thoại - avg_first_response_time: Avg first response time - avg_resolution_time: Avg resolution time + avg_first_response_time: Thời gian phản hồi đầu tiên trung bình + avg_resolution_time: Thời gian giải quyết trung bình label_csv: label_title: Nhãn conversations_count: Số hội thoại - avg_first_response_time: Avg first response time - avg_resolution_time: Avg resolution time + avg_first_response_time: Thời gian phản hồi đầu tiên trung bình + avg_resolution_time: Thời gian giải quyết trung bình team_csv: team_name: Tên nhóm conversations_count: Số hội thoại - avg_first_response_time: Avg first response time - avg_resolution_time: Avg resolution time + avg_first_response_time: Thời gian phản hồi đầu tiên trung bình + avg_resolution_time: Thời gian giải quyết trung bình resolution_count: Số lượng giải quyết - avg_customer_waiting_time: Avg customer waiting time + avg_customer_waiting_time: Thời gian chờ đợi trung bình của khách hàng conversation_traffic_csv: timezone: Múi giờ sla_csv: - conversation_id: Conversation ID + conversation_id: ID hội thoại sla_policy_breached: SLA Policy - assignee: Assignee + assignee: Đại lý được chỉ định team: Nhóm inbox: Hộp thư đến labels: Nhãn @@ -118,22 +118,22 @@ vi: recorded_at: Ngày nghi notifications: notification_title: - conversation_creation: "A conversation (#%{display_id}) has been created in %{inbox_name}" - conversation_assignment: "A conversation (#%{display_id}) has been assigned to you" - assigned_conversation_new_message: "A new message is created in conversation (#%{display_id})" - conversation_mention: "You have been mentioned in conversation (#%{display_id})" - sla_missed_first_response: "SLA target first response missed for conversation (#%{display_id})" - sla_missed_next_response: "SLA target next response missed for conversation (#%{display_id})" - sla_missed_resolution: "SLA target resolution missed for conversation (#%{display_id})" - attachment: "Attachment" - no_content: "No content" + conversation_creation: "Một cuộc trò chuyện (#%{display_id}) đã được tạo trong %{inbox_name}" + conversation_assignment: "Một cuộc trò chuyện (#%{display_id}) đã được chỉ định cho bạn" + assigned_conversation_new_message: "Một tin nhắn mới được tạo trong cuộc trò chuyện (#%{display_id})" + conversation_mention: "Bạn đã được nhắc đến trong cuộc trò chuyện (#%{display_id})" + sla_missed_first_response: "Mục tiêu SLA phản hồi đầu tiên bị bỏ lỡ cho cuộc trò chuyện (#%{display_id})" + sla_missed_next_response: "Mục tiêu SLA phản hồi tiếp theo bị bỏ lỡ cho cuộc trò chuyện (#%{display_id})" + sla_missed_resolution: "Độ phân giải mục tiêu SLA bị bỏ lỡ cho cuộc trò chuyện (#%{display_id})" + attachment: "Tập tin đính kèm" + no_content: "Không có nội dung" conversations: messages: instagram_story_content: "%{story_sender} đã đề cập đến bạn trong hội thoại: " instagram_deleted_story_content: Hội thoại này không còn nữa. deleted: Tin nhắn đã bị xoá delivery_status: - error_code: "Error code: %{error_code}" + error_code: "Mã lỗi: %{error_code}" activity: status: resolved: "Cuộc trò chuyện được đánh dấu là đã giải quyết bởi %{user_name}" @@ -221,7 +221,7 @@ vi: common: home: Trang Chủ last_updated_on: 'Cập nhật lần cuối: %{last_updated_on}' - view_all_articles: View all + view_all_articles: Xem tất cả article: bài viết articles: bài viết author: tác giả @@ -233,17 +233,17 @@ vi: footer: made_with: Tạo bởi header: - go_to_homepage: Website + go_to_homepage: Trang web appearance: - system: System - light: Light - dark: Dark - featured_articles: Featured Articles + system: Hệ thống + light: Sáng + dark: Tối + featured_articles: Bài viết nổi bật uncategorized: Chưa được phân loại 404: - title: Page not found - description: We couldn't find the page you were looking for. - back_to_home: Go to home page + title: Không tìm thấy trang + description: Chúng tôi không thể tìm thấy trang bạn đang tìm kiếm. + back_to_home: Tới trang chủ slack_unfurl: fields: name: Tên @@ -255,10 +255,10 @@ vi: button: Mở cuộc trò chuyện time_units: days: - other: "%{count} days" + other: "%{count} ngày" hours: - other: "%{count} hours" + other: "%{count} giờ" minutes: - other: "%{count} minutes" + other: "%{count} phút" seconds: - other: "%{count} seconds" + other: "%{count} giây" From 8f3234cf4bbab7c170e4cd8d0a8097e1709b2c9b Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Date: Thu, 27 Jun 2024 01:51:41 +0530 Subject: [PATCH 5/6] feat: Add video message viewing in to the user bubble in widget (#9642) --- .../widget/components/UserMessage.vue | 16 +++++++++++ .../widget/components/VideoBubble.vue | 27 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 app/javascript/widget/components/VideoBubble.vue diff --git a/app/javascript/widget/components/UserMessage.vue b/app/javascript/widget/components/UserMessage.vue index 0631b2f90..5a97422db 100755 --- a/app/javascript/widget/components/UserMessage.vue +++ b/app/javascript/widget/components/UserMessage.vue @@ -39,6 +39,14 @@ :readable-time="readableTime" @error="onImageLoadError" /> + + + +defineProps({ + url: { type: String, default: '' }, + readableTime: { type: String, default: '' }, +}); + +const emits = defineEmits(['error']); + +const onVideoError = () => { + emits('error'); +}; + + From 7ed7c1b618b3212220f949cd56871d5b8c5a02e8 Mon Sep 17 00:00:00 2001 From: Sojan Date: Wed, 26 Jun 2024 17:04:56 -0700 Subject: [PATCH 6/6] Bump version to 3.10.2 --- config/app.yml | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/app.yml b/config/app.yml index 8a133ee53..b8c238254 100644 --- a/config/app.yml +++ b/config/app.yml @@ -1,5 +1,5 @@ shared: &shared - version: '3.10.1' + version: '3.10.2' development: <<: *shared diff --git a/package.json b/package.json index 6b017503e..79ac41e02 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@chatwoot/chatwoot", - "version": "3.10.1", + "version": "3.10.2", "license": "MIT", "scripts": { "eslint": "eslint app/**/*.{js,vue}",