From bcdb73502ecb1a535b88a72422f136e9eabba2a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 22:15:48 +0530 Subject: [PATCH 1/3] chore(deps): bump addressable from 2.8.7 to 2.9.0 (#14019) Bumps [addressable](https://github.com/sporkmonger/addressable) from 2.8.7 to 2.9.0.
Changelog

Sourced from addressable's changelog.

Addressable 2.9.0

Addressable 2.8.10

Addressable 2.8.9

#569: sporkmonger/addressable#569 #571: sporkmonger/addressable#571 #564: sporkmonger/addressable#564

Addressable 2.8.8

#561: sporkmonger/addressable#561 #558: sporkmonger/addressable#558

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=addressable&package-manager=bundler&previous-version=2.8.7&new-version=2.9.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/chatwoot/chatwoot/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sony Mathew --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 7d29e0b02..5912172f5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -108,8 +108,8 @@ GEM acts-as-taggable-on (12.0.0) activerecord (>= 7.1, < 8.1) zeitwerk (>= 2.4, < 3.0) - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) + addressable (2.9.0) + public_suffix (>= 2.0.2, < 8.0) administrate (0.20.1) actionpack (>= 6.0, < 8.0) actionview (>= 6.0, < 8.0) @@ -676,7 +676,7 @@ GEM method_source (~> 1.0) pry-rails (0.3.9) pry (>= 0.10.4) - public_suffix (6.0.2) + public_suffix (7.0.5) puma (6.4.3) nio4r (~> 2.0) pundit (2.3.0) From 80fccbc5267e0dbfeceb4732d911e694298a6df6 Mon Sep 17 00:00:00 2001 From: Lomuzord Date: Wed, 29 Apr 2026 19:49:52 +0200 Subject: [PATCH 2/3] fix: render slack emoji shortcodes as unicode characters (#12928) This PR fixes an issue where Slack emojis are rendered as text shortcodes (e.g. :rocket:) instead of the actual emoji characters in Chatwoot messages. It introduces a new EmojiFormatter class that uses the emoji-data mapping to convert shortcodes to unicode characters. --------- Co-authored-by: Sony Mathew Co-authored-by: Sony Mathew <2040199+sony-mathew@users.noreply.github.com> --- Gemfile | 1 + Gemfile.lock | 2 ++ lib/integrations/slack/emoji_formatter.rb | 16 +++++++++++++++ .../slack/slack_message_helper.rb | 7 ++++++- .../slack/emoji_formatter_spec.rb | 20 +++++++++++++++++++ 5 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 lib/integrations/slack/emoji_formatter.rb create mode 100644 spec/lib/integrations/slack/emoji_formatter_spec.rb diff --git a/Gemfile b/Gemfile index c4989c538..8c0bb82a5 100644 --- a/Gemfile +++ b/Gemfile @@ -22,6 +22,7 @@ gem 'time_diff' gem 'tzinfo-data' gem 'valid_email2' gem 'email-provider-info' +gem 'gemoji' # compress javascript config.assets.js_compressor gem 'uglifier' ##-- used for single column multiple binary flags in notification settings/feature flagging --## diff --git a/Gemfile.lock b/Gemfile.lock index 5912172f5..d85999b57 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -349,6 +349,7 @@ GEM googleapis-common-protos-types (>= 1.3.1, < 2.a) googleauth (~> 1.0) grpc (~> 1.36) + gemoji (4.1.0) geocoder (1.8.1) gli (2.22.2) ostruct @@ -1075,6 +1076,7 @@ DEPENDENCIES fcm flag_shih_tzu foreman + gemoji geocoder gmail_xoauth google-cloud-dialogflow-v2 (>= 0.24.0) diff --git a/lib/integrations/slack/emoji_formatter.rb b/lib/integrations/slack/emoji_formatter.rb new file mode 100644 index 000000000..727c14784 --- /dev/null +++ b/lib/integrations/slack/emoji_formatter.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class Integrations::Slack::EmojiFormatter + def self.format(text) + return text if text.blank? + + text.gsub(/:([a-zA-Z0-9_+-]+):/) do |match| + short_code = Regexp.last_match(1) + # gemoji exposes find_by_alias; Rails/DynamicFindBy is a false positive because Emoji is not an ActiveRecord model. + # rubocop:disable Rails/DynamicFindBy + emoji = Emoji.find_by_alias(short_code) + # rubocop:enable Rails/DynamicFindBy + emoji ? emoji.raw : match + end + end +end diff --git a/lib/integrations/slack/slack_message_helper.rb b/lib/integrations/slack/slack_message_helper.rb index 3af57a4c3..110156b06 100644 --- a/lib/integrations/slack/slack_message_helper.rb +++ b/lib/integrations/slack/slack_message_helper.rb @@ -35,7 +35,7 @@ module Integrations::Slack::SlackMessageHelper message_type: :outgoing, account_id: conversation.account_id, inbox_id: conversation.inbox_id, - content: Slack::Messages::Formatting.unescape(params[:event][:text] || ''), + content: formatted_message_content, external_source_id_slack: params[:event][:ts], private: private_note?, sender: resolved_sender, @@ -104,6 +104,11 @@ module Integrations::Slack::SlackMessageHelper [nil, nil, nil] end + def formatted_message_content + text = Slack::Messages::Formatting.unescape(params[:event][:text] || '') + Integrations::Slack::EmojiFormatter.format(text) + end + def private_note? params[:event][:text].strip.downcase.starts_with?('note:', 'private:') end diff --git a/spec/lib/integrations/slack/emoji_formatter_spec.rb b/spec/lib/integrations/slack/emoji_formatter_spec.rb new file mode 100644 index 000000000..d34706e43 --- /dev/null +++ b/spec/lib/integrations/slack/emoji_formatter_spec.rb @@ -0,0 +1,20 @@ +require 'rails_helper' + +describe Integrations::Slack::EmojiFormatter do + describe '.format' do + it 'replaces emoji shortcodes with unicode characters' do + expect(described_class.format('Hello :smile:')).to eq('Hello 😄') + expect(described_class.format('Good job :+1:')).to eq('Good job 👍') + expect(described_class.format('Unknown :unknown_emoji:')).to eq('Unknown :unknown_emoji:') + end + + it 'handles nil or empty text' do + expect(described_class.format(nil)).to be_nil + expect(described_class.format('')).to eq('') + end + + it 'replaces multiple emojis' do + expect(described_class.format(':smile: :+1:')).to eq('😄 👍') + end + end +end From cd9c8e3303a89e5be3eebe85fa1c385acb07f73a Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 29 Apr 2026 19:57:14 +0200 Subject: [PATCH 3/3] fix: skip self-mention notification in private notes (#14318) When an agent mentions themselves in a private note, they no longer receive a redundant notification for their own mention. Closes: #4096 # Pull Request Template ## Description Agents who mention themselves in a private note no longer receive a conversation_mention notification. Previously, the mention service would generate a notification for every mentioned user without checking whether the sender and the mentioned user were the same person. --- app/services/messages/mention_service.rb | 6 ++++ .../services/messages/mention_service_spec.rb | 30 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/app/services/messages/mention_service.rb b/app/services/messages/mention_service.rb index 43bc17fb8..8171f8168 100644 --- a/app/services/messages/mention_service.rb +++ b/app/services/messages/mention_service.rb @@ -50,6 +50,8 @@ class Messages::MentionService def generate_notifications_for_mentions(validated_mentioned_ids) validated_mentioned_ids.each do |user_id| + next if self_mention?(user_id) + NotificationBuilder.new( notification_type: 'conversation_mention', user: User.find(user_id), @@ -60,6 +62,10 @@ class Messages::MentionService end end + def self_mention?(user_id) + message.sender_type == 'User' && user_id.to_i == message.sender_id + end + def add_mentioned_users_as_participants(validated_mentioned_ids) validated_mentioned_ids.each do |user_id| message.conversation.conversation_participants.find_or_create_by(user_id: user_id) diff --git a/spec/services/messages/mention_service_spec.rb b/spec/services/messages/mention_service_spec.rb index 7cdb8ffe7..a7bddcc4e 100644 --- a/spec/services/messages/mention_service_spec.rb +++ b/spec/services/messages/mention_service_spec.rb @@ -165,6 +165,36 @@ describe Messages::MentionService do end end + context 'when the message sender mentions themselves' do + it 'skips the sender notification while notifying other mentioned users' do + message = build( + :message, + conversation: conversation, + account: account, + content: "hey (mention://user/#{first_agent.id}/#{first_agent.name}) and (mention://user/#{second_agent.id}/#{second_agent.name})", + private: true, + sender: first_agent + ) + + described_class.new(message: message).perform + + expect(NotificationBuilder).not_to have_received(:new).with( + notification_type: 'conversation_mention', + user: first_agent, + account: account, + primary_actor: message.conversation, + secondary_actor: message + ) + expect(NotificationBuilder).to have_received(:new).with( + notification_type: 'conversation_mention', + user: second_agent, + account: account, + primary_actor: message.conversation, + secondary_actor: message + ) + end + end + context 'when mentioned user is not an inbox member' do let!(:non_member_user) { create(:user, account: account) }