diff --git a/.github/workflows/plugin-linting.yml b/.github/workflows/plugin-linting.yml index 61610901b..1fa91eae6 100644 --- a/.github/workflows/plugin-linting.yml +++ b/.github/workflows/plugin-linting.yml @@ -55,3 +55,7 @@ jobs: - name: Rubocop if: ${{ always() }} run: bundle exec rubocop . + + - name: Syntax Tree + if: ${{ always() }} + run: bundle exec stree check --print-width=100 --plugins=plugin/trailing_comma **/*.rb Gemfile **/*.rake diff --git a/Gemfile b/Gemfile index 7da32ec03..3142de0a7 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,8 @@ # frozen_string_literal: true -source 'https://rubygems.org' +source "https://rubygems.org" group :development do - gem 'rubocop-discourse' + gem "rubocop-discourse", git: "https://github.com/discourse/rubocop-discourse/", branch: "stree" + gem "syntax_tree" end diff --git a/Gemfile.lock b/Gemfile.lock index 9c730491b..4a75a02c1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,14 +1,26 @@ +GIT + remote: https://github.com/discourse/rubocop-discourse/ + revision: 8afca6460a423a11a2e0bf1f7051b18dd9a7231b + branch: stree + specs: + rubocop-discourse (2.5.0) + rubocop (>= 1.1.0) + rubocop-rspec (>= 2.0.0) + GEM remote: https://rubygems.org/ specs: ast (2.4.2) + json (2.6.2) parallel (1.22.1) parser (3.1.2.0) ast (~> 2.4.1) + prettier_print (0.1.0) rainbow (3.1.1) regexp_parser (2.5.0) rexml (3.2.5) - rubocop (1.30.1) + rubocop (1.31.2) + json (~> 2.3) parallel (~> 1.10) parser (>= 3.1.0.0) rainbow (>= 2.2.2, < 4.0) @@ -17,15 +29,14 @@ GEM rubocop-ast (>= 1.18.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.18.0) + rubocop-ast (1.19.1) parser (>= 3.1.1.0) - rubocop-discourse (2.5.0) - rubocop (>= 1.1.0) - rubocop-rspec (>= 2.0.0) - rubocop-rspec (2.11.1) - rubocop (~> 1.19) + rubocop-rspec (2.12.1) + rubocop (~> 1.31) ruby-progressbar (1.11.0) - unicode-display_width (2.1.0) + syntax_tree (3.2.0) + prettier_print + unicode-display_width (2.2.0) PLATFORMS arm64-darwin-20 @@ -36,7 +47,8 @@ PLATFORMS x86_64-linux DEPENDENCIES - rubocop-discourse + rubocop-discourse! + syntax_tree BUNDLED WITH 2.3.14 diff --git a/app/controllers/admin/admin_incoming_chat_webhooks_controller.rb b/app/controllers/admin/admin_incoming_chat_webhooks_controller.rb index d590dd0a6..a8eb1e465 100644 --- a/app/controllers/admin/admin_incoming_chat_webhooks_controller.rb +++ b/app/controllers/admin/admin_incoming_chat_webhooks_controller.rb @@ -4,19 +4,21 @@ class DiscourseChat::AdminIncomingChatWebhooksController < Admin::AdminControlle requires_plugin DiscourseChat::PLUGIN_NAME def index - render_serialized({ - chat_channels: ChatChannel.public_channels, - incoming_chat_webhooks: IncomingChatWebhook.includes(:chat_channel).all - }, AdminChatIndexSerializer, root: false) + render_serialized( + { + chat_channels: ChatChannel.public_channels, + incoming_chat_webhooks: IncomingChatWebhook.includes(:chat_channel).all, + }, + AdminChatIndexSerializer, + root: false, + ) end def create - params.require([:name, :chat_channel_id]) + params.require(%i[name chat_channel_id]) chat_channel = ChatChannel.find_by(id: params[:chat_channel_id]) - if chat_channel.nil? || chat_channel.direct_message_channel? - raise Discourse::NotFound - end + raise Discourse::NotFound if chat_channel.nil? || chat_channel.direct_message_channel? webhook = IncomingChatWebhook.new(name: params[:name], chat_channel: chat_channel) if webhook.save @@ -27,23 +29,21 @@ def create end def update - params.require([:incoming_chat_webhook_id, :name, :chat_channel_id]) + params.require(%i[incoming_chat_webhook_id name chat_channel_id]) webhook = IncomingChatWebhook.find_by(id: params[:incoming_chat_webhook_id]) raise Discourse::NotFound unless webhook chat_channel = ChatChannel.find_by(id: params[:chat_channel_id]) - if chat_channel.nil? || chat_channel.direct_message_channel? - raise Discourse::NotFound - end + raise Discourse::NotFound if chat_channel.nil? || chat_channel.direct_message_channel? if webhook.update( - name: params[:name], - description: params[:description], - emoji: params[:emoji], - username: params[:username], - chat_channel: chat_channel - ) + name: params[:name], + description: params[:description], + emoji: params[:emoji], + username: params[:username], + chat_channel: chat_channel, + ) render json: success_json else render_json_error(webhook) diff --git a/app/controllers/api/category_chatables_controller.rb b/app/controllers/api/category_chatables_controller.rb index 4c74207fe..4e406fb16 100644 --- a/app/controllers/api/category_chatables_controller.rb +++ b/app/controllers/api/category_chatables_controller.rb @@ -5,17 +5,22 @@ def permissions category = Category.find(params[:id]) if category.read_restricted? - permissions = Group - .joins(:category_groups) - .where(category_groups: { category_id: category.id }) - .joins('LEFT OUTER JOIN group_users ON groups.id = group_users.group_id') - .group('groups.id', 'groups.name') - .pluck('groups.name', 'COUNT(group_users.user_id)') + permissions = + Group + .joins(:category_groups) + .where(category_groups: { category_id: category.id }) + .joins("LEFT OUTER JOIN group_users ON groups.id = group_users.group_id") + .group("groups.id", "groups.name") + .pluck("groups.name", "COUNT(group_users.user_id)") - group_names = permissions.map { |p| "@#{p[0]}" } - members_count = permissions.sum { |p| p[1].to_i } + group_names = permissions.map { |p| "@#{p[0]}" } + members_count = permissions.sum { |p| p[1].to_i } - permissions_result = { allowed_groups: group_names, members_count: members_count, private: true } + permissions_result = { + allowed_groups: group_names, + members_count: members_count, + private: true, + } else everyone_group = Group.find(Group::AUTO_GROUPS[:everyone]) diff --git a/app/controllers/api/chat_channel_memberships_controller.rb b/app/controllers/api/chat_channel_memberships_controller.rb index df4a9bf2f..c682e22f5 100644 --- a/app/controllers/api/chat_channel_memberships_controller.rb +++ b/app/controllers/api/chat_channel_memberships_controller.rb @@ -7,12 +7,13 @@ def index offset = (params[:offset] || 0).to_i limit = (params[:limit] || 50).to_i.clamp(1, 50) - memberships = ChatChannelMembershipsQuery.call( - channel, - offset: offset, - limit: limit, - username: params[:username] - ) + memberships = + ChatChannelMembershipsQuery.call( + channel, + offset: offset, + limit: limit, + username: params[:username], + ) render_serialized(memberships, UserChatChannelMembershipSerializer, root: false) end diff --git a/app/controllers/api/chat_channel_notifications_settings_controller.rb b/app/controllers/api/chat_channel_notifications_settings_controller.rb index 95c0fb5cd..fc8bd22b6 100644 --- a/app/controllers/api/chat_channel_notifications_settings_controller.rb +++ b/app/controllers/api/chat_channel_notifications_settings_controller.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -MEMBERSHIP_EDITABLE_PARAMS = [:muted, :desktop_notification_level, :mobile_notification_level] +MEMBERSHIP_EDITABLE_PARAMS = %i[muted desktop_notification_level mobile_notification_level] class DiscourseChat::Api::ChatChannelNotificationsSettingsController < DiscourseChat::Api::ChatChannelsController def update diff --git a/app/controllers/api/chat_channels_controller.rb b/app/controllers/api/chat_channels_controller.rb index 565d29e51..538bd5566 100644 --- a/app/controllers/api/chat_channels_controller.rb +++ b/app/controllers/api/chat_channels_controller.rb @@ -5,15 +5,16 @@ class DiscourseChat::Api::ChatChannelsController < DiscourseChat::Api def index - options = { - status: params[:status] ? ChatChannel.statuses[params[:status]] : nil - }.merge(params.permit(:filter, :limit, :offset)).symbolize_keys! - - channels = DiscourseChat::ChatChannelFetcher.secured_public_channels( - guardian, - UserChatChannelMembership.where(user: current_user), - options - ) + options = { status: params[:status] ? ChatChannel.statuses[params[:status]] : nil }.merge( + params.permit(:filter, :limit, :offset), + ).symbolize_keys! + + channels = + DiscourseChat::ChatChannelFetcher.secured_public_channels( + guardian, + UserChatChannelMembership.where(user: current_user), + options, + ) render_serialized(channels, ChatChannelSerializer) end @@ -24,13 +25,13 @@ def update chat_channel = find_chat_channel if chat_channel.direct_message_channel? - raise Discourse::InvalidParameters.new(I18n.t("chat.errors.cant_update_direct_message_channel")) + raise Discourse::InvalidParameters.new( + I18n.t("chat.errors.cant_update_direct_message_channel"), + ) end params_to_edit = editable_params(params, chat_channel) - params_to_edit.each do |k, v| - params_to_edit[k] = nil if params_to_edit[k].blank? - end + params_to_edit.each { |k, v| params_to_edit[k] = nil if params_to_edit[k].blank? } if ActiveRecord::Type::Boolean.new.deserialize(params_to_edit[:auto_join_users]) auto_join_limiter(chat_channel).performed! @@ -56,23 +57,29 @@ def find_chat_channel end def find_membership - membership = UserChatChannelMembership - .includes(:user, :chat_channel) - .find_by!(user: current_user, chat_channel_id: params.require(:chat_channel_id)) + membership = + UserChatChannelMembership.includes(:user, :chat_channel).find_by!( + user: current_user, + chat_channel_id: params.require(:chat_channel_id), + ) guardian.ensure_can_see_chat_channel!(membership.chat_channel) membership end def auto_join_limiter(chat_channel) - RateLimiter.new(current_user, "auto_join_users_channel_#{chat_channel.id}", 1, 3.minutes, apply_limit_to_staff: true) + RateLimiter.new( + current_user, + "auto_join_users_channel_#{chat_channel.id}", + 1, + 3.minutes, + apply_limit_to_staff: true, + ) end def editable_params(params, chat_channel) permitted_params = CHAT_CHANNEL_EDITABLE_PARAMS - if chat_channel.category_channel? - permitted_params += CATEGORY_CHAT_CHANNEL_EDITABLE_PARAMS - end + permitted_params += CATEGORY_CHAT_CHANNEL_EDITABLE_PARAMS if chat_channel.category_channel? params.permit(*permitted_params) end diff --git a/app/controllers/chat_base_controller.rb b/app/controllers/chat_base_controller.rb index e6fe0b40f..58cc7fd8e 100644 --- a/app/controllers/chat_base_controller.rb +++ b/app/controllers/chat_base_controller.rb @@ -12,13 +12,9 @@ def ensure_can_chat end def set_channel_and_chatable_with_access_check(chat_channel_id: nil) - if chat_channel_id.blank? - params.require(:chat_channel_id) - end + params.require(:chat_channel_id) if chat_channel_id.blank? id_or_name = chat_channel_id || params[:chat_channel_id] - @chat_channel = DiscourseChat::ChatChannelFetcher.find_with_access_check( - id_or_name, guardian - ) + @chat_channel = DiscourseChat::ChatChannelFetcher.find_with_access_check(id_or_name, guardian) @chatable = @chat_channel.chatable end end diff --git a/app/controllers/chat_channels_controller.rb b/app/controllers/chat_channels_controller.rb index d5341dcec..898e661b8 100644 --- a/app/controllers/chat_channels_controller.rb +++ b/app/controllers/chat_channels_controller.rb @@ -1,11 +1,7 @@ # frozen_string_literal: true class DiscourseChat::ChatChannelsController < DiscourseChat::ChatBaseController - before_action :set_channel_and_chatable_with_access_check, except: [ - :index, - :create, - :search - ] + before_action :set_channel_and_chatable_with_access_check, except: %i[index create search] def index structured = DiscourseChat::ChatChannelFetcher.structured(guardian) @@ -29,15 +25,14 @@ def unfollow end def create - params.require([:id, :name]) + params.require(%i[id name]) guardian.ensure_can_create_chat_channel! - raise Discourse::InvalidParameters.new(:name) if params[:name].length > SiteSetting.max_topic_title_length + if params[:name].length > SiteSetting.max_topic_title_length + raise Discourse::InvalidParameters.new(:name) + end - exists = ChatChannel.exists?( - chatable_type: 'Category', - chatable_id: params[:id], - name: params[:name] - ) + exists = + ChatChannel.exists?(chatable_type: "Category", chatable_id: params[:id], name: params[:name]) if exists raise Discourse::InvalidParameters.new(I18n.t("chat.errors.channel_exists_for_category")) end @@ -47,13 +42,14 @@ def create auto_join_users = ActiveRecord::Type::Boolean.new.deserialize(params[:auto_join_users]) || false - chat_channel = ChatChannel.create!( - chatable: chatable, - name: params[:name], - description: params[:description], - user_count: 1, - auto_join_users: auto_join_users - ) + chat_channel = + ChatChannel.create!( + chatable: chatable, + name: params[:name], + description: params[:description], + user_count: 1, + auto_join_users: auto_join_users, + ) chat_channel.user_chat_channel_memberships.create!(user: current_user, following: true) if chat_channel.auto_join_users @@ -84,13 +80,14 @@ def search params.require(:filter) filter = params[:filter]&.downcase memberships = UserChatChannelMembership.where(user: current_user) - public_channels = DiscourseChat::ChatChannelFetcher.secured_public_channels( - guardian, - memberships, - following: false, - filter: filter, - status: :open - ) + public_channels = + DiscourseChat::ChatChannelFetcher.secured_public_channels( + guardian, + memberships, + following: false, + filter: filter, + status: :open, + ) users = User.joins(:user_option).where.not(id: current_user.id) if !DiscourseChat.allowed_group_ids.include?(Group::AUTO_GROUPS[:everyone]) @@ -102,24 +99,37 @@ def search if SiteSetting.prioritize_username_in_ux || !SiteSetting.enable_names users = users.where("users.username_lower ILIKE ?", like_filter) else - users = users.where("LOWER(users.name) ILIKE ? OR users.username_lower ILIKE ?", like_filter, like_filter) + users = + users.where( + "LOWER(users.name) ILIKE ? OR users.username_lower ILIKE ?", + like_filter, + like_filter, + ) end users = users.limit(25).uniq - direct_message_channels = users.count > 0 ? - ChatChannel - .includes(chatable: :users) - .joins(direct_message_channel: :direct_message_users) - .group(1) - .having("ARRAY[?] <@ ARRAY_AGG(user_id) AND ARRAY[?] && ARRAY_AGG(user_id)", [current_user.id], users.map(&:id)) : [] + direct_message_channels = + ( + if users.count > 0 + ChatChannel + .includes(chatable: :users) + .joins(direct_message_channel: :direct_message_users) + .group(1) + .having( + "ARRAY[?] <@ ARRAY_AGG(user_id) AND ARRAY[?] && ARRAY_AGG(user_id)", + [current_user.id], + users.map(&:id), + ) + else + [] + end + ) user_ids_with_channel = [] direct_message_channels.each do |dm_channel| user_ids = dm_channel.chatable.users.map(&:id) - if user_ids.count < 3 - user_ids_with_channel.concat(user_ids) - end + user_ids_with_channel.concat(user_ids) if user_ids.count < 3 end users_without_channel = users.filter { |u| !user_ids_with_channel.include?(u.id) } @@ -130,11 +140,15 @@ def search users_without_channel << current_user end - render_serialized({ - public_channels: public_channels, - direct_message_channels: direct_message_channels, - users: users_without_channel - }, ChatChannelSearchSerializer, root: false) + render_serialized( + { + public_channels: public_channels, + direct_message_channels: direct_message_channels, + users: users_without_channel, + }, + ChatChannelSearchSerializer, + root: false, + ) end def archive @@ -155,8 +169,8 @@ def archive topic_id: params[:topic_id], topic_title: params[:title], category_id: params[:category_id], - tags: params[:tags] - } + tags: params[:tags], + }, ) render json: success_json @@ -179,9 +193,8 @@ def change_status # we only want to use this endpoint for open/closed status changes, # the others are more "special" and are handled by the archive endpoint - if !ChatChannel.statuses.keys.include?(params[:status]) || - params[:status] == "read_only" || - params[:status] == "archive" + if !ChatChannel.statuses.keys.include?(params[:status]) || params[:status] == "read_only" || + params[:status] == "archive" raise Discourse::InvalidParameters end @@ -207,8 +220,8 @@ def destroy "chat_channel_delete", { chat_channel_id: @chat_channel.id, - chat_channel_name: @chat_channel.title(current_user) - } + chat_channel_name: @chat_channel.title(current_user), + }, ) end rescue ActiveRecord::Rollback diff --git a/app/controllers/chat_controller.rb b/app/controllers/chat_controller.rb index 490ebc2ca..47ff39d0e 100644 --- a/app/controllers/chat_controller.rb +++ b/app/controllers/chat_controller.rb @@ -3,32 +3,27 @@ class DiscourseChat::ChatController < DiscourseChat::ChatBaseController PAST_MESSAGE_LIMIT = 20 FUTURE_MESSAGE_LIMIT = 40 - PAST = 'past' - FUTURE = 'future' + PAST = "past" + FUTURE = "future" CHAT_DIRECTIONS = [PAST, FUTURE] # Other endpoints use set_channel_and_chatable_with_access_check, but # these endpoints require a standalone find because they need to be # able to get deleted channels and recover them. - before_action :find_chatable, only: [:enable_chat, :disable_chat] - before_action :find_chat_message, only: [ - :delete, - :restore, - :lookup_message, - :edit_message, - :rebake, - :message_link - ] - before_action :set_channel_and_chatable_with_access_check, except: [ - :respond, - :enable_chat, - :disable_chat, - :message_link, - :lookup_message, - :set_user_chat_status, - :dismiss_retention_reminder, - :flag - ] + before_action :find_chatable, only: %i[enable_chat disable_chat] + before_action :find_chat_message, + only: %i[delete restore lookup_message edit_message rebake message_link] + before_action :set_channel_and_chatable_with_access_check, + except: %i[ + respond + enable_chat + disable_chat + message_link + lookup_message + set_user_chat_status + dismiss_retention_reminder + flag + ] def respond render @@ -37,9 +32,7 @@ def respond def enable_chat chat_channel = ChatChannel.with_deleted.find_by(chatable: @chatable) - if chat_channel - guardian.ensure_can_see_chat_channel!(chat_channel) - end + guardian.ensure_can_see_chat_channel!(chat_channel) if chat_channel if chat_channel && chat_channel.trashed? chat_channel.recover! @@ -57,10 +50,11 @@ def enable_chat end if success - membership = UserChatChannelMembership.find_or_initialize_by( - chat_channel: chat_channel, - user: current_user - ) + membership = + UserChatChannelMembership.find_or_initialize_by( + chat_channel: chat_channel, + user: current_user, + ) membership.following = true membership.save! render_serialized(chat_channel, ChatChannelSerializer) @@ -72,9 +66,7 @@ def enable_chat def disable_chat chat_channel = ChatChannel.with_deleted.find_by(chatable: @chatable) guardian.ensure_can_see_chat_channel!(chat_channel) - if chat_channel.trashed? - return render json: success_json - end + return render json: success_json if chat_channel.trashed? chat_channel.trash!(current_user) success = chat_channel.save @@ -93,11 +85,12 @@ def disable_chat def create_message DiscourseChat::ChatMessageRateLimiter.run!(current_user) - @user_chat_channel_membership = UserChatChannelMembership.find_by( - chat_channel: @chat_channel, - user: current_user, - following: true - ) + @user_chat_channel_membership = + UserChatChannelMembership.find_by( + chat_channel: @chat_channel, + user: current_user, + following: true, + ) raise Discourse::InvalidAccess unless @user_chat_channel_membership reply_to_msg_id = params[:in_reply_to_id] @@ -108,23 +101,20 @@ def create_message content = params[:message] - chat_message_creator = DiscourseChat::ChatMessageCreator.create( - chat_channel: @chat_channel, - user: current_user, - in_reply_to_id: reply_to_msg_id, - content: content, - staged_id: params[:staged_id], - upload_ids: params[:upload_ids] - ) + chat_message_creator = + DiscourseChat::ChatMessageCreator.create( + chat_channel: @chat_channel, + user: current_user, + in_reply_to_id: reply_to_msg_id, + content: content, + staged_id: params[:staged_id], + upload_ids: params[:upload_ids], + ) - if chat_message_creator.failed? - return render_json_error(chat_message_creator.error) - end + return render_json_error(chat_message_creator.error) if chat_message_creator.failed? @chat_channel.touch(:last_message_sent_at) - @user_chat_channel_membership.update( - last_read_message_id: chat_message_creator.chat_message.id - ) + @user_chat_channel_membership.update(last_read_message_id: chat_message_creator.chat_message.id) if @chat_channel.direct_message_channel? @chat_channel.user_chat_channel_memberships.update_all(following: true) @@ -134,35 +124,42 @@ def create_message ChatPublisher.publish_user_tracking_state( current_user, @chat_channel.id, - chat_message_creator.chat_message.id + chat_message_creator.chat_message.id, ) render json: success_json end def edit_message guardian.ensure_can_edit_chat!(@message) - chat_message_updater = DiscourseChat::ChatMessageUpdater.update( - chat_message: @message, - new_content: params[:new_message], - upload_ids: params[:upload_ids] || [] - ) + chat_message_updater = + DiscourseChat::ChatMessageUpdater.update( + chat_message: @message, + new_content: params[:new_message], + upload_ids: params[:upload_ids] || [], + ) - if chat_message_updater.failed? - return render_json_error(chat_message_updater.error) - end + return render_json_error(chat_message_updater.error) if chat_message_updater.failed? render json: success_json end def update_user_last_read - membership = UserChatChannelMembership.find_by(user: current_user, chat_channel: @chat_channel, following: true) + membership = + UserChatChannelMembership.find_by( + user: current_user, + chat_channel: @chat_channel, + following: true, + ) raise Discourse::NotFound if membership.nil? if membership.last_read_message_id && params[:message_id].to_i < membership.last_read_message_id raise Discourse::InvalidParameters.new(:message_id) end - unless ChatMessage.with_deleted.exists?(chat_channel_id: @chat_channel.id, id: params[:message_id]) + unless ChatMessage.with_deleted.exists?( + chat_channel_id: @chat_channel.id, + id: params[:message_id], + ) raise Discourse::NotFound end @@ -172,17 +169,13 @@ def update_user_last_read .where(notification_type: Notification.types[:chat_mention]) .where(user: current_user) .where(read: false) - .joins('INNER JOIN chat_mentions ON chat_mentions.notification_id = notifications.id') - .joins('INNER JOIN chat_messages ON chat_mentions.chat_message_id = chat_messages.id') - .where('chat_messages.id <= ?', params[:message_id].to_i) - .where('chat_messages.chat_channel_id = ?', @chat_channel.id) + .joins("INNER JOIN chat_mentions ON chat_mentions.notification_id = notifications.id") + .joins("INNER JOIN chat_messages ON chat_mentions.chat_message_id = chat_messages.id") + .where("chat_messages.id <= ?", params[:message_id].to_i) + .where("chat_messages.chat_channel_id = ?", @chat_channel.id) .update_all(read: true) - ChatPublisher.publish_user_tracking_state( - current_user, - @chat_channel.id, - params[:message_id] - ) + ChatPublisher.publish_user_tracking_state(current_user, @chat_channel.id, params[:message_id]) render json: success_json end @@ -191,7 +184,11 @@ def messages page_size = params[:page_size]&.to_i || 1000 direction = params[:direction].to_s message_id = params[:message_id] - if page_size > 50 || (message_id.blank? ^ direction.blank? && (direction.present? && !CHAT_DIRECTIONS.include?(direction))) + if page_size > 50 || + ( + message_id.blank? ^ direction.blank? && + (direction.present? && !CHAT_DIRECTIONS.include?(direction)) + ) raise Discourse::InvalidParameters end @@ -199,7 +196,7 @@ def messages messages = messages.with_deleted if guardian.can_moderate_chat?(@chatable) if message_id.present? - condition = direction == PAST ? '<' : '>' + condition = direction == PAST ? "<" : ">" messages = messages.where("id #{condition} ?", message_id.to_i) end @@ -221,26 +218,25 @@ def messages can_load_more_past = messages.size == page_size end - chat_view = ChatView.new( - chat_channel: @chat_channel, - chat_messages: direction == FUTURE ? messages : messages.reverse, - user: current_user, - can_load_more_past: can_load_more_past, - can_load_more_future: can_load_more_future - ) + chat_view = + ChatView.new( + chat_channel: @chat_channel, + chat_messages: direction == FUTURE ? messages : messages.reverse, + user: current_user, + can_load_more_past: can_load_more_past, + can_load_more_future: can_load_more_future, + ) render_serialized(chat_view, ChatViewSerializer, root: false) end def react - params.require([:message_id, :emoji, :react_action]) + params.require(%i[message_id emoji react_action]) guardian.ensure_can_react! - DiscourseChat::ChatMessageReactor.new( - current_user, @chat_channel - ).react!( + DiscourseChat::ChatMessageReactor.new(current_user, @chat_channel).react!( message_id: params[:message_id], react_action: params[:react_action].to_sym, - emoji: params[:emoji] + emoji: params[:emoji], ) render json: success_json @@ -280,10 +276,11 @@ def message_link return render_404 if @message.blank? || @message.deleted_at.present? return render_404 if @message.chat_channel.blank? set_channel_and_chatable_with_access_check(chat_channel_id: @message.chat_channel_id) - render json: success_json.merge( - chat_channel_id: @chat_channel.id, - chat_channel_title: @chat_channel.title(current_user) - ) + render json: + success_json.merge( + chat_channel_id: @chat_channel.id, + chat_channel_title: @chat_channel.title(current_user), + ) end def lookup_message @@ -291,26 +288,29 @@ def lookup_message messages = preloaded_chat_message_query.where(chat_channel: @chat_channel) messages = messages.with_deleted if guardian.can_moderate_chat?(@chatable) - past_messages = messages - .where("created_at < ?", @message.created_at) - .order(created_at: :desc) - .limit(PAST_MESSAGE_LIMIT) - - future_messages = messages - .where("created_at > ?", @message.created_at) - .order(created_at: :asc) - .limit(FUTURE_MESSAGE_LIMIT) + past_messages = + messages + .where("created_at < ?", @message.created_at) + .order(created_at: :desc) + .limit(PAST_MESSAGE_LIMIT) + + future_messages = + messages + .where("created_at > ?", @message.created_at) + .order(created_at: :asc) + .limit(FUTURE_MESSAGE_LIMIT) can_load_more_past = past_messages.count == PAST_MESSAGE_LIMIT can_load_more_future = future_messages.count == FUTURE_MESSAGE_LIMIT messages = [past_messages.reverse, [@message], future_messages].reduce([], :concat) - chat_view = ChatView.new( - chat_channel: @chat_channel, - chat_messages: messages, - user: current_user, - can_load_more_past: can_load_more_past, - can_load_more_future: can_load_more_future - ) + chat_view = + ChatView.new( + chat_channel: @chat_channel, + chat_messages: messages, + user: current_user, + can_load_more_past: can_load_more_past, + can_load_more_future: can_load_more_future, + ) render_serialized(chat_view, ChatViewSerializer, root: false) end @@ -324,28 +324,27 @@ def set_user_chat_status def invite_users params.require(:user_ids) - users = User - .includes(:groups) - .joins(:user_option) - .where(user_options: { chat_enabled: true }) - .not_suspended - .where(id: params[:user_ids]) + users = + User + .includes(:groups) + .joins(:user_option) + .where(user_options: { chat_enabled: true }) + .not_suspended + .where(id: params[:user_ids]) users.each do |user| guardian = Guardian.new(user) if guardian.can_chat?(user) && guardian.can_see_chat_channel?(@chat_channel) data = { - message: 'chat.invitation_notification', + message: "chat.invitation_notification", chat_channel_id: @chat_channel.id, chat_channel_title: @chat_channel.title(user), invited_by_username: current_user.username, } - if params[:chat_message_id] - data[:chat_message_id] = params[:chat_message_id] - end + data[:chat_message_id] = params[:chat_message_id] if params[:chat_message_id] user.notifications.create( notification_type: Notification.types[:chat_invitation], high_priority: true, - data: data.to_json + data: data.to_json, ) end end @@ -356,11 +355,18 @@ def invite_users def dismiss_retention_reminder params.require(:chatable_type) guardian.ensure_can_chat!(current_user) - raise Discourse::InvalidParameters unless ChatChannel.chatable_types.include?(params[:chatable_type]) + unless ChatChannel.chatable_types.include?(params[:chatable_type]) + raise Discourse::InvalidParameters + end - field = ChatChannel.public_channel_chatable_types.include?(params[:chatable_type]) ? - :dismissed_channel_retention_reminder : - :dismissed_dm_retention_reminder + field = + ( + if ChatChannel.public_channel_chatable_types.include?(params[:chatable_type]) + :dismissed_channel_retention_reminder + else + :dismissed_dm_retention_reminder + end + ) current_user.user_option.update(field => true) render json: success_json end @@ -369,9 +375,12 @@ def quote_messages params.require(:message_ids) message_ids = params[:message_ids].map(&:to_i) - markdown = ChatTranscriptService.new( - @chat_channel, current_user, messages_or_ids: message_ids - ).generate_markdown + markdown = + ChatTranscriptService.new( + @chat_channel, + current_user, + messages_or_ids: message_ids, + ).generate_markdown render json: success_json.merge(markdown: markdown) end @@ -380,29 +389,36 @@ def move_messages_to_channel params.require(:destination_channel_id) raise Discourse::InvalidAccess if !guardian.can_move_chat_messages?(@chat_channel) - destination_channel = DiscourseChat::ChatChannelFetcher.find_with_access_check(params[:destination_channel_id], guardian) + destination_channel = + DiscourseChat::ChatChannelFetcher.find_with_access_check( + params[:destination_channel_id], + guardian, + ) begin message_ids = params[:message_ids].map(&:to_i) - moved_messages = DiscourseChat::MessageMover.new( - acting_user: current_user, source_channel: @chat_channel, message_ids: message_ids - ).move_to_channel(destination_channel) - rescue DiscourseChat::MessageMover::NoMessagesFound, DiscourseChat::MessageMover::InvalidChannel => err + moved_messages = + DiscourseChat::MessageMover.new( + acting_user: current_user, + source_channel: @chat_channel, + message_ids: message_ids, + ).move_to_channel(destination_channel) + rescue DiscourseChat::MessageMover::NoMessagesFound, + DiscourseChat::MessageMover::InvalidChannel => err return render_json_error(err.message) end - render json: success_json.merge( - destination_channel_id: destination_channel.id, - destination_channel_title: destination_channel.title(current_user), - first_moved_message_id: moved_messages.first.id - ) + render json: + success_json.merge( + destination_channel_id: destination_channel.id, + destination_channel_title: destination_channel.title(current_user), + first_moved_message_id: moved_messages.first.id, + ) end def flag params.require([:chat_message_id]) - chat_message = ChatMessage - .includes(:chat_channel) - .find_by(id: params[:chat_message_id]) + chat_message = ChatMessage.includes(:chat_channel).find_by(id: params[:chat_message_id]) raise Discourse::InvalidParameters unless chat_message set_channel_and_chatable_with_access_check(chat_channel_id: chat_message.chat_channel_id) @@ -419,13 +435,11 @@ def flag def set_draft if params[:data].present? - ChatDraft - .find_or_initialize_by(user: current_user, chat_channel_id: @chat_channel.id) - .update(data: params[:data]) + ChatDraft.find_or_initialize_by(user: current_user, chat_channel_id: @chat_channel.id).update( + data: params[:data], + ) else - ChatDraft - .where(user: current_user, chat_channel_id: @chat_channel.id) - .destroy_all + ChatDraft.where(user: current_user, chat_channel_id: @chat_channel.id).destroy_all end render json: success_json @@ -451,10 +465,8 @@ def find_chatable end def find_chat_message - @message = ChatMessage - .unscoped - .includes(chat_channel: :chatable) - .find_by(id: params[:message_id]) + @message = + ChatMessage.unscoped.includes(chat_channel: :chatable).find_by(id: params[:message_id]) raise Discourse::NotFound unless @message end diff --git a/app/controllers/direct_messages_controller.rb b/app/controllers/direct_messages_controller.rb index f01067d42..788b0e826 100644 --- a/app/controllers/direct_messages_controller.rb +++ b/app/controllers/direct_messages_controller.rb @@ -16,10 +16,11 @@ def index direct_message_channel = DirectMessageChannel.for_user_ids(users.map(&:id).uniq) if direct_message_channel - chat_channel = ChatChannel.find_by( - chatable_id: direct_message_channel.id, - chatable_type: 'DirectMessageChannel' - ) + chat_channel = + ChatChannel.find_by( + chatable_id: direct_message_channel.id, + chatable_type: "DirectMessageChannel", + ) render_serialized(chat_channel, ChatChannelSerializer, root: "chat_channel") else render body: nil, status: 404 @@ -31,17 +32,12 @@ def index def users_from_usernames(current_user, params) params.require(:usernames) - usernames = if params[:usernames].is_a?(String) - params[:usernames].split(",") - else - params[:usernames] - end + usernames = + (params[:usernames].is_a?(String) ? params[:usernames].split(",") : params[:usernames]) users = [current_user] other_usernames = usernames - [current_user.username] - if other_usernames.any? - users.concat(User.where(username: other_usernames).to_a) - end + users.concat(User.where(username: other_usernames).to_a) if other_usernames.any? users end end diff --git a/app/controllers/incoming_chat_webhooks_controller.rb b/app/controllers/incoming_chat_webhooks_controller.rb index 5818dfe39..4345a7407 100644 --- a/app/controllers/incoming_chat_webhooks_controller.rb +++ b/app/controllers/incoming_chat_webhooks_controller.rb @@ -11,9 +11,7 @@ class DiscourseChat::IncomingChatWebhooksController < ApplicationController def create_message debug_payload - hijack do - process_webhook_payload(text: params[:text], key: params[:key]) - end + hijack { process_webhook_payload(text: params[:text], key: params[:key]) } end # See https://api.slack.com/reference/messaging/payload for the @@ -24,16 +22,17 @@ def create_message_slack_compatible debug_payload # See note in validate_payload on why this is needed - attachments = if params[:payload].present? - payload = params[:payload] - if String === payload - payload = JSON.parse(payload) - payload.deep_symbolize_keys! + attachments = + if params[:payload].present? + payload = params[:payload] + if String === payload + payload = JSON.parse(payload) + payload.deep_symbolize_keys! + end + payload[:attachments] + else + params[:attachments] end - payload[:attachments] - else - params[:attachments] - end if params[:text].present? text = DiscourseChat::SlackCompatibility.process_text(params[:text]) @@ -41,9 +40,7 @@ def create_message_slack_compatible text = DiscourseChat::SlackCompatibility.process_legacy_attachments(attachments) end - hijack do - process_webhook_payload(text: text, key: params[:key]) - end + hijack { process_webhook_payload(text: text, key: params[:key]) } rescue JSON::ParserError raise Discourse::InvalidParameters end @@ -54,12 +51,13 @@ def process_webhook_payload(text:, key:) validate_message_length(text) webhook = find_and_rate_limit_webhook(key) - chat_message_creator = DiscourseChat::ChatMessageCreator.create( - chat_channel: webhook.chat_channel, - user: Discourse.system_user, - content: text, - incoming_chat_webhook: webhook - ) + chat_message_creator = + DiscourseChat::ChatMessageCreator.create( + chat_channel: webhook.chat_channel, + user: Discourse.system_user, + content: text, + incoming_chat_webhook: webhook, + ) if chat_message_creator.failed? render_json_error(chat_message_creator.error) else @@ -72,13 +70,20 @@ def find_and_rate_limit_webhook(key) raise Discourse::NotFound unless webhook # Rate limit to 10 messages per-minute. We can move to a site setting in the future if needed. - RateLimiter.new(nil, "incoming_chat_webhook_#{webhook.id}", WEBHOOK_MESSAGES_PER_MINUTE_LIMIT, 1.minute).performed! + RateLimiter.new( + nil, + "incoming_chat_webhook_#{webhook.id}", + WEBHOOK_MESSAGES_PER_MINUTE_LIMIT, + 1.minute, + ).performed! webhook end def validate_message_length(message) return if message.length <= WEBHOOK_MAX_MESSAGE_LENGTH - raise Discourse::InvalidParameters.new("Body cannot be over #{WEBHOOK_MAX_MESSAGE_LENGTH} characters") + raise Discourse::InvalidParameters.new( + "Body cannot be over #{WEBHOOK_MAX_MESSAGE_LENGTH} characters", + ) end def validate_payload @@ -95,8 +100,10 @@ def validate_payload def debug_payload return if !SiteSetting.chat_debug_webhook_payloads Rails.logger.warn( - "Debugging chat webhook payload: " + \ - JSON.dump({ payload: params[:payload], attachments: params[:attachments], text: params[:text] }) + "Debugging chat webhook payload: " + + JSON.dump( + { payload: params[:payload], attachments: params[:attachments], text: params[:text] }, + ), ) end end diff --git a/app/jobs/regular/auto_join_channel_batch.rb b/app/jobs/regular/auto_join_channel_batch.rb index 8d2b324db..e07adb81e 100644 --- a/app/jobs/regular/auto_join_channel_batch.rb +++ b/app/jobs/regular/auto_join_channel_batch.rb @@ -6,11 +6,12 @@ def execute(args) return "starts_at or ends_at missing" if args[:starts_at].blank? || args[:ends_at].blank? return "End is higher than start" if args[:ends_at] < args[:starts_at] - channel = ChatChannel.find_by( - id: args[:chat_channel_id], - auto_join_users: true, - chatable_type: 'Category' - ) + channel = + ChatChannel.find_by( + id: args[:chat_channel_id], + auto_join_users: true, + chatable_type: "Category", + ) return if !channel @@ -24,7 +25,7 @@ def execute(args) suspended_until: Time.zone.now, last_seen_at: 3.months.ago, channel_category: channel.chatable_id, - mode: UserChatChannelMembership.join_modes[:automatic] + mode: UserChatChannelMembership.join_modes[:automatic], } new_member_ids = DB.query_single(create_memberships_query(category), query_args) @@ -35,10 +36,7 @@ def execute(args) WHERE id = :channel_id SQL - ChatPublisher.publish_new_channel( - channel.reload, - User.where(id: new_member_ids) - ) + ChatPublisher.publish_new_channel(channel.reload, User.where(id: new_member_ids)) end private @@ -53,12 +51,10 @@ def create_memberships_query(category) uccm.chat_channel_id = :chat_channel_id AND uccm.user_id = users.id SQL - if category.read_restricted? - query += <<~SQL + query += <<~SQL if category.read_restricted? INNER JOIN group_users gu ON gu.user_id = users.id LEFT OUTER JOIN category_groups cg ON cg.group_id = gu.group_id SQL - end query += <<~SQL WHERE (users.id >= :start AND users.id <= :end) AND @@ -70,13 +66,11 @@ def create_memberships_query(category) uccm.id IS NULL SQL - if category.read_restricted? - query += <<~SQL + query += <<~SQL if category.read_restricted? AND cg.category_id = :channel_category SQL - end - query += 'RETURNING user_chat_channel_memberships.user_id' + query += "RETURNING user_chat_channel_memberships.user_id" end end end diff --git a/app/jobs/regular/auto_manage_channel_memberships.rb b/app/jobs/regular/auto_manage_channel_memberships.rb index c8f08647c..8675d676d 100644 --- a/app/jobs/regular/auto_manage_channel_memberships.rb +++ b/app/jobs/regular/auto_manage_channel_memberships.rb @@ -3,11 +3,12 @@ module Jobs class AutoManageChannelMemberships < ::Jobs::Base def execute(args) - channel = ChatChannel.includes(:chatable).find_by( - id: args[:chat_channel_id], - auto_join_users: true, - chatable_type: 'Category' - ) + channel = + ChatChannel.includes(:chatable).find_by( + id: args[:chat_channel_id], + auto_join_users: true, + chatable_type: "Category", + ) return if !channel @@ -21,7 +22,9 @@ def execute(args) Jobs.enqueue( :auto_join_channel_batch, - chat_channel_id: channel.id, starts_at: starts_at, ends_at: ends_at + chat_channel_id: channel.id, + starts_at: starts_at, + ends_at: ends_at, ) processed += batch.size @@ -33,30 +36,30 @@ def execute(args) def auto_join_query(channel) category = channel.chatable - users = User - .real - .activated - .not_suspended - .not_staged - .distinct - .select(:id, 'users.id AS query_user_id') - .where('last_seen_at IS NULL OR last_seen_at > ?', 3.months.ago) - .joins(:user_option) - .where(user_options: { chat_enabled: true }) - .joins( - <<~SQL + users = + User + .real + .activated + .not_suspended + .not_staged + .distinct + .select(:id, "users.id AS query_user_id") + .where("last_seen_at IS NULL OR last_seen_at > ?", 3.months.ago) + .joins(:user_option) + .where(user_options: { chat_enabled: true }) + .joins(<<~SQL) LEFT OUTER JOIN user_chat_channel_memberships uccm ON uccm.chat_channel_id = #{channel.id} AND uccm.user_id = users.id SQL - ) - .where('uccm.id IS NULL') + .where("uccm.id IS NULL") if category.read_restricted? - users = users - .joins(:group_users) - .joins('INNER JOIN category_groups cg ON cg.group_id = group_users.group_id') - .where('cg.category_id = ?', channel.chatable_id) + users = + users + .joins(:group_users) + .joins("INNER JOIN category_groups cg ON cg.group_id = group_users.group_id") + .where("cg.category_id = ?", channel.chatable_id) end users diff --git a/app/jobs/regular/chat_channel_archive.rb b/app/jobs/regular/chat_channel_archive.rb index b6b1e7868..4be04ec8a 100644 --- a/app/jobs/regular/chat_channel_archive.rb +++ b/app/jobs/regular/chat_channel_archive.rb @@ -9,7 +9,9 @@ def execute(args = {}) # this should not really happen, but better to do this than throw an error if channel_archive.blank? - Rails.logger.warn("Chat channel archive #{args[:chat_channel_archive_id]} could not be found, aborting archive job.") + Rails.logger.warn( + "Chat channel archive #{args[:chat_channel_archive_id]} could not be found, aborting archive job.", + ) return end @@ -17,10 +19,8 @@ def execute(args = {}) DistributedMutex.synchronize( "archive_chat_channel_#{channel_archive.chat_channel_id}", - validity: 20.minutes - ) do - DiscourseChat::ChatChannelArchiveService.new(channel_archive).execute - end + validity: 20.minutes, + ) { DiscourseChat::ChatChannelArchiveService.new(channel_archive).execute } end end end diff --git a/app/jobs/regular/chat_channel_delete.rb b/app/jobs/regular/chat_channel_delete.rb index 815995549..ac89be4db 100644 --- a/app/jobs/regular/chat_channel_delete.rb +++ b/app/jobs/regular/chat_channel_delete.rb @@ -7,7 +7,9 @@ def execute(args = {}) # this should not really happen, but better to do this than throw an error if chat_channel.blank? - Rails.logger.warn("Chat channel #{args[:chat_channel_id]} could not be found, aborting delete job.") + Rails.logger.warn( + "Chat channel #{args[:chat_channel_id]} could not be found, aborting delete job.", + ) return end @@ -23,7 +25,9 @@ def execute(args = {}) ChatDraft.where(chat_channel: chat_channel).delete_all UserChatChannelMembership.where(chat_channel: chat_channel).delete_all - Rails.logger.debug("Deleting chat messages, mentions, revisions, and uploads for channel #{chat_channel.id}") + Rails.logger.debug( + "Deleting chat messages, mentions, revisions, and uploads for channel #{chat_channel.id}", + ) ChatMessage.transaction do chat_messages = ChatMessage.where(chat_channel: chat_channel) message_ids = chat_messages.select(:id) @@ -38,7 +42,8 @@ def execute(args = {}) # only the messages and the channel are Trashable, everything else gets # permanently destroyed chat_messages.update_all( - deleted_by_id: chat_channel.deleted_by_id, deleted_at: Time.zone.now + deleted_by_id: chat_channel.deleted_by_id, + deleted_at: Time.zone.now, ) end end diff --git a/app/jobs/regular/chat_notify_mentioned.rb b/app/jobs/regular/chat_notify_mentioned.rb index 0e1337d70..85699fb66 100644 --- a/app/jobs/regular/chat_notify_mentioned.rb +++ b/app/jobs/regular/chat_notify_mentioned.rb @@ -2,10 +2,15 @@ module Jobs class ChatNotifyMentioned < ::Jobs::Base - def execute(args = {}) - @chat_message = ChatMessage.includes(:user, :revisions, chat_channel: :chatable).find_by(id: args[:chat_message_id]) - return if @chat_message.nil? || @chat_message.revisions.where("created_at > ?", args[:timestamp]).any? + @chat_message = + ChatMessage.includes(:user, :revisions, chat_channel: :chatable).find_by( + id: args[:chat_message_id], + ) + if @chat_message.nil? || + @chat_message.revisions.where("created_at > ?", args[:timestamp]).any? + return + end @creator = @chat_message.user @chat_channel = @chat_message.chat_channel @@ -13,9 +18,7 @@ def execute(args = {}) @already_notified_user_ids = args[:already_notified_user_ids] || [] user_ids_to_notify = args[:to_notify_ids_map] || {} - user_ids_to_notify.each do |mention_type, ids| - process_mentions(ids, mention_type.to_sym) - end + user_ids_to_notify.each { |mention_type, ids| process_mentions(ids, mention_type.to_sym) } end private @@ -24,7 +27,7 @@ def get_memberships(user_ids) UserChatChannelMembership.includes(:user).where( user_id: (user_ids - @already_notified_user_ids), chat_channel_id: @chat_message.chat_channel_id, - following: true + following: true, ) end @@ -33,17 +36,19 @@ def build_data_for(membership, identifier_type:) chat_message_id: @chat_message.id, chat_channel_id: @chat_channel.id, mentioned_by_username: @creator.username, - is_direct_message_channel: @is_direct_message_channel + is_direct_message_channel: @is_direct_message_channel, } - data[:chat_channel_title] = @chat_channel.title(membership.user) unless @is_direct_message_channel + data[:chat_channel_title] = @chat_channel.title( + membership.user, + ) unless @is_direct_message_channel return data if identifier_type == :direct_mentions case identifier_type when :here_mentions - data[:identifier] = 'here' + data[:identifier] = "here" when :global_mentions - data[:identifier] = 'all' + data[:identifier] = "all" else data[:is_group_mention] = true end @@ -57,28 +62,36 @@ def build_payload_for(membership, identifier_type:) username: @creator.username, tag: DiscourseChat::ChatNotifier.push_notification_tag(:mention, @chat_channel.id), excerpt: @chat_message.push_notification_excerpt, - post_url: "/chat/channel/#{@chat_channel.id}/#{@chat_channel.title(membership.user)}?messageId=#{@chat_message.id}" + post_url: + "/chat/channel/#{@chat_channel.id}/#{@chat_channel.title(membership.user)}?messageId=#{@chat_message.id}", } - translation_prefix = @is_direct_message_channel ? - "discourse_push_notifications.popup.direct_message_chat_mention" : - "discourse_push_notifications.popup.chat_mention" + translation_prefix = + ( + if @is_direct_message_channel + "discourse_push_notifications.popup.direct_message_chat_mention" + else + "discourse_push_notifications.popup.chat_mention" + end + ) translation_suffix = identifier_type == :direct_mentions ? "direct" : "other" - identifier_text = case identifier_type - when :here_mentions - '@here' - when :global_mentions - '@all' - when :direct_mentions - '' + identifier_text = + case identifier_type + when :here_mentions + "@here" + when :global_mentions + "@all" + when :direct_mentions + "" else - "@#{identifier_type}" - end + "@#{identifier_type}" + end - payload[:translated_title] = I18n.t("#{translation_prefix}.#{translation_suffix}", + payload[:translated_title] = I18n.t( + "#{translation_prefix}.#{translation_suffix}", username: @creator.username, identifier: identifier_text, - channel: @chat_channel.title(membership.user) + channel: @chat_channel.title(membership.user), ) payload @@ -87,21 +100,30 @@ def build_payload_for(membership, identifier_type:) def create_notification!(membership, notification_data) is_read = DiscourseChat::ChatNotifier.user_has_seen_message?(membership, @chat_message.id) - notification = Notification.create!( - notification_type: Notification.types[:chat_mention], - user_id: membership.user_id, - high_priority: true, - data: notification_data.to_json, - read: is_read + notification = + Notification.create!( + notification_type: Notification.types[:chat_mention], + user_id: membership.user_id, + high_priority: true, + data: notification_data.to_json, + read: is_read, + ) + ChatMention.create!( + notification: notification, + user: membership.user, + chat_message: @chat_message, ) - ChatMention.create!(notification: notification, user: membership.user, chat_message: @chat_message) end def send_notifications(membership, notification_data, os_payload) create_notification!(membership, notification_data) if !membership.desktop_notifications_never? - MessageBus.publish("/chat/notification-alert/#{membership.user_id}", os_payload, user_ids: [membership.user_id]) + MessageBus.publish( + "/chat/notification-alert/#{membership.user_id}", + os_payload, + user_ids: [membership.user_id], + ) end if !membership.mobile_notifications_never? diff --git a/app/jobs/regular/chat_notify_watching.rb b/app/jobs/regular/chat_notify_watching.rb index 27c86e204..0dfac3d33 100644 --- a/app/jobs/regular/chat_notify_watching.rb +++ b/app/jobs/regular/chat_notify_watching.rb @@ -3,7 +3,8 @@ module Jobs class ChatNotifyWatching < ::Jobs::Base def execute(args = {}) - @chat_message = ChatMessage.includes(:user, chat_channel: :chatable).find_by(id: args[:chat_message_id]) + @chat_message = + ChatMessage.includes(:user, chat_channel: :chatable).find_by(id: args[:chat_message_id]) return if @chat_message.nil? @creator = @chat_message.user @@ -19,12 +20,13 @@ def execute(args = {}) .where.not(user_id: args[:except_user_ids]) .where(chat_channel_id: @chat_channel.id) .where(following: true) - .where("desktop_notification_level = ? OR mobile_notification_level = ?", - always_notification_level, always_notification_level) + .where( + "desktop_notification_level = ? OR mobile_notification_level = ?", + always_notification_level, + always_notification_level, + ) .merge(User.not_suspended) - .each do |membership| - send_notifications(membership) - end + .each { |membership| send_notifications(membership) } end def send_notifications(membership) @@ -34,9 +36,14 @@ def send_notifications(membership) return if DiscourseChat::ChatNotifier.user_has_seen_message?(membership, @chat_message.id) return if online_user_ids.include?(user.id) - translation_key = @is_direct_message_channel ? - "discourse_push_notifications.popup.new_direct_chat_message" : - "discourse_push_notifications.popup.new_chat_message" + translation_key = + ( + if @is_direct_message_channel + "discourse_push_notifications.popup.new_direct_chat_message" + else + "discourse_push_notifications.popup.new_chat_message" + end + ) translation_args = { username: @creator.username } translation_args[:channel] = @chat_channel.title(user) unless @is_direct_message_channel @@ -47,16 +54,14 @@ def send_notifications(membership) post_url: "/chat/channel/#{@chat_channel.id}/#{@chat_channel.title(user)}", translated_title: I18n.t(translation_key, translation_args), tag: DiscourseChat::ChatNotifier.push_notification_tag(:message, @chat_channel.id), - excerpt: @chat_message.push_notification_excerpt + excerpt: @chat_message.push_notification_excerpt, } if membership.desktop_notifications_always? MessageBus.publish("/chat/notification-alert/#{user.id}", payload, user_ids: [user.id]) end - if membership.mobile_notifications_always? - PostAlerter.push_notification(user, payload) - end + PostAlerter.push_notification(user, payload) if membership.mobile_notifications_always? end def online_user_ids diff --git a/app/jobs/regular/process_chat_message.rb b/app/jobs/regular/process_chat_message.rb index 7c161a944..a24e14f88 100644 --- a/app/jobs/regular/process_chat_message.rb +++ b/app/jobs/regular/process_chat_message.rb @@ -3,17 +3,17 @@ module Jobs class ProcessChatMessage < ::Jobs::Base def execute(args = {}) - DistributedMutex.synchronize("process_chat_message_#{args[:chat_message_id]}", validity: 10.minutes) do + DistributedMutex.synchronize( + "process_chat_message_#{args[:chat_message_id]}", + validity: 10.minutes, + ) do chat_message = ChatMessage.find_by(id: args[:chat_message_id]) return if !chat_message processor = DiscourseChat::ChatMessageProcessor.new(chat_message) processor.run! if args[:is_dirty] || processor.dirty? - chat_message.update( - cooked: processor.html, - cooked_version: ChatMessage::BAKED_VERSION - ) + chat_message.update(cooked: processor.html, cooked_version: ChatMessage::BAKED_VERSION) ChatPublisher.publish_processed!(chat_message) end end diff --git a/app/jobs/scheduled/delete_old_chat_messages.rb b/app/jobs/scheduled/delete_old_chat_messages.rb index d0c0fb964..025c9d2f1 100644 --- a/app/jobs/scheduled/delete_old_chat_messages.rb +++ b/app/jobs/scheduled/delete_old_chat_messages.rb @@ -15,7 +15,8 @@ def delete_public_channel_messages ChatMessage .in_public_channel .created_before(SiteSetting.chat_channel_retention_days.days.ago) - .in_batches(of: 200).each do |relation| + .in_batches(of: 200) + .each do |relation| destroyed_ids = relation.destroy_all.pluck(:id) reset_last_read_message_id(destroyed_ids) end @@ -27,7 +28,8 @@ def delete_dm_channel_messages ChatMessage .in_dm_channel .created_before(SiteSetting.chat_dm_retention_days.days.ago) - .in_batches(of: 200).each do |relation| + .in_batches(of: 200) + .each do |relation| destroyed_ids = relation.destroy_all.pluck(:id) reset_last_read_message_id(destroyed_ids) end @@ -38,9 +40,9 @@ def valid_day_value?(setting_name) end def reset_last_read_message_id(ids) - UserChatChannelMembership - .where(last_read_message_id: ids) - .update_all(last_read_message_id: nil) + UserChatChannelMembership.where(last_read_message_id: ids).update_all( + last_read_message_id: nil, + ) end end end diff --git a/app/jobs/scheduled/update_user_counts_for_chat_channels.rb b/app/jobs/scheduled/update_user_counts_for_chat_channels.rb index f42206caa..3c5999842 100644 --- a/app/jobs/scheduled/update_user_counts_for_chat_channels.rb +++ b/app/jobs/scheduled/update_user_counts_for_chat_channels.rb @@ -5,19 +5,18 @@ class UpdateUserCountsForChatChannels < ::Jobs::Scheduled every 2.hours def execute(args = {}) - ChatChannel.find_each do |chat_channel| - set_user_count(chat_channel) - end + ChatChannel.find_each { |chat_channel| set_user_count(chat_channel) } end def set_user_count(chat_channel) current_count = chat_channel.user_count || 0 - new_count = chat_channel - .user_chat_channel_memberships - .joins(:user) - .where(following: true) - .merge(User.activated.not_suspended.not_staged) - .count + new_count = + chat_channel + .user_chat_channel_memberships + .joins(:user) + .where(following: true) + .merge(User.activated.not_suspended.not_staged) + .count return if current_count == new_count diff --git a/app/models/chat_channel.rb b/app/models/chat_channel.rb index 148dc4411..70f6b067a 100644 --- a/app/models/chat_channel.rb +++ b/app/models/chat_channel.rb @@ -3,34 +3,38 @@ class ChatChannel < ActiveRecord::Base include Trashable attribute :muted, default: false - attribute :desktop_notification_level, default: UserChatChannelMembership::DEFAULT_NOTIFICATION_LEVEL - attribute :mobile_notification_level, default: UserChatChannelMembership::DEFAULT_NOTIFICATION_LEVEL + attribute :desktop_notification_level, + default: UserChatChannelMembership::DEFAULT_NOTIFICATION_LEVEL + attribute :mobile_notification_level, + default: UserChatChannelMembership::DEFAULT_NOTIFICATION_LEVEL attribute :following, default: false attribute :unread_count, default: 0 attribute :unread_mentions, default: 0 attribute :last_read_message_id, default: nil belongs_to :chatable, polymorphic: true - belongs_to :direct_message_channel, -> { where(chat_channels: { chatable_type: 'DirectMessageChannel' }) }, foreign_key: 'chatable_id' + belongs_to :direct_message_channel, + -> { where(chat_channels: { chatable_type: "DirectMessageChannel" }) }, + foreign_key: "chatable_id" has_many :chat_messages has_many :user_chat_channel_memberships has_one :chat_channel_archive - enum status: { - open: 0, - read_only: 1, - closed: 2, - archived: 3 - }, _scopes: false + enum status: { open: 0, read_only: 1, closed: 2, archived: 3 }, _scopes: false - validates :name, length: { maximum: Proc.new { SiteSetting.max_topic_title_length } }, presence: true, allow_nil: true + validates :name, + length: { + maximum: Proc.new { SiteSetting.max_topic_title_length }, + }, + presence: true, + allow_nil: true def add(user) ActiveRecord::Base.transaction do - membership = UserChatChannelMembership - .find_or_initialize_by(user_id: user.id, chat_channel: self) + membership = + UserChatChannelMembership.find_or_initialize_by(user_id: user.id, chat_channel: self) if !membership.following membership.following = true @@ -44,8 +48,7 @@ def add(user) def remove(user) ActiveRecord::Base.transaction do - membership = UserChatChannelMembership - .find_by!(user_id: user.id, chat_channel: self) + membership = UserChatChannelMembership.find_by!(user_id: user.id, chat_channel: self) if membership.following membership.update!(following: false) @@ -104,17 +107,11 @@ def chatable_has_custom_fields? end def allowed_user_ids - direct_message_channel? ? - chatable.user_ids : - nil + direct_message_channel? ? chatable.user_ids : nil end def allowed_group_ids - if category_channel? - chatable.secure_group_ids - else - nil - end + category_channel? ? chatable.secure_group_ids : nil end def public_channel_title @@ -145,7 +142,7 @@ def change_status(acting_user, target_status) log_channel_status_change( acting_user: acting_user, new_status: target_status, - old_status: old_status + old_status: old_status, ) end @@ -174,9 +171,9 @@ def self.public_channel_chatable_types end def self.public_channels - where(chatable_type: public_channel_chatable_types) - .where("categories.id IS NOT NULL") - .joins("LEFT JOIN categories ON categories.id = chat_channels.chatable_id AND chat_channels.chatable_type = 'Category'") + where(chatable_type: public_channel_chatable_types).where("categories.id IS NOT NULL").joins( + "LEFT JOIN categories ON categories.id = chat_channels.chatable_id AND chat_channels.chatable_type = 'Category'", + ) end private @@ -189,7 +186,7 @@ def log_channel_status_change(acting_user:, new_status:, old_status:) :chat_channel_status_change, channel: self, old_status: old_status, - new_status: new_status + new_status: new_status, ) StaffActionLogger.new(acting_user).log_custom( @@ -198,8 +195,8 @@ def log_channel_status_change(acting_user:, new_status:, old_status:) chat_channel_id: self.id, chat_channel_name: self.name, previous_value: old_status, - new_value: new_status - } + new_value: new_status, + }, ) ChatPublisher.publish_channel_status(self) diff --git a/app/models/chat_channel_archive.rb b/app/models/chat_channel_archive.rb index 2c647e396..e84cdb35e 100644 --- a/app/models/chat_channel_archive.rb +++ b/app/models/chat_channel_archive.rb @@ -2,13 +2,12 @@ class ChatChannelArchive < ActiveRecord::Base belongs_to :chat_channel - belongs_to :archived_by, class_name: 'User' + belongs_to :archived_by, class_name: "User" - belongs_to :destination_topic, class_name: 'Topic' + belongs_to :destination_topic, class_name: "Topic" def complete? - self.archived_messages >= self.total_messages && - self.chat_channel.chat_messages.count.zero? + self.archived_messages >= self.total_messages && self.chat_channel.chat_messages.count.zero? end def failed? diff --git a/app/models/chat_message.rb b/app/models/chat_message.rb index 326db01fb..b61904465 100644 --- a/app/models/chat_message.rb +++ b/app/models/chat_message.rb @@ -18,19 +18,19 @@ class ChatMessage < ActiveRecord::Base has_one :chat_webhook_event, dependent: :destroy has_one :chat_mention, dependent: :destroy - scope :in_public_channel, -> { - joins(:chat_channel) - .where(chat_channel: { chatable_type: ChatChannel.public_channel_chatable_types }) - } + scope :in_public_channel, + -> { + joins(:chat_channel).where( + chat_channel: { + chatable_type: ChatChannel.public_channel_chatable_types, + }, + ) + } - scope :in_dm_channel, -> { - joins(:chat_channel) - .where(chat_channel: { chatable_type: "DirectMessageChannel" }) - } + scope :in_dm_channel, + -> { joins(:chat_channel).where(chat_channel: { chatable_type: "DirectMessageChannel" }) } - scope :created_before, -> (date) { - where("chat_messages.created_at < ?", date) - } + scope :created_before, ->(date) { where("chat_messages.created_at < ?", date) } def validate_message(has_uploads:) WatchedWordsValidator.new(attributes: [:message]).validate(self) @@ -39,7 +39,10 @@ def validate_message(has_uploads:) if !has_uploads && message_too_short? self.errors.add( :base, - I18n.t("chat.errors.minimum_length_not_met", minimum: SiteSetting.chat_minimum_message_length) + I18n.t( + "chat.errors.minimum_length_not_met", + minimum: SiteSetting.chat_minimum_message_length, + ), ) end end @@ -48,14 +51,10 @@ def attach_uploads(uploads) return if uploads.blank? now = Time.now - record_attrs = uploads.map do |upload| - { - upload_id: upload.id, - chat_message_id: self.id, - created_at: now, - updated_at: now - } - end + record_attrs = + uploads.map do |upload| + { upload_id: upload.id, chat_message_id: self.id, created_at: now, updated_at: now } + end ChatUpload.insert_all!(record_attrs) end @@ -79,16 +78,9 @@ def push_notification_excerpt end def add_flag(user) - reviewable = ReviewableChatMessage.needs_review!( - created_by: user, - target: self, - ) + reviewable = ReviewableChatMessage.needs_review!(created_by: user, target: self) reviewable.update(target_created_by: self.user) - reviewable.add_score( - user, - ReviewableScore.types[:needs_review], - force_review: true - ) + reviewable.add_score(user, ReviewableScore.types[:needs_review], force_review: true) reviewable end @@ -102,16 +94,13 @@ def to_markdown if self.message.present? msg = self.message - if self.chat_uploads.any? - markdown << msg + "\n" - else - markdown << msg - end + self.chat_uploads.any? ? markdown << msg + "\n" : markdown << msg end - self.chat_uploads.order(:created_at).each do |chat_upload| - markdown << UploadMarkdown.new(chat_upload.upload).to_markdown - end + self + .chat_uploads + .order(:created_at) + .each { |chat_upload| markdown << UploadMarkdown.new(chat_upload.upload).to_markdown } markdown.reject(&:empty?).join("\n") end @@ -124,13 +113,8 @@ def cook def rebake!(invalidate_oneboxes: false, priority: nil) previous_cooked = self.cooked new_cooked = self.class.cook(message, invalidate_oneboxes: invalidate_oneboxes) - update_columns( - cooked: new_cooked, - cooked_version: BAKED_VERSION - ) - args = { - chat_message_id: self.id, - } + update_columns(cooked: new_cooked, cooked_version: BAKED_VERSION) + args = { chat_message_id: self.id } args[:queue] = priority.to_s if priority && priority != :normal args[:is_dirty] = true if previous_cooked != new_cooked @@ -138,10 +122,10 @@ def rebake!(invalidate_oneboxes: false, priority: nil) end def self.uncooked - where('cooked_version <> ? or cooked_version IS NULL', BAKED_VERSION) + where("cooked_version <> ? or cooked_version IS NULL", BAKED_VERSION) end - MARKDOWN_FEATURES = %w{ + MARKDOWN_FEATURES = %w[ anchor bbcode-block bbcode-inline @@ -162,9 +146,9 @@ def self.uncooked text-post-process upload-protocol watched-words - } + ] - MARKDOWN_IT_RULES = %w{ + MARKDOWN_IT_RULES = %w[ autolink list backticks @@ -178,24 +162,26 @@ def self.uncooked strikethrough blockquote emphasis - } + ] def self.cook(message, opts = {}) - cooked = PrettyText.cook( - message, - features_override: MARKDOWN_FEATURES + DiscoursePluginRegistry.chat_markdown_features.to_a, - markdown_it_rules: MARKDOWN_IT_RULES, - force_quote_link: true - ) - - result = Oneboxer.apply(cooked) do |url| - if opts[:invalidate_oneboxes] - Oneboxer.invalidate(url) - InlineOneboxer.invalidate(url) + cooked = + PrettyText.cook( + message, + features_override: MARKDOWN_FEATURES + DiscoursePluginRegistry.chat_markdown_features.to_a, + markdown_it_rules: MARKDOWN_IT_RULES, + force_quote_link: true, + ) + + result = + Oneboxer.apply(cooked) do |url| + if opts[:invalidate_oneboxes] + Oneboxer.invalidate(url) + InlineOneboxer.invalidate(url) + end + onebox = Oneboxer.cached_onebox(url) + onebox end - onebox = Oneboxer.cached_onebox(url) - onebox - end cooked = result.to_html if result.changed? cooked diff --git a/app/models/chat_view.rb b/app/models/chat_view.rb index ad9e9b724..9df0df18d 100644 --- a/app/models/chat_view.rb +++ b/app/models/chat_view.rb @@ -3,7 +3,13 @@ class ChatView attr_reader :user, :chat_channel, :chat_messages, :can_load_more_past, :can_load_more_future - def initialize(chat_channel:, chat_messages:, user:, can_load_more_past: nil, can_load_more_future: nil) + def initialize( + chat_channel:, + chat_messages:, + user:, + can_load_more_past: nil, + can_load_more_future: nil + ) @chat_channel = chat_channel @chat_messages = chat_messages @user = user @@ -44,13 +50,13 @@ def get_reviewable_ids ids = {} - DB.query( - sql, - pending: ReviewableScore.statuses[:pending], - message_ids: @chat_messages.map(&:id) - ).each do |row| - ids[row.target_id] = row.reviewable_id - end + DB + .query( + sql, + pending: ReviewableScore.statuses[:pending], + message_ids: @chat_messages.map(&:id), + ) + .each { |row| ids[row.target_id] = row.reviewable_id } ids end @@ -72,13 +78,9 @@ def get_user_flag_statuses statuses = {} - DB.query( - sql, - message_ids: @chat_messages.map(&:id), - user_id: @user.id - ).each do |row| - statuses[row.target_id] = row.status - end + DB + .query(sql, message_ids: @chat_messages.map(&:id), user_id: @user.id) + .each { |row| statuses[row.target_id] = row.status } statuses end diff --git a/app/models/direct_message_channel.rb b/app/models/direct_message_channel.rb index a7b01758b..7a4bdcdf8 100644 --- a/app/models/direct_message_channel.rb +++ b/app/models/direct_message_channel.rb @@ -12,17 +12,21 @@ def chat_channel_title_for_user(chat_channel, acting_user) users = direct_message_users.map(&:user) - [acting_user] # direct message to self - return I18n.t("chat.channel.dm_title.single_user", user: "@#{acting_user.username}") if users.empty? + if users.empty? + return I18n.t("chat.channel.dm_title.single_user", user: "@#{acting_user.username}") + end # all users deleted return chat_channel.id if !users.first usernames_formatted = users.sort_by(&:username).map { |u| "@#{u.username}" } if usernames_formatted.size > 5 - return I18n.t( - "chat.channel.dm_title.multi_user_truncated", - users: usernames_formatted[0..4].join(", "), - leftover: usernames_formatted.length - 5 + return( + I18n.t( + "chat.channel.dm_title.multi_user_truncated", + users: usernames_formatted[0..4].join(", "), + leftover: usernames_formatted.length - 5, + ) ) end diff --git a/app/models/incoming_chat_webhook.rb b/app/models/incoming_chat_webhook.rb index 9045650f0..e71b539a0 100644 --- a/app/models/incoming_chat_webhook.rb +++ b/app/models/incoming_chat_webhook.rb @@ -4,9 +4,7 @@ class IncomingChatWebhook < ActiveRecord::Base belongs_to :chat_channel has_many :chat_webhook_events - before_create do - self.key = SecureRandom.hex(12) - end + before_create { self.key = SecureRandom.hex(12) } def url "#{Discourse.base_url}/chat/hooks/#{key}.json" diff --git a/app/models/reviewable_chat_message.rb b/app/models/reviewable_chat_message.rb index df1b6850b..6d9f25e12 100644 --- a/app/models/reviewable_chat_message.rb +++ b/app/models/reviewable_chat_message.rb @@ -1,9 +1,8 @@ # frozen_string_literal: true -require_dependency 'reviewable' +require_dependency "reviewable" class ReviewableChatMessage < Reviewable - def self.on_score_updated(reviewable) # Silence user if new score is over the `score_to_silence_user` return if reviewable.type != self.name @@ -19,16 +18,18 @@ def self.on_score_updated(reviewable) user, Discourse.system_user, silenced_till: auto_silence_duration.minutes.from_now, - reason: I18n.t("chat.errors.auto_silence_from_flags") + reason: I18n.t("chat.errors.auto_silence_from_flags"), ) end def self.action_aliases - { agree_and_keep_hidden: :agree_and_delete, + { + agree_and_keep_hidden: :agree_and_delete, agree_and_silence: :agree_and_delete, agree_and_suspend: :agree_and_delete, delete_and_agree: :agree_and_delete, - disagree_and_restore: :disagree } + disagree_and_restore: :disagree, + } end def self.score_to_silence_user @@ -55,28 +56,39 @@ def build_actions(actions, guardian, args) return unless pending? return if chat_message.blank? - agree = actions.add_bundle("#{id}-agree", icon: 'thumbs-up', label: 'reviewables.actions.agree.title') + agree = + actions.add_bundle("#{id}-agree", icon: "thumbs-up", label: "reviewables.actions.agree.title") if chat_message.deleted_at? - build_action(actions, :agree_and_restore, icon: 'far-eye', bundle: agree) - build_action(actions, :agree_and_keep_deleted, icon: 'thumbs-up', bundle: agree) - build_action(actions, :disagree_and_restore, icon: 'thumbs-down') + build_action(actions, :agree_and_restore, icon: "far-eye", bundle: agree) + build_action(actions, :agree_and_keep_deleted, icon: "thumbs-up", bundle: agree) + build_action(actions, :disagree_and_restore, icon: "thumbs-down") else - build_action(actions, :agree_and_delete, icon: 'far-eye-slash', bundle: agree) - build_action(actions, :agree_and_keep_message, icon: 'thumbs-up', bundle: agree) - build_action(actions, :disagree, icon: 'thumbs-down') + build_action(actions, :agree_and_delete, icon: "far-eye-slash", bundle: agree) + build_action(actions, :agree_and_keep_message, icon: "thumbs-up", bundle: agree) + build_action(actions, :disagree, icon: "thumbs-down") end if guardian.can_suspend?(chat_message_creator) - build_action(actions, :agree_and_suspend, icon: 'ban', bundle: agree, client_action: 'suspend') - build_action(actions, :agree_and_silence, icon: 'microphone-slash', bundle: agree, client_action: 'silence') + build_action( + actions, + :agree_and_suspend, + icon: "ban", + bundle: agree, + client_action: "suspend", + ) + build_action( + actions, + :agree_and_silence, + icon: "microphone-slash", + bundle: agree, + client_action: "silence", + ) end - build_action(actions, :ignore, icon: 'external-link-alt') + build_action(actions, :ignore, icon: "external-link-alt") - unless chat_message.deleted_at? - build_action(actions, :delete_and_agree, icon: 'far-trash-alt') - end + build_action(actions, :delete_and_agree, icon: "far-trash-alt") unless chat_message.deleted_at? end def perform_agree_and_keep_message(performed_by, args) @@ -84,21 +96,15 @@ def perform_agree_and_keep_message(performed_by, args) end def perform_agree_and_restore(performed_by, args) - agree do - chat_message.recover! - end + agree { chat_message.recover! } end def perform_agree_and_delete(performed_by, args) - agree do - chat_message.trash!(performed_by) - end + agree { chat_message.trash!(performed_by) } end def perform_disagree_and_restore(performed_by, args) - disagree do - chat_message.recover! - end + disagree { chat_message.recover! } end def perform_disagree(performed_by, args) @@ -110,9 +116,7 @@ def perform_ignore(performed_by, args) end def perform_delete_and_ignore(performed_by, args) - ignore do - chat_message.trash!(performed_by) - end + ignore { chat_message.trash!(performed_by) } end private @@ -140,7 +144,15 @@ def ignore end end - def build_action(actions, id, icon:, button_class: nil, bundle: nil, client_action: nil, confirm: false) + def build_action( + actions, + id, + icon:, + button_class: nil, + bundle: nil, + client_action: nil, + confirm: false + ) actions.add(id, bundle: bundle) do |action| prefix = "reviewables.actions.#{id}" action.icon = icon diff --git a/app/models/user_chat_channel_membership.rb b/app/models/user_chat_channel_membership.rb index ca31d3a94..f9170739b 100644 --- a/app/models/user_chat_channel_membership.rb +++ b/app/models/user_chat_channel_membership.rb @@ -6,24 +6,12 @@ class UserChatChannelMembership < ActiveRecord::Base belongs_to :last_read_message, class_name: "ChatMessage", optional: true DEFAULT_NOTIFICATION_LEVEL = :mention - NOTIFICATION_LEVELS = { - never: 0, - mention: 1, - always: 2 - } - VALIDATED_ATTRS = [ - :following, - :muted, - :desktop_notification_level, - :mobile_notification_level - ] + NOTIFICATION_LEVELS = { never: 0, mention: 1, always: 2 } + VALIDATED_ATTRS = %i[following muted desktop_notification_level mobile_notification_level] enum desktop_notification_level: NOTIFICATION_LEVELS, _prefix: :desktop_notifications enum mobile_notification_level: NOTIFICATION_LEVELS, _prefix: :mobile_notifications - enum join_mode: { - manual: 0, - automatic: 1 - } + enum join_mode: { manual: 0, automatic: 1 } validate :changes_for_direct_message_channels @@ -35,7 +23,9 @@ def enforce_automatic_channel_memberships(channel) def enforce_automatic_user_membership(channel, user) Jobs.enqueue( :auto_join_channel_batch, - chat_channel_id: channel.id, starts_at: user.id, ends_at: user.id + chat_channel_id: channel.id, + starts_at: user.id, + ends_at: user.id, ) end end @@ -43,7 +33,8 @@ def enforce_automatic_user_membership(channel, user) private def changes_for_direct_message_channels - needs_validation = VALIDATED_ATTRS.any? { |attr| changed_attribute_names_to_save.include?(attr.to_s) } + needs_validation = + VALIDATED_ATTRS.any? { |attr| changed_attribute_names_to_save.include?(attr.to_s) } if needs_validation && chat_channel.direct_message_channel? errors.add(:muted) if muted errors.add(:desktop_notification_level) if desktop_notification_level.to_sym != :always diff --git a/app/queries/chat_channel_memberships_query.rb b/app/queries/chat_channel_memberships_query.rb index 5c1cb37fc..c21a6e123 100644 --- a/app/queries/chat_channel_memberships_query.rb +++ b/app/queries/chat_channel_memberships_query.rb @@ -2,29 +2,31 @@ class ChatChannelMembershipsQuery def self.call(channel, limit: 50, offset: 0, username: nil) - query = UserChatChannelMembership - .includes(:user) - .where(user: User.activated.not_suspended.not_staged) - .where(chat_channel: channel, following: true) + query = + UserChatChannelMembership + .includes(:user) + .where(user: User.activated.not_suspended.not_staged) + .where(chat_channel: channel, following: true) if username.present? if SiteSetting.prioritize_username_in_ux || !SiteSetting.enable_names - query = query - .where('users.username_lower ILIKE ?', "%#{username}%") + query = query.where("users.username_lower ILIKE ?", "%#{username}%") else - query = query - .where('LOWER(users.name) ILIKE ? OR users.username_lower ILIKE ?', "%#{username}%", "%#{username}%") + query = + query.where( + "LOWER(users.name) ILIKE ? OR users.username_lower ILIKE ?", + "%#{username}%", + "%#{username}%", + ) end end if SiteSetting.prioritize_username_in_ux || !SiteSetting.enable_names - query = query.order('users.username_lower ASC') + query = query.order("users.username_lower ASC") else - query = query.order('users.name ASC, users.username_lower ASC') + query = query.order("users.name ASC, users.username_lower ASC") end - query - .offset(offset) - .limit(limit) + query.offset(offset).limit(limit) end end diff --git a/app/serializers/chat_channel_settings_serializer.rb b/app/serializers/chat_channel_settings_serializer.rb index 3d2a502a8..04f16a72f 100644 --- a/app/serializers/chat_channel_settings_serializer.rb +++ b/app/serializers/chat_channel_settings_serializer.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true class ChatChannelSettingsSerializer < ChatChannelSerializer - attributes :desktop_notification_level, - :mobile_notification_level, - :following + attributes :desktop_notification_level, :mobile_notification_level, :following end diff --git a/app/serializers/chat_message_serializer.rb b/app/serializers/chat_message_serializer.rb index cefae8973..97231bb37 100644 --- a/app/serializers/chat_message_serializer.rb +++ b/app/serializers/chat_message_serializer.rb @@ -2,17 +2,17 @@ class ChatMessageSerializer < ApplicationSerializer attributes :id, - :message, - :cooked, - :created_at, - :excerpt, - :deleted_at, - :deleted_by_id, - :reviewable_id, - :user_flag_status, - :edited, - :reactions, - :bookmark + :message, + :cooked, + :created_at, + :excerpt, + :deleted_at, + :deleted_by_id, + :reviewable_id, + :user_flag_status, + :edited, + :reactions, + :bookmark has_one :user, serializer: BasicUserSerializer, embed: :objects has_one :chat_webhook_event, serializer: ChatWebhookEventSerializer, embed: :objects @@ -21,17 +21,21 @@ class ChatMessageSerializer < ApplicationSerializer def reactions reactions_hash = {} - object.reactions.group_by(&:emoji).each do |emoji, reactions| - users = reactions[0..6].map(&:user).filter { |user| user.id != scope&.user&.id }[0..5] - - next unless Emoji.exists?(emoji) - - reactions_hash[emoji] = { - count: reactions.count, - users: ActiveModel::ArraySerializer.new(users, each_serializer: BasicUserSerializer).as_json, - reacted: users_reactions.include?(emoji) - } - end + object + .reactions + .group_by(&:emoji) + .each do |emoji, reactions| + users = reactions[0..6].map(&:user).filter { |user| user.id != scope&.user&.id }[0..5] + + next unless Emoji.exists?(emoji) + + reactions_hash[emoji] = { + count: reactions.count, + users: + ActiveModel::ArraySerializer.new(users, each_serializer: BasicUserSerializer).as_json, + reacted: users_reactions.include?(emoji), + } + end reactions_hash end @@ -40,7 +44,8 @@ def include_reactions? end def users_reactions - @users_reactions ||= object.reactions.select { |reaction| reaction.user_id == scope&.user&.id }.map(&:emoji) + @users_reactions ||= + object.reactions.select { |reaction| reaction.user_id == scope&.user&.id }.map(&:emoji) end def users_bookmark @@ -58,7 +63,7 @@ def bookmark name: users_bookmark.name, auto_delete_preference: users_bookmark.auto_delete_preference, bookmarkable_id: users_bookmark.bookmarkable_id, - bookmarkable_type: users_bookmark.bookmarkable_type + bookmarkable_type: users_bookmark.bookmarkable_type, } end diff --git a/app/serializers/chat_view_serializer.rb b/app/serializers/chat_view_serializer.rb index fc878579e..ef4f1061c 100644 --- a/app/serializers/chat_view_serializer.rb +++ b/app/serializers/chat_view_serializer.rb @@ -9,7 +9,7 @@ def chat_messages each_serializer: ChatMessageSerializer, reviewable_ids: object.reviewable_ids, user_flag_statuses: object.user_flag_statuses, - scope: scope + scope: scope, ) end @@ -23,7 +23,9 @@ def meta can_delete_others: scope.can_delete_other_chats?(object.chat_channel.chatable), } meta_hash[:can_load_more_past] = object.can_load_more_past unless object.can_load_more_past.nil? - meta_hash[:can_load_more_future] = object.can_load_more_future unless object.can_load_more_future.nil? + meta_hash[ + :can_load_more_future + ] = object.can_load_more_future unless object.can_load_more_future.nil? meta_hash end end diff --git a/app/serializers/chat_webhook_event_serializer.rb b/app/serializers/chat_webhook_event_serializer.rb index 915457d87..3fb674c65 100644 --- a/app/serializers/chat_webhook_event_serializer.rb +++ b/app/serializers/chat_webhook_event_serializer.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true class ChatWebhookEventSerializer < ApplicationSerializer - attributes :username, - :emoji + attributes :username, :emoji end diff --git a/app/serializers/incoming_chat_webhook_serializer.rb b/app/serializers/incoming_chat_webhook_serializer.rb index 28fc2c63a..7f097e62b 100644 --- a/app/serializers/incoming_chat_webhook_serializer.rb +++ b/app/serializers/incoming_chat_webhook_serializer.rb @@ -3,11 +3,5 @@ class IncomingChatWebhookSerializer < ApplicationSerializer has_one :chat_channel, serializer: ChatChannelSerializer, embed: :objects - attributes :id, - :name, - :description, - :emoji, - :url, - :username, - :updated_at + attributes :id, :name, :description, :emoji, :url, :username, :updated_at end diff --git a/app/serializers/reviewable_chat_message_serializer.rb b/app/serializers/reviewable_chat_message_serializer.rb index 36236e4f5..3132b04b9 100644 --- a/app/serializers/reviewable_chat_message_serializer.rb +++ b/app/serializers/reviewable_chat_message_serializer.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_dependency 'reviewable_serializer' +require_dependency "reviewable_serializer" class ReviewableChatMessageSerializer < ReviewableSerializer has_one :chat_message, serializer: ChatMessageSerializer, root: false, embed: :objects diff --git a/app/services/chat_publisher.rb b/app/services/chat_publisher.rb index 45bbbc114..1851651f3 100644 --- a/app/services/chat_publisher.rb +++ b/app/services/chat_publisher.rb @@ -2,12 +2,20 @@ module ChatPublisher def self.publish_new!(chat_channel, chat_message, staged_id) - content = ChatMessageSerializer.new(chat_message, { scope: anonymous_guardian, root: :chat_message }).as_json + content = + ChatMessageSerializer.new( + chat_message, + { scope: anonymous_guardian, root: :chat_message }, + ).as_json content[:type] = :sent content[:stagedId] = staged_id permissions = permissions(chat_channel) MessageBus.publish("/chat/#{chat_channel.id}", content.as_json, permissions) - MessageBus.publish("/chat/#{chat_channel.id}/new-messages", { message_id: chat_message.id, user_id: chat_message.user_id }, permissions) + MessageBus.publish( + "/chat/#{chat_channel.id}/new-messages", + { message_id: chat_message.id, user_id: chat_message.user_id }, + permissions, + ) end def self.publish_processed!(chat_message) @@ -16,20 +24,28 @@ def self.publish_processed!(chat_message) type: :processed, chat_message: { id: chat_message.id, - cooked: chat_message.cooked - } + cooked: chat_message.cooked, + }, } MessageBus.publish("/chat/#{chat_channel.id}", content.as_json, permissions(chat_channel)) end def self.publish_edit!(chat_channel, chat_message) - content = ChatMessageSerializer.new(chat_message, { scope: anonymous_guardian, root: :chat_message }).as_json + content = + ChatMessageSerializer.new( + chat_message, + { scope: anonymous_guardian, root: :chat_message }, + ).as_json content[:type] = :edit MessageBus.publish("/chat/#{chat_channel.id}", content.as_json, permissions(chat_channel)) end def self.publish_refresh!(chat_channel, chat_message) - content = ChatMessageSerializer.new(chat_message, { scope: anonymous_guardian, root: :chat_message }).as_json + content = + ChatMessageSerializer.new( + chat_message, + { scope: anonymous_guardian, root: :chat_message }, + ).as_json content[:type] = :refresh MessageBus.publish("/chat/#{chat_channel.id}", content.as_json, permissions(chat_channel)) end @@ -40,9 +56,13 @@ def self.publish_reaction!(chat_channel, chat_message, action, user, emoji) user: BasicUserSerializer.new(user, root: false).as_json, emoji: emoji, type: :reaction, - chat_message_id: chat_message.id + chat_message_id: chat_message.id, } - MessageBus.publish("/chat/message-reactions/#{chat_message.id}", content.as_json, permissions(chat_channel)) + MessageBus.publish( + "/chat/message-reactions/#{chat_message.id}", + content.as_json, + permissions(chat_channel), + ) MessageBus.publish("/chat/#{chat_channel.id}", content.as_json, permissions(chat_channel)) end @@ -54,7 +74,7 @@ def self.publish_delete!(chat_channel, chat_message) MessageBus.publish( "/chat/#{chat_channel.id}", { type: "delete", deleted_id: chat_message.id, deleted_at: chat_message.deleted_at }, - permissions(chat_channel) + permissions(chat_channel), ) end @@ -62,12 +82,16 @@ def self.publish_bulk_delete!(chat_channel, deleted_message_ids) MessageBus.publish( "/chat/#{chat_channel.id}", { typ: "bulk_delete", deleted_ids: deleted_message_ids, deleted_at: Time.zone.now }, - permissions(chat_channel) + permissions(chat_channel), ) end def self.publish_restore!(chat_channel, chat_message) - content = ChatMessageSerializer.new(chat_message, { scope: anonymous_guardian, root: :chat_message }).as_json + content = + ChatMessageSerializer.new( + chat_message, + { scope: anonymous_guardian, root: :chat_message }, + ).as_json content[:type] = :restore MessageBus.publish("/chat/#{chat_channel.id}", content.as_json, permissions(chat_channel)) end @@ -79,20 +103,16 @@ def self.publish_flag!(chat_message, user, reviewable) { type: "self_flagged", user_flag_status: ReviewableScore.statuses[:pending], - chat_message_id: chat_message.id + chat_message_id: chat_message.id, }.as_json, - user_ids: [user.id] + user_ids: [user.id], ) # Publish flag with link to reviewable to staff MessageBus.publish( "/chat/#{chat_message.chat_channel_id}", - { - type: "flag", - chat_message_id: chat_message.id, - reviewable_id: reviewable.id - }.as_json, - group_ids: [Group::AUTO_GROUPS[:staff]] + { type: "flag", chat_message_id: chat_message.id, reviewable_id: reviewable.id }.as_json, + group_ids: [Group::AUTO_GROUPS[:staff]], ) end @@ -100,59 +120,82 @@ def self.publish_user_tracking_state(user, chat_channel_id, chat_message_id) MessageBus.publish( "/chat/user-tracking-state/#{user.id}", { chat_channel_id: chat_channel_id, chat_message_id: chat_message_id.to_i }.as_json, - user_ids: [user.id] + user_ids: [user.id], ) end def self.publish_new_mention(user_id, chat_channel_id, chat_message_id) - MessageBus.publish("/chat/#{chat_channel_id}/new-mentions", { message_id: chat_message_id }.as_json, user_ids: [user_id]) + MessageBus.publish( + "/chat/#{chat_channel_id}/new-mentions", + { message_id: chat_message_id }.as_json, + user_ids: [user_id], + ) end def self.publish_new_channel(chat_channel, users) users.each do |user| - serialized_channel = ChatChannelSerializer.new( - chat_channel, - scope: Guardian.new(user), # We need a guardian here for direct messages - root: :chat_channel - ).as_json + serialized_channel = + ChatChannelSerializer.new( + chat_channel, + scope: Guardian.new(user), # We need a guardian here for direct messages + root: :chat_channel, + ).as_json MessageBus.publish("/chat/new-channel", serialized_channel, user_ids: [user.id]) end end - def self.publish_inaccessible_mentions(user_id, chat_message, cannot_chat_users, without_membership) - MessageBus.publish("/chat/#{chat_message.chat_channel_id}", { + def self.publish_inaccessible_mentions( + user_id, + chat_message, + cannot_chat_users, + without_membership + ) + MessageBus.publish( + "/chat/#{chat_message.chat_channel_id}", + { type: :mention_warning, chat_message_id: chat_message.id, - cannot_see: ActiveModel::ArraySerializer.new(cannot_chat_users, each_serializer: BasicUserSerializer).as_json, - without_membership: ActiveModel::ArraySerializer.new(without_membership, each_serializer: BasicUserSerializer).as_json, + cannot_see: + ActiveModel::ArraySerializer.new( + cannot_chat_users, + each_serializer: BasicUserSerializer, + ).as_json, + without_membership: + ActiveModel::ArraySerializer.new( + without_membership, + each_serializer: BasicUserSerializer, + ).as_json, }, - user_ids: [user_id] + user_ids: [user_id], ) end def self.publish_chat_channel_edit(chat_channel, acting_user) - MessageBus.publish("/chat/channel-edits", { + MessageBus.publish( + "/chat/channel-edits", + { chat_channel_id: chat_channel.id, name: chat_channel.title(acting_user), description: chat_channel.description, }, - permissions(chat_channel) + permissions(chat_channel), ) end def self.publish_channel_status(chat_channel) MessageBus.publish( "/chat/channel-status", - { - chat_channel_id: chat_channel.id, - status: chat_channel.status - }, - permissions(chat_channel) + { chat_channel_id: chat_channel.id, status: chat_channel.status }, + permissions(chat_channel), ) end def self.publish_archive_status( - chat_channel, archive_status:, archived_messages:, archive_topic_id:, total_messages: + chat_channel, + archive_status:, + archived_messages:, + archive_topic_id:, + total_messages: ) MessageBus.publish( "/chat/channel-archive-status", @@ -162,9 +205,9 @@ def self.publish_archive_status( archive_completed: archive_status == :success, archived_messages: archived_messages, total_messages: total_messages, - archive_topic_id: archive_topic_id + archive_topic_id: archive_topic_id, }, - permissions(chat_channel) + permissions(chat_channel), ) end diff --git a/db/migrate/20210225230057_create_chat_tables.rb b/db/migrate/20210225230057_create_chat_tables.rb index 059275896..21844f7ea 100644 --- a/db/migrate/20210225230057_create_chat_tables.rb +++ b/db/migrate/20210225230057_create_chat_tables.rb @@ -22,6 +22,6 @@ def change t.text :message end - add_index :topic_chat_messages, [:topic_id, :created_at] + add_index :topic_chat_messages, %i[topic_id created_at] end end diff --git a/db/migrate/20210706214013_rename_topic_chats_to_chat_channels.rb b/db/migrate/20210706214013_rename_topic_chats_to_chat_channels.rb index a7d43489f..134417d38 100644 --- a/db/migrate/20210706214013_rename_topic_chats_to_chat_channels.rb +++ b/db/migrate/20210706214013_rename_topic_chats_to_chat_channels.rb @@ -15,7 +15,7 @@ def up change_column :chat_channels, :chatable_id, :integer, unique: false add_column :chat_channels, :chatable_type, :string change_column_null :chat_channels, :chatable_type, false - add_index :chat_channels, [:chatable_id, :chatable_type] + add_index :chat_channels, %i[chatable_id chatable_type] # topic_chat_messages table changes rename_table :topic_chat_messages, :chat_messages diff --git a/db/migrate/20210730134847_create_user_chat_channel_last_read.rb b/db/migrate/20210730134847_create_user_chat_channel_last_read.rb index 44a5021ac..a0b730468 100644 --- a/db/migrate/20210730134847_create_user_chat_channel_last_read.rb +++ b/db/migrate/20210730134847_create_user_chat_channel_last_read.rb @@ -8,6 +8,9 @@ def change t.integer :user_id, null: false end - add_index :user_chat_channel_last_reads, [:chat_channel_id, :user_id], unique: true, name: "user_chat_channel_reads_index" + add_index :user_chat_channel_last_reads, + %i[chat_channel_id user_id], + unique: true, + name: "user_chat_channel_reads_index" end end diff --git a/db/migrate/20210812145801_create_direct_message_tables.rb b/db/migrate/20210812145801_create_direct_message_tables.rb index d976f3280..3b231e078 100644 --- a/db/migrate/20210812145801_create_direct_message_tables.rb +++ b/db/migrate/20210812145801_create_direct_message_tables.rb @@ -13,8 +13,8 @@ def change end add_index :direct_message_users, - [:direct_message_channel_id, :user_id], - unique: true, - name: "direct_message_users_index" + %i[direct_message_channel_id user_id], + unique: true, + name: "direct_message_users_index" end end diff --git a/db/migrate/20210819202912_create_incoming_chat_webhooks.rb b/db/migrate/20210819202912_create_incoming_chat_webhooks.rb index 5d0d65139..23cc115b7 100644 --- a/db/migrate/20210819202912_create_incoming_chat_webhooks.rb +++ b/db/migrate/20210819202912_create_incoming_chat_webhooks.rb @@ -12,6 +12,6 @@ def change t.timestamps end - add_index :incoming_chat_webhooks, [:key, :chat_channel_id] + add_index :incoming_chat_webhooks, %i[key chat_channel_id] end end diff --git a/db/migrate/20210823160357_create_chat_webhook_events.rb b/db/migrate/20210823160357_create_chat_webhook_events.rb index 7cc3b1740..4e9576775 100644 --- a/db/migrate/20210823160357_create_chat_webhook_events.rb +++ b/db/migrate/20210823160357_create_chat_webhook_events.rb @@ -8,8 +8,8 @@ def change end add_index :chat_webhook_events, - [:chat_message_id, :incoming_chat_webhook_id], - unique: true, - name: "chat_webhook_events_index" + %i[chat_message_id incoming_chat_webhook_id], + unique: true, + name: "chat_webhook_events_index" end end diff --git a/db/migrate/20210901130308_create_user_chat_channel_membership.rb b/db/migrate/20210901130308_create_user_chat_channel_membership.rb index b083cdc11..81eea0a67 100644 --- a/db/migrate/20210901130308_create_user_chat_channel_membership.rb +++ b/db/migrate/20210901130308_create_user_chat_channel_membership.rb @@ -13,21 +13,18 @@ def change end add_index :user_chat_channel_memberships, - [ - :user_id, - :chat_channel_id, - :desktop_notification_level, - :mobile_notification_level, - :following - ], - name: "user_chat_channel_memberships_index" + %i[ + user_id + chat_channel_id + desktop_notification_level + mobile_notification_level + following + ], + name: "user_chat_channel_memberships_index" add_index :user_chat_channel_memberships, - [ - :user_id, - :chat_channel_id, - ], - unique: true, - name: "user_chat_channel_unique_memberships" + %i[user_id chat_channel_id], + unique: true, + name: "user_chat_channel_unique_memberships" end end diff --git a/db/migrate/20211022151713_create_chat_message_post_connections.rb b/db/migrate/20211022151713_create_chat_message_post_connections.rb index f9ac0cbdf..dab116d3c 100644 --- a/db/migrate/20211022151713_create_chat_message_post_connections.rb +++ b/db/migrate/20211022151713_create_chat_message_post_connections.rb @@ -8,8 +8,8 @@ def change end add_index :chat_message_post_connections, - [:post_id, :chat_message_id], - unique: true, - name: "chat_message_post_connections_index" + %i[post_id chat_message_id], + unique: true, + name: "chat_message_post_connections_index" end end diff --git a/db/migrate/20211129171229_create_chat_uploads.rb b/db/migrate/20211129171229_create_chat_uploads.rb index bd6d73d9d..7eb9d5668 100644 --- a/db/migrate/20211129171229_create_chat_uploads.rb +++ b/db/migrate/20211129171229_create_chat_uploads.rb @@ -8,6 +8,6 @@ def change t.timestamps end - add_index :chat_uploads, [:chat_message_id, :upload_id], unique: true + add_index :chat_uploads, %i[chat_message_id upload_id], unique: true end end diff --git a/db/migrate/20211201171813_create_chat_reactions.rb b/db/migrate/20211201171813_create_chat_reactions.rb index d5459753c..377849e50 100644 --- a/db/migrate/20211201171813_create_chat_reactions.rb +++ b/db/migrate/20211201171813_create_chat_reactions.rb @@ -9,8 +9,8 @@ def change end add_index :chat_message_reactions, - [:chat_message_id, :user_id, :emoji], - unique: true, - name: :chat_message_reactions_index + %i[chat_message_id user_id emoji], + unique: true, + name: :chat_message_reactions_index end end diff --git a/db/migrate/20211210191830_create_chat_mentions.rb b/db/migrate/20211210191830_create_chat_mentions.rb index 99a3689b0..26f42042d 100644 --- a/db/migrate/20211210191830_create_chat_mentions.rb +++ b/db/migrate/20211210191830_create_chat_mentions.rb @@ -8,6 +8,9 @@ def change t.timestamps end - add_index :chat_mentions, [:chat_message_id, :user_id, :notification_id], unique: true, name: "chat_mentions_index" + add_index :chat_mentions, + %i[chat_message_id user_id notification_id], + unique: true, + name: "chat_mentions_index" end end diff --git a/db/migrate/20220309174820_add_last_message_created_at_to_chat_channels.rb b/db/migrate/20220309174820_add_last_message_created_at_to_chat_channels.rb index 29957e552..5f07d4cf8 100644 --- a/db/migrate/20220309174820_add_last_message_created_at_to_chat_channels.rb +++ b/db/migrate/20220309174820_add_last_message_created_at_to_chat_channels.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class AddLastMessageCreatedAtToChatChannels < ActiveRecord::Migration[6.1] def change - add_column :chat_channels, :last_message_sent_at, :datetime, default: -> { 'CURRENT_TIMESTAMP' } + add_column :chat_channels, :last_message_sent_at, :datetime, default: -> { "CURRENT_TIMESTAMP" } change_column_null :chat_channels, :last_message_sent_at, false end end diff --git a/db/migrate/20220328142120_create_user_chat_message_statuses.rb b/db/migrate/20220328142120_create_user_chat_message_statuses.rb index 353632bab..a1012b44a 100644 --- a/db/migrate/20220328142120_create_user_chat_message_statuses.rb +++ b/db/migrate/20220328142120_create_user_chat_message_statuses.rb @@ -10,7 +10,9 @@ def change t.timestamps end - add_index :chat_message_email_statuses, [:user_id, :chat_message_id], name: "chat_message_email_status_user_message_index" + add_index :chat_message_email_statuses, + %i[user_id chat_message_id], + name: "chat_message_email_status_user_message_index" add_index :chat_message_email_statuses, :status add_column :user_options, :chat_email_frequency, :integer, default: 1, null: false diff --git a/db/post_migrate/20220321235638_drop_chat_message_post_connections_table.rb b/db/post_migrate/20220321235638_drop_chat_message_post_connections_table.rb index de68e377d..ca6d3b795 100644 --- a/db/post_migrate/20220321235638_drop_chat_message_post_connections_table.rb +++ b/db/post_migrate/20220321235638_drop_chat_message_post_connections_table.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'migration/table_dropper' +require "migration/table_dropper" class DropChatMessagePostConnectionsTable < ActiveRecord::Migration[6.1] def up diff --git a/db/post_migrate/20220504080457_drop_old_chat_message_post_id_action_code_columns.rb b/db/post_migrate/20220504080457_drop_old_chat_message_post_id_action_code_columns.rb index 696c11018..6ca1c406b 100644 --- a/db/post_migrate/20220504080457_drop_old_chat_message_post_id_action_code_columns.rb +++ b/db/post_migrate/20220504080457_drop_old_chat_message_post_id_action_code_columns.rb @@ -1,17 +1,10 @@ # frozen_string_literal: true class DropOldChatMessagePostIdActionCodeColumns < ActiveRecord::Migration[7.0] - DROPPED_COLUMNS ||= { - chat_messages: %i{ - post_id - action_code - } - } + DROPPED_COLUMNS ||= { chat_messages: %i[post_id action_code] } def up - DROPPED_COLUMNS.each do |table, columns| - Migration::ColumnDropper.execute_drop(table, columns) - end + DROPPED_COLUMNS.each { |table, columns| Migration::ColumnDropper.execute_drop(table, columns) } end def down diff --git a/db/post_migrate/20220516142658_remove_email_statuses_table.rb b/db/post_migrate/20220516142658_remove_email_statuses_table.rb index 6f40b4ece..4daf38ae0 100644 --- a/db/post_migrate/20220516142658_remove_email_statuses_table.rb +++ b/db/post_migrate/20220516142658_remove_email_statuses_table.rb @@ -3,7 +3,7 @@ class RemoveEmailStatusesTable < ActiveRecord::Migration[7.0] def up remove_index :chat_message_email_statuses, :status - remove_index :chat_message_email_statuses, [:user_id, :chat_message_id] + remove_index :chat_message_email_statuses, %i[user_id chat_message_id] Migration::TableDropper.execute_drop("chat_message_email_statuses") end diff --git a/db/post_migrate/20220531105951_drop_user_chat_channel_last_reads.rb b/db/post_migrate/20220531105951_drop_user_chat_channel_last_reads.rb index 0bbee87be..b746266d8 100644 --- a/db/post_migrate/20220531105951_drop_user_chat_channel_last_reads.rb +++ b/db/post_migrate/20220531105951_drop_user_chat_channel_last_reads.rb @@ -1,16 +1,14 @@ # frozen_string_literal: true -require 'migration/table_dropper' +require "migration/table_dropper" # usage has been dropped in https://github.com/discourse/discourse-chat/commit/1c110b71b28411dc7ac3ab9e3950e0bbf38d7970 # but table never got dropped class DropUserChatChannelLastReads < ActiveRecord::Migration[7.0] - DROPPED_TABLES ||= %i{ user_chat_channel_last_reads } + DROPPED_TABLES ||= %i[user_chat_channel_last_reads] def up - DROPPED_TABLES.each do |table| - Migration::TableDropper.execute_drop(table) - end + DROPPED_TABLES.each { |table| Migration::TableDropper.execute_drop(table) } end def down diff --git a/db/post_migrate/20220630074200_drop_chat_isolated_from_user_options.rb b/db/post_migrate/20220630074200_drop_chat_isolated_from_user_options.rb index 7c1866eff..0a4114941 100644 --- a/db/post_migrate/20220630074200_drop_chat_isolated_from_user_options.rb +++ b/db/post_migrate/20220630074200_drop_chat_isolated_from_user_options.rb @@ -1,14 +1,10 @@ # frozen_string_literal: true class DropChatIsolatedFromUserOptions < ActiveRecord::Migration[7.0] - DROPPED_COLUMNS ||= { - user_options: %i{ chat_isolated } - } + DROPPED_COLUMNS ||= { user_options: %i[chat_isolated] } def up - DROPPED_COLUMNS.each do |table, columns| - Migration::ColumnDropper.execute_drop(table, columns) - end + DROPPED_COLUMNS.each { |table, columns| Migration::ColumnDropper.execute_drop(table, columns) } end def down diff --git a/db/post_migrate/20220701195731_convert_chatable_topics_to_categories.rb b/db/post_migrate/20220701195731_convert_chatable_topics_to_categories.rb index 6b78149a9..05e61f5f8 100644 --- a/db/post_migrate/20220701195731_convert_chatable_topics_to_categories.rb +++ b/db/post_migrate/20220701195731_convert_chatable_topics_to_categories.rb @@ -16,9 +16,9 @@ def up # soft delete all posts small actions DB.exec( "UPDATE posts SET deleted_at = :deleted_at, deleted_by_id = :deleted_by_id WHERE action_code IN (:action_codes)", - action_codes: ['chat.enabled', 'chat.disabled'], + action_codes: %w[chat.enabled chat.disabled], deleted_at: Time.zone.now, - deleted_by_id: Discourse::SYSTEM_USER_ID + deleted_by_id: Discourse::SYSTEM_USER_ID, ) # removes all chat custom fields diff --git a/lib/chat_channel_archive_service.rb b/lib/chat_channel_archive_service.rb index bc513c27c..8f64bb6bf 100644 --- a/lib/chat_channel_archive_service.rb +++ b/lib/chat_channel_archive_service.rb @@ -23,15 +23,16 @@ def self.begin_archive_process(chat_channel:, acting_user:, topic_params:) ChatChannelArchive.transaction do chat_channel.read_only!(acting_user) - archive = ChatChannelArchive.create!( - chat_channel: chat_channel, - archived_by: acting_user, - total_messages: chat_channel.chat_messages.count, - destination_topic_id: topic_params[:topic_id], - destination_topic_title: topic_params[:topic_title], - destination_category_id: topic_params[:category_id], - destination_tags: topic_params[:tags], - ) + archive = + ChatChannelArchive.create!( + chat_channel: chat_channel, + archived_by: acting_user, + total_messages: chat_channel.chat_messages.count, + destination_topic_id: topic_params[:topic_id], + destination_topic_title: topic_params[:topic_title], + destination_category_id: topic_params[:category_id], + destination_tags: topic_params[:tags], + ) Jobs.enqueue(:chat_channel_archive, chat_channel_archive_id: archive.id) archive @@ -40,7 +41,10 @@ def self.begin_archive_process(chat_channel:, acting_user:, topic_params:) def self.retry_archive_process(chat_channel:) return if !chat_channel.chat_channel_archive&.failed? - Jobs.enqueue(:chat_channel_archive, chat_channel_archive_id: chat_channel.chat_channel_archive.id) + Jobs.enqueue( + :chat_channel_archive, + chat_channel_archive_id: chat_channel.chat_channel_archive.id, + ) end attr_reader :chat_channel_archive, :chat_channel, :chat_channel_title @@ -57,7 +61,9 @@ def execute begin ensure_destination_topic_exists! - Rails.logger.info("Creating posts from message batches for #{chat_channel_title} archive, #{chat_channel_archive.total_messages} messages to archive (#{chat_channel_archive.total_messages / ARCHIVED_MESSAGES_PER_POST} posts).") + Rails.logger.info( + "Creating posts from message batches for #{chat_channel_title} archive, #{chat_channel_archive.total_messages} messages to archive (#{chat_channel_archive.total_messages / ARCHIVED_MESSAGES_PER_POST} posts).", + ) # a batch should be idempotent, either the post is created and the # messages are deleted or we roll back the whole thing. @@ -69,20 +75,21 @@ def execute # another future improvement is to send a MessageBus message for each # completed batch, so the UI can receive updates and show a progress # bar or something similar - chat_channel.chat_messages.find_in_batches( - batch_size: ARCHIVED_MESSAGES_PER_POST - ) do |chat_messages| - create_post( - ChatTranscriptService.new( - chat_channel, - chat_channel_archive.archived_by, - messages_or_ids: chat_messages, - opts: { no_link: true, include_reactions: true } - ).generate_markdown - ) do - delete_message_batch(chat_messages.map(&:id)) + chat_channel + .chat_messages + .find_in_batches(batch_size: ARCHIVED_MESSAGES_PER_POST) do |chat_messages| + create_post( + ChatTranscriptService.new( + chat_channel, + chat_channel_archive.archived_by, + messages_or_ids: chat_messages, + opts: { + no_link: true, + include_reactions: true, + }, + ).generate_markdown, + ) { delete_message_batch(chat_messages.map(&:id)) } end - end kick_all_users complete_archive @@ -97,23 +104,20 @@ def execute def create_post(raw) pc = nil Post.transaction do - pc = PostCreator.new( - Discourse.system_user, - raw: raw, - - # we must skip these because the posts are created in a big transaction, - # we do them all at the end instead - skip_jobs: true, - - # we do not want to be sending out notifications etc. from this - # automatic background process - import_mode: true, - - # don't want to be stopped by watched word or post length validations - skip_validations: true, - - topic_id: chat_channel_archive.destination_topic_id - ) + pc = + PostCreator.new( + Discourse.system_user, + raw: raw, + # we must skip these because the posts are created in a big transaction, + # we do them all at the end instead + skip_jobs: true, + # we do not want to be sending out notifications etc. from this + # automatic background process + import_mode: true, + # don't want to be stopped by watched word or post length validations + skip_validations: true, + topic_id: chat_channel_archive.destination_topic_id, + ) pc.create @@ -127,16 +131,17 @@ def ensure_destination_topic_exists! if !chat_channel_archive.destination_topic.present? Rails.logger.info("Creating topic for #{chat_channel_title} archive.") Topic.transaction do - topic_creator = TopicCreator.new( - Discourse.system_user, - Guardian.new(chat_channel_archive.archived_by), - { - title: chat_channel_archive.destination_topic_title, - category: chat_channel_archive.destination_category_id, - tags: chat_channel_archive.destination_tags, - import_mode: true - } - ) + topic_creator = + TopicCreator.new( + Discourse.system_user, + Guardian.new(chat_channel_archive.archived_by), + { + title: chat_channel_archive.destination_topic_title, + category: chat_channel_archive.destination_category_id, + tags: chat_channel_archive.destination_tags, + import_mode: true, + }, + ) chat_channel_archive.update!(destination_topic: topic_creator.create) end @@ -146,8 +151,8 @@ def ensure_destination_topic_exists! I18n.t( "chat.channel.archive.first_post_raw", channel_name: chat_channel_title, - channel_url: chat_channel.url - ) + channel_url: chat_channel.url, + ), ) else Rails.logger.info("Topic already exists for #{chat_channel_title} archive.") @@ -173,15 +178,17 @@ def delete_message_batch(message_ids) ChatMessage.transaction do ChatMessage.where(id: message_ids).update_all( deleted_at: DateTime.now, - deleted_by_id: chat_channel_archive.archived_by.id + deleted_by_id: chat_channel_archive.archived_by.id, ) chat_channel_archive.update!( - archived_messages: chat_channel_archive.archived_messages + message_ids.length + archived_messages: chat_channel_archive.archived_messages + message_ids.length, ) end - Rails.logger.info("Archived #{chat_channel_archive.archived_messages} messages for #{chat_channel_title} archive.") + Rails.logger.info( + "Archived #{chat_channel_archive.archived_messages} messages for #{chat_channel_title} archive.", + ) end def complete_archive @@ -194,7 +201,7 @@ def notify_archiver(result, error: nil) base_translation_params = { channel_name: chat_channel_title, topic_title: chat_channel_archive.destination_topic.title, - topic_url: chat_channel_archive.destination_topic.url + topic_url: chat_channel_archive.destination_topic.url, } if result == :failed @@ -203,20 +210,25 @@ def notify_archiver(result, error: nil) message: "Error when archiving chat channel #{chat_channel_title}.", env: { chat_channel_id: chat_channel.id, - chat_channel_name: chat_channel_title - } - ) - error_translation_params = base_translation_params.merge( - channel_url: chat_channel.url, - messages_archived: chat_channel_archive.archived_messages + chat_channel_name: chat_channel_title, + }, ) + error_translation_params = + base_translation_params.merge( + channel_url: chat_channel.url, + messages_archived: chat_channel_archive.archived_messages, + ) chat_channel_archive.update(archive_error: error.message) SystemMessage.create_from_system_user( - chat_channel_archive.archived_by, :chat_channel_archive_failed, error_translation_params + chat_channel_archive.archived_by, + :chat_channel_archive_failed, + error_translation_params, ) else SystemMessage.create_from_system_user( - chat_channel_archive.archived_by, :chat_channel_archive_complete, base_translation_params + chat_channel_archive.archived_by, + :chat_channel_archive_complete, + base_translation_params, ) end @@ -225,13 +237,14 @@ def notify_archiver(result, error: nil) archive_status: result, archived_messages: chat_channel_archive.archived_messages, archive_topic_id: chat_channel_archive.destination_topic_id, - total_messages: chat_channel_archive.total_messages + total_messages: chat_channel_archive.total_messages, ) end def kick_all_users UserChatChannelMembership.where(chat_channel: chat_channel).update_all( - following: false, last_read_message_id: chat_channel.chat_messages.last&.id + following: false, + last_read_message_id: chat_channel.chat_messages.last&.id, ) end end diff --git a/lib/chat_channel_fetcher.rb b/lib/chat_channel_fetcher.rb index 679f51c44..681bacb00 100644 --- a/lib/chat_channel_fetcher.rb +++ b/lib/chat_channel_fetcher.rb @@ -6,35 +6,43 @@ module DiscourseChat::ChatChannelFetcher def self.structured(guardian) memberships = UserChatChannelMembership.where(user_id: guardian.user.id) { - public_channels: secured_public_channels( - guardian, - memberships, - status: :open, - following: true - ), - direct_message_channels: secured_direct_message_channels( - guardian.user.id, - memberships, - guardian - ), + public_channels: + secured_public_channels(guardian, memberships, status: :open, following: true), + direct_message_channels: + secured_direct_message_channels(guardian.user.id, memberships, guardian), } end def self.all_secured_channel_ids(guardian) allowed_channel_ids_sql = <<~SQL -- secured category chat channels - #{ChatChannel.select(:id).joins( - "INNER JOIN categories ON categories.id = chat_channels.chatable_id AND chat_channels.chatable_type = 'Category'" - ).where("categories.id IN (:allowed_category_ids)", allowed_category_ids: guardian.allowed_category_ids).to_sql} + #{ + ChatChannel + .select(:id) + .joins( + "INNER JOIN categories ON categories.id = chat_channels.chatable_id AND chat_channels.chatable_type = 'Category'", + ) + .where( + "categories.id IN (:allowed_category_ids)", + allowed_category_ids: guardian.allowed_category_ids, + ) + .to_sql + } UNION -- secured direct message chat channels - #{ChatChannel.select(:id).joins( - "INNER JOIN direct_message_channels ON direct_message_channels.id = chat_channels.chatable_id + #{ + ChatChannel + .select(:id) + .joins( + "INNER JOIN direct_message_channels ON direct_message_channels.id = chat_channels.chatable_id AND chat_channels.chatable_type = 'DirectMessageChannel' - INNER JOIN direct_message_users ON direct_message_users.direct_message_channel_id = direct_message_channels.id" - ).where("direct_message_users.user_id = :user_id", user_id: guardian.user.id).to_sql} + INNER JOIN direct_message_users ON direct_message_users.direct_message_channel_id = direct_message_channels.id", + ) + .where("direct_message_users.user_id = :user_id", user_id: guardian.user.id) + .to_sql + } SQL DB.query_single(<<~SQL, user_id: guardian.user.id) @@ -48,26 +56,33 @@ def self.all_secured_channel_ids(guardian) end def self.secured_public_channels(guardian, memberships, options = { following: true }) - channels = ChatChannel.includes(:chatable, :chat_channel_archive) - .joins("LEFT JOIN categories ON categories.id = chat_channels.chatable_id AND chat_channels.chatable_type = 'Category'") - .where(chatable_type: ChatChannel.public_channel_chatable_types) + channels = + ChatChannel + .includes(:chatable, :chat_channel_archive) + .joins( + "LEFT JOIN categories ON categories.id = chat_channels.chatable_id AND chat_channels.chatable_type = 'Category'", + ) + .where(chatable_type: ChatChannel.public_channel_chatable_types) - if options[:status].present? - channels = channels.where(status: options[:status]) - end + channels = channels.where(status: options[:status]) if options[:status].present? if options[:filter].present? - channels = channels - .where(<<~SQL, filter: "%#{options[:filter].downcase}%") + channels = + channels.where(<<~SQL, filter: "%#{options[:filter].downcase}%").order( chat_channels.name ILIKE :filter OR categories.name ILIKE :filter SQL - .order('chat_channels.name ASC, categories.name ASC') + "chat_channels.name ASC, categories.name ASC", + ) end if options[:following].present? - channels = channels - .joins(:user_chat_channel_memberships) - .where(user_chat_channel_memberships: { user_id: guardian.user.id, following: true }) + channels = + channels.joins(:user_chat_channel_memberships).where( + user_chat_channel_memberships: { + user_id: guardian.user.id, + following: true, + }, + ) end options[:limit] = (options[:limit] || MAX_RESULTS).to_i.clamp(1, MAX_RESULTS) @@ -81,14 +96,18 @@ def self.secured_public_channels(guardian, memberships, options = { following: t def self.preload_custom_fields_for(channels) preload_fields = Category.instance_variable_get(:@custom_field_types).keys - Category.preload_custom_fields(channels.select { |c| c.chatable_type == 'Category' }.map(&:chatable), preload_fields) + Category.preload_custom_fields( + channels.select { |c| c.chatable_type == "Category" }.map(&:chatable), + preload_fields, + ) end def self.filter_public_channels(channels, memberships, guardian) - mention_notifications = Notification.unread.where( - user_id: guardian.user.id, - notification_type: Notification.types[:chat_mention], - ) + mention_notifications = + Notification.unread.where( + user_id: guardian.user.id, + notification_type: Notification.types[:chat_mention], + ) mention_notification_data = mention_notifications.map { |m| JSON.parse(m.data) } unread_counts_per_channel = unread_counts(channels, guardian.user.id) @@ -98,30 +117,35 @@ def self.filter_public_channels(channels, memberships, guardian) membership = memberships.find { |m| m.chat_channel_id == channel.id } if membership - channel = decorate_channel_from_membership( - guardian.user.id, - channel, - membership, - mention_notification_data - ) - - if !channel.muted - channel.unread_count = unread_counts_per_channel[channel.id] - end + channel = + decorate_channel_from_membership( + guardian.user.id, + channel, + membership, + mention_notification_data, + ) + + channel.unread_count = unread_counts_per_channel[channel.id] if !channel.muted end channel end end - def self.decorate_channel_from_membership(user_id, channel, membership, mention_notification_data = nil) + def self.decorate_channel_from_membership( + user_id, + channel, + membership, + mention_notification_data = nil + ) channel.last_read_message_id = membership.last_read_message_id channel.muted = membership.muted if mention_notification_data - channel.unread_mentions = mention_notification_data.count { |data| - data["chat_channel_id"] == channel.id && - data["chat_message_id"] > (membership.last_read_message_id || 0) - } + channel.unread_mentions = + mention_notification_data.count do |data| + data["chat_channel_id"] == channel.id && + data["chat_message_id"] > (membership.last_read_message_id || 0) + end end channel.following = membership.following channel.desktop_notification_level = membership.desktop_notification_level @@ -130,15 +154,18 @@ def self.decorate_channel_from_membership(user_id, channel, membership, mention_ end def self.secured_direct_message_channels(user_id, memberships, guardian) - channels = ChatChannel - .includes(chatable: [{ direct_message_users: :user }, :users ]) - .joins(:user_chat_channel_memberships) - .where(user_chat_channel_memberships: { user_id: user_id, following: true }) - .where(chatable_type: "DirectMessageChannel") - .order(last_message_sent_at: :desc) - .to_a - - preload_fields = User.allowed_user_custom_fields(guardian) + UserField.all.pluck(:id).map { |fid| "#{User::USER_FIELD_PREFIX}#{fid}" } + channels = + ChatChannel + .includes(chatable: [{ direct_message_users: :user }, :users]) + .joins(:user_chat_channel_memberships) + .where(user_chat_channel_memberships: { user_id: user_id, following: true }) + .where(chatable_type: "DirectMessageChannel") + .order(last_message_sent_at: :desc) + .to_a + + preload_fields = + User.allowed_user_custom_fields(guardian) + + UserField.all.pluck(:id).map { |fid| "#{User::USER_FIELD_PREFIX}#{fid}" } User.preload_custom_fields(channels.map { |c| c.chatable.users }.flatten, preload_fields) unread_counts_per_channel = unread_counts(channels, user_id) @@ -146,11 +173,12 @@ def self.secured_direct_message_channels(user_id, memberships, guardian) channels.filter_map do |channel| next if !guardian.can_see_chat_channel?(channel) - channel = decorate_channel_from_membership( - user_id, - channel, - memberships.find { |m| m.user_id == user_id && m.chat_channel_id == channel.id } - ) + channel = + decorate_channel_from_membership( + user_id, + channel, + memberships.find { |m| m.user_id == user_id && m.chat_channel_id == channel.id }, + ) # direct message channels cannot be muted, so we always need the unread count channel.unread_count = unread_counts_per_channel[channel.id] @@ -182,9 +210,10 @@ def self.find_with_access_check(channel_id_or_name, guardian) rescue ArgumentError end - base_channel_relation = ChatChannel - .includes(:chatable) - .joins("LEFT JOIN categories ON categories.id = chat_channels.chatable_id AND chat_channels.chatable_type = 'Category'") + base_channel_relation = + ChatChannel.includes(:chatable).joins( + "LEFT JOIN categories ON categories.id = chat_channels.chatable_id AND chat_channels.chatable_type = 'Category'", + ) if guardian.user.staff? base_channel_relation = base_channel_relation.includes(:chat_channel_archive) @@ -193,7 +222,11 @@ def self.find_with_access_check(channel_id_or_name, guardian) if channel_id_or_name.is_a? Integer chat_channel = base_channel_relation.find_by(id: channel_id_or_name) else - chat_channel = base_channel_relation.find_by("LOWER(categories.name) = :name OR LOWER(chat_channels.name) = :name", name: channel_id_or_name.downcase) + chat_channel = + base_channel_relation.find_by( + "LOWER(categories.name) = :name OR LOWER(chat_channels.name) = :name", + name: channel_id_or_name.downcase, + ) end raise Discourse::NotFound if chat_channel.blank? diff --git a/lib/chat_mailer.rb b/lib/chat_mailer.rb index 843295f18..3d4e64a72 100644 --- a/lib/chat_mailer.rb +++ b/lib/chat_mailer.rb @@ -7,17 +7,21 @@ def self.send_unread_mentions_summary users_with_unprocessed_unread_mentions.find_each do |user| # user#memberships_with_unread_messages is a nested array that looks like [[membership_id, unread_message_id]] # Find the max unread id per membership. - membership_and_max_unread_mention_ids = user.memberships_with_unread_messages - .group_by { |memberships| memberships[0] } - .transform_values do |membership_and_msg_ids| - membership_and_msg_ids.max_by { |membership, msg| msg } - end.values + membership_and_max_unread_mention_ids = + user + .memberships_with_unread_messages + .group_by { |memberships| memberships[0] } + .transform_values do |membership_and_msg_ids| + membership_and_msg_ids.max_by { |membership, msg| msg } + end + .values - Jobs.enqueue(:user_email, + Jobs.enqueue( + :user_email, type: "chat_summary", user_id: user.id, force_respect_seen_recently: true, - memberships_to_update_data: membership_and_max_unread_mention_ids + memberships_to_update_data: membership_and_max_unread_mention_ids, ) end end @@ -29,26 +33,24 @@ def self.users_with_unprocessed_unread_mentions allowed_group_ids = DiscourseChat.allowed_group_ids User - .select('users.id', 'ARRAY_AGG(ARRAY[uccm.id, c_msg.id]) AS memberships_with_unread_messages') + .select("users.id", "ARRAY_AGG(ARRAY[uccm.id, c_msg.id]) AS memberships_with_unread_messages") .joins(:user_option) .where(user_options: { chat_enabled: true, chat_email_frequency: when_away_frequency }) - .where('users.last_seen_at < ?', 15.minutes.ago) + .where("users.last_seen_at < ?", 15.minutes.ago) .joins(:groups) .where(groups: { id: allowed_group_ids }) - .joins('INNER JOIN user_chat_channel_memberships uccm ON uccm.user_id = users.id') - .joins('INNER JOIN chat_channels cc ON cc.id = uccm.chat_channel_id') - .joins('INNER JOIN chat_messages c_msg ON c_msg.chat_channel_id = uccm.chat_channel_id') - .joins('LEFT OUTER JOIN chat_mentions c_mentions ON c_mentions.chat_message_id = c_msg.id') + .joins("INNER JOIN user_chat_channel_memberships uccm ON uccm.user_id = users.id") + .joins("INNER JOIN chat_channels cc ON cc.id = uccm.chat_channel_id") + .joins("INNER JOIN chat_messages c_msg ON c_msg.chat_channel_id = uccm.chat_channel_id") + .joins("LEFT OUTER JOIN chat_mentions c_mentions ON c_mentions.chat_message_id = c_msg.id") .where(uccm: { following: true }) - .where('c_msg.deleted_at IS NULL AND c_msg.user_id <> users.id') - .where('c_msg.created_at > ?', 1.week.ago) - .where( - <<~SQL + .where("c_msg.deleted_at IS NULL AND c_msg.user_id <> users.id") + .where("c_msg.created_at > ?", 1.week.ago) + .where(<<~SQL) (c_mentions.user_id = uccm.user_id OR cc.chatable_type = 'DirectMessageChannel') AND (uccm.last_read_message_id IS NULL OR c_msg.id > uccm.last_read_message_id) AND (uccm.last_unread_mention_when_emailed_id IS NULL OR c_msg.id > uccm.last_unread_mention_when_emailed_id) SQL - ) - .group('users.id, uccm.user_id') + .group("users.id, uccm.user_id") end end diff --git a/lib/chat_message_bookmarkable.rb b/lib/chat_message_bookmarkable.rb index 83286e469..517699a5f 100644 --- a/lib/chat_message_bookmarkable.rb +++ b/lib/chat_message_bookmarkable.rb @@ -16,11 +16,12 @@ def self.preload_associations def self.list_query(user, guardian) accessible_channel_ids = DiscourseChat::ChatChannelFetcher.all_secured_channel_ids(guardian) return if accessible_channel_ids.empty? - user.bookmarks_of_type("ChatMessage") + user + .bookmarks_of_type("ChatMessage") .joins( "INNER JOIN chat_messages ON chat_messages.id = bookmarks.bookmarkable_id AND chat_messages.deleted_at IS NULL - AND bookmarks.bookmarkable_type = 'ChatMessage'" + AND bookmarks.bookmarkable_type = 'ChatMessage'", ) .where("chat_messages.chat_channel_id IN (?)", accessible_channel_ids) end @@ -30,19 +31,22 @@ def self.search_query(bookmarks, query, ts_query, &bookmarkable_search) end def self.validate_before_create(guardian, bookmarkable) - raise Discourse::InvalidAccess if bookmarkable.blank? || !guardian.can_see_chat_channel?(bookmarkable.chat_channel) + if bookmarkable.blank? || !guardian.can_see_chat_channel?(bookmarkable.chat_channel) + raise Discourse::InvalidAccess + end end def self.reminder_handler(bookmark) send_reminder_notification( bookmark, data: { - title: I18n.t( - "chat.bookmarkable.notification_title", - channel_name: bookmark.bookmarkable.chat_channel.title(bookmark.user) - ), - bookmarkable_url: bookmark.bookmarkable.url - } + title: + I18n.t( + "chat.bookmarkable.notification_title", + channel_name: bookmark.bookmarkable.chat_channel.title(bookmark.user), + ), + bookmarkable_url: bookmark.bookmarkable.url, + }, ) end diff --git a/lib/chat_message_creator.rb b/lib/chat_message_creator.rb index c72ae090e..2e7b83d97 100644 --- a/lib/chat_message_creator.rb +++ b/lib/chat_message_creator.rb @@ -8,7 +8,15 @@ def self.create(opts) instance end - def initialize(chat_channel:, in_reply_to_id: nil, user:, content:, staged_id: nil, incoming_chat_webhook: nil, upload_ids: nil) + def initialize( + chat_channel:, + in_reply_to_id: nil, + user:, + content:, + staged_id: nil, + incoming_chat_webhook: nil, + upload_ids: nil + ) @chat_channel = chat_channel @user = user @guardian = Guardian.new(user) @@ -19,12 +27,13 @@ def initialize(chat_channel:, in_reply_to_id: nil, user:, content:, staged_id: n @upload_ids = upload_ids || [] @error = nil - @chat_message = ChatMessage.new( - chat_channel: @chat_channel, - user_id: @user.id, - in_reply_to_id: @in_reply_to_id, - message: @content, - ) + @chat_message = + ChatMessage.new( + chat_channel: @chat_channel, + user_id: @user.id, + in_reply_to_id: @in_reply_to_id, + message: @content, + ) end def create @@ -41,7 +50,7 @@ def create Jobs.enqueue(:process_chat_message, { chat_message_id: @chat_message.id }) DiscourseChat::ChatNotifier.notify_new( chat_message: @chat_message, - timestamp: @chat_message.created_at + timestamp: @chat_message.created_at, ) rescue => error @error = error @@ -58,8 +67,8 @@ def validate_channel_status! return if @guardian.can_create_channel_message?(@chat_channel) raise StandardError.new( - I18n.t("chat.errors.channel_new_message_disallowed", status: @chat_channel.status_name) - ) + I18n.t("chat.errors.channel_new_message_disallowed", status: @chat_channel.status_name), + ) end def validate_message!(has_uploads:) @@ -73,7 +82,7 @@ def create_chat_webhook_event return if @incoming_chat_webhook.blank? ChatWebhookEvent.create( chat_message: @chat_message, - incoming_chat_webhook: @incoming_chat_webhook + incoming_chat_webhook: @incoming_chat_webhook, ) end diff --git a/lib/chat_message_rate_limiter.rb b/lib/chat_message_rate_limiter.rb index 3d96ca14c..76f2ca25a 100644 --- a/lib/chat_message_rate_limiter.rb +++ b/lib/chat_message_rate_limiter.rb @@ -13,9 +13,14 @@ def initialize(user) def run! return if @user.staff? - allowed_message_count = @user.trust_level == TrustLevel[0] ? - SiteSetting.chat_allowed_messages_for_trust_level_0 : - SiteSetting.chat_allowed_messages_for_other_trust_levels + allowed_message_count = + ( + if @user.trust_level == TrustLevel[0] + SiteSetting.chat_allowed_messages_for_trust_level_0 + else + SiteSetting.chat_allowed_messages_for_other_trust_levels + end + ) return if allowed_message_count.zero? @rate_limiter = RateLimiter.new(@user, "create_chat_message", allowed_message_count, 30.seconds) @@ -38,7 +43,7 @@ def silence_user @user, Discourse.system_user, silenced_till: silenced_for_minutes.minutes.from_now, - reason: I18n.t("chat.errors.rate_limit_exceeded") + reason: I18n.t("chat.errors.rate_limit_exceeded"), ) end end diff --git a/lib/chat_message_reactor.rb b/lib/chat_message_reactor.rb index d3e1a7679..5fe42f66c 100644 --- a/lib/chat_message_reactor.rb +++ b/lib/chat_message_reactor.rb @@ -44,10 +44,8 @@ def validate_reaction!(react_action, emoji) end def enforce_channel_membership! - existing_membership = UserChatChannelMembership.find_or_initialize_by( - chat_channel: @chat_channel, - user: @user, - ) + existing_membership = + UserChatChannelMembership.find_or_initialize_by(chat_channel: @chat_channel, user: @user) unless existing_membership&.following existing_membership.following = true @@ -58,23 +56,24 @@ def enforce_channel_membership! def validate_channel_status! return if @guardian.can_create_channel_message?(@chat_channel) raise Discourse::InvalidAccess.new( - nil, - nil, - custom_message: "chat.errors.channel_modify_message_disallowed", - custom_message_params: { status: @chat_channel.status_name } - ) + nil, + nil, + custom_message: "chat.errors.channel_modify_message_disallowed", + custom_message_params: { + status: @chat_channel.status_name, + }, + ) end def validate_max_reactions!(message, react_action, emoji) if react_action == ADD_REACTION && - message.reactions.count('DISTINCT emoji') >= MAX_REACTIONS_LIMIT && - !message.reactions.exists?(emoji: emoji) - + message.reactions.count("DISTINCT emoji") >= MAX_REACTIONS_LIMIT && + !message.reactions.exists?(emoji: emoji) raise Discourse::InvalidAccess.new( - nil, - nil, - custom_message: "chat.errors.max_reactions_limit_reached" - ) + nil, + nil, + custom_message: "chat.errors.max_reactions_limit_reached", + ) end end @@ -87,12 +86,6 @@ def create_reaction(message, react_action, emoji) end def publish_reaction(message, react_action, emoji) - ChatPublisher.publish_reaction!( - @chat_channel, - message, - react_action, - @user, - emoji - ) + ChatPublisher.publish_reaction!(@chat_channel, message, react_action, @user, emoji) end end diff --git a/lib/chat_message_updater.rb b/lib/chat_message_updater.rb index 88e3bb3fd..9dd098772 100644 --- a/lib/chat_message_updater.rb +++ b/lib/chat_message_updater.rb @@ -33,7 +33,7 @@ def update Jobs.enqueue(:process_chat_message, { chat_message_id: @chat_message.id }) DiscourseChat::ChatNotifier.notify_edit( chat_message: @chat_message, - timestamp: revision.created_at + timestamp: revision.created_at, ) rescue => error @error = error @@ -49,8 +49,11 @@ def failed? def validate_channel_status! return if @guardian.can_modify_channel_message?(@chat_channel) raise StandardError.new( - I18n.t("chat.errors.channel_modify_message_disallowed", status: @chat_channel.status_name) - ) + I18n.t( + "chat.errors.channel_modify_message_disallowed", + status: @chat_channel.status_name, + ), + ) end def validate_message!(has_uploads:) @@ -83,6 +86,9 @@ def update_uploads(upload_info) end def save_revision! - @chat_message.revisions.create!(old_message: @old_message_content, new_message: @chat_message.message) + @chat_message.revisions.create!( + old_message: @old_message_content, + new_message: @chat_message.message, + ) end end diff --git a/lib/chat_notifier.rb b/lib/chat_notifier.rb index 955242d2c..7c157f5ff 100644 --- a/lib/chat_notifier.rb +++ b/lib/chat_notifier.rb @@ -37,7 +37,8 @@ def notify_new end notify_creator_of_inaccessible_mentions( - inaccessible[:unreachable], inaccessible[:welcome_to_join] + inaccessible[:unreachable], + inaccessible[:welcome_to_join], ) notify_mentioned_users(to_notify) @@ -47,7 +48,8 @@ def notify_new end def notify_edit - existing_notifications = ChatMention.includes(:user, :notification).where(chat_message: @chat_message) + existing_notifications = + ChatMention.includes(:user, :notification).where(chat_message: @chat_message) already_notified_user_ids = existing_notifications.map(&:user_id) to_notify = list_users_to_notify @@ -65,7 +67,8 @@ def notify_edit return if needs_notification_ids.blank? notify_creator_of_inaccessible_mentions( - inaccessible[:unreachable], inaccessible[:welcome_to_join] + inaccessible[:unreachable], + inaccessible[:welcome_to_join], ) notify_mentioned_users(to_notify, already_notified_user_ids: already_notified_user_ids) @@ -92,11 +95,12 @@ def list_users_to_notify end def chat_users - users = User.includes(:do_not_disturb_timings, :push_subscriptions, :user_chat_channel_memberships) + users = + User.includes(:do_not_disturb_timings, :push_subscriptions, :user_chat_channel_memberships) users .distinct - .joins('LEFT OUTER JOIN user_chat_channel_memberships uccm ON uccm.user_id = users.id') + .joins("LEFT OUTER JOIN user_chat_channel_memberships uccm ON uccm.user_id = users.id") .joins(:user_option) .real .not_suspended @@ -105,8 +109,12 @@ def chat_users end def rest_of_the_channel - chat_users - .where(user_chat_channel_memberships: { following: true, chat_channel_id: @chat_channel.id }) + chat_users.where( + user_chat_channel_memberships: { + following: true, + chat_channel_id: @chat_channel.id, + }, + ) end def members_accepting_channel_wide_notifications @@ -114,7 +122,8 @@ def members_accepting_channel_wide_notifications end def direct_mentions_from_cooked - @direct_mentions_from_cooked ||= Nokogiri::HTML5.fragment(@chat_message.cooked).css(".mention").map(&:text) + @direct_mentions_from_cooked ||= + Nokogiri::HTML5.fragment(@chat_message.cooked).css(".mention").map(&:text) end def normalized_mentions(mentions) @@ -155,26 +164,31 @@ def expand_here_mention(to_notify, already_covered_ids) end def group_users_to_notify(users) - potential_participants, unreachable = users.partition do |user| - guardian = Guardian.new(user) - guardian.can_chat?(user) && guardian.can_see_chat_channel?(@chat_channel) - end - - participants, welcome_to_join = potential_participants.partition do |participant| - participant.user_chat_channel_memberships.any? { |m| m.chat_channel_id == @chat_channel.id && m.following == true } - end + potential_participants, unreachable = + users.partition do |user| + guardian = Guardian.new(user) + guardian.can_chat?(user) && guardian.can_see_chat_channel?(@chat_channel) + end + + participants, welcome_to_join = + potential_participants.partition do |participant| + participant.user_chat_channel_memberships.any? do |m| + m.chat_channel_id == @chat_channel.id && m.following == true + end + end { already_participating: participants || [], welcome_to_join: welcome_to_join || [], - unreachable: unreachable || [] + unreachable: unreachable || [], } end def expand_direct_mentions(to_notify, already_covered_ids) - direct_mentions = chat_users - .where(username_lower: normalized_mentions(direct_mentions_from_cooked)) - .where.not(id: already_covered_ids) + direct_mentions = + chat_users + .where(username_lower: normalized_mentions(direct_mentions_from_cooked)) + .where.not(id: already_covered_ids) grouped = group_users_to_notify(direct_mentions) @@ -185,17 +199,18 @@ def expand_direct_mentions(to_notify, already_covered_ids) end def group_name_mentions - @group_mentions_from_cooked ||= normalized_mentions( - Nokogiri::HTML5 - .fragment(@chat_message.cooked) - .css(".mention-group") - .map(&:text) - ) + @group_mentions_from_cooked ||= + normalized_mentions( + Nokogiri::HTML5.fragment(@chat_message.cooked).css(".mention-group").map(&:text), + ) end def mentionable_groups - @mentionable_groups ||= Group.mentionable(@user, include_public: false) - .where('LOWER(name) IN (?)', group_name_mentions) + @mentionable_groups ||= + Group.mentionable(@user, include_public: false).where( + "LOWER(name) IN (?)", + group_name_mentions, + ) end def expand_group_mentions(to_notify, already_covered_ids) @@ -203,9 +218,8 @@ def expand_group_mentions(to_notify, already_covered_ids) mentionable_groups.each { |g| to_notify[g.name.downcase] = [] } - reached_by_group = chat_users - .joins(:groups).where(groups: mentionable_groups) - .where.not(id: already_covered_ids) + reached_by_group = + chat_users.joins(:groups).where(groups: mentionable_groups).where.not(id: already_covered_ids) grouped = group_users_to_notify(reached_by_group) @@ -227,23 +241,34 @@ def expand_group_mentions(to_notify, already_covered_ids) def notify_creator_of_inaccessible_mentions(unreachable, welcome_to_join) return if unreachable.empty? && welcome_to_join.empty? - ChatPublisher.publish_inaccessible_mentions(@user.id, @chat_message, unreachable, welcome_to_join) + ChatPublisher.publish_inaccessible_mentions( + @user.id, + @chat_message, + unreachable, + welcome_to_join, + ) end def notify_mentioned_users(to_notify, already_notified_user_ids: []) - Jobs.enqueue(:chat_notify_mentioned, { - chat_message_id: @chat_message.id, - to_notify_ids_map: to_notify.as_json, - already_notified_user_ids: already_notified_user_ids, - timestamp: @timestamp.iso8601(6) - }) + Jobs.enqueue( + :chat_notify_mentioned, + { + chat_message_id: @chat_message.id, + to_notify_ids_map: to_notify.as_json, + already_notified_user_ids: already_notified_user_ids, + timestamp: @timestamp.iso8601(6), + }, + ) end def notify_watching_users(except: []) - Jobs.enqueue(:chat_notify_watching, { - chat_message_id: @chat_message.id, - except_user_ids: except, - timestamp: @timestamp.iso8601(6) - }) + Jobs.enqueue( + :chat_notify_watching, + { + chat_message_id: @chat_message.id, + except_user_ids: except, + timestamp: @timestamp.iso8601(6), + }, + ) end end diff --git a/lib/chat_seeder.rb b/lib/chat_seeder.rb index f586a860a..1f889d2ad 100644 --- a/lib/chat_seeder.rb +++ b/lib/chat_seeder.rb @@ -18,17 +18,14 @@ def create_category_channel_from(category_id) category = Category.find_by(id: category_id) return if category.nil? - name = if category_id == SiteSetting.meta_category_id - I18n.t('chat.channel.default_titles.site_feedback') - else - nil - end + name = + if category_id == SiteSetting.meta_category_id + I18n.t("chat.channel.default_titles.site_feedback") + else + nil + end - chat_channel = ChatChannel.create!( - chatable: category, - auto_join_users: true, - name: name - ) + chat_channel = ChatChannel.create!(chatable: category, auto_join_users: true, name: name) category.custom_fields[DiscourseChat::HAS_CHAT_ENABLED] = true category.save! UserChatChannelMembership.enforce_automatic_channel_memberships(chat_channel) diff --git a/lib/chat_statistics.rb b/lib/chat_statistics.rb index d90dc6c6b..80e8b1850 100644 --- a/lib/chat_statistics.rb +++ b/lib/chat_statistics.rb @@ -3,40 +3,49 @@ class DiscourseChat::Statistics def self.about_messages { - last_day: ChatMessage.where('created_at > ?', 1.days.ago).count, - "7_days" => ChatMessage.where('created_at > ?', 7.days.ago).count, - "30_days" => ChatMessage.where('created_at > ?', 30.days.ago).count, - previous_30_days: ChatMessage.where('created_at BETWEEN ? AND ?', 60.days.ago, 30.days.ago).count, - count: ChatMessage.count + :last_day => ChatMessage.where("created_at > ?", 1.days.ago).count, + "7_days" => ChatMessage.where("created_at > ?", 7.days.ago).count, + "30_days" => ChatMessage.where("created_at > ?", 30.days.ago).count, + :previous_30_days => + ChatMessage.where("created_at BETWEEN ? AND ?", 60.days.ago, 30.days.ago).count, + :count => ChatMessage.count, } end def self.about_channels { - last_day: ChatChannel.where(status: :open).where('created_at > ?', 1.days.ago).count, - "7_days" => ChatChannel.where(status: :open).where('created_at > ?', 7.days.ago).count, - "30_days" => ChatChannel.where(status: :open).where('created_at > ?', 30.days.ago).count, - previous_30_days: ChatChannel.where(status: :open).where('created_at BETWEEN ? AND ?', 60.days.ago, 30.days.ago).count, - count: ChatChannel.where(status: :open).count + :last_day => ChatChannel.where(status: :open).where("created_at > ?", 1.days.ago).count, + "7_days" => ChatChannel.where(status: :open).where("created_at > ?", 7.days.ago).count, + "30_days" => ChatChannel.where(status: :open).where("created_at > ?", 30.days.ago).count, + :previous_30_days => + ChatChannel + .where(status: :open) + .where("created_at BETWEEN ? AND ?", 60.days.ago, 30.days.ago) + .count, + :count => ChatChannel.where(status: :open).count, } end def self.about_users { - last_day: ChatMessage.where('created_at > ?', 1.days.ago).distinct.count(:user_id), - "7_days" => ChatMessage.where('created_at > ?', 7.days.ago).distinct.count(:user_id), - "30_days" => ChatMessage.where('created_at > ?', 30.days.ago).distinct.count(:user_id), - previous_30_days: ChatMessage.where('created_at BETWEEN ? AND ?', 60.days.ago, 30.days.ago).distinct.count(:user_id), - count: ChatMessage.distinct.count(:user_id), + :last_day => ChatMessage.where("created_at > ?", 1.days.ago).distinct.count(:user_id), + "7_days" => ChatMessage.where("created_at > ?", 7.days.ago).distinct.count(:user_id), + "30_days" => ChatMessage.where("created_at > ?", 30.days.ago).distinct.count(:user_id), + :previous_30_days => + ChatMessage + .where("created_at BETWEEN ? AND ?", 60.days.ago, 30.days.ago) + .distinct + .count(:user_id), + :count => ChatMessage.distinct.count(:user_id), } end def self.monthly start_of_month = Time.zone.now.beginning_of_month { - messages: ChatMessage.where('created_at > ?', start_of_month).count, - channels: ChatChannel.where(status: :open).where('created_at > ?', start_of_month).count, - users: ChatMessage.where('created_at > ?', start_of_month).distinct.count(:user_id) + messages: ChatMessage.where("created_at > ?", start_of_month).count, + channels: ChatChannel.where(status: :open).where("created_at > ?", start_of_month).count, + users: ChatMessage.where("created_at > ?", start_of_month).distinct.count(:user_id), } end end diff --git a/lib/chat_transcript_service.rb b/lib/chat_transcript_service.rb index dfc41fa3f..6326494cd 100644 --- a/lib/chat_transcript_service.rb +++ b/lib/chat_transcript_service.rb @@ -66,14 +66,13 @@ def render private def reactions_attr - reaction_data = @message_data.reduce([]) do |array, msg_data| - if msg_data[:reactions].any? - array << msg_data[:reactions].map do |react| - "#{react.emoji}:#{react.usernames}" + reaction_data = + @message_data.reduce([]) do |array, msg_data| + if msg_data[:reactions].any? + array << msg_data[:reactions].map { |react| "#{react.emoji}:#{react.usernames}" } end + array end - array - end return if reaction_data.empty? "reactions=\"#{reaction_data.join(";")}\"" end @@ -107,25 +106,27 @@ def generate_markdown previous_message = nil rendered_markdown = [] all_messages_same_user = messages.count(:user_id) == 1 - open_bbcode_tag = ChatTranscriptBBCode.new( - channel: @channel, - acting_user: @acting_user, - multiquote: messages.length > 1, - chained: !all_messages_same_user, - no_link: @opts[:no_link], - include_reactions: @opts[:include_reactions] - ) + open_bbcode_tag = + ChatTranscriptBBCode.new( + channel: @channel, + acting_user: @acting_user, + multiquote: messages.length > 1, + chained: !all_messages_same_user, + no_link: @opts[:no_link], + include_reactions: @opts[:include_reactions], + ) messages.each.with_index do |message, idx| if previous_message.present? && previous_message.user_id != message.user_id rendered_markdown << open_bbcode_tag.render - open_bbcode_tag = ChatTranscriptBBCode.new( - acting_user: @acting_user, - chained: !all_messages_same_user, - no_link: @opts[:no_link], - include_reactions: @opts[:include_reactions] - ) + open_bbcode_tag = + ChatTranscriptBBCode.new( + acting_user: @acting_user, + chained: !all_messages_same_user, + no_link: @opts[:no_link], + include_reactions: @opts[:include_reactions], + ) end if @opts[:include_reactions] @@ -144,11 +145,11 @@ def generate_markdown private def messages - @messages ||= ChatMessage.includes( - :user, chat_uploads: :upload - ).where( - id: @message_ids, chat_channel_id: @channel.id - ).order(:created_at) + @messages ||= + ChatMessage + .includes(:user, chat_uploads: :upload) + .where(id: @message_ids, chat_channel_id: @channel.id) + .order(:created_at) end ## diff --git a/lib/direct_message_channel_creator.rb b/lib/direct_message_channel_creator.rb index 5f79ca194..5db5663f4 100644 --- a/lib/direct_message_channel_creator.rb +++ b/lib/direct_message_channel_creator.rb @@ -7,7 +7,8 @@ def self.create!(target_users:) if direct_messages_channel chat_channel = ChatChannel.find_by!(chatable: direct_messages_channel) else - direct_messages_channel = DirectMessageChannel.create!(user_ids: unique_target_users.map(&:id)) + direct_messages_channel = + DirectMessageChannel.create!(user_ids: unique_target_users.map(&:id)) chat_channel = ChatChannel.create!(chatable: direct_messages_channel) end @@ -20,12 +21,18 @@ def self.create!(target_users:) def self.update_memberships(unique_target_users, chat_channel_id) unique_target_users.each do |user| - membership = UserChatChannelMembership.find_or_initialize_by(user_id: user.id, chat_channel_id: chat_channel_id) + membership = + UserChatChannelMembership.find_or_initialize_by( + user_id: user.id, + chat_channel_id: chat_channel_id, + ) if membership.new_record? membership.last_read_message_id = nil - membership.desktop_notification_level = UserChatChannelMembership::NOTIFICATION_LEVELS[:always] - membership.mobile_notification_level = UserChatChannelMembership::NOTIFICATION_LEVELS[:always] + membership.desktop_notification_level = + UserChatChannelMembership::NOTIFICATION_LEVELS[:always] + membership.mobile_notification_level = + UserChatChannelMembership::NOTIFICATION_LEVELS[:always] membership.muted = false end diff --git a/lib/discourse_dev/direct_channel.rb b/lib/discourse_dev/direct_channel.rb index def5c10ac..c4ea2a9aa 100644 --- a/lib/discourse_dev/direct_channel.rb +++ b/lib/discourse_dev/direct_channel.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require 'discourse_dev/record' -require 'faker' +require "discourse_dev/record" +require "faker" module DiscourseDev class DirectChannel < Record @@ -15,10 +15,7 @@ def data admin_user = ::User.find_by(username: admin_username) end - [ - User.new.create!, - admin_user || User.new.create! - ] + [User.new.create!, admin_user || User.new.create!] end def create! diff --git a/lib/discourse_dev/message.rb b/lib/discourse_dev/message.rb index 121515db2..f36d06158 100644 --- a/lib/discourse_dev/message.rb +++ b/lib/discourse_dev/message.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require 'discourse_dev/record' -require 'faker' +require "discourse_dev/record" +require "faker" module DiscourseDev class Message < Record @@ -11,7 +11,7 @@ def initialize def data if Faker::Boolean.boolean(true_ratio: 0.5) - channel = ::ChatChannel.where(chatable_type: 'DirectMessageChannel').order("RANDOM()").first + channel = ::ChatChannel.where(chatable_type: "DirectMessageChannel").order("RANDOM()").first channel.user_chat_channel_memberships.update_all(following: true) user = channel.chatable.users.order("RANDOM()").first else @@ -20,11 +20,7 @@ def data user = membership.user end - { - user: user, - content: Faker::Lorem.paragraph, - chat_channel: channel, - } + { user: user, content: Faker::Lorem.paragraph, chat_channel: channel } end def create! diff --git a/lib/discourse_dev/public_channel.rb b/lib/discourse_dev/public_channel.rb index 0c42b937c..2d18b45b1 100644 --- a/lib/discourse_dev/public_channel.rb +++ b/lib/discourse_dev/public_channel.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require 'discourse_dev/record' -require 'faker' +require "discourse_dev/record" +require "faker" module DiscourseDev class PublicChannel < Record @@ -23,18 +23,25 @@ def data def create! super do |channel| - Faker::Number.between(from: 5, to: 10).times do - if Faker::Boolean.boolean(true_ratio: 0.5) - admin_username = DiscourseDev::Config.new.config[:admin][:username] rescue nil - admin_user = ::User.find_by(username: admin_username) if admin_username - end + Faker::Number + .between(from: 5, to: 10) + .times do + if Faker::Boolean.boolean(true_ratio: 0.5) + admin_username = + begin + DiscourseDev::Config.new.config[:admin][:username] + rescue StandardError + nil + end + admin_user = ::User.find_by(username: admin_username) if admin_username + end - ::UserChatChannelMembership.find_or_create_by!( - user: admin_user || User.new.create!, - chat_channel: channel, - following: true, - ) - end + ::UserChatChannelMembership.find_or_create_by!( + user: admin_user || User.new.create!, + chat_channel: channel, + following: true, + ) + end end end end diff --git a/lib/duplicate_message_validator.rb b/lib/duplicate_message_validator.rb index 4531c741b..31da101a9 100644 --- a/lib/duplicate_message_validator.rb +++ b/lib/duplicate_message_validator.rb @@ -9,9 +9,10 @@ def initialize(chat_message) def validate return if SiteSetting.chat_duplicate_message_sensitivity.zero? - matrix = DiscourseChat::DuplicateMessageValidator.sensitivity_matrix( - SiteSetting.chat_duplicate_message_sensitivity - ) + matrix = + DiscourseChat::DuplicateMessageValidator.sensitivity_matrix( + SiteSetting.chat_duplicate_message_sensitivity, + ) # Check if the length of the message is too short to check for a duplicate message return if chat_message.message.length < matrix[:min_message_length] @@ -20,10 +21,14 @@ def validate return if (chat_message.chat_channel.user_count || 0) < matrix[:min_user_count] # Check if the same duplicate message has been posted in the last N seconds by any user - return if !chat_message.chat_channel.chat_messages - .where("created_at > ?", matrix[:min_past_seconds].seconds.ago) - .where(message: chat_message.message) - .exists? + if !chat_message + .chat_channel + .chat_messages + .where("created_at > ?", matrix[:min_past_seconds].seconds.ago) + .where(message: chat_message.message) + .exists? + return + end chat_message.errors.add(:base, I18n.t("chat.errors.duplicate_message")) end @@ -32,12 +37,10 @@ def self.sensitivity_matrix(sensitivity) { # 0.1 sensitivity = 100 users and 1.0 sensitivity = 5 users. min_user_count: (-1.0 * 105.5 * sensitivity + 110.55).to_i, - # 0.1 sensitivity = 30 chars and 1.0 sensitivity = 10 chars. min_message_length: (-1.0 * 22.2 * sensitivity + 32.22).to_i, - # 0.1 sensitivity = 10 seconds and 1.0 sensitivity = 60 seconds. - min_past_seconds: (55.55 * sensitivity + 4.5).to_i + min_past_seconds: (55.55 * sensitivity + 4.5).to_i, } end end diff --git a/lib/email_controller_helper/chat_summary_unsubscriber.rb b/lib/email_controller_helper/chat_summary_unsubscriber.rb index b4a3e82c0..ab4b06a75 100644 --- a/lib/email_controller_helper/chat_summary_unsubscriber.rb +++ b/lib/email_controller_helper/chat_summary_unsubscriber.rb @@ -5,12 +5,16 @@ class ChatSummaryUnsubscriber < BaseEmailUnsubscriber def prepare_unsubscribe_options(controller) super(controller) - chat_email_frequencies = UserOption.chat_email_frequencies.map do |(freq, _)| - [I18n.t("unsubscribe.chat_summary.#{freq}"), freq] - end + chat_email_frequencies = + UserOption.chat_email_frequencies.map do |(freq, _)| + [I18n.t("unsubscribe.chat_summary.#{freq}"), freq] + end controller.instance_variable_set(:@chat_email_frequencies, chat_email_frequencies) - controller.instance_variable_set(:@current_chat_email_frequency, key_owner.user_option.chat_email_frequency) + controller.instance_variable_set( + :@current_chat_email_frequency, + key_owner.user_option.chat_email_frequency, + ) end def unsubscribe(params) diff --git a/lib/extensions/user_email_extension.rb b/lib/extensions/user_email_extension.rb index b52ee566b..e067e7d25 100644 --- a/lib/extensions/user_email_extension.rb +++ b/lib/extensions/user_email_extension.rb @@ -4,11 +4,11 @@ module DiscourseChat::UserEmailExtension def execute(args) super(args) - if args[:type] == 'chat_summary' && args[:memberships_to_update_data].present? + if args[:type] == "chat_summary" && args[:memberships_to_update_data].present? args[:memberships_to_update_data].to_a.each do |membership_id, max_unread_mention_id| - UserChatChannelMembership - .find_by(user: args[:user_id], id: membership_id.to_i) - &.update(last_unread_mention_when_emailed_id: max_unread_mention_id.to_i) + UserChatChannelMembership.find_by(user: args[:user_id], id: membership_id.to_i)&.update( + last_unread_mention_when_emailed_id: max_unread_mention_id.to_i, + ) end end end diff --git a/lib/extensions/user_notifications_extension.rb b/lib/extensions/user_notifications_extension.rb index 4f7f74d76..bcc6ce51a 100644 --- a/lib/extensions/user_notifications_extension.rb +++ b/lib/extensions/user_notifications_extension.rb @@ -5,23 +5,26 @@ def chat_summary(user, opts) guardian = Guardian.new(user) return unless guardian.can_chat?(user) - @messages = ChatMessage - .joins(:user, :chat_channel) - .where.not(user: user) - .where('chat_messages.created_at > ?', 1.week.ago) - .joins('LEFT OUTER JOIN chat_mentions cm ON cm.chat_message_id = chat_messages.id') - .joins('INNER JOIN user_chat_channel_memberships uccm ON uccm.chat_channel_id = chat_channels.id') - .where(uccm: { following: true, user_id: user.id }) - .where( - <<~SQL + @messages = + ChatMessage + .joins(:user, :chat_channel) + .where.not(user: user) + .where("chat_messages.created_at > ?", 1.week.ago) + .joins("LEFT OUTER JOIN chat_mentions cm ON cm.chat_message_id = chat_messages.id") + .joins( + "INNER JOIN user_chat_channel_memberships uccm ON uccm.chat_channel_id = chat_channels.id", + ) + .where(uccm: { following: true, user_id: user.id }) + .where(<<~SQL) (cm.user_id = #{user.id} OR chat_channels.chatable_type = 'DirectMessageChannel') AND (uccm.last_read_message_id IS NULL OR chat_messages.id > uccm.last_read_message_id) AND (uccm.last_unread_mention_when_emailed_id IS NULL OR chat_messages.id > uccm.last_unread_mention_when_emailed_id) SQL - ).to_a + .to_a return if @messages.empty? @grouped_messages = @messages.group_by { |message| message.chat_channel } - @grouped_messages = @grouped_messages.select { |channel, _| guardian.can_see_chat_channel?(channel) } + @grouped_messages = + @grouped_messages.select { |channel, _| guardian.can_see_chat_channel?(channel) } return if @grouped_messages.empty? @grouped_messages.each do |chat_channel, messages| @@ -38,13 +41,13 @@ def chat_summary(user, opts) add_unsubscribe_link = UnsubscribeKey.respond_to?(:get_unsubscribe_strategy_for) if add_unsubscribe_link - unsubscribe_key = UnsubscribeKey.create_key_for(@user, 'chat_summary') + unsubscribe_key = UnsubscribeKey.create_key_for(@user, "chat_summary") @unsubscribe_link = "#{Discourse.base_url}/email/unsubscribe/#{unsubscribe_key}" opts[:unsubscribe_url] = @unsubscribe_link end opts = { - from_alias: I18n.t('user_notifications.chat_summary.from', site_name: Email.site_title), + from_alias: I18n.t("user_notifications.chat_summary.from", site_name: Email.site_title), subject: summary_subject(user, @grouped_messages), add_unsubscribe_link: add_unsubscribe_link, } @@ -62,9 +65,9 @@ def summary_subject(user, grouped_messages) first_message_from = non_dm_channels.pop if first_message_from first_message_title = first_message_from.title(user) - subject_key = 'chat_channel' + subject_key = "chat_channel" else - subject_key = 'direct_message' + subject_key = "direct_message" first_message_from = dm_users.pop first_message_title = first_message_from.username end @@ -73,7 +76,14 @@ def summary_subject(user, grouped_messages) email_prefix: @email_prefix, count: total_count_for_subject, message_title: first_message_title, - others: other_channels_text(user, total_count_for_subject, first_message_from, non_dm_channels, dm_users) + others: + other_channels_text( + user, + total_count_for_subject, + first_message_from, + non_dm_channels, + dm_users, + ), } I18n.t(with_subject_prefix(subject_key), **subject_opts) @@ -83,9 +93,15 @@ def with_subject_prefix(key) "user_notifications.chat_summary.subject.#{key}" end - def other_channels_text(user, total_count, first_message_from, other_non_dm_channels, other_dm_users) + def other_channels_text( + user, + total_count, + first_message_from, + other_non_dm_channels, + other_dm_users + ) return if total_count <= 1 - return I18n.t(with_subject_prefix('others'), count: total_count - 1) if total_count > 2 + return I18n.t(with_subject_prefix("others"), count: total_count - 1) if total_count > 2 if other_non_dm_channels.empty? second_message_from = other_dm_users.first @@ -97,6 +113,6 @@ def other_channels_text(user, total_count, first_message_from, other_non_dm_chan return second_message_title if first_message_from.class == second_message_from.class - I18n.t(with_subject_prefix('other_direct_message'), message_title: second_message_title) + I18n.t(with_subject_prefix("other_direct_message"), message_title: second_message_title) end end diff --git a/lib/extensions/user_option_extension.rb b/lib/extensions/user_option_extension.rb index e42452399..7d10d064a 100644 --- a/lib/extensions/user_option_extension.rb +++ b/lib/extensions/user_option_extension.rb @@ -4,16 +4,13 @@ module DiscourseChat::UserOptionExtension # TODO: remove last_emailed_for_chat and chat_isolated in 2023 def self.prepended(base) if base.ignored_columns - base.ignored_columns = base.ignored_columns + [:last_emailed_for_chat, :chat_isolated] + base.ignored_columns = base.ignored_columns + %i[last_emailed_for_chat chat_isolated] else - base.ignored_columns = [:last_emailed_for_chat, :chat_isolated] + base.ignored_columns = %i[last_emailed_for_chat chat_isolated] end def base.chat_email_frequencies - @chat_email_frequencies ||= { - never: 0, - when_away: 1 - } + @chat_email_frequencies ||= { never: 0, when_away: 1 } end base.enum chat_email_frequency: base.chat_email_frequencies diff --git a/lib/guardian_extensions.rb b/lib/guardian_extensions.rb index cc2f3de7f..d52268169 100644 --- a/lib/guardian_extensions.rb +++ b/lib/guardian_extensions.rb @@ -114,9 +114,11 @@ def can_delete_chat?(message, chatable) return false if @user.silenced? return false if !can_modify_channel_message?(message.chat_channel) - message.user_id == current_user.id ? - can_delete_own_chats?(chatable) : + if message.user_id == current_user.id + can_delete_own_chats?(chatable) + else can_delete_other_chats?(chatable) + end end def can_delete_own_chats?(chatable) @@ -135,9 +137,11 @@ def can_delete_other_chats?(chatable) def can_restore_chat?(message, chatable) return false if !can_modify_channel_message?(message.chat_channel) - message.user_id == current_user.id ? - can_restore_own_chats?(chatable) : + if message.user_id == current_user.id + can_restore_own_chats?(chatable) + else can_delete_other_chats?(chatable) + end end def can_restore_own_chats?(chatable) diff --git a/lib/message_mover.rb b/lib/message_mover.rb index 7be5cc218..7ef4259e9 100644 --- a/lib/message_mover.rb +++ b/lib/message_mover.rb @@ -17,8 +17,10 @@ # notifications, revisions, mentions, uploads) will be updated to the new # message IDs via a moved_chat_messages temporary table. class DiscourseChat::MessageMover - class NoMessagesFound < StandardError; end - class InvalidChannel < StandardError; end + class NoMessagesFound < StandardError + end + class InvalidChannel < StandardError + end def initialize(acting_user:, source_channel:, message_ids:) @source_channel = source_channel @@ -41,10 +43,11 @@ def move_to_channel(destination_channel) ChatMessage.transaction do create_temp_table - moved_messages = find_messages( - create_destination_messages_in_channel(destination_channel), - destination_channel - ) + moved_messages = + find_messages( + create_destination_messages_in_channel(destination_channel), + destination_channel, + ) bulk_insert_movement_metadata update_references delete_source_messages @@ -74,10 +77,10 @@ def create_temp_table end def bulk_insert_movement_metadata - values_sql = @movement_metadata.map do |mm| - "(#{mm[:old_id]}, #{mm[:new_id]})" - end.join(",\n") - DB.exec("INSERT INTO moved_chat_messages(old_chat_message_id, new_chat_message_id) VALUES #{values_sql}") + values_sql = @movement_metadata.map { |mm| "(#{mm[:old_id]}, #{mm[:new_id]})" }.join(",\n") + DB.exec( + "INSERT INTO moved_chat_messages(old_chat_message_id, new_chat_message_id) VALUES #{values_sql}", + ) end ## @@ -87,7 +90,7 @@ def bulk_insert_movement_metadata def create_destination_messages_in_channel(destination_channel) query_args = { message_ids: @ordered_source_message_ids, - destination_channel_id: destination_channel.id + destination_channel_id: destination_channel.id, } moved_message_ids = DB.query_single(<<~SQL, query_args) INSERT INTO chat_messages(chat_channel_id, user_id, message, cooked, cooked_version, created_at, updated_at) @@ -103,12 +106,10 @@ def create_destination_messages_in_channel(destination_channel) RETURNING id SQL - @movement_metadata = moved_message_ids.map.with_index do |chat_message_id, idx| - { - old_id: @ordered_source_message_ids[idx], - new_id: chat_message_id - } - end + @movement_metadata = + moved_message_ids.map.with_index do |chat_message_id, idx| + { old_id: @ordered_source_message_ids[idx], new_id: chat_message_id } + end moved_message_ids end @@ -158,13 +159,14 @@ def add_moved_placeholder(destination_channel, first_moved_message) DiscourseChat::ChatMessageCreator.create( chat_channel: @source_channel, user: Discourse.system_user, - content: I18n.t( - "chat.channel.messages_moved", - count: @source_message_ids.length, - acting_username: @acting_user.username, - channel_name: destination_channel.title(@acting_user), - first_moved_message_url: first_moved_message.url - ) + content: + I18n.t( + "chat.channel.messages_moved", + count: @source_message_ids.length, + acting_username: @acting_user.username, + channel_name: destination_channel.title(@acting_user), + first_moved_message_url: first_moved_message.url, + ), ) end end diff --git a/lib/post_notification_handler.rb b/lib/post_notification_handler.rb index 671e1fe95..9c3bd6384 100644 --- a/lib/post_notification_handler.rb +++ b/lib/post_notification_handler.rb @@ -24,22 +24,17 @@ def handle opts = { user_id: post.user.id, display_username: post.user.username } quoted_users.each do |user| - # PostAlerter.create_notification handles many edge cases, such as # muting, ignoring, double notifications etc. - PostAlerter.new.create_notification( - user, - Notification.types[:chat_quoted], - post, - opts - ) + PostAlerter.new.create_notification(user, Notification.types[:chat_quoted], post, opts) end end private def extract_quoted_users(post) - usernames = post.raw.scan(/\[chat quote=\"([^;]+);.+\"\]/).uniq.map { |q| q.first.strip.downcase } + usernames = + post.raw.scan(/\[chat quote=\"([^;]+);.+\"\]/).uniq.map { |q| q.first.strip.downcase } User.where.not(id: post.user_id).where(username_lower: usernames) end end diff --git a/lib/tasks/chat.rake b/lib/tasks/chat.rake index bad5be82a..a53e1b319 100644 --- a/lib/tasks/chat.rake +++ b/lib/tasks/chat.rake @@ -1,23 +1,23 @@ # frozen_string_literal: true if Discourse.allow_dev_populate? - chat_task = Rake::Task['dev:populate'] - chat_task.enhance { + chat_task = Rake::Task["dev:populate"] + chat_task.enhance do SiteSetting.chat_enabled = true DiscourseDev::PublicChannel.populate! DiscourseDev::DirectChannel.populate! DiscourseDev::Message.populate! - } + end - desc 'Generates sample content for chat' - task 'chat:populate' => ['db:load_config'] do |_, args| + desc "Generates sample content for chat" + task "chat:populate" => ["db:load_config"] do |_, args| DiscourseDev::PublicChannel.new.populate!(ignore_current_count: true) DiscourseDev::DirectChannel.new.populate!(ignore_current_count: true) DiscourseDev::Message.new.populate!(ignore_current_count: true) end - desc 'Generates sample messages in channels' - task 'chat:message:populate' => ['db:load_config'] do |_, args| + desc "Generates sample messages in channels" + task "chat:message:populate" => ["db:load_config"] do |_, args| DiscourseDev::Message.new.populate!(ignore_current_count: true) end end diff --git a/lib/tasks/chat_message.rake b/lib/tasks/chat_message.rake index 26b78541c..603722e4b 100644 --- a/lib/tasks/chat_message.rake +++ b/lib/tasks/chat_message.rake @@ -1,19 +1,16 @@ - # frozen_string_literal: true -task 'chat_messages:rebake_uncooked_chat_messages' => :environment do +task "chat_messages:rebake_uncooked_chat_messages" => :environment do # rebaking uncooked chat_messages can very quickly saturate sidekiq # this provides an insurance policy so you can safely run and stop # this rake task without worrying about your sidekiq imploding Jobs.run_immediately! - ENV['RAILS_DB'] ? rebake_uncooked_chat_messages : rebake_uncooked_chat_messages_all_sites + ENV["RAILS_DB"] ? rebake_uncooked_chat_messages : rebake_uncooked_chat_messages_all_sites end def rebake_uncooked_chat_messages_all_sites - RailsMultisite::ConnectionManagement.each_connection do |db| - rebake_uncooked_chat_messages - end + RailsMultisite::ConnectionManagement.each_connection { |db| rebake_uncooked_chat_messages } end def rebake_uncooked_chat_messages @@ -31,9 +28,7 @@ def rebake_uncooked_chat_messages # may have been cooked in interim chat_message = uncooked.where(id: id).first - if chat_message - rebake_chat_message(chat_message) - end + rebake_chat_message(chat_message) if chat_message print_status(rebaked += 1, total) end @@ -42,21 +37,22 @@ def rebake_uncooked_chat_messages end def rebake_chat_message(chat_message, opts = {}) - if !opts[:priority] - opts[:priority] = :ultra_low - end + opts[:priority] = :ultra_low if !opts[:priority] chat_message.rebake!(**opts) rescue => e - puts "", "Failed to rebake chat message (chat_message_id: #{chat_message.id})", e, e.backtrace.join("\n") + puts "", + "Failed to rebake chat message (chat_message_id: #{chat_message.id})", + e, + e.backtrace.join("\n") end -task 'chat:make_channel_to_test_archiving', [:user_for_membership] => :environment do |t, args| +task "chat:make_channel_to_test_archiving", [:user_for_membership] => :environment do |t, args| user_for_membership = args[:user_for_membership] # do not want this running in production! return if !Rails.env.development? - require 'fabrication' + require "fabrication" Dir[Rails.root.join("spec/fabricators/*.rb")].each { |f| require f } messages = [ @@ -77,7 +73,7 @@ task 'chat:make_channel_to_test_archiving', [:user_for_membership] => :environme "Nullam porttitor leo a leo `cursus`, id hendrerit dui ultrices.", "Pellentesque ut @#{user_for_membership} ut ex pulvinar pharetra sit amet ac leo.", "Vestibulum sit amet enim et lectus tincidunt rhoncus hendrerit in enim.", - <<~MSG + <<~MSG, some bigger message ```ruby @@ -91,11 +87,24 @@ task 'chat:make_channel_to_test_archiving', [:user_for_membership] => :environme chat_channel = nil Topic.transaction do - topic = Fabricate(:topic, user: make_test_user, title: "Testing topic for chat archiving #{SecureRandom.hex(4)}") - Fabricate(:post, topic: topic, user: topic.user, raw: "This is some cool first post for archive stuff") - chat_channel = ChatChannel.create( - chatable: topic, chatable_type: "Topic", name: "testing channel for archiving #{SecureRandom.hex(4)}" + topic = + Fabricate( + :topic, + user: make_test_user, + title: "Testing topic for chat archiving #{SecureRandom.hex(4)}", + ) + Fabricate( + :post, + topic: topic, + user: topic.user, + raw: "This is some cool first post for archive stuff", ) + chat_channel = + ChatChannel.create( + chatable: topic, + chatable_type: "Topic", + name: "testing channel for archiving #{SecureRandom.hex(4)}", + ) end puts "topic: #{topic.id}, #{topic.title}" @@ -120,7 +129,7 @@ task 'chat:make_channel_to_test_archiving', [:user_for_membership] => :environme chat_channel: chat_channel, last_read_message_id: 0, user: User.find_by(username: user_for_membership), - following: true + following: true, ) end diff --git a/plugin.rb b/plugin.rb index 4a4ce2ccb..f10e5bba5 100644 --- a/plugin.rb +++ b/plugin.rb @@ -9,52 +9,52 @@ enabled_site_setting :chat_enabled -register_asset 'stylesheets/mixins/chat-scrollbar.scss' -register_asset 'stylesheets/common/core-extensions.scss' -register_asset 'stylesheets/common/chat-channel-card.scss' -register_asset 'stylesheets/common/dc-filter-input.scss' -register_asset 'stylesheets/common/common.scss' -register_asset 'stylesheets/common/chat-browse.scss' -register_asset 'stylesheets/common/chat-drawer.scss' -register_asset 'stylesheets/common/chat-channel-preview-card.scss' -register_asset 'stylesheets/common/chat-channel-info.scss' -register_asset 'stylesheets/mobile/chat-channel-info.scss', :mobile -register_asset 'stylesheets/common/chat-draft-channel.scss' -register_asset 'stylesheets/common/chat-tabs.scss' -register_asset 'stylesheets/common/chat-form.scss' -register_asset 'stylesheets/common/d-progress-bar.scss' -register_asset 'stylesheets/common/incoming-chat-webhooks.scss' -register_asset 'stylesheets/mobile/chat-message.scss', :mobile -register_asset 'stylesheets/desktop/chat-message.scss', :desktop -register_asset 'stylesheets/common/chat-channel-title.scss' -register_asset 'stylesheets/common/full-page-chat-header.scss' -register_asset 'stylesheets/common/chat-reply.scss' -register_asset 'stylesheets/common/chat-message.scss' -register_asset 'stylesheets/common/chat-message-left-gutter.scss' -register_asset 'stylesheets/common/chat-message-info.scss' -register_asset 'stylesheets/common/chat-composer-inline-button.scss' -register_asset 'stylesheets/common/chat-replying-indicator.scss' -register_asset 'stylesheets/mobile/chat-replying-indicator.scss', :mobile -register_asset 'stylesheets/common/chat-composer.scss' -register_asset 'stylesheets/desktop/chat-composer.scss', :desktop -register_asset 'stylesheets/mobile/chat-composer.scss', :mobile -register_asset 'stylesheets/common/direct-message-creator.scss' -register_asset 'stylesheets/common/chat-message-collapser.scss' -register_asset 'stylesheets/common/chat-message-images.scss' -register_asset 'stylesheets/common/chat-transcript.scss' -register_asset 'stylesheets/common/chat-composer-dropdown.scss' -register_asset 'stylesheets/common/chat-retention-reminder.scss' -register_asset 'stylesheets/common/chat-composer-uploads.scss' -register_asset 'stylesheets/common/chat-composer-upload.scss' -register_asset 'stylesheets/common/chat-selection-manager.scss' -register_asset 'stylesheets/mobile/chat-selection-manager.scss', :mobile -register_asset 'stylesheets/common/chat-channel-selector-modal.scss' -register_asset 'stylesheets/mobile/mobile.scss', :mobile -register_asset 'stylesheets/desktop/desktop.scss', :desktop -register_asset 'stylesheets/sidebar-extensions.scss' -register_asset 'stylesheets/desktop/sidebar-extensions.scss', :desktop -register_asset 'stylesheets/common/chat-message-separator.scss' -register_asset 'stylesheets/common/chat-onebox.scss' +register_asset "stylesheets/mixins/chat-scrollbar.scss" +register_asset "stylesheets/common/core-extensions.scss" +register_asset "stylesheets/common/chat-channel-card.scss" +register_asset "stylesheets/common/dc-filter-input.scss" +register_asset "stylesheets/common/common.scss" +register_asset "stylesheets/common/chat-browse.scss" +register_asset "stylesheets/common/chat-drawer.scss" +register_asset "stylesheets/common/chat-channel-preview-card.scss" +register_asset "stylesheets/common/chat-channel-info.scss" +register_asset "stylesheets/mobile/chat-channel-info.scss", :mobile +register_asset "stylesheets/common/chat-draft-channel.scss" +register_asset "stylesheets/common/chat-tabs.scss" +register_asset "stylesheets/common/chat-form.scss" +register_asset "stylesheets/common/d-progress-bar.scss" +register_asset "stylesheets/common/incoming-chat-webhooks.scss" +register_asset "stylesheets/mobile/chat-message.scss", :mobile +register_asset "stylesheets/desktop/chat-message.scss", :desktop +register_asset "stylesheets/common/chat-channel-title.scss" +register_asset "stylesheets/common/full-page-chat-header.scss" +register_asset "stylesheets/common/chat-reply.scss" +register_asset "stylesheets/common/chat-message.scss" +register_asset "stylesheets/common/chat-message-left-gutter.scss" +register_asset "stylesheets/common/chat-message-info.scss" +register_asset "stylesheets/common/chat-composer-inline-button.scss" +register_asset "stylesheets/common/chat-replying-indicator.scss" +register_asset "stylesheets/mobile/chat-replying-indicator.scss", :mobile +register_asset "stylesheets/common/chat-composer.scss" +register_asset "stylesheets/desktop/chat-composer.scss", :desktop +register_asset "stylesheets/mobile/chat-composer.scss", :mobile +register_asset "stylesheets/common/direct-message-creator.scss" +register_asset "stylesheets/common/chat-message-collapser.scss" +register_asset "stylesheets/common/chat-message-images.scss" +register_asset "stylesheets/common/chat-transcript.scss" +register_asset "stylesheets/common/chat-composer-dropdown.scss" +register_asset "stylesheets/common/chat-retention-reminder.scss" +register_asset "stylesheets/common/chat-composer-uploads.scss" +register_asset "stylesheets/common/chat-composer-upload.scss" +register_asset "stylesheets/common/chat-selection-manager.scss" +register_asset "stylesheets/mobile/chat-selection-manager.scss", :mobile +register_asset "stylesheets/common/chat-channel-selector-modal.scss" +register_asset "stylesheets/mobile/mobile.scss", :mobile +register_asset "stylesheets/desktop/desktop.scss", :desktop +register_asset "stylesheets/sidebar-extensions.scss" +register_asset "stylesheets/desktop/sidebar-extensions.scss", :desktop +register_asset "stylesheets/common/chat-message-separator.scss" +register_asset "stylesheets/common/chat-onebox.scss" register_svg_icon "comments" register_svg_icon "comment-slash" @@ -66,7 +66,7 @@ register_svg_icon "file-image" # route: /admin/plugins/chat -add_admin_route 'chat.admin.title', 'chat' +add_admin_route "chat.admin.title", "chat" # Site setting validators must be loaded before initialize require_relative "lib/validators/chat_default_channel_validator.rb" @@ -87,97 +87,104 @@ def self.allowed_group_ids end def self.onebox_template - @onebox_template ||= begin - path = "#{Rails.root}/plugins/discourse-chat/lib/onebox/templates/discourse_chat.mustache" - File.read(path) - end + @onebox_template ||= + begin + path = "#{Rails.root}/plugins/discourse-chat/lib/onebox/templates/discourse_chat.mustache" + File.read(path) + end end end register_seedfu_fixtures(Rails.root.join("plugins", "discourse-chat", "db", "fixtures")) - load File.expand_path('../app/controllers/admin/admin_incoming_chat_webhooks_controller.rb', __FILE__) - load File.expand_path('../app/controllers/chat_base_controller.rb', __FILE__) - load File.expand_path('../app/controllers/chat_controller.rb', __FILE__) - load File.expand_path('../app/controllers/chat_channels_controller.rb', __FILE__) - load File.expand_path('../app/controllers/direct_messages_controller.rb', __FILE__) - load File.expand_path('../app/controllers/incoming_chat_webhooks_controller.rb', __FILE__) - load File.expand_path('../app/models/user_chat_channel_membership.rb', __FILE__) - load File.expand_path('../app/models/chat_channel.rb', __FILE__) - load File.expand_path('../app/models/chat_channel_archive.rb', __FILE__) - load File.expand_path('../app/models/chat_draft.rb', __FILE__) - load File.expand_path('../app/models/chat_message.rb', __FILE__) - load File.expand_path('../app/models/chat_message_reaction.rb', __FILE__) - load File.expand_path('../app/models/chat_message_revision.rb', __FILE__) - load File.expand_path('../app/models/chat_mention.rb', __FILE__) - load File.expand_path('../app/models/chat_upload.rb', __FILE__) - load File.expand_path('../app/models/chat_webhook_event.rb', __FILE__) - load File.expand_path('../app/models/direct_message_channel.rb', __FILE__) - load File.expand_path('../app/models/direct_message_user.rb', __FILE__) - load File.expand_path('../app/models/incoming_chat_webhook.rb', __FILE__) - load File.expand_path('../app/models/reviewable_chat_message.rb', __FILE__) - load File.expand_path('../app/models/chat_view.rb', __FILE__) - load File.expand_path('../app/serializers/chat_webhook_event_serializer.rb', __FILE__) - load File.expand_path('../app/serializers/chat_in_reply_to_serializer.rb', __FILE__) - load File.expand_path('../app/serializers/chat_message_serializer.rb', __FILE__) - load File.expand_path('../app/serializers/chat_channel_serializer.rb', __FILE__) - load File.expand_path('../app/serializers/chat_channel_settings_serializer.rb', __FILE__) - load File.expand_path('../app/serializers/chat_channel_index_serializer.rb', __FILE__) - load File.expand_path('../app/serializers/chat_channel_search_serializer.rb', __FILE__) - load File.expand_path('../app/serializers/chat_view_serializer.rb', __FILE__) - load File.expand_path('../app/serializers/direct_message_channel_serializer.rb', __FILE__) - load File.expand_path('../app/serializers/incoming_chat_webhook_serializer.rb', __FILE__) - load File.expand_path('../app/serializers/admin_chat_index_serializer.rb', __FILE__) - load File.expand_path('../app/serializers/user_chat_channel_membership_serializer.rb', __FILE__) - load File.expand_path('../app/serializers/user_chat_message_bookmark_serializer.rb', __FILE__) - load File.expand_path('../app/serializers/reviewable_chat_message_serializer.rb', __FILE__) - load File.expand_path('../lib/chat_channel_fetcher.rb', __FILE__) - load File.expand_path('../lib/chat_mailer.rb', __FILE__) - load File.expand_path('../lib/chat_message_creator.rb', __FILE__) - load File.expand_path('../lib/chat_message_processor.rb', __FILE__) - load File.expand_path('../lib/chat_message_updater.rb', __FILE__) - load File.expand_path('../lib/chat_message_rate_limiter.rb', __FILE__) - load File.expand_path('../lib/chat_message_reactor.rb', __FILE__) - load File.expand_path('../lib/chat_notifier.rb', __FILE__) - load File.expand_path('../lib/chat_seeder.rb', __FILE__) - load File.expand_path('../lib/chat_statistics.rb', __FILE__) - load File.expand_path('../lib/chat_transcript_service.rb', __FILE__) - load File.expand_path('../lib/duplicate_message_validator.rb', __FILE__) - load File.expand_path('../lib/message_mover.rb', __FILE__) - load File.expand_path('../lib/chat_message_bookmarkable.rb', __FILE__) - load File.expand_path('../lib/chat_channel_archive_service.rb', __FILE__) - load File.expand_path('../lib/direct_message_channel_creator.rb', __FILE__) - load File.expand_path('../lib/guardian_extensions.rb', __FILE__) - load File.expand_path('../lib/extensions/user_option_extension.rb', __FILE__) - load File.expand_path('../lib/extensions/user_notifications_extension.rb', __FILE__) - load File.expand_path('../lib/extensions/user_email_extension.rb', __FILE__) - load File.expand_path('../lib/slack_compatibility.rb', __FILE__) - load File.expand_path('../lib/post_notification_handler.rb', __FILE__) - load File.expand_path('../app/jobs/regular/auto_manage_channel_memberships.rb', __FILE__) - load File.expand_path('../app/jobs/regular/auto_join_channel_batch.rb', __FILE__) - load File.expand_path('../app/jobs/regular/process_chat_message.rb', __FILE__) - load File.expand_path('../app/jobs/regular/chat_channel_archive.rb', __FILE__) - load File.expand_path('../app/jobs/regular/chat_channel_delete.rb', __FILE__) - load File.expand_path('../app/jobs/regular/chat_notify_mentioned.rb', __FILE__) - load File.expand_path('../app/jobs/regular/chat_notify_watching.rb', __FILE__) - load File.expand_path('../app/jobs/scheduled/delete_old_chat_messages.rb', __FILE__) - load File.expand_path('../app/jobs/scheduled/update_user_counts_for_chat_channels.rb', __FILE__) - load File.expand_path('../app/jobs/scheduled/email_chat_notifications.rb', __FILE__) - load File.expand_path('../app/services/chat_publisher.rb', __FILE__) - load File.expand_path('../app/controllers/api_controller.rb', __FILE__) - load File.expand_path('../app/controllers/api/chat_channels_controller.rb', __FILE__) - load File.expand_path('../app/controllers/api/chat_channel_memberships_controller.rb', __FILE__) - load File.expand_path('../app/controllers/api/chat_channel_notifications_settings_controller.rb', __FILE__) - load File.expand_path('../app/controllers/api/category_chatables_controller.rb', __FILE__) - load File.expand_path('../app/queries/chat_channel_memberships_query.rb', __FILE__) + load File.expand_path( + "../app/controllers/admin/admin_incoming_chat_webhooks_controller.rb", + __FILE__, + ) + load File.expand_path("../app/controllers/chat_base_controller.rb", __FILE__) + load File.expand_path("../app/controllers/chat_controller.rb", __FILE__) + load File.expand_path("../app/controllers/chat_channels_controller.rb", __FILE__) + load File.expand_path("../app/controllers/direct_messages_controller.rb", __FILE__) + load File.expand_path("../app/controllers/incoming_chat_webhooks_controller.rb", __FILE__) + load File.expand_path("../app/models/user_chat_channel_membership.rb", __FILE__) + load File.expand_path("../app/models/chat_channel.rb", __FILE__) + load File.expand_path("../app/models/chat_channel_archive.rb", __FILE__) + load File.expand_path("../app/models/chat_draft.rb", __FILE__) + load File.expand_path("../app/models/chat_message.rb", __FILE__) + load File.expand_path("../app/models/chat_message_reaction.rb", __FILE__) + load File.expand_path("../app/models/chat_message_revision.rb", __FILE__) + load File.expand_path("../app/models/chat_mention.rb", __FILE__) + load File.expand_path("../app/models/chat_upload.rb", __FILE__) + load File.expand_path("../app/models/chat_webhook_event.rb", __FILE__) + load File.expand_path("../app/models/direct_message_channel.rb", __FILE__) + load File.expand_path("../app/models/direct_message_user.rb", __FILE__) + load File.expand_path("../app/models/incoming_chat_webhook.rb", __FILE__) + load File.expand_path("../app/models/reviewable_chat_message.rb", __FILE__) + load File.expand_path("../app/models/chat_view.rb", __FILE__) + load File.expand_path("../app/serializers/chat_webhook_event_serializer.rb", __FILE__) + load File.expand_path("../app/serializers/chat_in_reply_to_serializer.rb", __FILE__) + load File.expand_path("../app/serializers/chat_message_serializer.rb", __FILE__) + load File.expand_path("../app/serializers/chat_channel_serializer.rb", __FILE__) + load File.expand_path("../app/serializers/chat_channel_settings_serializer.rb", __FILE__) + load File.expand_path("../app/serializers/chat_channel_index_serializer.rb", __FILE__) + load File.expand_path("../app/serializers/chat_channel_search_serializer.rb", __FILE__) + load File.expand_path("../app/serializers/chat_view_serializer.rb", __FILE__) + load File.expand_path("../app/serializers/direct_message_channel_serializer.rb", __FILE__) + load File.expand_path("../app/serializers/incoming_chat_webhook_serializer.rb", __FILE__) + load File.expand_path("../app/serializers/admin_chat_index_serializer.rb", __FILE__) + load File.expand_path("../app/serializers/user_chat_channel_membership_serializer.rb", __FILE__) + load File.expand_path("../app/serializers/user_chat_message_bookmark_serializer.rb", __FILE__) + load File.expand_path("../app/serializers/reviewable_chat_message_serializer.rb", __FILE__) + load File.expand_path("../lib/chat_channel_fetcher.rb", __FILE__) + load File.expand_path("../lib/chat_mailer.rb", __FILE__) + load File.expand_path("../lib/chat_message_creator.rb", __FILE__) + load File.expand_path("../lib/chat_message_processor.rb", __FILE__) + load File.expand_path("../lib/chat_message_updater.rb", __FILE__) + load File.expand_path("../lib/chat_message_rate_limiter.rb", __FILE__) + load File.expand_path("../lib/chat_message_reactor.rb", __FILE__) + load File.expand_path("../lib/chat_notifier.rb", __FILE__) + load File.expand_path("../lib/chat_seeder.rb", __FILE__) + load File.expand_path("../lib/chat_statistics.rb", __FILE__) + load File.expand_path("../lib/chat_transcript_service.rb", __FILE__) + load File.expand_path("../lib/duplicate_message_validator.rb", __FILE__) + load File.expand_path("../lib/message_mover.rb", __FILE__) + load File.expand_path("../lib/chat_message_bookmarkable.rb", __FILE__) + load File.expand_path("../lib/chat_channel_archive_service.rb", __FILE__) + load File.expand_path("../lib/direct_message_channel_creator.rb", __FILE__) + load File.expand_path("../lib/guardian_extensions.rb", __FILE__) + load File.expand_path("../lib/extensions/user_option_extension.rb", __FILE__) + load File.expand_path("../lib/extensions/user_notifications_extension.rb", __FILE__) + load File.expand_path("../lib/extensions/user_email_extension.rb", __FILE__) + load File.expand_path("../lib/slack_compatibility.rb", __FILE__) + load File.expand_path("../lib/post_notification_handler.rb", __FILE__) + load File.expand_path("../app/jobs/regular/auto_manage_channel_memberships.rb", __FILE__) + load File.expand_path("../app/jobs/regular/auto_join_channel_batch.rb", __FILE__) + load File.expand_path("../app/jobs/regular/process_chat_message.rb", __FILE__) + load File.expand_path("../app/jobs/regular/chat_channel_archive.rb", __FILE__) + load File.expand_path("../app/jobs/regular/chat_channel_delete.rb", __FILE__) + load File.expand_path("../app/jobs/regular/chat_notify_mentioned.rb", __FILE__) + load File.expand_path("../app/jobs/regular/chat_notify_watching.rb", __FILE__) + load File.expand_path("../app/jobs/scheduled/delete_old_chat_messages.rb", __FILE__) + load File.expand_path("../app/jobs/scheduled/update_user_counts_for_chat_channels.rb", __FILE__) + load File.expand_path("../app/jobs/scheduled/email_chat_notifications.rb", __FILE__) + load File.expand_path("../app/services/chat_publisher.rb", __FILE__) + load File.expand_path("../app/controllers/api_controller.rb", __FILE__) + load File.expand_path("../app/controllers/api/chat_channels_controller.rb", __FILE__) + load File.expand_path("../app/controllers/api/chat_channel_memberships_controller.rb", __FILE__) + load File.expand_path( + "../app/controllers/api/chat_channel_notifications_settings_controller.rb", + __FILE__, + ) + load File.expand_path("../app/controllers/api/category_chatables_controller.rb", __FILE__) + load File.expand_path("../app/queries/chat_channel_memberships_query.rb", __FILE__) if Discourse.allow_dev_populate? - load File.expand_path('../lib/discourse_dev/public_channel.rb', __FILE__) - load File.expand_path('../lib/discourse_dev/direct_channel.rb', __FILE__) - load File.expand_path('../lib/discourse_dev/message.rb', __FILE__) + load File.expand_path("../lib/discourse_dev/public_channel.rb", __FILE__) + load File.expand_path("../lib/discourse_dev/direct_channel.rb", __FILE__) + load File.expand_path("../lib/discourse_dev/message.rb", __FILE__) end - UserNotifications.append_view_path(File.expand_path('../app/views', __FILE__)) + UserNotifications.append_view_path(File.expand_path("../app/views", __FILE__)) register_category_custom_field_type(DiscourseChat::HAS_CHAT_ENABLED, :boolean) @@ -195,130 +202,153 @@ def self.onebox_template Site.preloaded_category_custom_fields << DiscourseChat::HAS_CHAT_ENABLED Site.markdown_additional_options["chat"] = { limited_pretty_text_features: ChatMessage::MARKDOWN_FEATURES, - limited_pretty_text_markdown_rules: ChatMessage::MARKDOWN_IT_RULES + limited_pretty_text_markdown_rules: ChatMessage::MARKDOWN_IT_RULES, } Guardian.class_eval { include DiscourseChat::GuardianExtensions } UserNotifications.class_eval { prepend DiscourseChat::UserNotificationsExtension } UserOption.class_eval { prepend DiscourseChat::UserOptionExtension } - Category.class_eval { - has_one :chat_channel, as: :chatable - } - User.class_eval { + Category.class_eval { has_one :chat_channel, as: :chatable } + User.class_eval do has_many :user_chat_channel_memberships, dependent: :destroy has_many :chat_message_reactions, dependent: :destroy has_many :chat_mentions - } + end Jobs::UserEmail.class_eval { prepend DiscourseChat::UserEmailExtension } Bookmark.register_bookmarkable(ChatMessageBookmarkable) end - Oneboxer.register_local_handler('discourse_chat/chat') do |url, route| - queryParams = CGI.parse(URI.parse(url).query) rescue {} - messageId = queryParams['messageId']&.first - - if messageId.present? - message = ChatMessage.find_by(id: messageId) - next if !message - - chat_channel = message.chat_channel - user = message.user - next if !chat_channel || !user - else - chat_channel = ChatChannel.find_by(id: route[:channel_id]) - next if !chat_channel - end - - next if !Guardian.new.can_see_chat_channel?(chat_channel) - - name = if chat_channel.name.present? - chat_channel.name - end - - users = chat_channel - .user_chat_channel_memberships - .includes(:user).where(user: User.activated.not_suspended.not_staged) - .limit(10) - .map do |membership| - { - username: membership.user.username, - avatar_url: membership.user.avatar_template_url.gsub('{size}', '60'), - } + if Oneboxer.respond_to?(:register_local_handler) + Oneboxer.register_local_handler("discourse_chat/chat") do |url, route| + queryParams = + begin + CGI.parse(URI.parse(url).query) + rescue StandardError + {} + end + messageId = queryParams["messageId"]&.first + + if messageId.present? + message = ChatMessage.find_by(id: messageId) + next if !message + + chat_channel = message.chat_channel + user = message.user + next if !chat_channel || !user + else + chat_channel = ChatChannel.find_by(id: route[:channel_id]) + next if !chat_channel end - remaining_user_count_str = if chat_channel.user_count > users.size - I18n.t('chat.onebox.and_x_others', count: chat_channel.user_count - users.size) - end + next if !Guardian.new.can_see_chat_channel?(chat_channel) + + name = (chat_channel.name if chat_channel.name.present?) + + users = + chat_channel + .user_chat_channel_memberships + .includes(:user) + .where(user: User.activated.not_suspended.not_staged) + .limit(10) + .map do |membership| + { + username: membership.user.username, + avatar_url: membership.user.avatar_template_url.gsub("{size}", "60"), + } + end + + remaining_user_count_str = + if chat_channel.user_count > users.size + I18n.t("chat.onebox.and_x_others", count: chat_channel.user_count - users.size) + end - args = { - url: url, - channel_id: chat_channel.id, - channel_name: name, - description: chat_channel.description, - user_count_str: I18n.t('chat.onebox.x_members', count: chat_channel.user_count), - users: users, - remaining_user_count_str: remaining_user_count_str, - is_category: chat_channel.chatable_type == 'Category', - color: chat_channel.chatable_type == 'Category' ? chat_channel.chatable.color : nil, - } + args = { + url: url, + channel_id: chat_channel.id, + channel_name: name, + description: chat_channel.description, + user_count_str: I18n.t("chat.onebox.x_members", count: chat_channel.user_count), + users: users, + remaining_user_count_str: remaining_user_count_str, + is_category: chat_channel.chatable_type == "Category", + color: chat_channel.chatable_type == "Category" ? chat_channel.chatable.color : nil, + } + + if message.present? + args[:message_id] = message.id + args[:username] = message.user.username + args[:avatar_url] = message.user.avatar_template_url.gsub("{size}", "20") + args[:cooked] = message.cooked + args[:created_at] = message.created_at + args[:created_at_str] = message.created_at.iso8601 + end - if message.present? - args[:message_id] = message.id - args[:username] = message.user.username - args[:avatar_url] = message.user.avatar_template_url.gsub('{size}', '20') - args[:cooked] = message.cooked - args[:created_at] = message.created_at - args[:created_at_str] = message.created_at.iso8601 + Mustache.render(DiscourseChat.onebox_template, args) end + end - Mustache.render(DiscourseChat.onebox_template, args) - end if Oneboxer.respond_to?(:register_local_handler) - - InlineOneboxer.register_local_handler('discourse_chat/chat') do |url, route| - queryParams = CGI.parse(URI.parse(url).query) rescue {} - messageId = queryParams['messageId']&.first - - if messageId.present? - message = ChatMessage.find_by(id: messageId) - next if !message - - chat_channel = message.chat_channel - user = message.user - next if !chat_channel || !user - - title = I18n.t( - 'chat.onebox.inline_to_message', - message_id: message.id, - chat_channel: chat_channel.name, - username: user.username - ) - else - chat_channel = ChatChannel.find_by(id: route[:channel_id]) - next if !chat_channel - - title = if chat_channel.name.present? - I18n.t('chat.onebox.inline_to_channel', chat_channel: chat_channel.name) + if InlineOneboxer.respond_to?(:register_local_handler) + InlineOneboxer.register_local_handler("discourse_chat/chat") do |url, route| + queryParams = + begin + CGI.parse(URI.parse(url).query) + rescue StandardError + {} + end + messageId = queryParams["messageId"]&.first + + if messageId.present? + message = ChatMessage.find_by(id: messageId) + next if !message + + chat_channel = message.chat_channel + user = message.user + next if !chat_channel || !user + + title = + I18n.t( + "chat.onebox.inline_to_message", + message_id: message.id, + chat_channel: chat_channel.name, + username: user.username, + ) + else + chat_channel = ChatChannel.find_by(id: route[:channel_id]) + next if !chat_channel + + title = + if chat_channel.name.present? + I18n.t("chat.onebox.inline_to_channel", chat_channel: chat_channel.name) + end end - end - next if !Guardian.new.can_see_chat_channel?(chat_channel) + next if !Guardian.new.can_see_chat_channel?(chat_channel) - { url: url, title: title } - end if InlineOneboxer.respond_to?(:register_local_handler) + { url: url, title: title } + end + end if respond_to?(:register_upload_unused) register_upload_unused do |uploads| - uploads - .joins("LEFT JOIN chat_uploads cu ON cu.upload_id = uploads.id") - .where("cu.upload_id IS NULL") + uploads.joins("LEFT JOIN chat_uploads cu ON cu.upload_id = uploads.id").where( + "cu.upload_id IS NULL", + ) end end if respond_to?(:register_upload_in_use) register_upload_in_use do |upload| - ChatMessage.where("message LIKE ? OR message LIKE ?", "%#{upload.sha1}%", "%#{upload.base62_sha1}%").exists? || - ChatDraft.where("data LIKE ? OR data LIKE ?", "%#{upload.sha1}%", "%#{upload.base62_sha1}%").exists? + ChatMessage.where( + "message LIKE ? OR message LIKE ?", + "%#{upload.sha1}%", + "%#{upload.base62_sha1}%", + ).exists? || + ChatDraft.where( + "data LIKE ? OR data LIKE ?", + "%#{upload.sha1}%", + "%#{upload.base62_sha1}%", + ).exists? end end @@ -326,14 +356,10 @@ def self.onebox_template return false if !SiteSetting.chat_enabled return false if scope.user.blank? - scope.user.id != object.id && - scope.can_chat?(scope.user) && - scope.can_chat?(object) + scope.user.id != object.id && scope.can_chat?(scope.user) && scope.can_chat?(object) end - add_to_serializer(:current_user, :can_chat) do - true - end + add_to_serializer(:current_user, :can_chat) { true } add_to_serializer(:current_user, :include_can_chat?) do return @can_chat if defined?(@can_chat) @@ -341,9 +367,7 @@ def self.onebox_template @can_chat = SiteSetting.chat_enabled && scope.can_chat?(object) end - add_to_serializer(:current_user, :has_chat_enabled) do - true - end + add_to_serializer(:current_user, :has_chat_enabled) { true } add_to_serializer(:current_user, :include_has_chat_enabled?) do return @has_chat_enabled if defined?(@has_chat_enabled) @@ -351,33 +375,24 @@ def self.onebox_template @has_chat_enabled = include_can_chat? && object.user_option.chat_enabled end - add_to_serializer(:current_user, :chat_sound) do - object.user_option.chat_sound - end + add_to_serializer(:current_user, :chat_sound) { object.user_option.chat_sound } add_to_serializer(:current_user, :include_chat_sound?) do include_has_chat_enabled? && object.user_option.chat_sound end - add_to_serializer(:current_user, :needs_channel_retention_reminder) do - true - end + add_to_serializer(:current_user, :needs_channel_retention_reminder) { true } - add_to_serializer(:current_user, :needs_dm_retention_reminder) do - true - end + add_to_serializer(:current_user, :needs_dm_retention_reminder) { true } add_to_serializer(:current_user, :include_needs_channel_retention_reminder?) do - include_has_chat_enabled? && - object.staff? && + include_has_chat_enabled? && object.staff? && !object.user_option.dismissed_channel_retention_reminder && !SiteSetting.chat_channel_retention_days.zero? - end add_to_serializer(:current_user, :include_needs_dm_retention_reminder?) do - include_has_chat_enabled? && - !object.user_option.dismissed_dm_retention_reminder && + include_has_chat_enabled? && !object.user_option.dismissed_dm_retention_reminder && !SiteSetting.chat_dm_retention_days.zero? end @@ -385,26 +400,16 @@ def self.onebox_template ChatDraft .where(user_id: object.id) .pluck(:chat_channel_id, :data) - .map do |row| - { channel_id: row[0], data: row[1] } - end + .map { |row| { channel_id: row[0], data: row[1] } } end - add_to_serializer(:current_user, :include_chat_drafts?) do - include_has_chat_enabled? - end + add_to_serializer(:current_user, :include_chat_drafts?) { include_has_chat_enabled? } - add_to_serializer(:user_option, :chat_enabled) do - object.chat_enabled - end + add_to_serializer(:user_option, :chat_enabled) { object.chat_enabled } - add_to_serializer(:user_option, :chat_sound) do - object.chat_sound - end + add_to_serializer(:user_option, :chat_sound) { object.chat_sound } - add_to_serializer(:user_option, :include_chat_sound?) do - !object.chat_sound.blank? - end + add_to_serializer(:user_option, :include_chat_sound?) { !object.chat_sound.blank? } add_to_serializer(:user_option, :only_chat_push_notifications) do object.only_chat_push_notifications @@ -414,13 +419,11 @@ def self.onebox_template object.ignore_channel_wide_mention end - add_to_serializer(:user_option, :chat_email_frequency) do - object.chat_email_frequency - end + add_to_serializer(:user_option, :chat_email_frequency) { object.chat_email_frequency } RETENTION_SETTINGS_TO_USER_OPTION_FIELDS = { chat_channel_retention_days: :dismissed_channel_retention_reminder, - chat_dm_retention_days: :dismissed_dm_retention_reminder + chat_dm_retention_days: :dismissed_dm_retention_reminder, } on(:site_setting_changed) do |name, old_value, new_value| user_option_field = RETENTION_SETTINGS_TO_USER_OPTION_FIELDS[name.to_sym] @@ -429,7 +432,9 @@ def self.onebox_template UserOption.where(user_option_field => true).update_all(user_option_field => false) end rescue => e - Rails.logger.warn("Error updating user_options fields after chat retention settings changed: #{e}") + Rails.logger.warn( + "Error updating user_options fields after chat retention settings changed: #{e}", + ) end end @@ -446,14 +451,12 @@ def self.onebox_template end register_presence_channel_prefix("chat-reply") do |channel_name| - if chat_channel_id = channel_name[/\/chat-reply\/(\d+)/, 1] + if chat_channel_id = channel_name[%r{/chat-reply/(\d+)}, 1] chat_channel = ChatChannel.find(chat_channel_id) config = PresenceChannel::Config.new config.allowed_group_ids = chat_channel.allowed_group_ids config.allowed_user_ids = chat_channel.allowed_user_ids - if config.allowed_group_ids.nil? && config.allowed_user_ids.nil? - config.public = true - end + config.public = true if config.allowed_group_ids.nil? && config.allowed_user_ids.nil? config end rescue ActiveRecord::RecordNotFound @@ -461,20 +464,17 @@ def self.onebox_template end register_presence_channel_prefix("chat-user") do |channel_name| - if user_id = channel_name[/\/chat-user\/(chat|core)\/(\d+)/, 2] + if user_id = channel_name[%r{/chat-user/(chat|core)/(\d+)}, 2] user = User.find(user_id) config = PresenceChannel::Config.new - config.allowed_user_ids = [ user.id ] + config.allowed_user_ids = [user.id] config end rescue ActiveRecord::RecordNotFound nil end - CHAT_NOTIFICATION_TYPES = [ - Notification.types[:chat_mention], - Notification.types[:chat_message], - ] + CHAT_NOTIFICATION_TYPES = [Notification.types[:chat_mention], Notification.types[:chat_message]] register_push_notification_filter do |user, payload| if user.user_option.only_chat_push_notifications && user.user_option.chat_enabled CHAT_NOTIFICATION_TYPES.include?(payload[:notification_type]) @@ -483,30 +483,33 @@ def self.onebox_template end end - on(:reviewable_score_updated) do |reviewable| - ReviewableChatMessage.on_score_updated(reviewable) - end + on(:reviewable_score_updated) { |reviewable| ReviewableChatMessage.on_score_updated(reviewable) } on(:user_created) do |user| - ChatChannel.where(auto_join_users: true).each do |channel| - UserChatChannelMembership.enforce_automatic_user_membership(channel, user) - end + ChatChannel + .where(auto_join_users: true) + .each { |channel| UserChatChannelMembership.enforce_automatic_user_membership(channel, user) } end on(:user_confirmed_email) do |user| if user.active? - ChatChannel.where(auto_join_users: true).each do |channel| - UserChatChannelMembership.enforce_automatic_user_membership(channel, user) - end + ChatChannel + .where(auto_join_users: true) + .each do |channel| + UserChatChannelMembership.enforce_automatic_user_membership(channel, user) + end end end on(:user_added_to_group) do |user, group| - channels_to_add = ChatChannel - .distinct - .where(auto_join_users: true, chatable_type: 'Category') - .joins('INNER JOIN category_groups ON category_groups.category_id = chat_channels.chatable_id') - .where(category_groups: { group_id: group.id }) + channels_to_add = + ChatChannel + .distinct + .where(auto_join_users: true, chatable_type: "Category") + .joins( + "INNER JOIN category_groups ON category_groups.category_id = chat_channels.chatable_id", + ) + .where(category_groups: { group_id: group.id }) channels_to_add.each do |channel| UserChatChannelMembership.enforce_automatic_user_membership(channel, user) @@ -526,86 +529,99 @@ def self.onebox_template DiscourseChat::Engine.routes.draw do namespace :api do - get '/chat_channels' => 'chat_channels#index' - get '/chat_channels/:chat_channel_id/memberships' => 'chat_channel_memberships#index' - put '/chat_channels/:chat_channel_id' => 'chat_channels#update' - put '/chat_channels/:chat_channel_id/notifications_settings' => 'chat_channel_notifications_settings#update' + get "/chat_channels" => "chat_channels#index" + get "/chat_channels/:chat_channel_id/memberships" => "chat_channel_memberships#index" + put "/chat_channels/:chat_channel_id" => "chat_channels#update" + put "/chat_channels/:chat_channel_id/notifications_settings" => + "chat_channel_notifications_settings#update" # hints controller. Only used by staff members, we don't want to leak category permissions. - get '/category-chatables/:id/permissions' => 'category_chatables#permissions', format: :json, constraints: StaffConstraint.new + get "/category-chatables/:id/permissions" => "category_chatables#permissions", + :format => :json, + :constraints => StaffConstraint.new end # direct_messages_controller routes - get '/direct_messages' => 'direct_messages#index' - post '/direct_messages/create' => 'direct_messages#create' + get "/direct_messages" => "direct_messages#index" + post "/direct_messages/create" => "direct_messages#create" # incoming_webhooks_controller routes - post '/hooks/:key' => 'incoming_chat_webhooks#create_message' + post "/hooks/:key" => "incoming_chat_webhooks#create_message" # incoming_webhooks_controller routes - post '/hooks/:key/slack' => 'incoming_chat_webhooks#create_message_slack_compatible' + post "/hooks/:key/slack" => "incoming_chat_webhooks#create_message_slack_compatible" # chat_channel_controller routes - get '/chat_channels' => 'chat_channels#index' - put '/chat_channels' => 'chat_channels#create' - get '/chat_channels/search' => 'chat_channels#search' - post '/chat_channels/:chat_channel_id' => 'chat_channels#edit' - post '/chat_channels/:chat_channel_id/notification_settings' => 'chat_channels#notification_settings' - post '/chat_channels/:chat_channel_id/follow' => 'chat_channels#follow' - post '/chat_channels/:chat_channel_id/unfollow' => 'chat_channels#unfollow' - get '/chat_channels/:chat_channel_id' => 'chat_channels#show' - put '/chat_channels/:chat_channel_id/archive' => 'chat_channels#archive' - put '/chat_channels/:chat_channel_id/retry_archive' => 'chat_channels#retry_archive' - put '/chat_channels/:chat_channel_id/change_status' => 'chat_channels#change_status' - delete '/chat_channels/:chat_channel_id' => 'chat_channels#destroy' + get "/chat_channels" => "chat_channels#index" + put "/chat_channels" => "chat_channels#create" + get "/chat_channels/search" => "chat_channels#search" + post "/chat_channels/:chat_channel_id" => "chat_channels#edit" + post "/chat_channels/:chat_channel_id/notification_settings" => + "chat_channels#notification_settings" + post "/chat_channels/:chat_channel_id/follow" => "chat_channels#follow" + post "/chat_channels/:chat_channel_id/unfollow" => "chat_channels#unfollow" + get "/chat_channels/:chat_channel_id" => "chat_channels#show" + put "/chat_channels/:chat_channel_id/archive" => "chat_channels#archive" + put "/chat_channels/:chat_channel_id/retry_archive" => "chat_channels#retry_archive" + put "/chat_channels/:chat_channel_id/change_status" => "chat_channels#change_status" + delete "/chat_channels/:chat_channel_id" => "chat_channels#destroy" # chat_controller routes - get '/' => 'chat#respond' - get '/browse' => 'chat#respond' - get '/browse/all' => 'chat#respond' - get '/browse/closed' => 'chat#respond' - get '/browse/open' => 'chat#respond' - get '/browse/archived' => 'chat#respond' - get '/draft-channel' => 'chat#respond' - get '/channel/:channel_id' => 'chat#respond' - get '/channel/:channel_id/:channel_title' => 'chat#respond' - get '/channel/:channel_id/:channel_title/info' => 'chat#respond' - get '/channel/:channel_id/:channel_title/info/about' => 'chat#respond' - get '/channel/:channel_id/:channel_title/info/members' => 'chat#respond' - get '/channel/:channel_id/:channel_title/info/settings' => 'chat#respond' - post '/enable' => 'chat#enable_chat' - post '/disable' => 'chat#disable_chat' - post '/dismiss-retention-reminder' => 'chat#dismiss_retention_reminder' - get '/:chat_channel_id/messages' => 'chat#messages' - get '/message/:message_id' => 'chat#message_link' - put ':chat_channel_id/edit/:message_id' => 'chat#edit_message' - put ':chat_channel_id/react/:message_id' => 'chat#react' - delete '/:chat_channel_id/:message_id' => 'chat#delete' - put '/:chat_channel_id/:message_id/rebake' => 'chat#rebake' - post '/:chat_channel_id/:message_id/flag' => 'chat#flag' - post '/:chat_channel_id/quote' => 'chat#quote_messages' - put '/:chat_channel_id/move_messages_to_channel' => 'chat#move_messages_to_channel' - put '/:chat_channel_id/restore/:message_id' => 'chat#restore' - get '/lookup/:message_id' => 'chat#lookup_message' - put '/:chat_channel_id/read/:message_id' => 'chat#update_user_last_read' - put '/user_chat_enabled/:user_id' => 'chat#set_user_chat_status' - put '/:chat_channel_id/invite' => 'chat#invite_users' - post '/drafts' => 'chat#set_draft' - post '/:chat_channel_id' => 'chat#create_message' - put '/flag' => 'chat#flag' + get "/" => "chat#respond" + get "/browse" => "chat#respond" + get "/browse/all" => "chat#respond" + get "/browse/closed" => "chat#respond" + get "/browse/open" => "chat#respond" + get "/browse/archived" => "chat#respond" + get "/draft-channel" => "chat#respond" + get "/channel/:channel_id" => "chat#respond" + get "/channel/:channel_id/:channel_title" => "chat#respond" + get "/channel/:channel_id/:channel_title/info" => "chat#respond" + get "/channel/:channel_id/:channel_title/info/about" => "chat#respond" + get "/channel/:channel_id/:channel_title/info/members" => "chat#respond" + get "/channel/:channel_id/:channel_title/info/settings" => "chat#respond" + post "/enable" => "chat#enable_chat" + post "/disable" => "chat#disable_chat" + post "/dismiss-retention-reminder" => "chat#dismiss_retention_reminder" + get "/:chat_channel_id/messages" => "chat#messages" + get "/message/:message_id" => "chat#message_link" + put ":chat_channel_id/edit/:message_id" => "chat#edit_message" + put ":chat_channel_id/react/:message_id" => "chat#react" + delete "/:chat_channel_id/:message_id" => "chat#delete" + put "/:chat_channel_id/:message_id/rebake" => "chat#rebake" + post "/:chat_channel_id/:message_id/flag" => "chat#flag" + post "/:chat_channel_id/quote" => "chat#quote_messages" + put "/:chat_channel_id/move_messages_to_channel" => "chat#move_messages_to_channel" + put "/:chat_channel_id/restore/:message_id" => "chat#restore" + get "/lookup/:message_id" => "chat#lookup_message" + put "/:chat_channel_id/read/:message_id" => "chat#update_user_last_read" + put "/user_chat_enabled/:user_id" => "chat#set_user_chat_status" + put "/:chat_channel_id/invite" => "chat#invite_users" + post "/drafts" => "chat#set_draft" + post "/:chat_channel_id" => "chat#create_message" + put "/flag" => "chat#flag" end Discourse::Application.routes.append do - mount ::DiscourseChat::Engine, at: '/chat' - get '/admin/plugins/chat' => 'discourse_chat/admin_incoming_chat_webhooks#index', constraints: StaffConstraint.new - post '/admin/plugins/chat/hooks' => 'discourse_chat/admin_incoming_chat_webhooks#create', constraints: StaffConstraint.new - put '/admin/plugins/chat/hooks/:incoming_chat_webhook_id' => 'discourse_chat/admin_incoming_chat_webhooks#update', constraints: StaffConstraint.new - delete '/admin/plugins/chat/hooks/:incoming_chat_webhook_id' => 'discourse_chat/admin_incoming_chat_webhooks#destroy', constraints: StaffConstraint.new - get "u/:username/preferences/chat" => "users#preferences", constraints: { username: RouteFormat.username } + mount ::DiscourseChat::Engine, at: "/chat" + get "/admin/plugins/chat" => "discourse_chat/admin_incoming_chat_webhooks#index", + :constraints => StaffConstraint.new + post "/admin/plugins/chat/hooks" => "discourse_chat/admin_incoming_chat_webhooks#create", + :constraints => StaffConstraint.new + put "/admin/plugins/chat/hooks/:incoming_chat_webhook_id" => + "discourse_chat/admin_incoming_chat_webhooks#update", + :constraints => StaffConstraint.new + delete "/admin/plugins/chat/hooks/:incoming_chat_webhook_id" => + "discourse_chat/admin_incoming_chat_webhooks#destroy", + :constraints => StaffConstraint.new + get "u/:username/preferences/chat" => "users#preferences", + :constraints => { + username: RouteFormat.username, + } end if defined?(DiscourseAutomation) - add_automation_scriptable('send_chat_message') do + add_automation_scriptable("send_chat_message") do field :chat_channel_id, component: :text, required: true field :message, component: :message, required: true, accepts_placeholders: true field :sender, component: :user @@ -613,19 +629,20 @@ def self.onebox_template placeholder :channel_name script do |context, fields, automation| - sender = User.find_by(username: fields.dig('sender', 'value')) || Discourse.system_user - channel = ChatChannel.find_by(id: fields.dig('chat_channel_id', 'value')) - - placeholders = { - channel_name: channel.public_channel_title - }.merge(context['placeholders'] || {}) + sender = User.find_by(username: fields.dig("sender", "value")) || Discourse.system_user + channel = ChatChannel.find_by(id: fields.dig("chat_channel_id", "value")) - creator = DiscourseChat::ChatMessageCreator.create( - chat_channel: channel, - user: sender, - content: utils.apply_placeholders(fields.dig('message', 'value'), placeholders) + placeholders = { channel_name: channel.public_channel_title }.merge( + context["placeholders"] || {}, ) + creator = + DiscourseChat::ChatMessageCreator.create( + chat_channel: channel, + user: sender, + content: utils.apply_placeholders(fields.dig("message", "value"), placeholders), + ) + if creator.failed? Rails.logger.warn "[discourse-automation] Chat message failed to send, error was: #{creator.error}" end @@ -633,40 +650,37 @@ def self.onebox_template end end - add_api_key_scope(:chat, { - create_message: { - actions: %w[discourse_chat/chat#create_message], - params: %i[chat_channel_id] - } - }) + add_api_key_scope( + :chat, + { + create_message: { + actions: %w[discourse_chat/chat#create_message], + params: %i[chat_channel_id], + }, + }, + ) # Dark mode email styles Email::Styles.register_plugin_style do |fragment| - fragment.css('.chat-summary-header').each { |element| element[:dm] = 'header' } - fragment.css('.chat-summary-content').each { |element| element[:dm] = 'body' } + fragment.css(".chat-summary-header").each { |element| element[:dm] = "header" } + fragment.css(".chat-summary-content").each { |element| element[:dm] = "body" } end # TODO(roman): Remove `respond_to?` after 2.9 release if respond_to?(:register_email_unsubscriber) - load File.expand_path('../lib/email_controller_helper/chat_summary_unsubscriber.rb', __FILE__) - register_email_unsubscriber('chat_summary', EmailControllerHelper::ChatSummaryUnsubscriber) + load File.expand_path("../lib/email_controller_helper/chat_summary_unsubscriber.rb", __FILE__) + register_email_unsubscriber("chat_summary", EmailControllerHelper::ChatSummaryUnsubscriber) end register_about_stat_group("chat_messages", show_in_ui: true) do DiscourseChat::Statistics.about_messages end - register_about_stat_group("chat_channels") do - DiscourseChat::Statistics.about_channels - end + register_about_stat_group("chat_channels") { DiscourseChat::Statistics.about_channels } - register_about_stat_group("chat_users") do - DiscourseChat::Statistics.about_users - end + register_about_stat_group("chat_users") { DiscourseChat::Statistics.about_users } end -if Rails.env == 'test' - Dir[Rails.root.join("plugins/discourse-chat/spec/support/**/*.rb")].each do |f| - require f - end +if Rails.env == "test" + Dir[Rails.root.join("plugins/discourse-chat/spec/support/**/*.rb")].each { |f| require f } end diff --git a/spec/components/chat_mailer_spec.rb b/spec/components/chat_mailer_spec.rb index 51de3c9c5..17a6aaffd 100644 --- a/spec/components/chat_mailer_spec.rb +++ b/spec/components/chat_mailer_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe DiscourseChat::ChatMailer do before do @@ -17,16 +17,22 @@ Fabricate(:user_chat_channel_membership, user: @sender, chat_channel: @chat_channel) - @user_membership = Fabricate( - :user_chat_channel_membership, user: @user_1, - chat_channel: @chat_channel, last_read_message_id: nil - ) + @user_membership = + Fabricate( + :user_chat_channel_membership, + user: @user_1, + chat_channel: @chat_channel, + last_read_message_id: nil, + ) - @private_channel = DiscourseChat::DirectMessageChannelCreator.create!(target_users: [@sender, @user_1]) + @private_channel = + DiscourseChat::DirectMessageChannelCreator.create!(target_users: [@sender, @user_1]) end def assert_summary_skipped - expect(job_enqueued?(job: :user_email, args: { type: "chat_summary", user_id: @user_1.id })).to eq(false) + expect( + job_enqueued?(job: :user_email, args: { type: "chat_summary", user_id: @user_1.id }), + ).to eq(false) end def assert_only_queued_once @@ -34,12 +40,10 @@ def assert_only_queued_once expect(Jobs::UserEmail.jobs.size).to eq(1) end - describe 'for chat mentions' do - before do - @mention = Fabricate(:chat_mention, user: @user_1, chat_message: @chat_message) - end + describe "for chat mentions" do + before { @mention = Fabricate(:chat_mention, user: @user_1, chat_message: @chat_message) } - it 'skips users without chat access' do + it "skips users without chat access" do @chatters_group.remove(@user_1) described_class.send_unread_mentions_summary @@ -47,7 +51,7 @@ def assert_only_queued_once assert_summary_skipped end - it 'skips users with summaries disabled' do + it "skips users with summaries disabled" do @user_1.user_option.update(chat_email_frequency: UserOption.chat_email_frequencies[:never]) described_class.send_unread_mentions_summary @@ -63,21 +67,24 @@ def assert_only_queued_once assert_summary_skipped end - it 'skips without chat enabled' do - @user_1.user_option.update(chat_enabled: false, chat_email_frequency: UserOption.chat_email_frequencies[:when_away]) + it "skips without chat enabled" do + @user_1.user_option.update( + chat_enabled: false, + chat_email_frequency: UserOption.chat_email_frequencies[:when_away], + ) described_class.send_unread_mentions_summary assert_summary_skipped end - it 'queues a job for users that was mentioned and never read the channel before' do + it "queues a job for users that was mentioned and never read the channel before" do described_class.send_unread_mentions_summary assert_only_queued_once end - it 'skips the job when the user was mentioned but already read the message' do + it "skips the job when the user was mentioned but already read the message" do @user_membership.update!(last_read_message_id: @chat_message.id) described_class.send_unread_mentions_summary @@ -85,7 +92,7 @@ def assert_only_queued_once assert_summary_skipped end - it 'skips the job when the user is not following the channel anymore' do + it "skips the job when the user is not following the channel anymore" do @user_membership.update!(last_read_message_id: @chat_message.id - 1, following: false) described_class.send_unread_mentions_summary @@ -93,26 +100,31 @@ def assert_only_queued_once assert_summary_skipped end - it 'skips users with unread messages from a different channel' do + it "skips users with unread messages from a different channel" do @user_membership.update!(last_read_message_id: @chat_message.id) second_channel = Fabricate(:chat_channel) - Fabricate(:user_chat_channel_membership, user: @user_1, chat_channel: second_channel, last_read_message_id: @chat_message.id - 1) + Fabricate( + :user_chat_channel_membership, + user: @user_1, + chat_channel: second_channel, + last_read_message_id: @chat_message.id - 1, + ) described_class.send_unread_mentions_summary assert_summary_skipped end - it 'only queues the job once for users who are member of multiple groups with chat access' do + it "only queues the job once for users who are member of multiple groups with chat access" do chatters_group_2 = Fabricate(:group, users: [@user_1]) - SiteSetting.chat_allowed_groups = [@chatters_group, chatters_group_2].map(&:id).join('|') + SiteSetting.chat_allowed_groups = [@chatters_group, chatters_group_2].map(&:id).join("|") described_class.send_unread_mentions_summary assert_only_queued_once end - it 'skips users when the mention was deleted' do + it "skips users when the mention was deleted" do @chat_message.trash! described_class.send_unread_mentions_summary @@ -120,8 +132,11 @@ def assert_only_queued_once assert_summary_skipped end - it 'queues the job if the user has unread mentions and already read all the messages in the previous summary' do - @user_membership.update!(last_read_message_id: @chat_message.id, last_unread_mention_when_emailed_id: @chat_message.id) + it "queues the job if the user has unread mentions and already read all the messages in the previous summary" do + @user_membership.update!( + last_read_message_id: @chat_message.id, + last_unread_mention_when_emailed_id: @chat_message.id, + ) unread_message = Fabricate(:chat_message, chat_channel: @chat_channel, user: @sender) Fabricate(:chat_mention, user: @user_1, chat_message: unread_message) @@ -131,7 +146,7 @@ def assert_only_queued_once expect(Jobs::UserEmail.jobs.size).to eq(1) end - it 'skips users who were seen recently' do + it "skips users who were seen recently" do @user_1.update!(last_seen_at: 2.minutes.ago) described_class.send_unread_mentions_summary @@ -142,18 +157,26 @@ def assert_only_queued_once it "doesn't mix mentions from other users" do @mention.destroy! user_2 = Fabricate(:user, groups: [@chatters_group], last_seen_at: 20.minutes.ago) - user_2_membership = Fabricate(:user_chat_channel_membership, user: user_2, chat_channel: @chat_channel, last_read_message_id: nil) + user_2_membership = + Fabricate( + :user_chat_channel_membership, + user: user_2, + chat_channel: @chat_channel, + last_read_message_id: nil, + ) new_message = Fabricate(:chat_message, chat_channel: @chat_channel, user: @sender) Fabricate(:chat_mention, user: user_2, chat_message: new_message) described_class.send_unread_mentions_summary - expect(job_enqueued?(job: :user_email, args: { type: "chat_summary", user_id: @user_1.id })).to eq(false) + expect( + job_enqueued?(job: :user_email, args: { type: "chat_summary", user_id: @user_1.id }), + ).to eq(false) expect_job_enqueued(job: :user_email, args: { type: "chat_summary", user_id: user_2.id }) expect(Jobs::UserEmail.jobs.size).to eq(1) end - it 'skips users when the message is older than 1 week' do + it "skips users when the message is older than 1 week" do @chat_message.update!(created_at: 1.5.weeks.ago) described_class.send_unread_mentions_summary @@ -161,7 +184,7 @@ def assert_only_queued_once assert_summary_skipped end - describe 'update the user membership after we send the email' do + describe "update the user membership after we send the email" do before { Jobs.run_immediately! } it "doesn't send the same summary the summary again if the user haven't read any channel messages since the last one" do @@ -170,20 +193,28 @@ def assert_only_queued_once expect(@user_membership.reload.last_unread_mention_when_emailed_id).to eq(@chat_message.id) - another_channel_message = Fabricate(:chat_message, chat_channel: @chat_channel, user: @sender) + another_channel_message = + Fabricate(:chat_message, chat_channel: @chat_channel, user: @sender) Fabricate(:chat_mention, user: @user_1, chat_message: another_channel_message) - expect { described_class.send_unread_mentions_summary }.to change(Jobs::UserEmail.jobs, :size).by(0) + expect { described_class.send_unread_mentions_summary }.to change( + Jobs::UserEmail.jobs, + :size, + ).by(0) end - it 'only updates the last_message_read_when_emailed_id on the channel with unread mentions' do + it "only updates the last_message_read_when_emailed_id on the channel with unread mentions" do another_channel = Fabricate(:chat_channel) - another_channel_message = Fabricate(:chat_message, chat_channel: another_channel, user: @sender) + another_channel_message = + Fabricate(:chat_message, chat_channel: another_channel, user: @sender) Fabricate(:chat_mention, user: @user_1, chat_message: another_channel_message) - another_channel_membership = Fabricate( - :user_chat_channel_membership, user: @user_1, chat_channel: another_channel, - last_read_message_id: another_channel_message.id - ) + another_channel_membership = + Fabricate( + :user_chat_channel_membership, + user: @user_1, + chat_channel: another_channel, + last_read_message_id: another_channel_message.id, + ) @user_membership.update!(last_read_message_id: @chat_message.id - 1) described_class.send_unread_mentions_summary @@ -194,18 +225,16 @@ def assert_only_queued_once end end - describe 'for direct messages' do - before do - Fabricate(:chat_message, user: @sender, chat_channel: @private_channel) - end + describe "for direct messages" do + before { Fabricate(:chat_message, user: @sender, chat_channel: @private_channel) } - it 'queue a job when the user has unread private mentions' do + it "queue a job when the user has unread private mentions" do described_class.send_unread_mentions_summary assert_only_queued_once end - it 'only queues the job once when the user has mentions and private messages' do + it "only queues the job once when the user has mentions and private messages" do Fabricate(:chat_mention, user: @user_1, chat_message: @chat_message) described_class.send_unread_mentions_summary @@ -215,7 +244,13 @@ def assert_only_queued_once it "Doesn't mix or update mentions from other users when joining tables" do user_2 = Fabricate(:user, groups: [@chatters_group], last_seen_at: 20.minutes.ago) - user_2_membership = Fabricate(:user_chat_channel_membership, user: user_2, chat_channel: @chat_channel, last_read_message_id: @chat_message.id) + user_2_membership = + Fabricate( + :user_chat_channel_membership, + user: user_2, + chat_channel: @chat_channel, + last_read_message_id: @chat_message.id, + ) Fabricate(:chat_mention, user: user_2, chat_message: @chat_message) described_class.send_unread_mentions_summary diff --git a/spec/components/chat_message_creator_spec.rb b/spec/components/chat_message_creator_spec.rb index 459dc34ab..3c50285ff 100644 --- a/spec/components/chat_message_creator_spec.rb +++ b/spec/components/chat_message_creator_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe DiscourseChat::ChatMessageCreator do fab!(:admin1) { Fabricate(:admin) } @@ -8,11 +8,28 @@ fab!(:user1) { Fabricate(:user, group_ids: [Group::AUTO_GROUPS[:everyone]]) } fab!(:user2) { Fabricate(:user) } fab!(:user3) { Fabricate(:user) } - fab!(:admin_group) { Fabricate(:public_group, users: [admin1, admin2], mentionable_level: Group::ALIAS_LEVELS[:everyone]) } - fab!(:user_group) { Fabricate(:public_group, users: [user1, user2, user3], mentionable_level: Group::ALIAS_LEVELS[:everyone]) } + fab!(:admin_group) do + Fabricate( + :public_group, + users: [admin1, admin2], + mentionable_level: Group::ALIAS_LEVELS[:everyone], + ) + end + fab!(:user_group) do + Fabricate( + :public_group, + users: [user1, user2, user3], + mentionable_level: Group::ALIAS_LEVELS[:everyone], + ) + end fab!(:user_without_memberships) { Fabricate(:user) } fab!(:public_chat_channel) { Fabricate(:chat_channel, chatable: Fabricate(:topic)) } - fab!(:dm_chat_channel) { Fabricate(:chat_channel, chatable: Fabricate(:direct_message_channel, users: [user1, user2, user3])) } + fab!(:dm_chat_channel) do + Fabricate( + :chat_channel, + chatable: Fabricate(:direct_message_channel, users: [user1, user2, user3]), + ) + end before do SiteSetting.chat_enabled = true @@ -24,23 +41,28 @@ Fabricate(:user_chat_channel_membership, chat_channel: public_chat_channel, user: user) end - @direct_message_channel = DiscourseChat::DirectMessageChannelCreator.create!(target_users: [user1, user2]) + @direct_message_channel = + DiscourseChat::DirectMessageChannelCreator.create!(target_users: [user1, user2]) end describe "Integration tests with jobs running immediately" do - before do - Jobs.run_immediately! - end + before { Jobs.run_immediately! } it "errors when length is less than `chat_minimum_message_length`" do SiteSetting.chat_minimum_message_length = 10 - creator = DiscourseChat::ChatMessageCreator.create( - chat_channel: public_chat_channel, - user: user1, - content: "2 short" - ) + creator = + DiscourseChat::ChatMessageCreator.create( + chat_channel: public_chat_channel, + user: user1, + content: "2 short", + ) expect(creator.failed?).to eq(true) - expect(creator.error.message).to match(I18n.t("chat.errors.minimum_length_not_met", { minimum: SiteSetting.chat_minimum_message_length })) + expect(creator.error.message).to match( + I18n.t( + "chat.errors.minimum_length_not_met", + { minimum: SiteSetting.chat_minimum_message_length }, + ), + ) end it "allows message creation when length is less than `chat_minimum_message_length` when upload is present" do @@ -51,7 +73,7 @@ chat_channel: public_chat_channel, user: user1, content: "2 short", - upload_ids: [upload.id] + upload_ids: [upload.id], ) }.to change { ChatMessage.count }.by(1) end @@ -61,7 +83,7 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user1, - content: "this is a message" + content: "this is a message", ) }.to change { ChatMessage.count }.by(1) end @@ -71,21 +93,20 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user1, - content: "this is a @#{user1.username} message with @system @mentions @#{user2.username} and @#{user3.username}" + content: + "this is a @#{user1.username} message with @system @mentions @#{user2.username} and @#{user3.username}", ) # Only 2 mentions are created because user mentioned themselves, system, and an invalid username. - }.to change { ChatMention.count }.by(2) - .and change { - user1.chat_mentions.count - }.by(0) + }.to change { ChatMention.count }.by(2).and change { user1.chat_mentions.count }.by(0) end it "mentions are case insensitive" do - expect { DiscourseChat::ChatMessageCreator.create( - chat_channel: public_chat_channel, - user: user1, - content: "Hey @#{user2.username.upcase}" - ) + expect { + DiscourseChat::ChatMessageCreator.create( + chat_channel: public_chat_channel, + user: user1, + content: "Hey @#{user2.username.upcase}", + ) }.to change { user2.chat_mentions.count }.by(1) end @@ -94,16 +115,18 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user1, - content: "@all" + content: "@all", ) }.to change { ChatMention.count }.by(4) - UserChatChannelMembership.where(user: user2, chat_channel: public_chat_channel).update_all(following: false) + UserChatChannelMembership.where(user: user2, chat_channel: public_chat_channel).update_all( + following: false, + ) expect { DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user1, - content: "again! @all" + content: "again! @all", ) }.to change { ChatMention.count }.by(3) end @@ -118,7 +141,7 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user1, - content: "@here" + content: "@here", ) }.to change { ChatMention.count }.by(2) end @@ -129,7 +152,7 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user1, - content: "@here @#{user2.username}" + content: "@here @#{user2.username}", ) }.to change { user2.chat_mentions.count }.by(1) end @@ -144,7 +167,7 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user1, - content: "@here plus @#{user3.username}" + content: "@here plus @#{user3.username}", ) }.to change { user3.chat_mentions.count }.by(1) end @@ -154,7 +177,7 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user1, - content: "hello @#{user_without_memberships.username}" + content: "hello @#{user_without_memberships.username}", ) }.to change { ChatMention.count }.by(0) end @@ -166,7 +189,7 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user1, - content: "hi @#{user2.username} @#{user3.username}" + content: "hi @#{user2.username} @#{user3.username}", ) }.to change { ChatMention.count }.by(0) end @@ -177,7 +200,7 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user1, - content: "hi @#{user2.username}" + content: "hi @#{user2.username}", ) }.to change { ChatMention.count }.by(0) end @@ -187,36 +210,33 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: @direct_message_channel, user: user1, - content: "hello there @#{user2.username} and @#{user3.username}" + content: "hello there @#{user2.username} and @#{user3.username}", ) # Only user2 should be notified - }.to change { user2.chat_mentions.count }.by(1) - .and change { - user3.chat_mentions.count - }.by(0) + }.to change { user2.chat_mentions.count }.by(1).and change { user3.chat_mentions.count }.by(0) end - it 'creates a mention notifications for group users that are participating in private chat' do + it "creates a mention notifications for group users that are participating in private chat" do expect { DiscourseChat::ChatMessageCreator.create( chat_channel: @direct_message_channel, user: user1, - content: "hello there @#{user_group.name}" + content: "hello there @#{user_group.name}", ) # Only user2 should be notified - }.to change { user2.chat_mentions.count }.by(1) - .and change { - user3.chat_mentions.count - }.by(0) + }.to change { user2.chat_mentions.count }.by(1).and change { user3.chat_mentions.count }.by(0) end it "publishes inaccessible mentions when user isn't aren't a part of the channel" do - user3.user_chat_channel_memberships.where(chat_channel: public_chat_channel).update(following: false) + user3 + .user_chat_channel_memberships + .where(chat_channel: public_chat_channel) + .update(following: false) ChatPublisher.expects(:publish_inaccessible_mentions).once DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: admin1, - content: "hello @#{user3.username}" + content: "hello @#{user3.username}", ) end @@ -226,7 +246,7 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: admin1, - content: "hello @#{user3.username}" + content: "hello @#{user3.username}", ) end @@ -235,7 +255,7 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: admin1, - content: "hello @#{admin2.username}" + content: "hello @#{admin2.username}", ) end @@ -245,7 +265,7 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: @direct_message_channel, user: user1, - content: "hello @#{user2.username}" + content: "hello @#{user2.username}", ) }.to change { user2.chat_mentions.count }.by(0) end @@ -255,7 +275,7 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user1, - content: "@all" + content: "@all", ) }.to change { ChatMention.count }.by(4) @@ -264,7 +284,7 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user1, - content: "hi! @all" + content: "hi! @all", ) }.to change { ChatMention.count }.by(3) end @@ -281,7 +301,7 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user1, - content: "@here" + content: "@here", ) }.to change { ChatMention.count }.by(1) end @@ -292,12 +312,11 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user1, - content: "hello @#{admin_group.name}" + content: "hello @#{admin_group.name}", ) - }.to change { admin1.chat_mentions.count }.by(1) - .and change { - admin2.chat_mentions.count - }.by(1) + }.to change { admin1.chat_mentions.count }.by(1).and change { + admin2.chat_mentions.count + }.by(1) end it "doesn't mention users twice if they are direct mentioned and group mentioned" do @@ -305,12 +324,11 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user1, - content: "hello @#{admin_group.name} @#{admin1.username} and @#{admin2.username}" + content: "hello @#{admin_group.name} @#{admin1.username} and @#{admin2.username}", ) - }.to change { admin1.chat_mentions.count }.by(1) - .and change { - admin2.chat_mentions.count - }.by(1) + }.to change { admin1.chat_mentions.count }.by(1).and change { + admin2.chat_mentions.count + }.by(1) end it "creates chat mentions for group mentions and direct mentions" do @@ -318,15 +336,11 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user1, - content: "hello @#{admin_group.name} @#{user2.username}" + content: "hello @#{admin_group.name} @#{user2.username}", ) - }.to change { admin1.chat_mentions.count }.by(1) - .and change { - admin2.chat_mentions.count - }.by(1) - .and change { - user2.chat_mentions.count - }.by(1) + }.to change { admin1.chat_mentions.count }.by(1).and change { + admin2.chat_mentions.count + }.by(1).and change { user2.chat_mentions.count }.by(1) end it "creates chat mentions for group mentions and direct mentions" do @@ -334,18 +348,13 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user1, - content: "hello @#{admin_group.name} @#{user_group.name}" + content: "hello @#{admin_group.name} @#{user_group.name}", ) - }.to change { admin1.chat_mentions.count }.by(1) - .and change { - admin2.chat_mentions.count - }.by(1) - .and change { - user2.chat_mentions.count - }.by(1) - .and change { - user3.chat_mentions.count - }.by(1) + }.to change { admin1.chat_mentions.count }.by(1).and change { + admin2.chat_mentions.count + }.by(1).and change { user2.chat_mentions.count }.by(1).and change { + user3.chat_mentions.count + }.by(1) end it "doesn't create chat mentions for group mentions where the group is un-mentionable" do @@ -354,7 +363,7 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user1, - content: "hello @#{admin_group.name}" + content: "hello @#{admin_group.name}", ) }.to change { ChatMention.count }.by(0) end @@ -362,9 +371,9 @@ describe "push notifications" do before do - UserChatChannelMembership - .where(user: user1, chat_channel: public_chat_channel) - .update(mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always]) + UserChatChannelMembership.where(user: user1, chat_channel: public_chat_channel).update( + mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always], + ) PresenceChannel.clear_all! end @@ -373,7 +382,7 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user2, - content: "Beep boop" + content: "Beep boop", ) end @@ -383,7 +392,7 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user2, - content: "Beep boop" + content: "Beep boop", ) end end @@ -399,7 +408,7 @@ chat_channel: public_chat_channel, user: user1, content: "Beep boop", - upload_ids: [upload1.id] + upload_ids: [upload1.id], ) }.to change { ChatUpload.where(upload_id: upload1.id).count }.by(1) end @@ -410,14 +419,11 @@ chat_channel: public_chat_channel, user: user1, content: "Beep boop", - upload_ids: [upload1.id, upload2.id] + upload_ids: [upload1.id, upload2.id], ) - }.to change { - ChatUpload.where(upload_id: upload1.id).count - }.by(1) - .and change { - ChatUpload.where(upload_id: upload2.id).count - }.by(1) + }.to change { ChatUpload.where(upload_id: upload1.id).count }.by(1).and change { + ChatUpload.where(upload_id: upload2.id).count + }.by(1) end it "filters out uploads that weren't uploaded by the user" do @@ -426,11 +432,9 @@ chat_channel: public_chat_channel, user: user1, content: "Beep boop", - upload_ids: [private_upload.id] + upload_ids: [private_upload.id], ) - }.to change { - ChatUpload.where(upload_id: private_upload.id).count - }.by(0) + }.to change { ChatUpload.where(upload_id: private_upload.id).count }.by(0) end it "doesn't attach uploads when `chat_allow_uploads` is false" do @@ -440,7 +444,7 @@ chat_channel: public_chat_channel, user: user1, content: "Beep boop", - upload_ids: [upload1.id] + upload_ids: [upload1.id], ) }.to change { ChatUpload.where(upload_id: upload1.id).count }.by(0) end @@ -454,7 +458,7 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: public_chat_channel, user: user1, - content: "Hi @#{user2.username}" + content: "Hi @#{user2.username}", ) end.to change { ChatDraft.count }.by(-1) end @@ -463,31 +467,39 @@ fab!(:watched_word) { Fabricate(:watched_word) } it "errors when a blocked word is present" do - creator = DiscourseChat::ChatMessageCreator.create( - chat_channel: public_chat_channel, - user: user1, - content: "bad word - #{watched_word.word}" - ) + creator = + DiscourseChat::ChatMessageCreator.create( + chat_channel: public_chat_channel, + user: user1, + content: "bad word - #{watched_word.word}", + ) expect(creator.failed?).to eq(true) - expect(creator.error.message).to match(I18n.t("contains_blocked_word", { word: watched_word.word })) + expect(creator.error.message).to match( + I18n.t("contains_blocked_word", { word: watched_word.word }), + ) end end describe "channel statuses" do def create_message(user) - DiscourseChat::ChatMessageCreator.create(chat_channel: public_chat_channel, user: user, content: "test message") + DiscourseChat::ChatMessageCreator.create( + chat_channel: public_chat_channel, + user: user, + content: "test message", + ) end context "when channel is closed" do - before do - public_chat_channel.update(status: :closed) - end + before { public_chat_channel.update(status: :closed) } it "errors when trying to create the message for non-staff" do creator = create_message(user1) expect(creator.failed?).to eq(true) expect(creator.error.message).to eq( - I18n.t("chat.errors.channel_new_message_disallowed", status: public_chat_channel.status_name) + I18n.t( + "chat.errors.channel_new_message_disallowed", + status: public_chat_channel.status_name, + ), ) end @@ -497,39 +509,47 @@ def create_message(user) end context "when channel is read_only" do - before do - public_chat_channel.update(status: :read_only) - end + before { public_chat_channel.update(status: :read_only) } it "errors when trying to create the message for all users" do creator = create_message(user1) expect(creator.failed?).to eq(true) expect(creator.error.message).to eq( - I18n.t("chat.errors.channel_new_message_disallowed", status: public_chat_channel.status_name) + I18n.t( + "chat.errors.channel_new_message_disallowed", + status: public_chat_channel.status_name, + ), ) creator = create_message(admin1) expect(creator.failed?).to eq(true) expect(creator.error.message).to eq( - I18n.t("chat.errors.channel_new_message_disallowed", status: public_chat_channel.status_name) + I18n.t( + "chat.errors.channel_new_message_disallowed", + status: public_chat_channel.status_name, + ), ) end end context "when channel is archived" do - before do - public_chat_channel.update(status: :archived) - end + before { public_chat_channel.update(status: :archived) } it "errors when trying to create the message for all users" do creator = create_message(user1) expect(creator.failed?).to eq(true) expect(creator.error.message).to eq( - I18n.t("chat.errors.channel_new_message_disallowed", status: public_chat_channel.status_name) + I18n.t( + "chat.errors.channel_new_message_disallowed", + status: public_chat_channel.status_name, + ), ) creator = create_message(admin1) expect(creator.failed?).to eq(true) expect(creator.error.message).to eq( - I18n.t("chat.errors.channel_new_message_disallowed", status: public_chat_channel.status_name) + I18n.t( + "chat.errors.channel_new_message_disallowed", + status: public_chat_channel.status_name, + ), ) end end diff --git a/spec/components/chat_message_rate_limiter_spec.rb b/spec/components/chat_message_rate_limiter_spec.rb index e6471be3d..e4dc047d6 100644 --- a/spec/components/chat_message_rate_limiter_spec.rb +++ b/spec/components/chat_message_rate_limiter_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe DiscourseChat::ChatMessageRateLimiter do fab!(:user) { Fabricate(:user, trust_level: 3) } @@ -14,9 +14,7 @@ SiteSetting.chat_auto_silence_duration = 30 end - after do - limiter.clear! - end + after { limiter.clear! } it "does nothing when rate limits are not exceeded" do limiter.run! @@ -29,9 +27,7 @@ expect(user.reload.silenced?).to be false end - expect { - limiter.run! - }.to raise_error(RateLimiter::LimitExceeded) + expect { limiter.run! }.to raise_error(RateLimiter::LimitExceeded) expect(user.reload.silenced?).to be true expect(user.silenced_till).to be_within(0.1).of(30.minutes.from_now) @@ -41,9 +37,7 @@ user.update(trust_level: 0) # Should only be able to run once without hitting limit limiter.run! expect(user.reload.silenced?).to be false - expect { - limiter.run! - }.to raise_error(RateLimiter::LimitExceeded) + expect { limiter.run! }.to raise_error(RateLimiter::LimitExceeded) expect(user.reload.silenced?).to be true end @@ -61,9 +55,7 @@ limiter.run! expect(user.reload.silenced?).to be false - expect { - limiter.run! - }.to raise_error(RateLimiter::LimitExceeded) + expect { limiter.run! }.to raise_error(RateLimiter::LimitExceeded) expect(user.reload.silenced?).to be false end @@ -71,15 +63,12 @@ SiteSetting.chat_allowed_messages_for_other_trust_levels = 1 limiter.run! - expect { - limiter.run! - }.to raise_error(RateLimiter::LimitExceeded) - .and change { - UserHistory.where( - target_user: user, - acting_user: Discourse.system_user, - action: UserHistory.actions[:silence_user] - ).count - }.by(1) + expect { limiter.run! }.to raise_error(RateLimiter::LimitExceeded).and change { + UserHistory.where( + target_user: user, + acting_user: Discourse.system_user, + action: UserHistory.actions[:silence_user], + ).count + }.by(1) end end diff --git a/spec/components/chat_message_updater_spec.rb b/spec/components/chat_message_updater_spec.rb index 1720e05a5..2b45fab42 100644 --- a/spec/components/chat_message_updater_spec.rb +++ b/spec/components/chat_message_updater_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe DiscourseChat::ChatMessageUpdater do fab!(:admin1) { Fabricate(:admin) } @@ -9,7 +9,13 @@ fab!(:user2) { Fabricate(:user) } fab!(:user3) { Fabricate(:user) } fab!(:user4) { Fabricate(:user) } - fab!(:admin_group) { Fabricate(:public_group, users: [admin1, admin2], mentionable_level: Group::ALIAS_LEVELS[:everyone]) } + fab!(:admin_group) do + Fabricate( + :public_group, + users: [admin1, admin2], + mentionable_level: Group::ALIAS_LEVELS[:everyone], + ) + end fab!(:user_without_memberships) { Fabricate(:user) } fab!(:public_chat_channel) { Fabricate(:chat_channel, chatable: Fabricate(:topic)) } @@ -22,17 +28,19 @@ [admin1, admin2, user1, user2, user3, user4].each do |user| Fabricate(:user_chat_channel_membership, chat_channel: public_chat_channel, user: user) end - @direct_message_channel = DiscourseChat::DirectMessageChannelCreator.create!(target_users: [user1, user2]) + @direct_message_channel = + DiscourseChat::DirectMessageChannelCreator.create!(target_users: [user1, user2]) end def create_chat_message(user, message, channel, upload_ids: nil) - creator = DiscourseChat::ChatMessageCreator.create( - chat_channel: channel, - user: user, - in_reply_to_id: nil, - content: message, - upload_ids: upload_ids - ) + creator = + DiscourseChat::ChatMessageCreator.create( + chat_channel: channel, + user: user, + in_reply_to_id: nil, + content: message, + upload_ids: upload_ids, + ) creator.chat_message end @@ -42,12 +50,15 @@ def create_chat_message(user, message, channel, upload_ids: nil) chat_message = create_chat_message(user1, og_message, public_chat_channel) new_message = "2 short" - updater = DiscourseChat::ChatMessageUpdater.update( - chat_message: chat_message, - new_content: new_message - ) + updater = + DiscourseChat::ChatMessageUpdater.update(chat_message: chat_message, new_content: new_message) expect(updater.failed?).to eq(true) - expect(updater.error.message).to match(I18n.t("chat.errors.minimum_length_not_met", { minimum: SiteSetting.chat_minimum_message_length })) + expect(updater.error.message).to match( + I18n.t( + "chat.errors.minimum_length_not_met", + { minimum: SiteSetting.chat_minimum_message_length }, + ), + ) expect(chat_message.reload.message).to eq(og_message) end @@ -55,10 +66,7 @@ def create_chat_message(user, message, channel, upload_ids: nil) chat_message = create_chat_message(user1, "This will be changed", public_chat_channel) new_message = "Change to this!" - DiscourseChat::ChatMessageUpdater.update( - chat_message: chat_message, - new_content: new_message - ) + DiscourseChat::ChatMessageUpdater.update(chat_message: chat_message, new_content: new_message) expect(chat_message.reload.message).to eq(new_message) end @@ -67,12 +75,10 @@ def create_chat_message(user, message, channel, upload_ids: nil) expect { DiscourseChat::ChatMessageUpdater.update( chat_message: chat_message, - new_content: "this is a message with @system @mentions @#{user2.username} and @#{user3.username}" + new_content: + "this is a message with @system @mentions @#{user2.username} and @#{user3.username}", ) - }.to change { user2.chat_mentions.count }.by(1) - .and change { - user3.chat_mentions.count - }.by(1) + }.to change { user2.chat_mentions.count }.by(1).and change { user3.chat_mentions.count }.by(1) end it "doesn't create mentions for already mentioned users" do @@ -81,7 +87,7 @@ def create_chat_message(user, message, channel, upload_ids: nil) expect { DiscourseChat::ChatMessageUpdater.update( chat_message: chat_message, - new_content: message + " editedddd" + new_content: message + " editedddd", ) }.to change { ChatMention.count }.by(0) end @@ -93,29 +99,28 @@ def create_chat_message(user, message, channel, upload_ids: nil) expect { DiscourseChat::ChatMessageUpdater.update( chat_message: chat_message, - new_content: message + " @#{user_without_memberships.username}" + new_content: message + " @#{user_without_memberships.username}", ) }.to change { ChatMention.count }.by(0) end it "destroys mention notifications that should be removed" do - chat_message = create_chat_message(user1, "ping @#{user2.username} @#{user3.username}", public_chat_channel) + chat_message = + create_chat_message(user1, "ping @#{user2.username} @#{user3.username}", public_chat_channel) expect { DiscourseChat::ChatMessageUpdater.update( chat_message: chat_message, - new_content: "ping @#{user3.username}" + new_content: "ping @#{user3.username}", ) - }.to change { user2.chat_mentions.count }.by(-1) - .and change { - user3.chat_mentions.count - }.by(0) + }.to change { user2.chat_mentions.count }.by(-1).and change { user3.chat_mentions.count }.by(0) end it "creates new, leaves existing, and removes old mentions all at once" do - chat_message = create_chat_message(user1, "ping @#{user2.username} @#{user3.username}", public_chat_channel) + chat_message = + create_chat_message(user1, "ping @#{user2.username} @#{user3.username}", public_chat_channel) DiscourseChat::ChatMessageUpdater.update( chat_message: chat_message, - new_content: "ping @#{user3.username} @#{user4.username}" + new_content: "ping @#{user3.username} @#{user4.username}", ) expect(user2.chat_mentions.where(chat_message: chat_message)).not_to be_present @@ -124,11 +129,11 @@ def create_chat_message(user, message, channel, upload_ids: nil) end it "does not create new mentions in direct message for users who don't have access" do - chat_message = create_chat_message(user1, "ping nobody" , @direct_message_channel) + chat_message = create_chat_message(user1, "ping nobody", @direct_message_channel) expect { DiscourseChat::ChatMessageUpdater.update( chat_message: chat_message, - new_content: "ping @#{admin1.username}" + new_content: "ping @#{admin1.username}", ) }.to change { ChatMention.count }.by(0) end @@ -139,7 +144,7 @@ def create_chat_message(user, message, channel, upload_ids: nil) expect { DiscourseChat::ChatMessageUpdater.update( chat_message: chat_message, - new_content: "ping @#{admin_group.name}" + new_content: "ping @#{admin_group.name}", ) }.to change { ChatMention.where(chat_message: chat_message).count }.by(2) @@ -152,12 +157,11 @@ def create_chat_message(user, message, channel, upload_ids: nil) expect { DiscourseChat::ChatMessageUpdater.update( chat_message: chat_message, - new_content: "ping @#{admin_group.name} @#{admin2.username}" + new_content: "ping @#{admin_group.name} @#{admin2.username}", ) - }.to change { admin1.chat_mentions.count }.by(1) - .and change { - admin2.chat_mentions.count - }.by(0) + }.to change { admin1.chat_mentions.count }.by(1).and change { admin2.chat_mentions.count }.by( + 0, + ) end it "deletes old mentions when group mention is removed" do @@ -165,7 +169,7 @@ def create_chat_message(user, message, channel, upload_ids: nil) expect { DiscourseChat::ChatMessageUpdater.update( chat_message: chat_message, - new_content: "ping nobody anymore!" + new_content: "ping nobody anymore!", ) }.to change { ChatMention.where(chat_message: chat_message).count }.by(-2) @@ -178,10 +182,7 @@ def create_chat_message(user, message, channel, upload_ids: nil) old_message = "It's a thrsday!" new_message = "It's a thursday!" chat_message = create_chat_message(user1, old_message, public_chat_channel) - DiscourseChat::ChatMessageUpdater.update( - chat_message: chat_message, - new_content: new_message - ) + DiscourseChat::ChatMessageUpdater.update(chat_message: chat_message, new_content: new_message) revision = chat_message.revisions.last expect(revision.old_message).to eq(old_message) expect(revision.new_message).to eq(new_message) @@ -192,34 +193,52 @@ def create_chat_message(user, message, channel, upload_ids: nil) fab!(:upload2) { Fabricate(:upload, user: user1) } it "does nothing if the passed in upload_ids match the existing upload_ids" do - chat_message = create_chat_message(user1, "something", public_chat_channel, upload_ids: [upload1.id, upload2.id]) + chat_message = + create_chat_message( + user1, + "something", + public_chat_channel, + upload_ids: [upload1.id, upload2.id], + ) expect { DiscourseChat::ChatMessageUpdater.update( chat_message: chat_message, new_content: "I guess this is different", - upload_ids: [upload2.id, upload1.id] + upload_ids: [upload2.id, upload1.id], ) }.to change { ChatUpload.count }.by(0) end it "removes uploads that should be removed" do - chat_message = create_chat_message(user1, "something", public_chat_channel, upload_ids: [upload1.id, upload2.id]) + chat_message = + create_chat_message( + user1, + "something", + public_chat_channel, + upload_ids: [upload1.id, upload2.id], + ) expect { DiscourseChat::ChatMessageUpdater.update( chat_message: chat_message, new_content: "I guess this is different", - upload_ids: [upload1.id] + upload_ids: [upload1.id], ) }.to change { ChatUpload.where(upload_id: upload2.id).count }.by(-1) end it "removes all uploads if they should be removed" do - chat_message = create_chat_message(user1, "something", public_chat_channel, upload_ids: [upload1.id, upload2.id]) + chat_message = + create_chat_message( + user1, + "something", + public_chat_channel, + upload_ids: [upload1.id, upload2.id], + ) expect { DiscourseChat::ChatMessageUpdater.update( chat_message: chat_message, new_content: "I guess this is different", - upload_ids: [] + upload_ids: [], ) }.to change { ChatUpload.where(chat_message: chat_message).count }.by(-2) end @@ -230,7 +249,7 @@ def create_chat_message(user, message, channel, upload_ids: nil) DiscourseChat::ChatMessageUpdater.update( chat_message: chat_message, new_content: "I guess this is different", - upload_ids: [upload1.id] + upload_ids: [upload1.id], ) }.to change { ChatUpload.where(chat_message: chat_message).count }.by(1) end @@ -241,18 +260,19 @@ def create_chat_message(user, message, channel, upload_ids: nil) DiscourseChat::ChatMessageUpdater.update( chat_message: chat_message, new_content: "I guess this is different", - upload_ids: [upload1.id, upload2.id] + upload_ids: [upload1.id, upload2.id], ) }.to change { ChatUpload.where(chat_message: chat_message).count }.by(2) end it "doesn't remove existing uploads when BS upload ids are passed in" do - chat_message = create_chat_message(user1, "something", public_chat_channel, upload_ids: [upload1.id]) + chat_message = + create_chat_message(user1, "something", public_chat_channel, upload_ids: [upload1.id]) expect { DiscourseChat::ChatMessageUpdater.update( chat_message: chat_message, new_content: "I guess this is different", - upload_ids: [0] + upload_ids: [0], ) }.to change { ChatUpload.where(chat_message: chat_message).count }.by(0) end @@ -264,31 +284,43 @@ def create_chat_message(user, message, channel, upload_ids: nil) DiscourseChat::ChatMessageUpdater.update( chat_message: chat_message, new_content: "I guess this is different", - upload_ids: [upload1.id, upload2.id] + upload_ids: [upload1.id, upload2.id], ) }.to change { ChatUpload.where(chat_message: chat_message).count }.by(0) end it "doesn't remove existing uploads if `chat_allow_uploads` is false" do SiteSetting.chat_allow_uploads = false - chat_message = create_chat_message(user1, "something", public_chat_channel, upload_ids: [upload1.id, upload2.id]) + chat_message = + create_chat_message( + user1, + "something", + public_chat_channel, + upload_ids: [upload1.id, upload2.id], + ) expect { DiscourseChat::ChatMessageUpdater.update( chat_message: chat_message, new_content: "I guess this is different", - upload_ids: [] + upload_ids: [], ) }.to change { ChatUpload.where(chat_message: chat_message).count }.by(0) end it "updates if upload is present even if length is less than `chat_minimum_message_length`" do - chat_message = create_chat_message(user1, "something", public_chat_channel, upload_ids: [upload1.id, upload2.id]) + chat_message = + create_chat_message( + user1, + "something", + public_chat_channel, + upload_ids: [upload1.id, upload2.id], + ) SiteSetting.chat_minimum_message_length = 10 new_message = "hi :)" DiscourseChat::ChatMessageUpdater.update( chat_message: chat_message, new_content: new_message, - upload_ids: [upload1.id] + upload_ids: [upload1.id], ) expect(chat_message.reload.message).to eq(new_message) end @@ -299,13 +331,16 @@ def create_chat_message(user, message, channel, upload_ids: nil) it "errors when a blocked word is present" do chat_message = create_chat_message(user1, "something", public_chat_channel) - creator = DiscourseChat::ChatMessageCreator.create( - chat_channel: public_chat_channel, - user: user1, - content: "bad word - #{watched_word.word}" - ) + creator = + DiscourseChat::ChatMessageCreator.create( + chat_channel: public_chat_channel, + user: user1, + content: "bad word - #{watched_word.word}", + ) expect(creator.failed?).to eq(true) - expect(creator.error.message).to match(I18n.t("contains_blocked_word", { word: watched_word.word })) + expect(creator.error.message).to match( + I18n.t("contains_blocked_word", { word: watched_word.word }), + ) end end @@ -316,20 +351,21 @@ def update_message(user) message.update(user: user) DiscourseChat::ChatMessageUpdater.update( chat_message: message, - new_content: "I guess this is different" + new_content: "I guess this is different", ) end context "when channel is closed" do - before do - public_chat_channel.update(status: :closed) - end + before { public_chat_channel.update(status: :closed) } it "errors when trying to update the message for non-staff" do updater = update_message(user1) expect(updater.failed?).to eq(true) expect(updater.error.message).to eq( - I18n.t("chat.errors.channel_modify_message_disallowed", status: public_chat_channel.status_name) + I18n.t( + "chat.errors.channel_modify_message_disallowed", + status: public_chat_channel.status_name, + ), ) end @@ -340,39 +376,47 @@ def update_message(user) end context "when channel is read_only" do - before do - public_chat_channel.update(status: :read_only) - end + before { public_chat_channel.update(status: :read_only) } it "errors when trying to update the message for all users" do updater = update_message(user1) expect(updater.failed?).to eq(true) expect(updater.error.message).to eq( - I18n.t("chat.errors.channel_modify_message_disallowed", status: public_chat_channel.status_name) + I18n.t( + "chat.errors.channel_modify_message_disallowed", + status: public_chat_channel.status_name, + ), ) updater = update_message(admin1) expect(updater.failed?).to eq(true) expect(updater.error.message).to eq( - I18n.t("chat.errors.channel_modify_message_disallowed", status: public_chat_channel.status_name) + I18n.t( + "chat.errors.channel_modify_message_disallowed", + status: public_chat_channel.status_name, + ), ) end end context "when channel is archived" do - before do - public_chat_channel.update(status: :archived) - end + before { public_chat_channel.update(status: :archived) } it "errors when trying to update the message for all users" do updater = update_message(user1) expect(updater.failed?).to eq(true) expect(updater.error.message).to eq( - I18n.t("chat.errors.channel_modify_message_disallowed", status: public_chat_channel.status_name) + I18n.t( + "chat.errors.channel_modify_message_disallowed", + status: public_chat_channel.status_name, + ), ) updater = update_message(admin1) expect(updater.failed?).to eq(true) expect(updater.error.message).to eq( - I18n.t("chat.errors.channel_modify_message_disallowed", status: public_chat_channel.status_name) + I18n.t( + "chat.errors.channel_modify_message_disallowed", + status: public_chat_channel.status_name, + ), ) end end diff --git a/spec/components/chat_seeder_spec.rb b/spec/components/chat_seeder_spec.rb index fd1c8ddb6..957b2c6b8 100644 --- a/spec/components/chat_seeder_spec.rb +++ b/spec/components/chat_seeder_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe ChatSeeder do fab!(:staff_category) { Fabricate(:private_category, group: Group[:staff]) } @@ -22,7 +22,8 @@ def assert_channel_was_correctly_seeded(channel, group) expect(channel.auto_join_users).to eq(true) expected_members_count = GroupUser.where(group: group).count - memberships_count = UserChatChannelMembership.automatic.where(chat_channel: channel, following: true).count + memberships_count = + UserChatChannelMembership.automatic.where(chat_channel: channel, following: true).count expect(memberships_count).to eq(expected_members_count) end @@ -41,8 +42,8 @@ def assert_channel_was_correctly_seeded(channel, group) expect(SiteSetting.needs_chat_seeded).to eq(false) end - it 'applies a different name to the meta category channel' do - expected_name = I18n.t('chat.channel.default_titles.site_feedback') + it "applies a different name to the meta category channel" do + expected_name = I18n.t("chat.channel.default_titles.site_feedback") ChatSeeder.new.execute diff --git a/spec/fabricators/chat_fabricator.rb b/spec/fabricators/chat_fabricator.rb index 36131488d..21c909110 100644 --- a/spec/fabricators/chat_fabricator.rb +++ b/spec/fabricators/chat_fabricator.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true Fabricator(:chat_channel) do - name { ["Gaming Lounge", "Music Lodge", "Random", "Politics", "Sports Center", "Kino Buffs"].sample } + name do + ["Gaming Lounge", "Music Lodge", "Random", "Politics", "Sports Center", "Kino Buffs"].sample + end chatable { Fabricate(:category) } status { :open } end @@ -23,7 +25,7 @@ Fabricator(:chat_message_reaction) do chat_message { Fabricate(:chat_message) } user { Fabricate(:user) } - emoji { ["+1", "tada", "heart", "joffrey_facepalm"].sample } + emoji { %w[+1 tada heart joffrey_facepalm].sample } end Fabricator(:chat_upload) do @@ -39,22 +41,20 @@ Fabricator(:reviewable_chat_message) do reviewable_by_moderator true - type 'ReviewableChatMessage' + type "ReviewableChatMessage" created_by { Fabricate(:user) } - target_type 'ChatMessage' + target_type "ChatMessage" target { Fabricate(:chat_message) } - reviewable_scores { |p| [ - Fabricate.build(:reviewable_score, reviewable_id: p[:id]), - ]} + reviewable_scores { |p| [Fabricate.build(:reviewable_score, reviewable_id: p[:id])] } end -Fabricator(:direct_message_channel) do - users { [Fabricate(:user), Fabricate(:user)] } -end +Fabricator(:direct_message_channel) { users { [Fabricate(:user), Fabricate(:user)] } } Fabricator(:chat_webhook_event) do chat_message { Fabricate(:chat_message) } - incoming_chat_webhook { |attrs| Fabricate(:incoming_chat_webhook, chat_channel: attrs[:chat_message].chat_channel) } + incoming_chat_webhook do |attrs| + Fabricate(:incoming_chat_webhook, chat_channel: attrs[:chat_message].chat_channel) + end end Fabricator(:incoming_chat_webhook) do diff --git a/spec/integration/custom_api_key_scopes_spec.rb b/spec/integration/custom_api_key_scopes_spec.rb index 2d6fbd2aa..37ea37f01 100644 --- a/spec/integration/custom_api_key_scopes_spec.rb +++ b/spec/integration/custom_api_key_scopes_spec.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" -describe 'API keys scoped to chat#create_message' do +describe "API keys scoped to chat#create_message" do before do SiteSetting.chat_enabled = true SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:everyone] @@ -14,11 +14,7 @@ let(:chat_api_key) do key = ApiKey.create! - ApiKeyScope.create!( - resource: "chat", - action: "create_message", - api_key_id: key.id - ) + ApiKeyScope.create!(resource: "chat", action: "create_message", api_key_id: key.id) key end @@ -28,32 +24,36 @@ resource: "chat", action: "create_message", api_key_id: key.id, - allowed_parameters: { "chat_channel_id" => [chat_channel_2.id.to_s] } + allowed_parameters: { + "chat_channel_id" => [chat_channel_2.id.to_s], + }, ) key end - it 'cannot hit any other endpoints' do - get "/admin/users/list/active.json", headers: { - "Api-Key" => chat_api_key.key, - "Api-Username" => admin.username - } + it "cannot hit any other endpoints" do + get "/admin/users/list/active.json", + headers: { + "Api-Key" => chat_api_key.key, + "Api-Username" => admin.username, + } expect(response.status).to eq(404) - get "/latest.json", headers: { - "Api-Key" => chat_api_key.key, - "Api-Username" => admin.username - } + get "/latest.json", headers: { "Api-Key" => chat_api_key.key, "Api-Username" => admin.username } expect(response.status).to eq(403) end it "can create chat messages" do UserChatChannelMembership.create(user: admin, chat_channel: chat_channel, following: true) expect { - post "/chat/#{chat_channel.id}.json", headers: { - "Api-Key" => chat_api_key.key, - "Api-Username" => admin.username - }, params: { message: "asdfasdf asdfasdf" } + post "/chat/#{chat_channel.id}.json", + headers: { + "Api-Key" => chat_api_key.key, + "Api-Username" => admin.username, + }, + params: { + message: "asdfasdf asdfasdf", + } }.to change { ChatMessage.where(chat_channel: chat_channel).count }.by(1) expect(response.status).to eq(200) end @@ -61,10 +61,14 @@ it "cannot post in a channel it is not scoped for" do UserChatChannelMembership.create(user: admin, chat_channel: chat_channel, following: true) expect { - post "/chat/#{chat_channel.id}.json", headers: { - "Api-Key" => chat_channel_2_api_key.key, - "Api-Username" => admin.username - }, params: { message: "asdfasdf asdfasdf" } + post "/chat/#{chat_channel.id}.json", + headers: { + "Api-Key" => chat_channel_2_api_key.key, + "Api-Username" => admin.username, + }, + params: { + message: "asdfasdf asdfasdf", + } }.to change { ChatMessage.where(chat_channel: chat_channel).count }.by(0) expect(response.status).to eq(403) end @@ -72,10 +76,14 @@ it "can only post in scoped channels" do UserChatChannelMembership.create(user: admin, chat_channel: chat_channel_2, following: true) expect { - post "/chat/#{chat_channel_2.id}.json", headers: { - "Api-Key" => chat_channel_2_api_key.key, - "Api-Username" => admin.username - }, params: { message: "asdfasdf asdfasdf" } + post "/chat/#{chat_channel_2.id}.json", + headers: { + "Api-Key" => chat_channel_2_api_key.key, + "Api-Username" => admin.username, + }, + params: { + message: "asdfasdf asdfasdf", + } }.to change { ChatMessage.where(chat_channel: chat_channel_2).count }.by(1) expect(response.status).to eq(200) end diff --git a/spec/integration/plugin_api_spec.rb b/spec/integration/plugin_api_spec.rb index 4580e8b82..a26b24b54 100644 --- a/spec/integration/plugin_api_spec.rb +++ b/spec/integration/plugin_api_spec.rb @@ -1,15 +1,13 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" -describe 'Plugin API for discourse_chat' do - before do - SiteSetting.chat_enabled = true - end +describe "Plugin API for discourse_chat" do + before { SiteSetting.chat_enabled = true } let(:metadata) do metadata = Plugin::Metadata.new - metadata.name = 'test' + metadata.name = "test" metadata end @@ -19,8 +17,8 @@ plugin end - context 'discourse_chat.enable_markdown_feature' do - it 'stores the markdown feature' do + context "discourse_chat.enable_markdown_feature" do + it "stores the markdown feature" do plugin_instance.discourse_chat.enable_markdown_feature(:foo) expect(DiscoursePluginRegistry.chat_markdown_features.include?(:foo)).to be_truthy diff --git a/spec/integration/post_chat_quote_spec.rb b/spec/integration/post_chat_quote_spec.rb index 4f9d552cb..b68c070e3 100644 --- a/spec/integration/post_chat_quote_spec.rb +++ b/spec/integration/post_chat_quote_spec.rb @@ -3,12 +3,12 @@ describe "chat bbcode quoting in posts" do fab!(:post) { Fabricate(:post) } - before do - SiteSetting.chat_enabled = true - end + before { SiteSetting.chat_enabled = true } it "can render the simplest version" do - post.update!(raw: "[chat quote=\"martin;2321;2022-01-25T05:40:39Z\"]\nThis is a chat message.\n[/chat]") + post.update!( + raw: "[chat quote=\"martin;2321;2022-01-25T05:40:39Z\"]\nThis is a chat message.\n[/chat]", + ) expect(post.cooked.chomp).to eq(<<~COOKED.chomp)
@@ -27,7 +27,10 @@ end it "renders the channel name if provided with multiQuote" do - post.update!(raw: "[chat quote=\"martin;2321;2022-01-25T05:40:39Z\" channel=\"Cool Cats Club\" channelId=\"1234\" multiQuote=\"true\"]\nThis is a chat message.\n[/chat]") + post.update!( + raw: + "[chat quote=\"martin;2321;2022-01-25T05:40:39Z\" channel=\"Cool Cats Club\" channelId=\"1234\" multiQuote=\"true\"]\nThis is a chat message.\n[/chat]", + ) expect(post.cooked.chomp).to eq(<<~COOKED.chomp)
@@ -49,7 +52,10 @@ end it "renders the channel name if provided without multiQuote" do - post.update!(raw: "[chat quote=\"martin;2321;2022-01-25T05:40:39Z\" channel=\"Cool Cats Club\" channelId=\"1234\"]\nThis is a chat message.\n[/chat]") + post.update!( + raw: + "[chat quote=\"martin;2321;2022-01-25T05:40:39Z\" channel=\"Cool Cats Club\" channelId=\"1234\"]\nThis is a chat message.\n[/chat]", + ) expect(post.cooked.chomp).to eq(<<~COOKED.chomp)
@@ -70,7 +76,10 @@ end it "renders with the chained attribute for more compact quotes" do - post.update!(raw: "[chat quote=\"martin;2321;2022-01-25T05:40:39Z\" channel=\"Cool Cats Club\" channelId=\"1234\" chained=\"true\"]\nThis is a chat message.\n[/chat]") + post.update!( + raw: + "[chat quote=\"martin;2321;2022-01-25T05:40:39Z\" channel=\"Cool Cats Club\" channelId=\"1234\" chained=\"true\"]\nThis is a chat message.\n[/chat]", + ) expect(post.cooked.chomp).to eq(<<~COOKED.chomp)
@@ -91,7 +100,10 @@ end it "renders with the noLink attribute to remove the links to the individual messages from the datetimes" do - post.update!(raw: "[chat quote=\"martin;2321;2022-01-25T05:40:39Z\" channel=\"Cool Cats Club\" channelId=\"1234\" multiQuote=\"true\" noLink=\"true\"]\nThis is a chat message.\n[/chat]") + post.update!( + raw: + "[chat quote=\"martin;2321;2022-01-25T05:40:39Z\" channel=\"Cool Cats Club\" channelId=\"1234\" multiQuote=\"true\" noLink=\"true\"]\nThis is a chat message.\n[/chat]", + ) expect(post.cooked.chomp).to eq(<<~COOKED.chomp)
@@ -114,7 +126,10 @@ it "renders with the reactions attribute" do reactions_attr = "+1:martin;heart:martin,eviltrout" - post.update!(raw: "[chat quote=\"martin;2321;2022-01-25T05:40:39Z\" channel=\"Cool Cats Club\" channelId=\"1234\" reactions=\"#{reactions_attr}\"]\nThis is a chat message.\n[/chat]") + post.update!( + raw: + "[chat quote=\"martin;2321;2022-01-25T05:40:39Z\" channel=\"Cool Cats Club\" channelId=\"1234\" reactions=\"#{reactions_attr}\"]\nThis is a chat message.\n[/chat]", + ) expect(post.cooked.chomp).to eq(<<~COOKED.chomp)
@@ -154,10 +169,13 @@ HTML SiteSetting.enable_inline_onebox_on_all_domains = true - Oneboxer.stubs(:cached_onebox).with("https://en.wikipedia.org/wiki/Hyperlink").returns(full_onebox_html) + Oneboxer + .stubs(:cached_onebox) + .with("https://en.wikipedia.org/wiki/Hyperlink") + .returns(full_onebox_html) stub_request(:get, "https://en.wikipedia.org/wiki/Hyperlink").to_return( status: 200, - body: "Hyperlink - Wikipedia" + body: "Hyperlink - Wikipedia", ) post.update!(raw: <<~MD) diff --git a/spec/jobs/chat_channel_archive_spec.rb b/spec/jobs/chat_channel_archive_spec.rb index 6a9fd0913..e7d677fd2 100644 --- a/spec/jobs/chat_channel_archive_spec.rb +++ b/spec/jobs/chat_channel_archive_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe Jobs::ChatChannelArchive do fab!(:chat_channel) { Fabricate(:chat_channel) } @@ -12,15 +12,11 @@ archived_by: user, destination_topic_title: "This will be the archive topic", destination_category_id: category.id, - total_messages: 10 + total_messages: 10, ) end - before do - 10.times do - Fabricate(:chat_message, chat_channel: chat_channel) - end - end + before { 10.times { Fabricate(:chat_message, chat_channel: chat_channel) } } def run_job described_class.new.execute(chat_channel_archive_id: chat_archive.id) diff --git a/spec/jobs/chat_channel_delete_spec.rb b/spec/jobs/chat_channel_delete_spec.rb index 9ad157db9..746970524 100644 --- a/spec/jobs/chat_channel_delete_spec.rb +++ b/spec/jobs/chat_channel_delete_spec.rb @@ -14,31 +14,32 @@ end @message_ids = messages.map(&:id) - 10.times do - ChatMessageReaction.create(chat_message: messages.sample, user: users.sample) - end + 10.times { ChatMessageReaction.create(chat_message: messages.sample, user: users.sample) } 10.times do - ChatUpload.create(upload: Fabricate(:upload, user: users.sample), chat_message: messages.sample) + ChatUpload.create( + upload: Fabricate(:upload, user: users.sample), + chat_message: messages.sample, + ) end ChatMention.create( user: user2, chat_message: messages.sample, - notification: Fabricate(:notification) + notification: Fabricate(:notification), ) @incoming_chat_webhook_id = Fabricate(:incoming_chat_webhook, chat_channel: chat_channel) ChatWebhookEvent.create( incoming_chat_webhook: @incoming_chat_webhook_id, - chat_message: messages.sample + chat_message: messages.sample, ) revision_message = messages.sample ChatMessageRevision.create( chat_message: revision_message, old_message: "some old message", - new_message: revision_message.message + new_message: revision_message.message, ) ChatDraft.create(chat_channel: chat_channel, user: users.sample, data: "wow some draft") @@ -54,21 +55,23 @@ expect { described_class.new.execute(chat_channel_id: chat_channel.id) }.to change { IncomingChatWebhook.where(chat_channel_id: chat_channel.id).count }.by(-1).and change { - ChatWebhookEvent.where(incoming_chat_webhook_id: @incoming_chat_webhook_id).count - }.by(-1).and change { - ChatDraft.where(chat_channel: chat_channel).count - }.by(-1).and change { - UserChatChannelMembership.where(chat_channel: chat_channel).count - }.by(-3).and change { - ChatMessageRevision.where(chat_message_id: @message_ids).count - }.by(-1).and change { - ChatMention.where(chat_message_id: @message_ids).count - }.by(-1).and change { - ChatUpload.where(chat_message_id: @message_ids).count - }.by(-10).and change { - ChatMessage.where(id: @message_ids).count - }.by(-20).and change { - ChatMessageReaction.where(chat_message_id: @message_ids).count - }.by(-10) + ChatWebhookEvent.where(incoming_chat_webhook_id: @incoming_chat_webhook_id).count + }.by(-1).and change { ChatDraft.where(chat_channel: chat_channel).count }.by( + -1, + ).and change { + UserChatChannelMembership.where(chat_channel: chat_channel).count + }.by(-3).and change { + ChatMessageRevision.where(chat_message_id: @message_ids).count + }.by(-1).and change { + ChatMention.where(chat_message_id: @message_ids).count + }.by(-1).and change { + ChatUpload.where(chat_message_id: @message_ids).count + }.by(-10).and change { + ChatMessage.where(id: @message_ids).count + }.by(-20).and change { + ChatMessageReaction.where( + chat_message_id: @message_ids, + ).count + }.by(-10) end end diff --git a/spec/jobs/delete_old_chat_messages_spec.rb b/spec/jobs/delete_old_chat_messages_spec.rb index 788883626..4c1ac2f6e 100644 --- a/spec/jobs/delete_old_chat_messages_spec.rb +++ b/spec/jobs/delete_old_chat_messages_spec.rb @@ -1,41 +1,74 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe Jobs::DeleteOldChatMessages do - base_date = DateTime.parse('2020-12-01 00:00 UTC') + base_date = DateTime.parse("2020-12-01 00:00 UTC") fab!(:public_channel) { Fabricate(:chat_channel) } - fab!(:public_days_old_0) { + fab!(:public_days_old_0) do Fabricate(:chat_message, chat_channel: public_channel, message: "hi", created_at: base_date) - } - fab!(:public_days_old_10) { - Fabricate(:chat_message, chat_channel: public_channel, message: "hi", created_at: base_date - 10.days - 1.second) - } - fab!(:public_days_old_20) { - Fabricate(:chat_message, chat_channel: public_channel, message: "hi", created_at: base_date - 20.days - 1.second) - } - fab!(:public_days_old_30) { - Fabricate(:chat_message, chat_channel: public_channel, message: "hi", created_at: base_date - 30.days - 1.second) - } + end + fab!(:public_days_old_10) do + Fabricate( + :chat_message, + chat_channel: public_channel, + message: "hi", + created_at: base_date - 10.days - 1.second, + ) + end + fab!(:public_days_old_20) do + Fabricate( + :chat_message, + chat_channel: public_channel, + message: "hi", + created_at: base_date - 20.days - 1.second, + ) + end + fab!(:public_days_old_30) do + Fabricate( + :chat_message, + chat_channel: public_channel, + message: "hi", + created_at: base_date - 30.days - 1.second, + ) + end - fab!(:dm_channel) { Fabricate(:chat_channel, chatable: Fabricate(:direct_message_channel, users: [Fabricate(:user)])) } - fab!(:dm_days_old_0) { + fab!(:dm_channel) do + Fabricate( + :chat_channel, + chatable: Fabricate(:direct_message_channel, users: [Fabricate(:user)]), + ) + end + fab!(:dm_days_old_0) do Fabricate(:chat_message, chat_channel: dm_channel, message: "hi", created_at: base_date) - } - fab!(:dm_days_old_10) { - Fabricate(:chat_message, chat_channel: dm_channel, message: "hi", created_at: base_date - 10.days - 1.second) - } - fab!(:dm_days_old_20) { - Fabricate(:chat_message, chat_channel: dm_channel, message: "hi", created_at: base_date - 20.days - 1.second) - } - fab!(:dm_days_old_30) { - Fabricate(:chat_message, chat_channel: dm_channel, message: "hi", created_at: base_date - 30.days - 1.second) - } - - before do - freeze_time(base_date) end + fab!(:dm_days_old_10) do + Fabricate( + :chat_message, + chat_channel: dm_channel, + message: "hi", + created_at: base_date - 10.days - 1.second, + ) + end + fab!(:dm_days_old_20) do + Fabricate( + :chat_message, + chat_channel: dm_channel, + message: "hi", + created_at: base_date - 20.days - 1.second, + ) + end + fab!(:dm_days_old_30) do + Fabricate( + :chat_message, + chat_channel: dm_channel, + message: "hi", + created_at: base_date - 30.days - 1.second, + ) + end + + before { freeze_time(base_date) } it "doesn't delete messages when settings are 0" do SiteSetting.chat_channel_retention_days = 0 @@ -61,7 +94,15 @@ it "resets last_read_message_id from memberships" do SiteSetting.chat_channel_retention_days = 20 - membership = UserChatChannelMembership.create!(user: Fabricate(:user), chat_channel: public_channel, last_read_message_id: public_days_old_30.id, following: true, desktop_notification_level: 2, mobile_notification_level: 2) + membership = + UserChatChannelMembership.create!( + user: Fabricate(:user), + chat_channel: public_channel, + last_read_message_id: public_days_old_30.id, + following: true, + desktop_notification_level: 2, + mobile_notification_level: 2, + ) described_class.new.execute expect(membership.reload.last_read_message_id).to be_nil @@ -85,7 +126,15 @@ it "resets last_read_message_id from memberships" do SiteSetting.chat_dm_retention_days = 20 - membership = UserChatChannelMembership.create!(user: Fabricate(:user), chat_channel: dm_channel, last_read_message_id: dm_days_old_30.id, following: true, desktop_notification_level: 2, mobile_notification_level: 2) + membership = + UserChatChannelMembership.create!( + user: Fabricate(:user), + chat_channel: dm_channel, + last_read_message_id: dm_days_old_30.id, + following: true, + desktop_notification_level: 2, + mobile_notification_level: 2, + ) described_class.new.execute expect(membership.reload.last_read_message_id).to be_nil diff --git a/spec/jobs/process_chat_message_spec.rb b/spec/jobs/process_chat_message_spec.rb index 43a02a6ec..3b475c10d 100644 --- a/spec/jobs/process_chat_message_spec.rb +++ b/spec/jobs/process_chat_message_spec.rb @@ -1,19 +1,22 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe Jobs::ProcessChatMessage do fab!(:chat_message) { Fabricate(:chat_message, message: "https://discourse.org/team") } it "updates cooked with oneboxes" do - stub_request(:get, "https://discourse.org/team"). - to_return(status: 200, body: "a") + stub_request(:get, "https://discourse.org/team").to_return( + status: 200, + body: "a", + ) - stub_request(:head, "https://discourse.org/team"). - to_return(status: 200) + stub_request(:head, "https://discourse.org/team").to_return(status: 200) described_class.new.execute(chat_message_id: chat_message.id) - expect(chat_message.reload.cooked).to eq("

https://discourse.org/team

") + expect(chat_message.reload.cooked).to eq( + "

https://discourse.org/team

", + ) end context "is_dirty args is true" do diff --git a/spec/jobs/regular/auto_join_channel_batch_spec.rb b/spec/jobs/regular/auto_join_channel_batch_spec.rb index 060cdad5c..1bb5d208a 100644 --- a/spec/jobs/regular/auto_join_channel_batch_spec.rb +++ b/spec/jobs/regular/auto_join_channel_batch_spec.rb @@ -1,14 +1,14 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe Jobs::AutoJoinChannelBatch do - describe '#execute' do + describe "#execute" do fab!(:category) { Fabricate(:category) } let!(:user) { Fabricate(:user, last_seen_at: 15.minutes.ago) } let(:channel) { Fabricate(:chat_channel, auto_join_users: true, chatable: category) } - it 'joins all valid users in the batch' do + it "joins all valid users in the batch" do subject.execute(chat_channel_id: channel.id, starts_at: user.id, ends_at: user.id) assert_users_follows_channel(channel, [user]) @@ -47,13 +47,13 @@ assert_users_follows_channel(channel, [user]) end - it 'does nothing if the channel is invalid' do + it "does nothing if the channel is invalid" do subject.execute(chat_channel_id: -1, starts_at: user.id, ends_at: user.id) assert_user_skipped(channel, user) end - it 'does nothing if the channel chatable is not a category' do + it "does nothing if the channel chatable is not a category" do same_id = 99 another_category = Fabricate(:category, id: same_id) dm_channel = Fabricate(:direct_message_channel, id: same_id) @@ -64,7 +64,7 @@ assert_user_skipped(channel, user) end - it 'updates the channel user_count' do + it "updates the channel user_count" do initial_count = channel.user_count subject.execute(chat_channel_id: channel.id, starts_at: user.id, ends_at: user.id) @@ -72,7 +72,7 @@ expect(channel.reload.user_count).to eq(initial_count + 1) end - it 'ignores users without chat_enabled' do + it "ignores users without chat_enabled" do user.user_option.update!(chat_enabled: false) subject.execute(chat_channel_id: channel.id, starts_at: user.id, ends_at: user.id) @@ -80,14 +80,14 @@ assert_user_skipped(channel, user) end - it 'sets the join reason to automatic' do + it "sets the join reason to automatic" do subject.execute(chat_channel_id: channel.id, starts_at: user.id, ends_at: user.id) new_membership = UserChatChannelMembership.find_by(user: user, chat_channel: channel) expect(new_membership.automatic?).to eq(true) end - it 'skips anonymous users' do + it "skips anonymous users" do user_2 = Fabricate(:anonymous) subject.execute(chat_channel_id: channel.id, starts_at: user.id, ends_at: user_2.id) @@ -96,7 +96,7 @@ assert_user_skipped(channel, user_2) end - it 'skips non-active users' do + it "skips non-active users" do user_2 = Fabricate(:user, active: false, last_seen_at: 15.minutes.ago) subject.execute(chat_channel_id: channel.id, starts_at: user.id, ends_at: user_2.id) @@ -105,7 +105,7 @@ assert_user_skipped(channel, user_2) end - it 'skips staged users' do + it "skips staged users" do user_2 = Fabricate(:user, staged: true, last_seen_at: 15.minutes.ago) subject.execute(chat_channel_id: channel.id, starts_at: user.id, ends_at: user_2.id) @@ -114,7 +114,7 @@ assert_user_skipped(channel, user_2) end - it 'adds every user in the batch' do + it "adds every user in the batch" do user_2 = Fabricate(:user, last_seen_at: 15.minutes.ago) subject.execute(chat_channel_id: channel.id, starts_at: user.id, ends_at: user_2.id) @@ -122,10 +122,11 @@ assert_users_follows_channel(channel, [user, user_2]) end - it 'publishes a message only to joined users' do - messages = MessageBus.track_publish("/chat/new-channel") do - subject.execute(chat_channel_id: channel.id, starts_at: user.id, ends_at: user.id) - end + it "publishes a message only to joined users" do + messages = + MessageBus.track_publish("/chat/new-channel") do + subject.execute(chat_channel_id: channel.id, starts_at: user.id, ends_at: user.id) + end expect(messages.size).to eq(1) expect(messages.first.data.dig(:chat_channel, :id)).to eq(channel.id) @@ -158,7 +159,7 @@ assert_users_follows_channel(channel, [user]) end - it 'joins every user with access to the category' do + it "joins every user with access to the category" do another_user = Fabricate(:user, last_seen_at: 15.minutes.ago) chatters_group.add(another_user) diff --git a/spec/jobs/regular/auto_manage_channel_memberships_spec.rb b/spec/jobs/regular/auto_manage_channel_memberships_spec.rb index e231afcd4..2ff31e79d 100644 --- a/spec/jobs/regular/auto_manage_channel_memberships_spec.rb +++ b/spec/jobs/regular/auto_manage_channel_memberships_spec.rb @@ -1,14 +1,14 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe Jobs::AutoManageChannelMemberships do let(:user) { Fabricate(:user, last_seen_at: 15.minutes.ago) } let(:category) { Fabricate(:category, user: user) } let(:channel) { Fabricate(:chat_channel, auto_join_users: true, chatable: category) } - describe 'queues batches to automatically add users to a channel' do - it 'queues a batch for users with channel access' do + describe "queues batches to automatically add users to a channel" do + it "queues a batch for users with channel access" do assert_batches_enqueued(channel, 1) end @@ -16,7 +16,7 @@ assert_batches_enqueued(ChatChannel.new(id: -1), 0) end - it 'does nothing when the chatable is not a category' do + it "does nothing when the chatable is not a category" do same_id = 99 another_category = Fabricate(:category, id: same_id) dm_channel = Fabricate(:direct_message_channel, id: same_id) @@ -25,31 +25,31 @@ assert_batches_enqueued(channel, 0) end - it 'excludes users not seen in the last 3 months' do + it "excludes users not seen in the last 3 months" do user.update!(last_seen_at: 3.months.ago) assert_batches_enqueued(channel, 0) end - it 'includes users with last_seen_at set to null' do + it "includes users with last_seen_at set to null" do user.update!(last_seen_at: nil) assert_batches_enqueued(channel, 1) end - it 'excludes users without chat enabled' do + it "excludes users without chat enabled" do user.user_option.update!(chat_enabled: false) assert_batches_enqueued(channel, 0) end - it 'respects the max_chat_auto_joined_users setting' do + it "respects the max_chat_auto_joined_users setting" do SiteSetting.max_chat_auto_joined_users = 0 assert_batches_enqueued(channel, 0) end - it 'ignores users that are already channel members' do + it "ignores users that are already channel members" do UserChatChannelMembership.create!(user: user, chat_channel: channel, following: true) assert_batches_enqueued(channel, 0) @@ -61,25 +61,25 @@ assert_batches_enqueued(channel, 0) end - it 'skips non-active users' do + it "skips non-active users" do user.update!(active: false) assert_batches_enqueued(channel, 0) end - it 'skips suspended users' do + it "skips suspended users" do user.update!(suspended_till: 3.years.from_now) assert_batches_enqueued(channel, 0) end - it 'skips staged users' do + it "skips staged users" do user.update!(staged: true) assert_batches_enqueued(channel, 0) end - context 'when the category has read restricted access' do + context "when the category has read restricted access" do fab!(:chatters_group) { Fabricate(:group) } let(:private_category) { Fabricate(:private_category, group: chatters_group) } let(:channel) { Fabricate(:chat_channel, auto_join_users: true, chatable: private_category) } @@ -88,10 +88,10 @@ assert_batches_enqueued(channel, 0) end - context 'when the user has category access to a group' do + context "when the user has category access to a group" do before { chatters_group.add(user) } - it 'queues a batch' do + it "queues a batch" do assert_batches_enqueued(channel, 1) end end @@ -99,6 +99,9 @@ end def assert_batches_enqueued(channel, expected) - expect { subject.execute(chat_channel_id: channel.id) }.to change(Jobs::AutoJoinChannelBatch.jobs, :size).by(expected) + expect { subject.execute(chat_channel_id: channel.id) }.to change( + Jobs::AutoJoinChannelBatch.jobs, + :size, + ).by(expected) end end diff --git a/spec/jobs/regular/chat_notify_mentioned_spec.rb b/spec/jobs/regular/chat_notify_mentioned_spec.rb index 772153799..acdab1a82 100644 --- a/spec/jobs/regular/chat_notify_mentioned_spec.rb +++ b/spec/jobs/regular/chat_notify_mentioned_spec.rb @@ -1,75 +1,91 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe Jobs::ChatNotifyMentioned do fab!(:user_1) { Fabricate(:user) } fab!(:user_2) { Fabricate(:user) } fab!(:public_channel) { Fabricate(:chat_channel) } - let!(:personal_chat_channel) { DiscourseChat::DirectMessageChannelCreator.create!(target_users: [user_1, user_2]) } + let!(:personal_chat_channel) do + DiscourseChat::DirectMessageChannelCreator.create!(target_users: [user_1, user_2]) + end before do @chat_group = Fabricate(:group, users: [user_1, user_2]) - @personal_chat_channel = DiscourseChat::DirectMessageChannelCreator.create!(target_users: [user_1, user_2]) + @personal_chat_channel = + DiscourseChat::DirectMessageChannelCreator.create!(target_users: [user_1, user_2]) - [user_1, user_2].each { |u| Fabricate(:user_chat_channel_membership, chat_channel: public_channel, user: u) } + [user_1, user_2].each do |u| + Fabricate(:user_chat_channel_membership, chat_channel: public_channel, user: u) + end end def create_chat_message(channel: public_channel, user: user_1) Fabricate(:chat_message, chat_channel: channel, user: user, created_at: 10.minutes.ago) end - def track_desktop_notification(user: user_2, message:, to_notify_ids_map:, already_notified_user_ids: []) - MessageBus.track_publish("/chat/notification-alert/#{user.id}") do - subject.execute( - chat_message_id: message.id, - timestamp: message.created_at, - to_notify_ids_map: to_notify_ids_map, - already_notified_user_ids: already_notified_user_ids - ) - end.first + def track_desktop_notification( + user: user_2, + message:, + to_notify_ids_map:, + already_notified_user_ids: [] + ) + MessageBus + .track_publish("/chat/notification-alert/#{user.id}") do + subject.execute( + chat_message_id: message.id, + timestamp: message.created_at, + to_notify_ids_map: to_notify_ids_map, + already_notified_user_ids: already_notified_user_ids, + ) + end + .first end def track_core_notification(user: user_2, message:, to_notify_ids_map:) subject.execute( chat_message_id: message.id, timestamp: message.created_at, - to_notify_ids_map: to_notify_ids_map + to_notify_ids_map: to_notify_ids_map, ) Notification.where(user: user, notification_type: Notification.types[:chat_mention]).last end - describe 'scenarios where we should skip sending notifications' do - let(:to_notify_ids_map) do - { here_mentions: [user_2.id] } - end + describe "scenarios where we should skip sending notifications" do + let(:to_notify_ids_map) { { here_mentions: [user_2.id] } } it "does nothing if there is a newer version of the message" do message = create_chat_message - ChatMessageRevision.create!(chat_message: message, old_message: 'a', new_message: 'b') + ChatMessageRevision.create!(chat_message: message, old_message: "a", new_message: "b") PostAlerter.expects(:push_notification).never - desktop_notification = track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) + desktop_notification = + track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) expect(desktop_notification).to be_nil - created_notification = Notification.where(user: user_2, notification_type: Notification.types[:chat_mention]).last + created_notification = + Notification.where(user: user_2, notification_type: Notification.types[:chat_mention]).last expect(created_notification).to be_nil end - it 'does nothing when user is not following the channel' do + it "does nothing when user is not following the channel" do message = create_chat_message - UserChatChannelMembership.where(chat_channel: public_channel, user: user_2).update!(following: false) + UserChatChannelMembership.where(chat_channel: public_channel, user: user_2).update!( + following: false, + ) PostAlerter.expects(:push_notification).never - desktop_notification = track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) + desktop_notification = + track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) expect(desktop_notification).to be_nil - created_notification = Notification.where(user: user_2, notification_type: Notification.types[:chat_mention]).last + created_notification = + Notification.where(user: user_2, notification_type: Notification.types[:chat_mention]).last expect(created_notification).to be_nil end @@ -80,29 +96,34 @@ def track_core_notification(user: user_2, message:, to_notify_ids_map:) PostAlerter.expects(:push_notification).never - desktop_notification = track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) + desktop_notification = + track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) expect(desktop_notification).to be_nil - created_notification = Notification.where(user: user_2, notification_type: Notification.types[:chat_mention]).last + created_notification = + Notification.where(user: user_2, notification_type: Notification.types[:chat_mention]).last expect(created_notification).to be_nil end - it 'does nothing if user is included in the already_notified_user_ids' do + it "does nothing if user is included in the already_notified_user_ids" do message = create_chat_message PostAlerter.expects(:push_notification).never - desktop_notification = track_desktop_notification( - message: message, to_notify_ids_map: to_notify_ids_map, - already_notified_user_ids: [user_2.id] - ) + desktop_notification = + track_desktop_notification( + message: message, + to_notify_ids_map: to_notify_ids_map, + already_notified_user_ids: [user_2.id], + ) expect(desktop_notification).to be_nil - created_notification = Notification.where(user: user_2, notification_type: Notification.types[:chat_mention]).last + created_notification = + Notification.where(user: user_2, notification_type: Notification.types[:chat_mention]).last expect(created_notification).to be_nil end - it 'does nothing if user is not participating in a private channel' do + it "does nothing if user is not participating in a private channel" do user_3 = Fabricate(:user) @chat_group.add(user_3) to_notify_map = { direct_mentions: [user_3.id] } @@ -111,63 +132,65 @@ def track_core_notification(user: user_2, message:, to_notify_ids_map:) PostAlerter.expects(:push_notification).never - desktop_notification = track_desktop_notification( - message: message, to_notify_ids_map: to_notify_map, - ) + desktop_notification = + track_desktop_notification(message: message, to_notify_ids_map: to_notify_map) expect(desktop_notification).to be_nil - created_notification = Notification.where(user: user_3, notification_type: Notification.types[:chat_mention]).last + created_notification = + Notification.where(user: user_3, notification_type: Notification.types[:chat_mention]).last expect(created_notification).to be_nil end - it 'skips desktop notifications based on user preferences' do + it "skips desktop notifications based on user preferences" do message = create_chat_message - UserChatChannelMembership - .find_by(chat_channel: public_channel, user: user_2) - .update!(desktop_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:never]) + UserChatChannelMembership.find_by(chat_channel: public_channel, user: user_2).update!( + desktop_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:never], + ) - desktop_notification = track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) + desktop_notification = + track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) expect(desktop_notification).to be_nil end - it 'skips push notifications based on user preferences' do + it "skips push notifications based on user preferences" do message = create_chat_message - UserChatChannelMembership - .find_by(chat_channel: public_channel, user: user_2) - .update!(mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:never]) + UserChatChannelMembership.find_by(chat_channel: public_channel, user: user_2).update!( + mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:never], + ) PostAlerter.expects(:push_notification).never subject.execute( chat_message_id: message.id, timestamp: message.created_at, - to_notify_ids_map: to_notify_ids_map + to_notify_ids_map: to_notify_ids_map, ) end end - shared_examples 'creates different notifications with basic data' do + shared_examples "creates different notifications with basic data" do let(:expected_channel_title) { public_channel.title(user_2) } - it 'works for desktop notifications' do + it "works for desktop notifications" do message = create_chat_message - desktop_notification = track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) + desktop_notification = + track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) expect(desktop_notification).to be_present expect(desktop_notification.data[:notification_type]).to eq(Notification.types[:chat_mention]) expect(desktop_notification.data[:username]).to eq(user_1.username) expect(desktop_notification.data[:tag]).to eq( - DiscourseChat::ChatNotifier.push_notification_tag(:mention, public_channel.id) + DiscourseChat::ChatNotifier.push_notification_tag(:mention, public_channel.id), ) expect(desktop_notification.data[:excerpt]).to eq(message.push_notification_excerpt) expect(desktop_notification.data[:post_url]).to eq( - "/chat/channel/#{public_channel.id}/#{expected_channel_title}?messageId=#{message.id}" + "/chat/channel/#{public_channel.id}/#{expected_channel_title}?messageId=#{message.id}", ) end - it 'works for push notifications' do + it "works for push notifications" do message = create_chat_message PostAlerter.expects(:push_notification).with( @@ -177,22 +200,24 @@ def track_core_notification(user: user_2, message:, to_notify_ids_map:) username: user_1.username, tag: DiscourseChat::ChatNotifier.push_notification_tag(:mention, public_channel.id), excerpt: message.push_notification_excerpt, - post_url: "/chat/channel/#{public_channel.id}/#{expected_channel_title}?messageId=#{message.id}", - translated_title: payload_translated_title - } + post_url: + "/chat/channel/#{public_channel.id}/#{expected_channel_title}?messageId=#{message.id}", + translated_title: payload_translated_title, + }, ) subject.execute( chat_message_id: message.id, timestamp: message.created_at, - to_notify_ids_map: to_notify_ids_map + to_notify_ids_map: to_notify_ids_map, ) end - it 'works for core notifications' do + it "works for core notifications" do message = create_chat_message - created_notification = track_core_notification(message: message, to_notify_ids_map: to_notify_ids_map) + created_notification = + track_core_notification(message: message, to_notify_ids_map: to_notify_ids_map) expect(created_notification).to be_present expect(created_notification.high_priority).to eq(true) @@ -206,202 +231,217 @@ def track_core_notification(user: user_2, message:, to_notify_ids_map:) expect(data_hash[:is_direct_message_channel]).to eq(false) expect(data_hash[:chat_channel_title]).to eq(expected_channel_title) - chat_mention = ChatMention.where(notification: created_notification, user: user_2, chat_message: message) + chat_mention = + ChatMention.where(notification: created_notification, user: user_2, chat_message: message) expect(chat_mention).to be_present end end - describe '#execute' do - describe 'global mention notifications' do - let(:to_notify_ids_map) do - { global_mentions: [user_2.id] } - end + describe "#execute" do + describe "global mention notifications" do + let(:to_notify_ids_map) { { global_mentions: [user_2.id] } } let(:payload_translated_title) do - I18n.t("discourse_push_notifications.popup.chat_mention.other", + I18n.t( + "discourse_push_notifications.popup.chat_mention.other", username: user_1.username, - identifier: '@all', - channel: public_channel.title(user_2) + identifier: "@all", + channel: public_channel.title(user_2), ) end - include_examples 'creates different notifications with basic data' + include_examples "creates different notifications with basic data" - it 'includes global mention specific data to core notifications' do + it "includes global mention specific data to core notifications" do message = create_chat_message - created_notification = track_core_notification(message: message, to_notify_ids_map: to_notify_ids_map) + created_notification = + track_core_notification(message: message, to_notify_ids_map: to_notify_ids_map) data_hash = created_notification.data_hash - expect(data_hash[:identifier]).to eq('all') + expect(data_hash[:identifier]).to eq("all") end - it 'includes global mention specific data to desktop notifications' do + it "includes global mention specific data to desktop notifications" do message = create_chat_message - desktop_notification = track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) + desktop_notification = + track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) expect(desktop_notification.data[:translated_title]).to eq(payload_translated_title) end - context 'on private channels' do - it 'users a different translated title' do + context "on private channels" do + it "users a different translated title" do message = create_chat_message(channel: @personal_chat_channel) - desktop_notification = track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) + desktop_notification = + track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) - expected_title = I18n.t("discourse_push_notifications.popup.direct_message_chat_mention.other", - username: user_1.username, - identifier: '@all', - ) + expected_title = + I18n.t( + "discourse_push_notifications.popup.direct_message_chat_mention.other", + username: user_1.username, + identifier: "@all", + ) expect(desktop_notification.data[:translated_title]).to eq(expected_title) end end end - describe 'here mention notifications' do - let(:to_notify_ids_map) do - { here_mentions: [user_2.id] } - end + describe "here mention notifications" do + let(:to_notify_ids_map) { { here_mentions: [user_2.id] } } let(:payload_translated_title) do - I18n.t("discourse_push_notifications.popup.chat_mention.other", + I18n.t( + "discourse_push_notifications.popup.chat_mention.other", username: user_1.username, - identifier: '@here', - channel: public_channel.title(user_2) + identifier: "@here", + channel: public_channel.title(user_2), ) end - include_examples 'creates different notifications with basic data' + include_examples "creates different notifications with basic data" - it 'includes here mention specific data to core notifications' do + it "includes here mention specific data to core notifications" do message = create_chat_message - created_notification = track_core_notification(message: message, to_notify_ids_map: to_notify_ids_map) + created_notification = + track_core_notification(message: message, to_notify_ids_map: to_notify_ids_map) data_hash = created_notification.data_hash - expect(data_hash[:identifier]).to eq('here') + expect(data_hash[:identifier]).to eq("here") end - it 'includes here mention specific data to desktop notifications' do + it "includes here mention specific data to desktop notifications" do message = create_chat_message - desktop_notification = track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) + desktop_notification = + track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) expect(desktop_notification.data[:translated_title]).to eq(payload_translated_title) end - context 'on private channels' do - it 'users a different translated title' do + context "on private channels" do + it "users a different translated title" do message = create_chat_message(channel: @personal_chat_channel) - desktop_notification = track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) + desktop_notification = + track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) - expected_title = I18n.t("discourse_push_notifications.popup.direct_message_chat_mention.other", - username: user_1.username, - identifier: '@here', - ) + expected_title = + I18n.t( + "discourse_push_notifications.popup.direct_message_chat_mention.other", + username: user_1.username, + identifier: "@here", + ) expect(desktop_notification.data[:translated_title]).to eq(expected_title) end end end - describe 'direct mention notifications' do - let(:to_notify_ids_map) do - { direct_mentions: [user_2.id] } - end + describe "direct mention notifications" do + let(:to_notify_ids_map) { { direct_mentions: [user_2.id] } } let(:payload_translated_title) do - I18n.t("discourse_push_notifications.popup.chat_mention.direct", + I18n.t( + "discourse_push_notifications.popup.chat_mention.direct", username: user_1.username, - identifier: '', - channel: public_channel.title(user_2) + identifier: "", + channel: public_channel.title(user_2), ) end - include_examples 'creates different notifications with basic data' + include_examples "creates different notifications with basic data" - it 'includes here mention specific data to core notifications' do + it "includes here mention specific data to core notifications" do message = create_chat_message - created_notification = track_core_notification(message: message, to_notify_ids_map: to_notify_ids_map) + created_notification = + track_core_notification(message: message, to_notify_ids_map: to_notify_ids_map) data_hash = created_notification.data_hash expect(data_hash[:identifier]).to be_nil end - it 'includes here mention specific data to desktop notifications' do + it "includes here mention specific data to desktop notifications" do message = create_chat_message - desktop_notification = track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) + desktop_notification = + track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) expect(desktop_notification.data[:translated_title]).to eq(payload_translated_title) end - context 'on private channels' do - it 'users a different translated title' do + context "on private channels" do + it "users a different translated title" do message = create_chat_message(channel: @personal_chat_channel) - desktop_notification = track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) + desktop_notification = + track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) - expected_title = I18n.t("discourse_push_notifications.popup.direct_message_chat_mention.direct", - username: user_1.username, - identifier: '', - ) + expected_title = + I18n.t( + "discourse_push_notifications.popup.direct_message_chat_mention.direct", + username: user_1.username, + identifier: "", + ) expect(desktop_notification.data[:translated_title]).to eq(expected_title) end end end - describe 'group mentions' do - let(:to_notify_ids_map) do - { - @chat_group.name.to_sym => [user_2.id] - } - end + describe "group mentions" do + let(:to_notify_ids_map) { { @chat_group.name.to_sym => [user_2.id] } } let(:payload_translated_title) do - I18n.t("discourse_push_notifications.popup.chat_mention.other", + I18n.t( + "discourse_push_notifications.popup.chat_mention.other", username: user_1.username, identifier: "@#{@chat_group.name}", - channel: public_channel.title(user_2) + channel: public_channel.title(user_2), ) end - include_examples 'creates different notifications with basic data' + include_examples "creates different notifications with basic data" - it 'includes here mention specific data to core notifications' do + it "includes here mention specific data to core notifications" do message = create_chat_message - created_notification = track_core_notification(message: message, to_notify_ids_map: to_notify_ids_map) + created_notification = + track_core_notification(message: message, to_notify_ids_map: to_notify_ids_map) data_hash = created_notification.data_hash expect(data_hash[:identifier]).to be_nil expect(data_hash[:is_group_mention]).to eq(true) end - it 'includes here mention specific data to desktop notifications' do + it "includes here mention specific data to desktop notifications" do message = create_chat_message - desktop_notification = track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) + desktop_notification = + track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) expect(desktop_notification.data[:translated_title]).to eq(payload_translated_title) end - context 'on private channels' do - it 'users a different translated title' do + context "on private channels" do + it "users a different translated title" do message = create_chat_message(channel: @personal_chat_channel) - desktop_notification = track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) + desktop_notification = + track_desktop_notification(message: message, to_notify_ids_map: to_notify_ids_map) - expected_title = I18n.t("discourse_push_notifications.popup.direct_message_chat_mention.other", - username: user_1.username, - identifier: "@#{@chat_group.name}", - ) + expected_title = + I18n.t( + "discourse_push_notifications.popup.direct_message_chat_mention.other", + username: user_1.username, + identifier: "@#{@chat_group.name}", + ) expect(desktop_notification.data[:translated_title]).to eq(expected_title) end diff --git a/spec/jobs/scheduled/email_chat_notifications_spec.rb b/spec/jobs/scheduled/email_chat_notifications_spec.rb index 4ae55b27f..434be2e33 100644 --- a/spec/jobs/scheduled/email_chat_notifications_spec.rb +++ b/spec/jobs/scheduled/email_chat_notifications_spec.rb @@ -1,24 +1,20 @@ # frozen_string_literal: true describe Jobs::EmailChatNotifications do - before do - Jobs.run_immediately! - end + before { Jobs.run_immediately! } - context 'chat is enabled' do - before do - SiteSetting.chat_enabled = true - end + context "chat is enabled" do + before { SiteSetting.chat_enabled = true } - it 'starts the mailer' do + it "starts the mailer" do DiscourseChat::ChatMailer.expects(:send_unread_mentions_summary) Jobs.enqueue(:email_chat_notifications) end end - context 'chat is not enabled' do - it 'does nothing' do + context "chat is not enabled" do + it "does nothing" do DiscourseChat::ChatMailer.expects(:send_unread_mentions_summary).never Jobs.enqueue(:email_chat_notifications) diff --git a/spec/jobs/update_user_counts_for_chat_channels_spec.rb b/spec/jobs/update_user_counts_for_chat_channels_spec.rb index f581a20d5..fd53e4a80 100644 --- a/spec/jobs/update_user_counts_for_chat_channels_spec.rb +++ b/spec/jobs/update_user_counts_for_chat_channels_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe Jobs::UpdateUserCountsForChatChannels do fab!(:chat_channel_1) { Fabricate(:chat_channel, user_count: 0) } diff --git a/spec/lib/chat_channel_archive_service_spec.rb b/spec/lib/chat_channel_archive_service_spec.rb index bd9d9402d..7ad0735dd 100644 --- a/spec/lib/chat_channel_archive_service_spec.rb +++ b/spec/lib/chat_channel_archive_service_spec.rb @@ -1,35 +1,35 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe DiscourseChat::ChatChannelArchiveService do - class FakeArchiveError < StandardError; end + class FakeArchiveError < StandardError + end fab!(:channel) { Fabricate(:chat_channel) } fab!(:user) { Fabricate(:user, admin: true) } fab!(:category) { Fabricate(:category) } - let(:topic_params) do - { - topic_title: "This will be a new topic", - category_id: category.id - } - end + let(:topic_params) { { topic_title: "This will be a new topic", category_id: category.id } } subject { DiscourseChat::ChatChannelArchiveService } describe "#begin_archive_process" do - before do - 3.times do - Fabricate(:chat_message, chat_channel: channel) - end - end + before { 3.times { Fabricate(:chat_message, chat_channel: channel) } } it "marks the channel as read_only" do - subject.begin_archive_process(chat_channel: channel, acting_user: user, topic_params: topic_params) + subject.begin_archive_process( + chat_channel: channel, + acting_user: user, + topic_params: topic_params, + ) expect(channel.reload.status).to eq("read_only") end it "creates the chat channel archive record to save progress and topic params" do - subject.begin_archive_process(chat_channel: channel, acting_user: user, topic_params: topic_params) + subject.begin_archive_process( + chat_channel: channel, + acting_user: user, + topic_params: topic_params, + ) channel_archive = ChatChannelArchive.find_by(chat_channel: channel) expect(channel_archive.archived_by).to eq(user) expect(channel_archive.destination_topic_title).to eq("This will be a new topic") @@ -39,49 +39,77 @@ class FakeArchiveError < StandardError; end end it "enqueues the archive job" do - channel_archive = subject.begin_archive_process(chat_channel: channel, acting_user: user, topic_params: topic_params) - expect(job_enqueued?(job: :chat_channel_archive, args: { chat_channel_archive_id: channel_archive.id })).to eq(true) + channel_archive = + subject.begin_archive_process( + chat_channel: channel, + acting_user: user, + topic_params: topic_params, + ) + expect( + job_enqueued?( + job: :chat_channel_archive, + args: { + chat_channel_archive_id: channel_archive.id, + }, + ), + ).to eq(true) end it "does nothing if there is already an archive record for the channel" do - subject.begin_archive_process(chat_channel: channel, acting_user: user, topic_params: topic_params) + subject.begin_archive_process( + chat_channel: channel, + acting_user: user, + topic_params: topic_params, + ) expect { - subject.begin_archive_process(chat_channel: channel, acting_user: user, topic_params: topic_params) + subject.begin_archive_process( + chat_channel: channel, + acting_user: user, + topic_params: topic_params, + ) }.not_to change { ChatChannelArchive.count } end it "does not count already deleted messages toward the archive total" do new_message = Fabricate(:chat_message, chat_channel: channel) new_message.trash! - channel_archive = subject.begin_archive_process(chat_channel: channel, acting_user: user, topic_params: topic_params) + channel_archive = + subject.begin_archive_process( + chat_channel: channel, + acting_user: user, + topic_params: topic_params, + ) expect(channel_archive.total_messages).to eq(3) end end describe "#execute" do def create_messages(num) - num.times do - Fabricate(:chat_message, chat_channel: channel) - end + num.times { Fabricate(:chat_message, chat_channel: channel) } end def start_archive - @channel_archive = subject.begin_archive_process(chat_channel: channel, acting_user: user, topic_params: topic_params) + @channel_archive = + subject.begin_archive_process( + chat_channel: channel, + acting_user: user, + topic_params: topic_params, + ) end context "when archiving to a new topic" do let(:topic_params) do - { - topic_title: "This will be a new topic", - category_id: category.id, - tags: ["news", "gossip"] - } + { topic_title: "This will be a new topic", category_id: category.id, tags: %w[news gossip] } end it "makes a topic, deletes all the messages, creates posts for batches of messages, and changes the channel to archived" do create_messages(50) && start_archive reaction_message = ChatMessage.last - ChatMessageReaction.create!(chat_message: reaction_message, user: Fabricate(:user), emoji: "+1") + ChatMessageReaction.create!( + chat_message: reaction_message, + user: Fabricate(:user), + emoji: "+1", + ) stub_const(DiscourseChat::ChatChannelArchiveService, "ARCHIVED_MESSAGES_PER_POST", 5) do subject.new(@channel_archive).execute end @@ -90,19 +118,22 @@ def start_archive expect(@channel_archive.destination_topic.title).to eq("This will be a new topic") expect(@channel_archive.destination_topic.category).to eq(category) expect(@channel_archive.destination_topic.user).to eq(Discourse.system_user) - expect(@channel_archive.destination_topic.tags.map(&:name)).to match_array(["news", "gossip"]) + expect(@channel_archive.destination_topic.tags.map(&:name)).to match_array(%w[news gossip]) topic = @channel_archive.destination_topic expect(topic.posts.count).to eq(11) - topic.posts.where.not(post_number: 1).each do |post| - expect(post.raw).to include("[chat") - expect(post.raw).to include("noLink=\"true\"") - expect(post.user).to eq(Discourse.system_user) - - if post.raw.include?(";#{reaction_message.id};") - expect(post.raw).to include("reactions=") + topic + .posts + .where.not(post_number: 1) + .each do |post| + expect(post.raw).to include("[chat") + expect(post.raw).to include("noLink=\"true\"") + expect(post.user).to eq(Discourse.system_user) + + if post.raw.include?(";#{reaction_message.id};") + expect(post.raw).to include("reactions=") + end end - end expect(topic.archived).to eq(true) expect(@channel_archive.archived_messages).to eq(50) @@ -131,31 +162,44 @@ def start_archive expect(@channel_archive.reload.complete?).to eq(true) pm_topic = Topic.private_messages.last expect(pm_topic.topic_allowed_users.first.user).to eq(@channel_archive.archived_by) - expect(pm_topic.title).to eq(I18n.t("system_messages.chat_channel_archive_complete.subject_template")) + expect(pm_topic.title).to eq( + I18n.t("system_messages.chat_channel_archive_complete.subject_template"), + ) end describe "channel members" do before do create_messages(3) - channel.chat_messages.map(&:user).each do |user| - UserChatChannelMembership.create!(chat_channel: channel, user: user, following: true) - end + channel + .chat_messages + .map(&:user) + .each do |user| + UserChatChannelMembership.create!(chat_channel: channel, user: user, following: true) + end end it "unfollows (leaves) the channel for all users" do - expect(UserChatChannelMembership.where(chat_channel: channel, following: true).count).to eq(3) + expect( + UserChatChannelMembership.where(chat_channel: channel, following: true).count, + ).to eq(3) start_archive subject.new(@channel_archive).execute expect(@channel_archive.reload.complete?).to eq(true) - expect(UserChatChannelMembership.where(chat_channel: channel, following: true).count).to eq(0) + expect( + UserChatChannelMembership.where(chat_channel: channel, following: true).count, + ).to eq(0) end it "resets unread state for all users" do - UserChatChannelMembership.last.update!(last_read_message_id: channel.chat_messages.first.id) + UserChatChannelMembership.last.update!( + last_read_message_id: channel.chat_messages.first.id, + ) start_archive subject.new(@channel_archive).execute expect(@channel_archive.reload.complete?).to eq(true) - expect(UserChatChannelMembership.last.last_read_message_id).to eq(channel.chat_messages.last.id) + expect(UserChatChannelMembership.last.last_read_message_id).to eq( + channel.chat_messages.last.id, + ) end end @@ -203,7 +247,7 @@ def start_archive create_messages(3) && start_archive @channel_archive.update( destination_topic_title: nil, - destination_topic_id: Fabricate(:topic).id + destination_topic_id: Fabricate(:topic).id, ) subject.new(@channel_archive).execute topic = @channel_archive.destination_topic @@ -217,22 +261,18 @@ def start_archive context "when archiving to an existing topic" do fab!(:topic) { Fabricate(:topic) } - let(:topic_params) do - { - topic_id: topic.id - } - end + let(:topic_params) { { topic_id: topic.id } } - before do - 3.times do - Fabricate(:post, topic: topic) - end - end + before { 3.times { Fabricate(:post, topic: topic) } } it "deletes all the messages, creates posts for batches of messages, and changes the channel to archived" do create_messages(50) && start_archive reaction_message = ChatMessage.last - ChatMessageReaction.create!(chat_message: reaction_message, user: Fabricate(:user), emoji: "+1") + ChatMessageReaction.create!( + chat_message: reaction_message, + user: Fabricate(:user), + emoji: "+1", + ) stub_const(DiscourseChat::ChatChannelArchiveService, "ARCHIVED_MESSAGES_PER_POST", 5) do subject.new(@channel_archive).execute end @@ -246,15 +286,18 @@ def start_archive # existing posts + 10 archive posts expect(topic.posts.count).to eq(13) - topic.posts.where.not(post_number: [1, 2, 3]).each do |post| - expect(post.raw).to include("[chat") - expect(post.raw).to include("noLink=\"true\"") - expect(post.user).to eq(Discourse.system_user) - - if post.raw.include?(";#{reaction_message.id};") - expect(post.raw).to include("reactions=") + topic + .posts + .where.not(post_number: [1, 2, 3]) + .each do |post| + expect(post.raw).to include("[chat") + expect(post.raw).to include("noLink=\"true\"") + expect(post.user).to eq(Discourse.system_user) + + if post.raw.include?(";#{reaction_message.id};") + expect(post.raw).to include("reactions=") + end end - end expect(topic.archived).to eq(false) expect(@channel_archive.archived_messages).to eq(50) @@ -266,7 +309,10 @@ def start_archive Rails.logger = @fake_logger = FakeLogger.new create_messages(35) && start_archive - DiscourseChat::ChatChannelArchiveService.any_instance.stubs(:create_post).raises(FakeArchiveError.new("this is a test error")) + DiscourseChat::ChatChannelArchiveService + .any_instance + .stubs(:create_post) + .raises(FakeArchiveError.new("this is a test error")) stub_const(DiscourseChat::ChatChannelArchiveService, "ARCHIVED_MESSAGES_PER_POST", 5) do expect { subject.new(@channel_archive).execute }.to raise_error(FakeArchiveError) @@ -276,7 +322,9 @@ def start_archive pm_topic = Topic.private_messages.last expect(pm_topic.topic_allowed_users.first.user).to eq(@channel_archive.archived_by) - expect(pm_topic.title).to eq(I18n.t("system_messages.chat_channel_archive_failed.subject_template")) + expect(pm_topic.title).to eq( + I18n.t("system_messages.chat_channel_archive_failed.subject_template"), + ) DiscourseChat::ChatChannelArchiveService.any_instance.unstub(:create_post) stub_const(DiscourseChat::ChatChannelArchiveService, "ARCHIVED_MESSAGES_PER_POST", 5) do diff --git a/spec/lib/chat_channel_fetcher_spec.rb b/spec/lib/chat_channel_fetcher_spec.rb index f70d64972..741ef8ae2 100644 --- a/spec/lib/chat_channel_fetcher_spec.rb +++ b/spec/lib/chat_channel_fetcher_spec.rb @@ -71,12 +71,12 @@ def memberships end context "last unread message has been deleted" do - fab!(:last_unread) { Fabricate(:chat_message, chat_channel: category_channel, message: "hi", user: user2) } - - before do - last_unread.update!(deleted_at: Time.zone.now) + fab!(:last_unread) do + Fabricate(:chat_message, chat_channel: category_channel, message: "hi", user: user2) end + before { last_unread.update!(deleted_at: Time.zone.now) } + it "returns the correct count" do unread_counts = subject.unread_counts([category_channel], user1) expect(unread_counts[category_channel.id]).to eq(0) @@ -105,11 +105,17 @@ def memberships context "when the user has memberships to all the channels" do before do - UserChatChannelMembership.create!(user: user1, chat_channel: category_channel, following: true) UserChatChannelMembership.create!( - user: user1, chat_channel: direct_message_channel1, following: true, + user: user1, + chat_channel: category_channel, + following: true, + ) + UserChatChannelMembership.create!( + user: user1, + chat_channel: direct_message_channel1, + following: true, desktop_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always], - mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always] + mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always], ) end @@ -119,9 +125,9 @@ def memberships it "returns all the channels if the user is a member of the DM channel also" do DirectMessageUser.create!(user: user1, direct_message_channel: dm_channel1) - expect(subject.all_secured_channel_ids(guardian)).to match_array([ - category_channel.id, direct_message_channel1.id - ]) + expect(subject.all_secured_channel_ids(guardian)).to match_array( + [category_channel.id, direct_message_channel1.id], + ) end it "does not include the category channel if the category is a private category the user cannot see" do @@ -137,95 +143,116 @@ def memberships let(:following) { false } it "does not include DM channels" do - expect(subject.secured_public_channels( - guardian, memberships, following: following - ).map(&:id)).to match_array([category_channel.id]) + expect( + subject.secured_public_channels(guardian, memberships, following: following).map(&:id), + ).to match_array([category_channel.id]) end it "can filter by channel name, or category name" do - expect(subject.secured_public_channels( - guardian, memberships, following: following, filter: "support" - ).map(&:id)).to match_array([category_channel.id]) + expect( + subject.secured_public_channels( + guardian, + memberships, + following: following, + filter: "support", + ).map(&:id), + ).to match_array([category_channel.id]) category_channel.update!(name: "cool stuff") - expect(subject.secured_public_channels( - guardian, memberships, following: following, filter: "cool stuff" - ).map(&:id)).to match_array([category_channel.id]) + expect( + subject.secured_public_channels( + guardian, + memberships, + following: following, + filter: "cool stuff", + ).map(&:id), + ).to match_array([category_channel.id]) end it "can filter by status" do - expect(subject.secured_public_channels( - guardian, memberships, status: "closed" - ).map(&:id)).to match_array([]) + expect( + subject.secured_public_channels(guardian, memberships, status: "closed").map(&:id), + ).to match_array([]) category_channel.closed!(Discourse.system_user) - expect(subject.secured_public_channels( - guardian, memberships, status: "closed" - ).map(&:id)).to match_array([category_channel.id]) + expect( + subject.secured_public_channels(guardian, memberships, status: "closed").map(&:id), + ).to match_array([category_channel.id]) end it "ensures offset is >= 0" do - expect(subject.secured_public_channels( - guardian, memberships, offset: -235 - ).map(&:id)).to match_array([category_channel.id]) + expect( + subject.secured_public_channels(guardian, memberships, offset: -235).map(&:id), + ).to match_array([category_channel.id]) end it "ensures limit is > 0" do - expect(subject.secured_public_channels( - guardian, memberships, limit: -1, offset: 0 - ).map(&:id)).to match_array([category_channel.id]) + expect( + subject.secured_public_channels(guardian, memberships, limit: -1, offset: 0).map(&:id), + ).to match_array([category_channel.id]) end it "ensures limit has a max value" do 25.times { Fabricate(:chat_channel) } - expect(subject.secured_public_channels( - guardian, memberships, limit: 25 - ).length).to eq(DiscourseChat::ChatChannelFetcher::MAX_RESULTS) + expect(subject.secured_public_channels(guardian, memberships, limit: 25).length).to eq( + DiscourseChat::ChatChannelFetcher::MAX_RESULTS, + ) end it "does not show the user category channels they cannot access" do category_channel.update!(chatable: private_category) - expect(subject.secured_public_channels( - guardian, memberships, following: following - ).map(&:id)).to be_empty + expect( + subject.secured_public_channels(guardian, memberships, following: following).map(&:id), + ).to be_empty end context "when scoping to the user's channel memberships" do let(:following) { true } it "only returns channels where the user is a member and is following the channel" do - expect(subject.secured_public_channels( - guardian, memberships, following: following - ).map(&:id)).to be_empty + expect( + subject.secured_public_channels(guardian, memberships, following: following).map(&:id), + ).to be_empty - UserChatChannelMembership.create!(user: user1, chat_channel: category_channel, following: true) + UserChatChannelMembership.create!( + user: user1, + chat_channel: category_channel, + following: true, + ) - expect(subject.secured_public_channels( - guardian, memberships, following: following - ).map(&:id)).to match_array([category_channel.id]) + expect( + subject.secured_public_channels(guardian, memberships, following: following).map(&:id), + ).to match_array([category_channel.id]) end it "includes the unread count based on mute settings" do - membership = UserChatChannelMembership.create!(user: user1, chat_channel: category_channel, following: true) + membership = + UserChatChannelMembership.create!( + user: user1, + chat_channel: category_channel, + following: true, + ) Fabricate(:chat_message, user: user2, chat_channel: category_channel) Fabricate(:chat_message, user: user2, chat_channel: category_channel) - result_category_channel = subject.secured_public_channels( - guardian, memberships, following: following - ).find { |chan| chan.id == category_channel.id } + result_category_channel = + subject + .secured_public_channels(guardian, memberships, following: following) + .find { |chan| chan.id == category_channel.id } expect(result_category_channel.unread_count).to eq(2) membership = memberships.last membership.update!(muted: true) - result_category_channel = subject.secured_public_channels( - guardian, memberships, following: following - ).find { |chan| chan.id == category_channel.id } + result_category_channel = + subject + .secured_public_channels(guardian, memberships, following: following) + .find { |chan| chan.id == category_channel.id } expect(result_category_channel.unread_count).to eq(0) end @@ -234,10 +261,20 @@ def memberships describe "#secured_direct_message_channels" do it "includes direct message channels the user is a member of ordered by last_message_sent_at" do - Fabricate(:user_chat_channel_membership_for_dm, chat_channel: direct_message_channel1, user: user1, following: true) + Fabricate( + :user_chat_channel_membership_for_dm, + chat_channel: direct_message_channel1, + user: user1, + following: true, + ) DirectMessageUser.create!(direct_message_channel: dm_channel1, user: user1) DirectMessageUser.create!(direct_message_channel: dm_channel1, user: user2) - Fabricate(:user_chat_channel_membership_for_dm, chat_channel: direct_message_channel2, user: user1, following: true) + Fabricate( + :user_chat_channel_membership_for_dm, + chat_channel: direct_message_channel2, + user: user1, + following: true, + ) DirectMessageUser.create!(direct_message_channel: dm_channel2, user: user1) DirectMessageUser.create!(direct_message_channel: dm_channel2, user: user2) @@ -245,16 +282,21 @@ def memberships direct_message_channel2.update!(last_message_sent_at: 1.hour.ago) expect( - subject.secured_direct_message_channels(user1.id, memberships, guardian).map(&:id) + subject.secured_direct_message_channels(user1.id, memberships, guardian).map(&:id), ).to eq([direct_message_channel2.id, direct_message_channel1.id]) end it "does not include direct message channels where the user is a member but not a direct_message_user" do - Fabricate(:user_chat_channel_membership_for_dm, chat_channel: direct_message_channel1, user: user1, following: true) + Fabricate( + :user_chat_channel_membership_for_dm, + chat_channel: direct_message_channel1, + user: user1, + following: true, + ) DirectMessageUser.create!(direct_message_channel: dm_channel1, user: user2) expect( - subject.secured_direct_message_channels(user1.id, memberships, guardian).map(&:id) + subject.secured_direct_message_channels(user1.id, memberships, guardian).map(&:id), ).not_to include(direct_message_channel1.id) end end @@ -262,12 +304,16 @@ def memberships describe ".find_with_access_check" do it "raises NotFound if the channel does not exist" do category_channel.destroy! - expect { subject.find_with_access_check(category_channel.id, guardian) }.to raise_error(Discourse::NotFound) + expect { subject.find_with_access_check(category_channel.id, guardian) }.to raise_error( + Discourse::NotFound, + ) end it "raises InvalidAccess if the user cannot see the channel" do category_channel.update!(chatable: private_category) - expect { subject.find_with_access_check(category_channel.id, guardian) }.to raise_error(Discourse::InvalidAccess) + expect { subject.find_with_access_check(category_channel.id, guardian) }.to raise_error( + Discourse::InvalidAccess, + ) end it "returns the chat channel if it is found and accessible" do diff --git a/spec/lib/chat_message_bookmarkable_spec.rb b/spec/lib/chat_message_bookmarkable_spec.rb index 0f9e7572d..65e4f89b3 100644 --- a/spec/lib/chat_message_bookmarkable_spec.rb +++ b/spec/lib/chat_message_bookmarkable_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe ChatMessageBookmarkable do fab!(:user) { Fabricate(:user) } @@ -17,7 +17,9 @@ let!(:message1) { Fabricate(:chat_message, chat_channel: channel) } let!(:message2) { Fabricate(:chat_message, chat_channel: channel) } - let!(:bookmark1) { Fabricate(:bookmark, user: user, bookmarkable: message1, name: "something i gotta do") } + let!(:bookmark1) do + Fabricate(:bookmark, user: user, bookmarkable: message1, name: "something i gotta do") + end let!(:bookmark2) { Fabricate(:bookmark, user: user, bookmarkable: message2) } let!(:bookmark3) { Fabricate(:bookmark) } @@ -25,7 +27,9 @@ describe "#perform_list_query" do it "returns all the user's bookmarks" do - expect(subject.perform_list_query(user, guardian).map(&:id)).to match_array([bookmark1.id, bookmark2.id]) + expect(subject.perform_list_query(user, guardian).map(&:id)).to match_array( + [bookmark1.id, bookmark2.id], + ) end it "does not return bookmarks for messages inside category chat channels the user cannot access" do @@ -35,7 +39,9 @@ bookmark1.reload user.reload guardian = Guardian.new(user) - expect(subject.perform_list_query(user, guardian).map(&:id)).to match_array([bookmark1.id, bookmark2.id]) + expect(subject.perform_list_query(user, guardian).map(&:id)).to match_array( + [bookmark1.id, bookmark2.id], + ) end it "does not return bookmarks for messages inside direct message chat channels the user cannot access" do @@ -46,7 +52,9 @@ bookmark1.reload user.reload guardian = Guardian.new(user) - expect(subject.perform_list_query(user, guardian).map(&:id)).to match_array([bookmark1.id, bookmark2.id]) + expect(subject.perform_list_query(user, guardian).map(&:id)).to match_array( + [bookmark1.id, bookmark2.id], + ) end it "does not return bookmarks for deleted messages" do @@ -57,23 +65,39 @@ end describe "#perform_search_query" do - before do - SearchIndexer.enable - end + before { SearchIndexer.enable } it "returns bookmarks that match by name" do ts_query = Search.ts_query(term: "gotta", ts_config: "simple") - expect(subject.perform_search_query(subject.perform_list_query(user, guardian), "%gotta%", ts_query).map(&:id)).to match_array([bookmark1.id]) + expect( + subject.perform_search_query( + subject.perform_list_query(user, guardian), + "%gotta%", + ts_query, + ).map(&:id), + ).to match_array([bookmark1.id]) end it "returns bookmarks that match by chat message message content" do message2.update(message: "some good soup") ts_query = Search.ts_query(term: "good soup", ts_config: "simple") - expect(subject.perform_search_query(subject.perform_list_query(user, guardian), "%good soup%", ts_query).map(&:id)).to match_array([bookmark2.id]) + expect( + subject.perform_search_query( + subject.perform_list_query(user, guardian), + "%good soup%", + ts_query, + ).map(&:id), + ).to match_array([bookmark2.id]) ts_query = Search.ts_query(term: "blah", ts_config: "simple") - expect(subject.perform_search_query(subject.perform_list_query(user, guardian), "%blah%", ts_query).map(&:id)).to eq([]) + expect( + subject.perform_search_query( + subject.perform_list_query(user, guardian), + "%blah%", + ts_query, + ).map(&:id), + ).to eq([]) end end @@ -93,20 +117,23 @@ describe "#reminder_handler" do it "creates a notification for the user with the correct details" do - expect { subject.send_reminder_notification(bookmark1) }.to change { Notification.count }.by(1) + expect { subject.send_reminder_notification(bookmark1) }.to change { Notification.count }.by( + 1, + ) notification = user.notifications.last expect(notification.notification_type).to eq(Notification.types[:bookmark_reminder]) expect(notification.data).to eq( { - title: I18n.t( - "chat.bookmarkable.notification_title", - channel_name: bookmark1.bookmarkable.chat_channel.title(bookmark1.user) - ), + title: + I18n.t( + "chat.bookmarkable.notification_title", + channel_name: bookmark1.bookmarkable.chat_channel.title(bookmark1.user), + ), bookmarkable_url: bookmark1.bookmarkable.url, display_username: bookmark1.user.username, bookmark_name: bookmark1.name, - bookmark_id: bookmark1.id - }.to_json + bookmark_id: bookmark1.id, + }.to_json, ) end end @@ -128,19 +155,25 @@ it "raises InvalidAccess if the user cannot see the chat channel" do expect { subject.validate_before_create(guardian, bookmark1.bookmarkable) }.not_to raise_error bookmark1.bookmarkable.chat_channel.update!(chatable: private_category) - expect { subject.validate_before_create(guardian, bookmark1.bookmarkable) }.to raise_error(Discourse::InvalidAccess) + expect { subject.validate_before_create(guardian, bookmark1.bookmarkable) }.to raise_error( + Discourse::InvalidAccess, + ) private_category.groups.last.add(user) bookmark1.reload user.reload guardian = Guardian.new(user) - expect { subject.validate_before_create(guardian, bookmark1.bookmarkable) }.not_to raise_error(Discourse::InvalidAccess) + expect { + subject.validate_before_create(guardian, bookmark1.bookmarkable) + }.not_to raise_error(Discourse::InvalidAccess) end it "raises InvalidAccess if the chat message is deleted" do expect { subject.validate_before_create(guardian, bookmark1.bookmarkable) }.not_to raise_error bookmark1.bookmarkable.trash! bookmark1.reload - expect { subject.validate_before_create(guardian, bookmark1.bookmarkable) }.to raise_error(Discourse::InvalidAccess) + expect { subject.validate_before_create(guardian, bookmark1.bookmarkable) }.to raise_error( + Discourse::InvalidAccess, + ) end end diff --git a/spec/lib/chat_message_reactor_spec.rb b/spec/lib/chat_message_reactor_spec.rb index c0d7f4f17..d1dd2a456 100644 --- a/spec/lib/chat_message_reactor_spec.rb +++ b/spec/lib/chat_message_reactor_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe DiscourseChat::ChatMessageReactor do fab!(:reacting_user) { Fabricate(:user) } @@ -9,101 +9,127 @@ fab!(:message_1) { Fabricate(:chat_message, chat_channel: channel, user: reacting_user) } let(:subject) { described_class.new(reacting_user, channel) } - it 'raises an error if the user cannot see the channel' do + it "raises an error if the user cannot see the channel" do channel.update!(chatable: Fabricate(:private_category, group: Group[:staff])) - expect { subject.react!(message_id: message_1.id, react_action: :add, emoji: ':+1:') }.to raise_error(Discourse::InvalidAccess) + expect { + subject.react!(message_id: message_1.id, react_action: :add, emoji: ":+1:") + }.to raise_error(Discourse::InvalidAccess) end - it 'raises an error if the user cannot react' do + it "raises an error if the user cannot react" do SpamRule::AutoSilence.new(reacting_user).silence_user - expect { subject.react!(message_id: message_1.id, react_action: :add, emoji: ':+1:') }.to raise_error(Discourse::InvalidAccess) + expect { + subject.react!(message_id: message_1.id, react_action: :add, emoji: ":+1:") + }.to raise_error(Discourse::InvalidAccess) end - it 'raises an error if the channel status is not open' do + it "raises an error if the channel status is not open" do channel.update!(status: ChatChannel.statuses[:archived]) - expect { subject.react!(message_id: message_1.id, react_action: :add, emoji: ':+1:') }.to raise_error(Discourse::InvalidAccess) + expect { + subject.react!(message_id: message_1.id, react_action: :add, emoji: ":+1:") + }.to raise_error(Discourse::InvalidAccess) channel.update!(status: ChatChannel.statuses[:open]) - expect { subject.react!(message_id: message_1.id, react_action: :add, emoji: ':+1:') }.to change(ChatMessageReaction, :count).by(1) + expect { + subject.react!(message_id: message_1.id, react_action: :add, emoji: ":+1:") + }.to change(ChatMessageReaction, :count).by(1) end - it 'raises an error if the reaction is not valid' do - expect { reactor.react!(message_id: message_1.id, react_action: :foo, emoji: ':+1:') }.to raise_error(Discourse::InvalidParameters) + it "raises an error if the reaction is not valid" do + expect { + reactor.react!(message_id: message_1.id, react_action: :foo, emoji: ":+1:") + }.to raise_error(Discourse::InvalidParameters) end - it 'raises an error if the emoji does not exist' do - expect { reactor.react!(message_id: message_1.id, react_action: :add, emoji: ':woohoo:') }.to raise_error(Discourse::InvalidParameters) + it "raises an error if the emoji does not exist" do + expect { + reactor.react!(message_id: message_1.id, react_action: :add, emoji: ":woohoo:") + }.to raise_error(Discourse::InvalidParameters) end - it 'raises an error if the message is not found' do - expect { reactor.react!(message_id: -999, react_action: :add, emoji: ':woohoo:') }.to raise_error(Discourse::InvalidParameters) + it "raises an error if the message is not found" do + expect { + reactor.react!(message_id: -999, react_action: :add, emoji: ":woohoo:") + }.to raise_error(Discourse::InvalidParameters) end - context 'max reactions has been reached' do + context "max reactions has been reached" do before do emojis = Emoji.all.slice(0, DiscourseChat::ChatMessageReactor::MAX_REACTIONS_LIMIT) emojis.each do |emoji| - ChatMessageReaction.create!(chat_message: message_1, user: reacting_user, emoji: ":#{emoji.name}:") + ChatMessageReaction.create!( + chat_message: message_1, + user: reacting_user, + emoji: ":#{emoji.name}:", + ) end end - it 'adding a reaction raises an error' do + it "adding a reaction raises an error" do expect { - reactor.react!(message_id: message_1.id, react_action: :add, emoji: ":#{Emoji.all.last.name}:") + reactor.react!( + message_id: message_1.id, + react_action: :add, + emoji: ":#{Emoji.all.last.name}:", + ) }.to raise_error(Discourse::InvalidAccess) end - it 'removing a reaction works' do + it "removing a reaction works" do expect { - reactor.react!(message_id: message_1.id, react_action: :add, emoji: ":#{Emoji.all.first.name}:") + reactor.react!( + message_id: message_1.id, + react_action: :add, + emoji: ":#{Emoji.all.first.name}:", + ) }.to_not raise_error(Discourse::InvalidAccess) end end - it 'creates a membership when not present' do + it "creates a membership when not present" do expect { - reactor.react!(message_id: message_1.id, react_action: :add, emoji: ':heart:') + reactor.react!(message_id: message_1.id, react_action: :add, emoji: ":heart:") }.to change(UserChatChannelMembership, :count).by(1) end - it 'doesn’t create a membership when present' do + it "doesn’t create a membership when present" do UserChatChannelMembership.create!(user: reacting_user, chat_channel: channel, following: true) expect { - reactor.react!(message_id: message_1.id, react_action: :add, emoji: ':heart:') + reactor.react!(message_id: message_1.id, react_action: :add, emoji: ":heart:") }.to change(UserChatChannelMembership, :count).by(0) end - it 'can add a reaction' do + it "can add a reaction" do expect { - reactor.react!(message_id: message_1.id, react_action: :add, emoji: ':heart:') + reactor.react!(message_id: message_1.id, react_action: :add, emoji: ":heart:") }.to change(ChatMessageReaction, :count).by(1) end - it 'doesn’t duplicate reactions' do + it "doesn’t duplicate reactions" do ChatMessageReaction.create!(chat_message: message_1, user: reacting_user, emoji: ":heart:") expect { - reactor.react!(message_id: message_1.id, react_action: :add, emoji: ':heart:') + reactor.react!(message_id: message_1.id, react_action: :add, emoji: ":heart:") }.to change(ChatMessageReaction, :count).by(0) end - it 'can remove an existing reaction' do + it "can remove an existing reaction" do ChatMessageReaction.create!(chat_message: message_1, user: reacting_user, emoji: ":heart:") expect { - reactor.react!(message_id: message_1.id, react_action: :remove, emoji: ':heart:') + reactor.react!(message_id: message_1.id, react_action: :remove, emoji: ":heart:") }.to change(ChatMessageReaction, :count).by(-1) end - it 'does nothing when removing if no reaction found' do + it "does nothing when removing if no reaction found" do expect { - reactor.react!(message_id: message_1.id, react_action: :remove, emoji: ':heart:') + reactor.react!(message_id: message_1.id, react_action: :remove, emoji: ":heart:") }.to change(ChatMessageReaction, :count).by(0) end - it 'publishes the reaction' do + it "publishes the reaction" do ChatPublisher.expects(:publish_reaction!).once - reactor.react!(message_id: message_1.id, react_action: :add, emoji: ':heart:') + reactor.react!(message_id: message_1.id, react_action: :add, emoji: ":heart:") end end diff --git a/spec/lib/chat_notifier_spec.rb b/spec/lib/chat_notifier_spec.rb index b4d0a9086..a42850fba 100644 --- a/spec/lib/chat_notifier_spec.rb +++ b/spec/lib/chat_notifier_spec.rb @@ -1,37 +1,46 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe DiscourseChat::ChatNotifier do - describe '#notify_new' do + describe "#notify_new" do fab!(:channel) { Fabricate(:chat_channel) } fab!(:user_1) { Fabricate(:user) } fab!(:user_2) { Fabricate(:user) } before do - @chat_group = Fabricate(:group, users: [user_1, user_2], mentionable_level: Group::ALIAS_LEVELS[:everyone]) + @chat_group = + Fabricate( + :group, + users: [user_1, user_2], + mentionable_level: Group::ALIAS_LEVELS[:everyone], + ) SiteSetting.chat_allowed_groups = @chat_group.id - [user_1, user_2].each { |u| Fabricate(:user_chat_channel_membership, chat_channel: channel, user: u) } + [user_1, user_2].each do |u| + Fabricate(:user_chat_channel_membership, chat_channel: channel, user: u) + end end def build_cooked_msg(message_body, user, chat_channel: channel) ChatMessage.new( - chat_channel: chat_channel, user: user, - message: message_body, created_at: 5.minutes.ago + chat_channel: chat_channel, + user: user, + message: message_body, + created_at: 5.minutes.ago, ).tap(&:cook) end - shared_examples 'channel-wide mentions' do + shared_examples "channel-wide mentions" do it "returns an empty list when the message doesn't include a channel mention" do - msg = build_cooked_msg(mention.gsub('@', ''), user_1) + msg = build_cooked_msg(mention.gsub("@", ""), user_1) to_notify = described_class.new(msg, msg.created_at).notify_new expect(to_notify[list_key]).to be_empty end - it 'will never include someone who is not accepting channel-wide notifications' do + it "will never include someone who is not accepting channel-wide notifications" do user_2.user_option.update!(ignore_channel_wide_mention: true) msg = build_cooked_msg(mention, user_1) @@ -40,7 +49,7 @@ def build_cooked_msg(message_body, user, chat_channel: channel) expect(to_notify[list_key]).to be_empty end - it 'includes all members of a channel except the sender' do + it "includes all members of a channel except the sender" do msg = build_cooked_msg(mention, user_1) to_notify = described_class.new(msg, msg.created_at).notify_new @@ -49,8 +58,8 @@ def build_cooked_msg(message_body, user, chat_channel: channel) end end - shared_examples 'ensure only channel members are notified' do - it 'will never include someone outside the channel' do + shared_examples "ensure only channel members are notified" do + it "will never include someone outside the channel" do user3 = Fabricate(:user) @chat_group.add(user3) another_channel = Fabricate(:chat_channel) @@ -62,10 +71,15 @@ def build_cooked_msg(message_body, user, chat_channel: channel) expect(to_notify[list_key]).to contain_exactly(user_2.id) end - it 'will never include someone not following the channel anymore' do + it "will never include someone not following the channel anymore" do user3 = Fabricate(:user) @chat_group.add(user3) - Fabricate(:user_chat_channel_membership, following: false, chat_channel: channel, user: user3) + Fabricate( + :user_chat_channel_membership, + following: false, + chat_channel: channel, + user: user3, + ) msg = build_cooked_msg(mention, user_1) to_notify = described_class.new(msg, msg.created_at).notify_new @@ -73,10 +87,15 @@ def build_cooked_msg(message_body, user, chat_channel: channel) expect(to_notify[list_key]).to contain_exactly(user_2.id) end - it 'will never include someone who is suspended' do + it "will never include someone who is suspended" do user3 = Fabricate(:user, suspended_till: 2.years.from_now) @chat_group.add(user3) - Fabricate(:user_chat_channel_membership, following: true, chat_channel: channel, user: user3) + Fabricate( + :user_chat_channel_membership, + following: true, + chat_channel: channel, + user: user3, + ) msg = build_cooked_msg(mention, user_1) @@ -86,26 +105,24 @@ def build_cooked_msg(message_body, user, chat_channel: channel) end end - describe 'global_mentions' do - let(:mention) { 'hello @all!' } + describe "global_mentions" do + let(:mention) { "hello @all!" } let(:list_key) { :global_mentions } - include_examples 'channel-wide mentions' - include_examples 'ensure only channel members are notified' + include_examples "channel-wide mentions" + include_examples "ensure only channel members are notified" end - describe 'here_mentions' do - let(:mention) { 'hello @here!' } + describe "here_mentions" do + let(:mention) { "hello @here!" } let(:list_key) { :here_mentions } - before do - user_2.update!(last_seen_at: 4.minutes.ago) - end + before { user_2.update!(last_seen_at: 4.minutes.ago) } - include_examples 'channel-wide mentions' - include_examples 'ensure only channel members are notified' + include_examples "channel-wide mentions" + include_examples "ensure only channel members are notified" - it 'includes users seen less than 5 minutes ago' do + it "includes users seen less than 5 minutes ago" do msg = build_cooked_msg(mention, user_1) to_notify = described_class.new(msg, msg.created_at).notify_new @@ -113,7 +130,7 @@ def build_cooked_msg(message_body, user, chat_channel: channel) expect(to_notify[list_key]).to contain_exactly(user_2.id) end - it 'excludes users seen more than 5 minutes ago' do + it "excludes users seen more than 5 minutes ago" do user_2.update!(last_seen_at: 6.minutes.ago) msg = build_cooked_msg(mention, user_1) @@ -122,7 +139,7 @@ def build_cooked_msg(message_body, user, chat_channel: channel) expect(to_notify[list_key]).to be_empty end - it 'excludes users mentioned directly' do + it "excludes users mentioned directly" do msg = build_cooked_msg("hello @here @#{user_2.username}!", user_1) to_notify = described_class.new(msg, msg.created_at).notify_new @@ -131,8 +148,8 @@ def build_cooked_msg(message_body, user, chat_channel: channel) end end - describe 'direct_mentions' do - it 'only include mentioned users' do + describe "direct_mentions" do + it "only include mentioned users" do user_3 = Fabricate(:user) @chat_group.add(user_3) another_channel = Fabricate(:chat_channel) @@ -163,22 +180,31 @@ def build_cooked_msg(message_body, user, chat_channel: channel) end end - describe 'group mentions' do + describe "group mentions" do fab!(:user_3) { Fabricate(:user) } - fab!(:group) { Fabricate(:public_group, users: [user_2, user_3], mentionable_level: Group::ALIAS_LEVELS[:everyone]) } + fab!(:group) do + Fabricate( + :public_group, + users: [user_2, user_3], + mentionable_level: Group::ALIAS_LEVELS[:everyone], + ) + end fab!(:other_channel) { Fabricate(:chat_channel) } - before do - @chat_group.add(user_3) - end + before { @chat_group.add(user_3) } let(:mention) { "hello @#{group.name}!" } let(:list_key) { group.name } - include_examples 'ensure only channel members are notified' + include_examples "ensure only channel members are notified" - it 'establishes a far-left precedence among group mentions' do - Fabricate(:user_chat_channel_membership, chat_channel: channel, user: user_3, following: true) + it "establishes a far-left precedence among group mentions" do + Fabricate( + :user_chat_channel_membership, + chat_channel: channel, + user: user_3, + following: true, + ) msg = build_cooked_msg("Hello @#{@chat_group.name} and @#{group.name}", user_1) to_notify = described_class.new(msg, msg.created_at).notify_new @@ -195,17 +221,18 @@ def build_cooked_msg(message_body, user, chat_channel: channel) end end - describe 'unreachable users' do + describe "unreachable users" do fab!(:user_3) { Fabricate(:user) } - it 'notify poster of users who are not allowed to use chat' do + it "notify poster of users who are not allowed to use chat" do msg = build_cooked_msg("Hello @#{user_3.username}", user_1) - messages = MessageBus.track_publish("/chat/#{channel.id}") do - to_notify = described_class.new(msg, msg.created_at).notify_new + messages = + MessageBus.track_publish("/chat/#{channel.id}") do + to_notify = described_class.new(msg, msg.created_at).notify_new - expect(to_notify[:direct_mentions]).to be_empty - end + expect(to_notify[:direct_mentions]).to be_empty + end unreachable_msg = messages.first @@ -215,19 +242,27 @@ def build_cooked_msg(message_body, user, chat_channel: channel) expect(unreachable_users).to contain_exactly(user_3.id) end - context 'in a personal message' do - let(:personal_chat_channel) { DiscourseChat::DirectMessageChannelCreator.create!(target_users: [user_1, user_2]) } + context "in a personal message" do + let(:personal_chat_channel) do + DiscourseChat::DirectMessageChannelCreator.create!(target_users: [user_1, user_2]) + end before { @chat_group.add(user_3) } - it 'notify posts of users who are not participating in a personal message' do - msg = build_cooked_msg("Hello @#{user_3.username}", user_1, chat_channel: personal_chat_channel) + it "notify posts of users who are not participating in a personal message" do + msg = + build_cooked_msg( + "Hello @#{user_3.username}", + user_1, + chat_channel: personal_chat_channel, + ) - messages = MessageBus.track_publish("/chat/#{personal_chat_channel.id}") do - to_notify = described_class.new(msg, msg.created_at).notify_new + messages = + MessageBus.track_publish("/chat/#{personal_chat_channel.id}") do + to_notify = described_class.new(msg, msg.created_at).notify_new - expect(to_notify[:direct_mentions]).to be_empty - end + expect(to_notify[:direct_mentions]).to be_empty + end unreachable_msg = messages.first @@ -237,15 +272,22 @@ def build_cooked_msg(message_body, user, chat_channel: channel) expect(unreachable_users).to contain_exactly(user_3.id) end - it 'notify posts of users who are part of the mentioned group but participating' do - group = Fabricate(:public_group, users: [user_2, user_3], mentionable_level: Group::ALIAS_LEVELS[:everyone]) - msg = build_cooked_msg("Hello @#{group.name}", user_1, chat_channel: personal_chat_channel) + it "notify posts of users who are part of the mentioned group but participating" do + group = + Fabricate( + :public_group, + users: [user_2, user_3], + mentionable_level: Group::ALIAS_LEVELS[:everyone], + ) + msg = + build_cooked_msg("Hello @#{group.name}", user_1, chat_channel: personal_chat_channel) - messages = MessageBus.track_publish("/chat/#{personal_chat_channel.id}") do - to_notify = described_class.new(msg, msg.created_at).notify_new + messages = + MessageBus.track_publish("/chat/#{personal_chat_channel.id}") do + to_notify = described_class.new(msg, msg.created_at).notify_new - expect(to_notify[group.name]).to contain_exactly(user_2.id) - end + expect(to_notify[group.name]).to contain_exactly(user_2.id) + end unreachable_msg = messages.first @@ -257,19 +299,20 @@ def build_cooked_msg(message_body, user, chat_channel: channel) end end - describe 'users who can be invited to join the channel' do + describe "users who can be invited to join the channel" do fab!(:user_3) { Fabricate(:user) } before { @chat_group.add(user_3) } - it 'can invite chat user without channel membership' do + it "can invite chat user without channel membership" do msg = build_cooked_msg("Hello @#{user_3.username}", user_1) - messages = MessageBus.track_publish("/chat/#{channel.id}") do - to_notify = described_class.new(msg, msg.created_at).notify_new + messages = + MessageBus.track_publish("/chat/#{channel.id}") do + to_notify = described_class.new(msg, msg.created_at).notify_new - expect(to_notify[:direct_mentions]).to be_empty - end + expect(to_notify[:direct_mentions]).to be_empty + end not_participating_msg = messages.first @@ -279,15 +322,21 @@ def build_cooked_msg(message_body, user, chat_channel: channel) expect(not_participating_users).to contain_exactly(user_3.id) end - it 'can invite chat user who no longer follows the channel' do - Fabricate(:user_chat_channel_membership, chat_channel: channel, user: user_3, following: false) + it "can invite chat user who no longer follows the channel" do + Fabricate( + :user_chat_channel_membership, + chat_channel: channel, + user: user_3, + following: false, + ) msg = build_cooked_msg("Hello @#{user_3.username}", user_1) - messages = MessageBus.track_publish("/chat/#{channel.id}") do - to_notify = described_class.new(msg, msg.created_at).notify_new + messages = + MessageBus.track_publish("/chat/#{channel.id}") do + to_notify = described_class.new(msg, msg.created_at).notify_new - expect(to_notify[:direct_mentions]).to be_empty - end + expect(to_notify[:direct_mentions]).to be_empty + end not_participating_msg = messages.first @@ -297,15 +346,21 @@ def build_cooked_msg(message_body, user, chat_channel: channel) expect(not_participating_users).to contain_exactly(user_3.id) end - it 'can invite other group members to channel' do - group = Fabricate(:public_group, users: [user_2, user_3], mentionable_level: Group::ALIAS_LEVELS[:everyone]) + it "can invite other group members to channel" do + group = + Fabricate( + :public_group, + users: [user_2, user_3], + mentionable_level: Group::ALIAS_LEVELS[:everyone], + ) msg = build_cooked_msg("Hello @#{group.name}", user_1) - messages = MessageBus.track_publish("/chat/#{channel.id}") do - to_notify = described_class.new(msg, msg.created_at).notify_new + messages = + MessageBus.track_publish("/chat/#{channel.id}") do + to_notify = described_class.new(msg, msg.created_at).notify_new - expect(to_notify[:direct_mentions]).to be_empty - end + expect(to_notify[:direct_mentions]).to be_empty + end not_participating_msg = messages.first diff --git a/spec/lib/chat_statistics_spec.rb b/spec/lib/chat_statistics_spec.rb index 06762d5a6..93fb8ec5a 100644 --- a/spec/lib/chat_statistics_spec.rb +++ b/spec/lib/chat_statistics_spec.rb @@ -13,9 +13,7 @@ def minus_time(time) fab!(:user4) { Fabricate(:user) } fab!(:user5) { Fabricate(:user) } - fab!(:channel1) do - Fabricate(:chat_channel, created_at: minus_time(1.hour)) - end + fab!(:channel1) { Fabricate(:chat_channel, created_at: minus_time(1.hour)) } fab!(:channel2) { Fabricate(:chat_channel, created_at: minus_time(2.days)) } fab!(:channel3) { Fabricate(:chat_channel, created_at: minus_time(6.days)) } fab!(:channel3) { Fabricate(:chat_channel, created_at: minus_time(20.days)) } @@ -24,25 +22,58 @@ def minus_time(time) fab!(:channel6) { Fabricate(:chat_channel, created_at: minus_time(40.days)) } fab!(:channel7) { Fabricate(:chat_channel, created_at: minus_time(100.days), status: :archived) } - fab!(:membership1) { Fabricate(:user_chat_channel_membership, user: user1, chat_channel: channel1) } - fab!(:membership2) { Fabricate(:user_chat_channel_membership, user: user2, chat_channel: channel1) } - fab!(:membership3) { Fabricate(:user_chat_channel_membership, user: user3, chat_channel: channel1) } - - fab!(:message1) { Fabricate(:chat_message, chat_channel: channel1, created_at: minus_time(5.minutes), user: user1) } - fab!(:message2) { Fabricate(:chat_message, chat_channel: channel1, created_at: minus_time(2.days), user: user2) } - fab!(:message3) { Fabricate(:chat_message, chat_channel: channel1, created_at: minus_time(6.days), user: user2) } - fab!(:message4) { Fabricate(:chat_message, chat_channel: channel1, created_at: minus_time(11.days), user: user2) } - fab!(:message5) { Fabricate(:chat_message, chat_channel: channel4, created_at: minus_time(12.days), user: user3) } - fab!(:message6) { Fabricate(:chat_message, chat_channel: channel1, created_at: minus_time(13.days), user: user2) } - fab!(:message7) { Fabricate(:chat_message, chat_channel: channel1, created_at: minus_time(16.days), user: user1) } - fab!(:message8) { Fabricate(:chat_message, chat_channel: channel1, created_at: minus_time(42.days), user: user3) } - fab!(:message9) { Fabricate(:chat_message, chat_channel: channel1, created_at: minus_time(42.days), user: user3, deleted_at: minus_time(10.days), deleted_by: user3) } - fab!(:message10) { Fabricate(:chat_message, chat_channel: channel1, created_at: minus_time(50.days), user: user4) } - fab!(:message10) { Fabricate(:chat_message, chat_channel: channel1, created_at: minus_time(62.days), user: user4) } + fab!(:membership1) do + Fabricate(:user_chat_channel_membership, user: user1, chat_channel: channel1) + end + fab!(:membership2) do + Fabricate(:user_chat_channel_membership, user: user2, chat_channel: channel1) + end + fab!(:membership3) do + Fabricate(:user_chat_channel_membership, user: user3, chat_channel: channel1) + end - before do - freeze_time(DateTime.parse("2022-07-08 09:30:00")) + fab!(:message1) do + Fabricate(:chat_message, chat_channel: channel1, created_at: minus_time(5.minutes), user: user1) + end + fab!(:message2) do + Fabricate(:chat_message, chat_channel: channel1, created_at: minus_time(2.days), user: user2) + end + fab!(:message3) do + Fabricate(:chat_message, chat_channel: channel1, created_at: minus_time(6.days), user: user2) + end + fab!(:message4) do + Fabricate(:chat_message, chat_channel: channel1, created_at: minus_time(11.days), user: user2) end + fab!(:message5) do + Fabricate(:chat_message, chat_channel: channel4, created_at: minus_time(12.days), user: user3) + end + fab!(:message6) do + Fabricate(:chat_message, chat_channel: channel1, created_at: minus_time(13.days), user: user2) + end + fab!(:message7) do + Fabricate(:chat_message, chat_channel: channel1, created_at: minus_time(16.days), user: user1) + end + fab!(:message8) do + Fabricate(:chat_message, chat_channel: channel1, created_at: minus_time(42.days), user: user3) + end + fab!(:message9) do + Fabricate( + :chat_message, + chat_channel: channel1, + created_at: minus_time(42.days), + user: user3, + deleted_at: minus_time(10.days), + deleted_by: user3, + ) + end + fab!(:message10) do + Fabricate(:chat_message, chat_channel: channel1, created_at: minus_time(50.days), user: user4) + end + fab!(:message10) do + Fabricate(:chat_message, chat_channel: channel1, created_at: minus_time(62.days), user: user4) + end + + before { freeze_time(DateTime.parse("2022-07-08 09:30:00")) } describe "#about_messages" do it "counts non-deleted messages created in all status channels in the time period accurately" do diff --git a/spec/lib/chat_transcript_service_spec.rb b/spec/lib/chat_transcript_service_spec.rb index 99d7491f2..0e0f9233e 100644 --- a/spec/lib/chat_transcript_service_spec.rb +++ b/spec/lib/chat_transcript_service_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe ChatTranscriptService do let(:acting_user) { Fabricate(:user) } @@ -13,7 +13,13 @@ def service(message_ids, opts: {}) end it "generates a simple chat transcript from one message" do - message = Fabricate(:chat_message, user: user1, chat_channel: channel, message: "an extremely insightful response :)") + message = + Fabricate( + :chat_message, + user: user1, + chat_channel: channel, + message: "an extremely insightful response :)", + ) expect(service(message.id).generate_markdown).to eq(<<~MARKDOWN) [chat quote="martinchat;#{message.id};#{message.created_at.iso8601}" channel="The Beam Discussions" channelId="#{channel.id}"] @@ -23,8 +29,15 @@ def service(message_ids, opts: {}) end it "generates a single chat transcript from multiple subsequent messages from the same user" do - message1 = Fabricate(:chat_message, user: user1, chat_channel: channel, message: "an extremely insightful response :)") - message2 = Fabricate(:chat_message, user: user1, chat_channel: channel, message: "if i say so myself") + message1 = + Fabricate( + :chat_message, + user: user1, + chat_channel: channel, + message: "an extremely insightful response :)", + ) + message2 = + Fabricate(:chat_message, user: user1, chat_channel: channel, message: "if i say so myself") message3 = Fabricate(:chat_message, user: user1, chat_channel: channel, message: "yay!") rendered = service([message1.id, message2.id, message3.id]).generate_markdown @@ -40,9 +53,30 @@ def service(message_ids, opts: {}) end it "generates chat messages in created_at order no matter what order the message_ids are passed in" do - message1 = Fabricate(:chat_message, created_at: 10.minute.ago, user: user1, chat_channel: channel, message: "an extremely insightful response :)") - message2 = Fabricate(:chat_message, created_at: 5.minutes.ago, user: user1, chat_channel: channel, message: "if i say so myself") - message3 = Fabricate(:chat_message, created_at: 1.minutes.ago, user: user1, chat_channel: channel, message: "yay!") + message1 = + Fabricate( + :chat_message, + created_at: 10.minute.ago, + user: user1, + chat_channel: channel, + message: "an extremely insightful response :)", + ) + message2 = + Fabricate( + :chat_message, + created_at: 5.minutes.ago, + user: user1, + chat_channel: channel, + message: "if i say so myself", + ) + message3 = + Fabricate( + :chat_message, + created_at: 1.minutes.ago, + user: user1, + chat_channel: channel, + message: "yay!", + ) rendered = service([message3.id, message1.id, message2.id]).generate_markdown expect(rendered).to eq(<<~MARKDOWN) @@ -57,7 +91,13 @@ def service(message_ids, opts: {}) end it "generates multiple chained chat transcripts for interleaving messages from different users" do - message1 = Fabricate(:chat_message, user: user1, chat_channel: channel, message: "an extremely insightful response :)") + message1 = + Fabricate( + :chat_message, + user: user1, + chat_channel: channel, + message: "an extremely insightful response :)", + ) message2 = Fabricate(:chat_message, user: user2, chat_channel: channel, message: "says you!") message3 = Fabricate(:chat_message, user: user1, chat_channel: channel, message: "aw :(") @@ -82,7 +122,14 @@ def service(message_ids, opts: {}) video = Fabricate(:upload, original_filename: "test_video.mp4", extension: "mp4") audio = Fabricate(:upload, original_filename: "test_audio.mp3", extension: "mp3") attachment = Fabricate(:upload, original_filename: "test_file.pdf", extension: "pdf") - image = Fabricate(:upload, width: 100, height: 200, original_filename: "test_img.jpg", extension: "jpg") + image = + Fabricate( + :upload, + width: 100, + height: 200, + original_filename: "test_img.jpg", + extension: "jpg", + ) cu1 = ChatUpload.create(chat_message: message, created_at: 10.seconds.ago, upload: video) cu2 = ChatUpload.create(chat_message: message, created_at: 9.seconds.ago, upload: audio) cu3 = ChatUpload.create(chat_message: message, created_at: 8.seconds.ago, upload: attachment) @@ -104,8 +151,21 @@ def service(message_ids, opts: {}) it "generates the correct markdown if a message has text and an upload" do SiteSetting.authorized_extensions = "mp4|mp3|pdf|jpg" - message = Fabricate(:chat_message, user: user1, chat_channel: channel, message: "this is a cool and funny picture") - image = Fabricate(:upload, width: 100, height: 200, original_filename: "test_img.jpg", extension: "jpg") + message = + Fabricate( + :chat_message, + user: user1, + chat_channel: channel, + message: "this is a cool and funny picture", + ) + image = + Fabricate( + :upload, + width: 100, + height: 200, + original_filename: "test_img.jpg", + extension: "jpg", + ) cu = ChatUpload.create(chat_message: message, created_at: 7.seconds.ago, upload: image) image_markdown = UploadMarkdown.new(image).to_markdown @@ -119,7 +179,13 @@ def service(message_ids, opts: {}) end it "generates a transcript with the noLink option" do - message = Fabricate(:chat_message, user: user1, chat_channel: channel, message: "an extremely insightful response :)") + message = + Fabricate( + :chat_message, + user: user1, + chat_channel: channel, + message: "an extremely insightful response :)", + ) expect(service(message.id, opts: { no_link: true }).generate_markdown).to eq(<<~MARKDOWN) [chat quote="martinchat;#{message.id};#{message.created_at.iso8601}" channel="The Beam Discussions" channelId="#{channel.id}" noLink="true"] @@ -129,17 +195,51 @@ def service(message_ids, opts: {}) end it "generates reaction data for single and subsequent messages" do - message = Fabricate(:chat_message, user: user1, chat_channel: channel, message: "an extremely insightful response :)") + message = + Fabricate( + :chat_message, + user: user1, + chat_channel: channel, + message: "an extremely insightful response :)", + ) message2 = Fabricate(:chat_message, user: user1, chat_channel: channel, message: "wow so tru") - message3 = Fabricate(:chat_message, user: user2, chat_channel: channel, message: "a new perspective") - - ChatMessageReaction.create!(chat_message: message, user: Fabricate(:user, username: "bjorn"), emoji: "heart") - ChatMessageReaction.create!(chat_message: message, user: Fabricate(:user, username: "sigurd"), emoji: "heart") - ChatMessageReaction.create!(chat_message: message, user: Fabricate(:user, username: "hvitserk"), emoji: "+1") - ChatMessageReaction.create!(chat_message: message2, user: Fabricate(:user, username: "ubbe"), emoji: "money_mouth_face") - ChatMessageReaction.create!(chat_message: message3, user: Fabricate(:user, username: "ivar"), emoji: "sob") - - expect(service([message.id, message2.id, message3.id], opts: { include_reactions: true }).generate_markdown).to eq(<<~MARKDOWN) + message3 = + Fabricate(:chat_message, user: user2, chat_channel: channel, message: "a new perspective") + + ChatMessageReaction.create!( + chat_message: message, + user: Fabricate(:user, username: "bjorn"), + emoji: "heart", + ) + ChatMessageReaction.create!( + chat_message: message, + user: Fabricate(:user, username: "sigurd"), + emoji: "heart", + ) + ChatMessageReaction.create!( + chat_message: message, + user: Fabricate(:user, username: "hvitserk"), + emoji: "+1", + ) + ChatMessageReaction.create!( + chat_message: message2, + user: Fabricate(:user, username: "ubbe"), + emoji: "money_mouth_face", + ) + ChatMessageReaction.create!( + chat_message: message3, + user: Fabricate(:user, username: "ivar"), + emoji: "sob", + ) + + expect( + service( + [message.id, message2.id, message3.id], + opts: { + include_reactions: true, + }, + ).generate_markdown, + ).to eq(<<~MARKDOWN) [chat quote="martinchat;#{message.id};#{message.created_at.iso8601}" channel="The Beam Discussions" channelId="#{channel.id}" multiQuote="true" chained="true" reactions="+1:hvitserk;heart:bjorn,sigurd;money_mouth_face:ubbe"] an extremely insightful response :) diff --git a/spec/lib/direct_message_channel_creator_spec.rb b/spec/lib/direct_message_channel_creator_spec.rb index 316c408e1..405def28b 100644 --- a/spec/lib/direct_message_channel_creator_spec.rb +++ b/spec/lib/direct_message_channel_creator_spec.rb @@ -1,29 +1,37 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe DiscourseChat::DirectMessageChannelCreator do fab!(:user_1) { Fabricate(:user) } fab!(:user_2) { Fabricate(:user) } context "existing direct message channel" do - fab!(:dm_chat_channel) { Fabricate(:chat_channel, chatable: Fabricate(:direct_message_channel, users: [user_1, user_2])) } - fab!(:own_chat_channel) { Fabricate(:chat_channel, chatable: Fabricate(:direct_message_channel, users: [user_1])) } + fab!(:dm_chat_channel) do + Fabricate( + :chat_channel, + chatable: Fabricate(:direct_message_channel, users: [user_1, user_2]), + ) + end + fab!(:own_chat_channel) do + Fabricate(:chat_channel, chatable: Fabricate(:direct_message_channel, users: [user_1])) + end it "doesn't create a new chat channel" do existing_channel = nil - expect { - existing_channel = subject.create!(target_users: [user_1, user_2]) - }.to change { ChatChannel.count }.by(0) + expect { existing_channel = subject.create!(target_users: [user_1, user_2]) }.to change { + ChatChannel.count + }.by(0) expect(existing_channel).to eq(dm_chat_channel) end it "creates UserChatChannelMembership records and sets their notification levels" do - expect { - subject.create!(target_users: [user_1, user_2]) - }.to change { UserChatChannelMembership.count }.by(2) + expect { subject.create!(target_users: [user_1, user_2]) }.to change { + UserChatChannelMembership.count + }.by(2) - user_1_membership = UserChatChannelMembership.find_by(user_id: user_1.id, chat_channel_id: dm_chat_channel) + user_1_membership = + UserChatChannelMembership.find_by(user_id: user_1.id, chat_channel_id: dm_chat_channel) expect(user_1_membership.last_read_message_id).to eq(nil) expect(user_1_membership.desktop_notification_level).to eq("always") expect(user_1_membership.mobile_notification_level).to eq("always") @@ -32,45 +40,49 @@ end it "publishes the new DM channel message bus message for each user" do - messages = MessageBus.track_publish do - subject.create!(target_users: [user_1, user_2]) - end.filter { |m| m.channel == "/chat/new-channel" } + messages = + MessageBus + .track_publish { subject.create!(target_users: [user_1, user_2]) } + .filter { |m| m.channel == "/chat/new-channel" } expect(messages.count).to eq(2) expect(messages.first[:data]).to be_kind_of(Hash) - expect(messages.map { |m| m.dig(:data, :chat_channel, :id) }).to eq([dm_chat_channel.id, dm_chat_channel.id]) + expect(messages.map { |m| m.dig(:data, :chat_channel, :id) }).to eq( + [dm_chat_channel.id, dm_chat_channel.id], + ) end it "allows a user to create a direct message to themselves, without creating a new channel" do existing_channel = nil - expect { - existing_channel = subject.create!(target_users: [user_1]) - }.to change { ChatChannel.count }.by(0).and change { UserChatChannelMembership.count }.by(1) + expect { existing_channel = subject.create!(target_users: [user_1]) }.to change { + ChatChannel.count + }.by(0).and change { UserChatChannelMembership.count }.by(1) expect(existing_channel).to eq(own_chat_channel) end it "deduplicates target_users" do existing_channel = nil - expect { - existing_channel = subject.create!(target_users: [user_1, user_1]) - }.to change { ChatChannel.count }.by(0).and change { UserChatChannelMembership.count }.by(1) + expect { existing_channel = subject.create!(target_users: [user_1, user_1]) }.to change { + ChatChannel.count + }.by(0).and change { UserChatChannelMembership.count }.by(1) expect(existing_channel).to eq(own_chat_channel) end end context "non existing direct message channel" do it "creates a new chat channel" do - expect { - subject.create!(target_users: [user_1, user_2]) - }.to change { ChatChannel.count }.by(1) + expect { subject.create!(target_users: [user_1, user_2]) }.to change { ChatChannel.count }.by( + 1, + ) end it "creates UserChatChannelMembership records and sets their notification levels" do - expect { - subject.create!(target_users: [user_1, user_2]) - }.to change { UserChatChannelMembership.count }.by(2) + expect { subject.create!(target_users: [user_1, user_2]) }.to change { + UserChatChannelMembership.count + }.by(2) chat_channel = ChatChannel.last - user_1_membership = UserChatChannelMembership.find_by(user_id: user_1.id, chat_channel_id: chat_channel) + user_1_membership = + UserChatChannelMembership.find_by(user_id: user_1.id, chat_channel_id: chat_channel) expect(user_1_membership.last_read_message_id).to eq(nil) expect(user_1_membership.desktop_notification_level).to eq("always") expect(user_1_membership.mobile_notification_level).to eq("always") @@ -79,26 +91,29 @@ end it "publishes the new DM channel message bus message for each user" do - messages = MessageBus.track_publish do - subject.create!(target_users: [user_1, user_2]) - end.filter { |m| m.channel == "/chat/new-channel" } + messages = + MessageBus + .track_publish { subject.create!(target_users: [user_1, user_2]) } + .filter { |m| m.channel == "/chat/new-channel" } chat_channel = ChatChannel.last expect(messages.count).to eq(2) expect(messages.first[:data]).to be_kind_of(Hash) - expect(messages.map { |m| m.dig(:data, :chat_channel, :id) }).to eq([chat_channel.id, chat_channel.id]) + expect(messages.map { |m| m.dig(:data, :chat_channel, :id) }).to eq( + [chat_channel.id, chat_channel.id], + ) end it "allows a user to create a direct message to themselves" do - expect { - subject.create!(target_users: [user_1]) - }.to change { ChatChannel.count }.by(1).and change { UserChatChannelMembership.count }.by(1) + expect { subject.create!(target_users: [user_1]) }.to change { ChatChannel.count }.by( + 1, + ).and change { UserChatChannelMembership.count }.by(1) end it "deduplicates target_users" do - expect { - subject.create!(target_users: [user_1, user_1]) - }.to change { ChatChannel.count }.by(1).and change { UserChatChannelMembership.count }.by(1) + expect { subject.create!(target_users: [user_1, user_1]) }.to change { ChatChannel.count }.by( + 1, + ).and change { UserChatChannelMembership.count }.by(1) end end end diff --git a/spec/lib/duplicate_message_validator_spec.rb b/spec/lib/duplicate_message_validator_spec.rb index 390281148..521c155a5 100644 --- a/spec/lib/duplicate_message_validator_spec.rb +++ b/spec/lib/duplicate_message_validator_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe DiscourseChat::DuplicateMessageValidator do let(:chat_channel) { Fabricate(:chat_channel) } @@ -21,7 +21,13 @@ def message_blocked?(message) chat_channel.update!(user_count: 100) message = "this is a 30 char message for test" - dupe = Fabricate(:chat_message, created_at: 1.second.ago, message: message, chat_channel: chat_channel) + dupe = + Fabricate( + :chat_message, + created_at: 1.second.ago, + message: message, + chat_channel: chat_channel, + ) expect(message_blocked?(message)).to eq(true) expect(message_blocked?("blah")).to eq(false) @@ -34,7 +40,13 @@ def message_blocked?(message) SiteSetting.chat_duplicate_message_sensitivity = 0.5 chat_channel.update!(user_count: 57) message = "this is a 21 char msg" - dupe = Fabricate(:chat_message, created_at: 1.second.ago, message: message, chat_channel: chat_channel) + dupe = + Fabricate( + :chat_message, + created_at: 1.second.ago, + message: message, + chat_channel: chat_channel, + ) expect(message_blocked?(message)).to eq(true) expect(message_blocked?("blah")).to eq(false) @@ -47,7 +59,13 @@ def message_blocked?(message) SiteSetting.chat_duplicate_message_sensitivity = 1.0 chat_channel.update!(user_count: 5) message = "10 char msg" - dupe = Fabricate(:chat_message, created_at: 1.second.ago, message: message, chat_channel: chat_channel) + dupe = + Fabricate( + :chat_message, + created_at: 1.second.ago, + message: message, + chat_channel: chat_channel, + ) expect(message_blocked?(message)).to eq(true) expect(message_blocked?("blah")).to eq(false) diff --git a/spec/lib/guardian_extensions_spec.rb b/spec/lib/guardian_extensions_spec.rb index ffcf19f0b..ced9dfaee 100644 --- a/spec/lib/guardian_extensions_spec.rb +++ b/spec/lib/guardian_extensions_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe DiscourseChat::GuardianExtensions do fab!(:user) { Fabricate(:user) } @@ -9,9 +9,7 @@ fab!(:staff_guardian) { Guardian.new(staff) } fab!(:chat_group) { Fabricate(:group) } - before do - SiteSetting.chat_allowed_groups = chat_group.id - end + before { SiteSetting.chat_allowed_groups = chat_group.id } it "cannot chat if the user is not in the DiscourseChat.allowed_group_ids" do SiteSetting.chat_allowed_groups = "" @@ -73,9 +71,7 @@ context "for category channel" do fab!(:category) { Fabricate(:category, read_restricted: true) } - before do - channel.update(chatable: category) - end + before { channel.update(chatable: category) } it "returns true if the user can see the category" do expect(Guardian.new(user).can_see_chat_channel?(channel)).to eq(false) @@ -102,9 +98,7 @@ context "for category channel" do fab!(:category) { Fabricate(:category, read_restricted: true) } - before do - channel.update(chatable: category) - end + before { channel.update(chatable: category) } it "returns true for staff and false for regular users" do expect(staff_guardian.can_moderate_chat?(channel.chatable)).to eq(true) @@ -127,9 +121,7 @@ context "for DM channel" do fab!(:dm_channel) { DirectMessageChannel.create! } - before do - channel.update(chatable_type: "DirectMessageType", chatable: dm_channel) - end + before { channel.update(chatable_type: "DirectMessageType", chatable: dm_channel) } it "returns true for staff and false for regular users" do expect(staff_guardian.can_moderate_chat?(channel.chatable)).to eq(true) diff --git a/spec/lib/message_mover_spec.rb b/spec/lib/message_mover_spec.rb index c8536ad94..c1c6abf2d 100644 --- a/spec/lib/message_mover_spec.rb +++ b/spec/lib/message_mover_spec.rb @@ -1,15 +1,36 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe DiscourseChat::MessageMover do fab!(:acting_user) { Fabricate(:admin, username: "testmovechat") } fab!(:source_channel) { Fabricate(:chat_channel) } fab!(:destination_channel) { Fabricate(:chat_channel) } - let!(:message1) { Fabricate(:chat_message, chat_channel: source_channel, created_at: 3.minutes.ago, message: "the first to be moved") } - let!(:message2) { Fabricate(:chat_message, chat_channel: source_channel, created_at: 2.minutes.ago, message: "message deux @testmovechat") } - let!(:message3) { Fabricate(:chat_message, chat_channel: source_channel, created_at: 1.minute.ago, message: "the third message") } + let!(:message1) do + Fabricate( + :chat_message, + chat_channel: source_channel, + created_at: 3.minutes.ago, + message: "the first to be moved", + ) + end + let!(:message2) do + Fabricate( + :chat_message, + chat_channel: source_channel, + created_at: 2.minutes.ago, + message: "message deux @testmovechat", + ) + end + let!(:message3) do + Fabricate( + :chat_message, + chat_channel: source_channel, + created_at: 1.minute.ago, + message: "the third message", + ) + end let!(:message4) { Fabricate(:chat_message, chat_channel: destination_channel) } let!(:message5) { Fabricate(:chat_message, chat_channel: destination_channel) } let!(:message6) { Fabricate(:chat_message, chat_channel: destination_channel) } @@ -19,7 +40,7 @@ described_class.new( acting_user: acting_user, source_channel: source_channel, - message_ids: move_message_ids + message_ids: move_message_ids, ) end @@ -33,14 +54,14 @@ def move! described_class.new( acting_user: acting_user, source_channel: Fabricate(:chat_channel, chatable: Fabricate(:direct_message_channel)), - message_ids: move_message_ids + message_ids: move_message_ids, ).move_to_channel(destination_channel) }.to raise_error(DiscourseChat::MessageMover::InvalidChannel) expect { described_class.new( acting_user: acting_user, source_channel: source_channel, - message_ids: move_message_ids + message_ids: move_message_ids, ).move_to_channel(Fabricate(:chat_channel, chatable: Fabricate(:direct_message_channel))) }.to raise_error(DiscourseChat::MessageMover::InvalidChannel) end @@ -54,9 +75,7 @@ def move! end it "deletes the messages from the source channel and sends messagebus delete messages" do - messages = MessageBus.track_publish do - move! - end + messages = MessageBus.track_publish { move! } expect(ChatMessage.where(id: move_message_ids)).to eq([]) deleted_messages = ChatMessage.with_deleted.where(id: move_message_ids).order(:id) expect(deleted_messages.count).to eq(3) @@ -69,26 +88,26 @@ def move! it "creates a message in the source channel to indicate that the messages have been moved" do move! placeholder_message = ChatMessage.where(chat_channel: source_channel).order(:created_at).last - destination_first_moved_message = ChatMessage.find_by(chat_channel: destination_channel, message: "the first to be moved") + destination_first_moved_message = + ChatMessage.find_by(chat_channel: destination_channel, message: "the first to be moved") expect(placeholder_message.message).to eq( I18n.t( "chat.channel.messages_moved", count: move_message_ids.length, acting_username: acting_user.username, channel_name: destination_channel.title(acting_user), - first_moved_message_url: destination_first_moved_message.url - ) + first_moved_message_url: destination_first_moved_message.url, + ), ) end it "preserves the order of the messages in the destination channel" do move! - moved_messages = ChatMessage.where(chat_channel: destination_channel).order("created_at ASC, id ASC").last(3) - expect(moved_messages.map(&:message)).to eq([ - "the first to be moved", - "message deux @testmovechat", - "the third message" - ]) + moved_messages = + ChatMessage.where(chat_channel: destination_channel).order("created_at ASC, id ASC").last(3) + expect(moved_messages.map(&:message)).to eq( + ["the first to be moved", "message deux @testmovechat", "the third message"], + ) end it "updates references for reactions, uploads, revisions, mentions, etc." do @@ -99,7 +118,8 @@ def move! webhook_event = Fabricate(:chat_webhook_event, chat_message: message3) move! - moved_messages = ChatMessage.where(chat_channel: destination_channel).order("created_at ASC, id ASC").last(3) + moved_messages = + ChatMessage.where(chat_channel: destination_channel).order("created_at ASC, id ASC").last(3) expect(reaction.reload.chat_message_id).to eq(moved_messages.first.id) expect(upload.reload.chat_message_id).to eq(moved_messages.first.id) expect(mention.reload.chat_message_id).to eq(moved_messages.second.id) diff --git a/spec/lib/post_notification_handler_spec.rb b/spec/lib/post_notification_handler_spec.rb index fad3046ca..eaf606856 100644 --- a/spec/lib/post_notification_handler_spec.rb +++ b/spec/lib/post_notification_handler_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe DiscourseChat::PostNotificationHandler do let(:acting_user) { Fabricate(:user) } @@ -10,23 +10,17 @@ fab!(:channel) { Fabricate(:chat_channel) } fab!(:message1) do - Fabricate( - :chat_message, - chat_channel: channel, - message: "hey this is the first message :)" - ) + Fabricate(:chat_message, chat_channel: channel, message: "hey this is the first message :)") end fab!(:message2) do Fabricate( :chat_message, chat_channel: channel, - message: "our true enemy. has yet. to reveal himself." + message: "our true enemy. has yet. to reveal himself.", ) end - before do - Notification.destroy_all - end + before { Notification.destroy_all } def expect_no_notification return_val = nil @@ -35,7 +29,8 @@ def expect_no_notification end def update_post_with_chat_quote(messages) - quote_markdown = ChatTranscriptService.new(channel, acting_user, messages_or_ids: messages).generate_markdown + quote_markdown = + ChatTranscriptService.new(channel, acting_user, messages_or_ids: messages).generate_markdown post.update!(raw: post.raw + "\n\n" + quote_markdown) end @@ -57,15 +52,30 @@ def update_post_with_chat_quote(messages) it "sends notifications to all of the quoted users" do update_post_with_chat_quote([message1, message2]) subject.handle - expect(Notification.where(user: message1.user, notification_type: Notification.types[:chat_quoted]).count).to eq(1) - expect(Notification.where(user: message2.user, notification_type: Notification.types[:chat_quoted]).count).to eq(1) + expect( + Notification.where( + user: message1.user, + notification_type: Notification.types[:chat_quoted], + ).count, + ).to eq(1) + expect( + Notification.where( + user: message2.user, + notification_type: Notification.types[:chat_quoted], + ).count, + ).to eq(1) end it "does not send the same chat_quoted notification twice to the same post and user" do update_post_with_chat_quote([message1, message2]) subject.handle subject.handle - expect(Notification.where(user: message1.user, notification_type: Notification.types[:chat_quoted]).count).to eq(1) + expect( + Notification.where( + user: message1.user, + notification_type: Notification.types[:chat_quoted], + ).count, + ).to eq(1) end it "does not send a notification if the user has got a reply notification to the quoted user for the same post" do @@ -75,10 +85,15 @@ def update_post_with_chat_quote(messages) notification_type: Notification.types[:replied], post_number: post.post_number, topic: post.topic, - user: message1.user + user: message1.user, ) subject.handle - expect(Notification.where(user: message1.user, notification_type: Notification.types[:chat_quoted]).count).to eq(0) + expect( + Notification.where( + user: message1.user, + notification_type: Notification.types[:chat_quoted], + ).count, + ).to eq(0) end context "when some users have already been notified for the post" do @@ -87,8 +102,18 @@ def update_post_with_chat_quote(messages) it "does not send notifications to those users" do update_post_with_chat_quote([message1, message2]) subject.handle - expect(Notification.where(user: message1.user, notification_type: Notification.types[:chat_quoted]).count).to eq(0) - expect(Notification.where(user: message2.user, notification_type: Notification.types[:chat_quoted]).count).to eq(1) + expect( + Notification.where( + user: message1.user, + notification_type: Notification.types[:chat_quoted], + ).count, + ).to eq(0) + expect( + Notification.where( + user: message2.user, + notification_type: Notification.types[:chat_quoted], + ).count, + ).to eq(1) end end end diff --git a/spec/lib/slack_compatibility_spec.rb b/spec/lib/slack_compatibility_spec.rb index c87709aa1..b84db2e18 100644 --- a/spec/lib/slack_compatibility_spec.rb +++ b/spec/lib/slack_compatibility_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe DiscourseChat::SlackCompatibility do describe "#process_text" do @@ -10,13 +10,19 @@ end it "converts mrkdwn links with titles to regular markdown" do - text = described_class.process_text("this is some text ") + text = + described_class.process_text("this is some text ") expect(text).to eq("this is some text [Discourse Forums](https://discourse.org)") end it "handles multiple links" do - text = described_class.process_text("this is some text with a second link to ") - expect(text).to eq("this is some text [Discourse Forums](https://discourse.org) with a second link to https://discourse.org/team") + text = + described_class.process_text( + "this is some text with a second link to ", + ) + expect(text).to eq( + "this is some text [Discourse Forums](https://discourse.org) with a second link to https://discourse.org/team", + ) end it "converts and to our mention format" do diff --git a/spec/mailers/user_notifications_spec.rb b/spec/mailers/user_notifications_spec.rb index 6593edb5b..6c84ea018 100644 --- a/spec/mailers/user_notifications_spec.rb +++ b/spec/mailers/user_notifications_spec.rb @@ -1,19 +1,26 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe UserNotifications do - describe '.chat_summary' do + describe ".chat_summary" do before do @chatters_group = Fabricate(:group) - @sender = Fabricate(:user, name: 'A name', username: 'username', group_ids: [@chatters_group.id]) + @sender = + Fabricate(:user, name: "A name", username: "username", group_ids: [@chatters_group.id]) @user = Fabricate(:user, group_ids: [@chatters_group.id]) @chat_channel = Fabricate(:chat_channel) @chat_message = Fabricate(:chat_message, user: @sender, chat_channel: @chat_channel) Fabricate(:user_chat_channel_membership, chat_channel: @chat_channel, user: @sender) - @user_membership = Fabricate(:user_chat_channel_membership, chat_channel: @chat_channel, user: @user, last_read_message_id: @chat_message.id - 2) + @user_membership = + Fabricate( + :user_chat_channel_membership, + chat_channel: @chat_channel, + user: @user, + last_read_message_id: @chat_message.id - 2, + ) SiteSetting.chat_enabled = true SiteSetting.chat_allowed_groups = @chatters_group.id @@ -25,20 +32,22 @@ expect(email.to).to be_blank end - describe 'email subject' do - context 'DM messages' do + describe "email subject" do + context "DM messages" do before do - @dm_channel = DiscourseChat::DirectMessageChannelCreator.create!(target_users: [@sender, @user]) + @dm_channel = + DiscourseChat::DirectMessageChannelCreator.create!(target_users: [@sender, @user]) Fabricate(:chat_message, user: @sender, chat_channel: @dm_channel) end - it 'includes the sender username in the subject' do - expected_subject = I18n.t( - 'user_notifications.chat_summary.subject.direct_message', - count: 1, - email_prefix: SiteSetting.title, - message_title: @sender.username - ) + it "includes the sender username in the subject" do + expected_subject = + I18n.t( + "user_notifications.chat_summary.subject.direct_message", + count: 1, + email_prefix: SiteSetting.title, + message_title: @sender.username, + ) email = described_class.chat_summary(@user, {}) @@ -46,16 +55,24 @@ expect(email.subject).to include(@sender.username) end - it 'only includes the name of the user who sent the message even if the DM has multiple participants' do + it "only includes the name of the user who sent the message even if the DM has multiple participants" do another_participant = Fabricate(:user, group_ids: [@chatters_group.id]) - Fabricate(:user_chat_channel_membership_for_dm, user: another_participant, chat_channel: @dm_channel) - DirectMessageUser.create!(direct_message_channel: @dm_channel.chatable, user: another_participant) - expected_subject = I18n.t( - 'user_notifications.chat_summary.subject.direct_message', - count: 1, - email_prefix: SiteSetting.title, - message_title: @sender.username + Fabricate( + :user_chat_channel_membership_for_dm, + user: another_participant, + chat_channel: @dm_channel, + ) + DirectMessageUser.create!( + direct_message_channel: @dm_channel.chatable, + user: another_participant, ) + expected_subject = + I18n.t( + "user_notifications.chat_summary.subject.direct_message", + count: 1, + email_prefix: SiteSetting.title, + message_title: @sender.username, + ) email = described_class.chat_summary(@user, {}) @@ -64,9 +81,12 @@ expect(email.subject).not_to include(another_participant.username) end - it 'includes both channel titles when there are exactly two with unread messages' do + it "includes both channel titles when there are exactly two with unread messages" do another_dm_user = Fabricate(:user, group_ids: [@chatters_group.id]) - dm_channel_2 = DiscourseChat::DirectMessageChannelCreator.create!(target_users: [another_dm_user, @user]) + dm_channel_2 = + DiscourseChat::DirectMessageChannelCreator.create!( + target_users: [another_dm_user, @user], + ) chat_message = Fabricate(:chat_message, user: another_dm_user, chat_channel: dm_channel_2) email = described_class.chat_summary(@user, {}) @@ -75,13 +95,16 @@ expect(email.subject).to include(another_dm_user.username) end - it 'displays a count when there are more than two DMs with unread messages' do + it "displays a count when there are more than two DMs with unread messages" do 2.times do another_dm_user = Fabricate(:user, group_ids: [@chatters_group.id]) - dm_channel = DiscourseChat::DirectMessageChannelCreator.create!(target_users: [another_dm_user, @user]) + dm_channel = + DiscourseChat::DirectMessageChannelCreator.create!( + target_users: [another_dm_user, @user], + ) chat_message = Fabricate(:chat_message, user: another_dm_user, chat_channel: dm_channel) end - expected_count_text = I18n.t('user_notifications.chat_summary.subject.others', count: 2) + expected_count_text = I18n.t("user_notifications.chat_summary.subject.others", count: 2) email = described_class.chat_summary(@user, {}) @@ -89,16 +112,17 @@ end end - context 'regular mentions' do + context "regular mentions" do before { Fabricate(:chat_mention, user: @user, chat_message: @chat_message) } - it 'includes the sender username in the subject' do - expected_subject = I18n.t( - 'user_notifications.chat_summary.subject.chat_channel', - count: 1, - email_prefix: SiteSetting.title, - message_title: @chat_channel.title(@user) - ) + it "includes the sender username in the subject" do + expected_subject = + I18n.t( + "user_notifications.chat_summary.subject.chat_channel", + count: 1, + email_prefix: SiteSetting.title, + message_title: @chat_channel.title(@user), + ) email = described_class.chat_summary(@user, {}) @@ -106,11 +130,21 @@ expect(email.subject).to include(@chat_channel.title(@user)) end - it 'includes both channel titles when there are exactly two with unread mentions' do - another_chat_channel = Fabricate(:chat_channel, name: 'Test channel') - another_chat_message = Fabricate(:chat_message, user: @sender, chat_channel: another_chat_channel) - Fabricate(:user_chat_channel_membership, chat_channel: another_chat_channel, user: @sender) - Fabricate(:user_chat_channel_membership, chat_channel: another_chat_channel, user: @user, last_read_message_id: another_chat_message.id - 2) + it "includes both channel titles when there are exactly two with unread mentions" do + another_chat_channel = Fabricate(:chat_channel, name: "Test channel") + another_chat_message = + Fabricate(:chat_message, user: @sender, chat_channel: another_chat_channel) + Fabricate( + :user_chat_channel_membership, + chat_channel: another_chat_channel, + user: @sender, + ) + Fabricate( + :user_chat_channel_membership, + chat_channel: another_chat_channel, + user: @user, + last_read_message_id: another_chat_message.id - 2, + ) Fabricate(:chat_mention, user: @user, chat_message: another_chat_message) email = described_class.chat_summary(@user, {}) @@ -119,15 +153,25 @@ expect(email.subject).to include(another_chat_channel.title(@user)) end - it 'displays a count when there are more than two channels with unread mentions' do + it "displays a count when there are more than two channels with unread mentions" do 2.times do |n| another_chat_channel = Fabricate(:chat_channel, name: "Test channel #{n}") - another_chat_message = Fabricate(:chat_message, user: @sender, chat_channel: another_chat_channel) - Fabricate(:user_chat_channel_membership, chat_channel: another_chat_channel, user: @sender) - Fabricate(:user_chat_channel_membership, chat_channel: another_chat_channel, user: @user, last_read_message_id: another_chat_message.id - 2) + another_chat_message = + Fabricate(:chat_message, user: @sender, chat_channel: another_chat_channel) + Fabricate( + :user_chat_channel_membership, + chat_channel: another_chat_channel, + user: @sender, + ) + Fabricate( + :user_chat_channel_membership, + chat_channel: another_chat_channel, + user: @user, + last_read_message_id: another_chat_message.id - 2, + ) Fabricate(:chat_mention, user: @user, chat_message: another_chat_message) end - expected_count_text = I18n.t('user_notifications.chat_summary.subject.others', count: 2) + expected_count_text = I18n.t("user_notifications.chat_summary.subject.others", count: 2) email = described_class.chat_summary(@user, {}) @@ -135,18 +179,20 @@ end end - context 'both unread DM messages and mentions' do + context "both unread DM messages and mentions" do before do - @dm_channel = DiscourseChat::DirectMessageChannelCreator.create!(target_users: [@sender, @user]) + @dm_channel = + DiscourseChat::DirectMessageChannelCreator.create!(target_users: [@sender, @user]) Fabricate(:chat_message, user: @sender, chat_channel: @dm_channel) Fabricate(:chat_mention, user: @user, chat_message: @chat_message) end - it 'always includes the DM second' do - expected_other_text = I18n.t( - 'user_notifications.chat_summary.subject.other_direct_message', - message_title: @sender.username - ) + it "always includes the DM second" do + expected_other_text = + I18n.t( + "user_notifications.chat_summary.subject.other_direct_message", + message_title: @sender.username, + ) email = described_class.chat_summary(@user, {}) @@ -155,12 +201,12 @@ end end - describe 'When there are mentions' do + describe "When there are mentions" do before { Fabricate(:chat_mention, user: @user, chat_message: @chat_message) } - describe 'selecting mentions' do + describe "selecting mentions" do it "doesn't return an email if the user can't see chat" do - SiteSetting.chat_allowed_groups = '' + SiteSetting.chat_allowed_groups = "" email = described_class.chat_summary(@user, {}) @@ -218,7 +264,12 @@ it "doesn't return an email when the unread count belongs to a different channel" do @user_membership.update!(last_read_message_id: @chat_message.id) second_channel = Fabricate(:chat_channel) - Fabricate(:user_chat_channel_membership, chat_channel: second_channel, user: @user, last_read_message_id: @chat_message.id - 1) + Fabricate( + :user_chat_channel_membership, + chat_channel: second_channel, + user: @user, + last_read_message_id: @chat_message.id - 1, + ) email = described_class.chat_summary(@user, {}) @@ -233,9 +284,10 @@ expect(email.to).to be_blank end - it 'returns an email when the user has unread private messages' do + it "returns an email when the user has unread private messages" do @user_membership.update!(last_read_message_id: @chat_message.id) - private_channel = DiscourseChat::DirectMessageChannelCreator.create!(target_users: [@sender, @user]) + private_channel = + DiscourseChat::DirectMessageChannelCreator.create!(target_users: [@sender, @user]) Fabricate(:chat_message, user: @sender, chat_channel: private_channel) email = described_class.chat_summary(@user, {}) @@ -243,8 +295,11 @@ expect(email.to).to contain_exactly(@user.email) end - it 'returns an email if the user read all the messages included in the previous summary' do - @user_membership.update!(last_read_message_id: @chat_message.id, last_unread_mention_when_emailed_id: @chat_message.id) + it "returns an email if the user read all the messages included in the previous summary" do + @user_membership.update!( + last_read_message_id: @chat_message.id, + last_unread_mention_when_emailed_id: @chat_message.id, + ) new_message = Fabricate(:chat_message, user: @sender, chat_channel: @chat_channel) Fabricate(:chat_mention, user: @user, chat_message: new_message) @@ -263,8 +318,8 @@ end end - describe 'mail contents' do - it 'returns an email when the user has unread mentions' do + describe "mail contents" do + it "returns an email when the user has unread mentions" do email = described_class.chat_summary(@user, {}) expect(email.to).to contain_exactly(@user.email) @@ -272,13 +327,16 @@ user_avatar = Nokogiri::HTML5.fragment(email.html_part.body.to_s).css(".message-row img") - expect(user_avatar.attribute('src').value).to eq(@sender.small_avatar_url) - expect(user_avatar.attribute('alt').value).to eq(@sender.username) + expect(user_avatar.attribute("src").value).to eq(@sender.small_avatar_url) + expect(user_avatar.attribute("alt").value).to eq(@sender.username) - more_messages_channel_link = Nokogiri::HTML5.fragment(email.html_part.body.to_s).css(".more-messages-link") + more_messages_channel_link = + Nokogiri::HTML5.fragment(email.html_part.body.to_s).css(".more-messages-link") - expect(more_messages_channel_link.attribute('href').value).to eq(@chat_message.full_url) - expect(more_messages_channel_link.text).to include(I18n.t("user_notifications.chat_summary.view_messages", count: 1)) + expect(more_messages_channel_link.attribute("href").value).to eq(@chat_message.full_url) + expect(more_messages_channel_link.text).to include( + I18n.t("user_notifications.chat_summary.view_messages", count: 1), + ) end it "displays the sender's name when the site is configured to prioritize it" do @@ -292,7 +350,7 @@ user_avatar = Nokogiri::HTML5.fragment(email.html_part.body.to_s).css(".message-row img") - expect(user_avatar.attribute('alt').value).to eq(@sender.name) + expect(user_avatar.attribute("alt").value).to eq(@sender.name) end it "displays the sender's username when the site is configured to prioritize it" do @@ -306,7 +364,7 @@ user_avatar = Nokogiri::HTML5.fragment(email.html_part.body.to_s).css(".message-row img") - expect(user_avatar.attribute('alt').value).to eq(@sender.username) + expect(user_avatar.attribute("alt").value).to eq(@sender.username) end it "displays the sender's username when names are disabled" do @@ -320,7 +378,7 @@ user_avatar = Nokogiri::HTML5.fragment(email.html_part.body.to_s).css(".message-row img") - expect(user_avatar.attribute('alt').value).to eq(@sender.username) + expect(user_avatar.attribute("alt").value).to eq(@sender.username) end it "displays the sender's username when the site is configured to prioritize it" do @@ -334,26 +392,38 @@ user_avatar = Nokogiri::HTML5.fragment(email.html_part.body.to_s).css(".message-row img") - expect(user_avatar.attribute('alt').value).to eq(@sender.username) + expect(user_avatar.attribute("alt").value).to eq(@sender.username) end - it 'includes a view more link when there are more than two mentions' do + it "includes a view more link when there are more than two mentions" do 2.times do msg = Fabricate(:chat_message, user: @sender, chat_channel: @chat_channel) Fabricate(:chat_mention, user: @user, chat_message: msg) end email = described_class.chat_summary(@user, {}) - more_messages_channel_link = Nokogiri::HTML5.fragment(email.html_part.body.to_s).css(".more-messages-link") + more_messages_channel_link = + Nokogiri::HTML5.fragment(email.html_part.body.to_s).css(".more-messages-link") - expect(more_messages_channel_link.attribute('href').value).to eq(@chat_message.full_url) - expect(more_messages_channel_link.text).to include(I18n.t("user_notifications.chat_summary.view_more", count: 1)) + expect(more_messages_channel_link.attribute("href").value).to eq(@chat_message.full_url) + expect(more_messages_channel_link.text).to include( + I18n.t("user_notifications.chat_summary.view_more", count: 1), + ) end it "doesn't repeat mentions we already sent" do - @user_membership.update!(last_read_message_id: @chat_message.id - 1, last_unread_mention_when_emailed_id: @chat_message.id) + @user_membership.update!( + last_read_message_id: @chat_message.id - 1, + last_unread_mention_when_emailed_id: @chat_message.id, + ) - new_message = Fabricate(:chat_message, user: @sender, chat_channel: @chat_channel, cooked: 'New message') + new_message = + Fabricate( + :chat_message, + user: @sender, + chat_channel: @chat_channel, + cooked: "New message", + ) Fabricate(:chat_mention, user: @user, chat_message: new_message) email = described_class.chat_summary(@user, {}) diff --git a/spec/models/chat_channel_spec.rb b/spec/models/chat_channel_spec.rb index e5f9bbf72..ce53f37ee 100644 --- a/spec/models/chat_channel_spec.rb +++ b/spec/models/chat_channel_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe ChatChannel do fab!(:user1) { Fabricate(:user) } @@ -9,7 +9,9 @@ fab!(:group) { Fabricate(:group) } fab!(:private_category) { Fabricate(:private_category, group: group) } fab!(:private_category_channel) { Fabricate(:chat_channel, chatable: private_category) } - fab!(:direct_message_channel) { Fabricate(:chat_channel, chatable: Fabricate(:direct_message_channel, users: [user1, user2])) } + fab!(:direct_message_channel) do + Fabricate(:chat_channel, chatable: Fabricate(:direct_message_channel, users: [user1, user2])) + end describe "#allowed_user_ids" do it "is correct for each channel type" do @@ -26,9 +28,7 @@ end describe "#closed!" do - before do - private_category_channel.update!(status: :open) - end + before { private_category_channel.update!(status: :open) } it "does nothing if user is not staff" do private_category_channel.closed!(user1) @@ -37,19 +37,19 @@ it "closes the channel, logs a staff action, and sends an event" do events = [] - messages = MessageBus.track_publish do - events = DiscourseEvent.track_events do - private_category_channel.closed!(staff) + messages = + MessageBus.track_publish do + events = DiscourseEvent.track_events { private_category_channel.closed!(staff) } end - end - expect(events).to include(event_name: :chat_channel_status_change, params: [{ - channel: private_category_channel, - old_status: :open, - new_status: :closed - }]) + expect(events).to include( + event_name: :chat_channel_status_change, + params: [{ channel: private_category_channel, old_status: :open, new_status: :closed }], + ) expect(messages.first.channel).to eq("/chat/channel-status") - expect(messages.first.data).to eq({ chat_channel_id: private_category_channel.id, status: "closed" }) + expect(messages.first.data).to eq( + { chat_channel_id: private_category_channel.id, status: "closed" }, + ) expect(private_category_channel.reload.closed?).to eq(true) expect( @@ -58,16 +58,14 @@ action: UserHistory.actions[:custom_staff], custom_type: "chat_channel_status_change", new_value: :closed, - previous_value: :open - ) + previous_value: :open, + ), ).to eq(true) end end describe "#open!" do - before do - private_category_channel.update!(status: :closed) - end + before { private_category_channel.update!(status: :closed) } it "does nothing if user is not staff" do private_category_channel.open!(user1) @@ -82,19 +80,19 @@ it "opens the channel, logs a staff action, and sends an event" do events = [] - messages = MessageBus.track_publish do - events = DiscourseEvent.track_events do - private_category_channel.open!(staff) + messages = + MessageBus.track_publish do + events = DiscourseEvent.track_events { private_category_channel.open!(staff) } end - end - expect(events).to include(event_name: :chat_channel_status_change, params: [{ - channel: private_category_channel, - old_status: :closed, - new_status: :open - }]) + expect(events).to include( + event_name: :chat_channel_status_change, + params: [{ channel: private_category_channel, old_status: :closed, new_status: :open }], + ) expect(messages.first.channel).to eq("/chat/channel-status") - expect(messages.first.data).to eq({ chat_channel_id: private_category_channel.id, status: "open" }) + expect(messages.first.data).to eq( + { chat_channel_id: private_category_channel.id, status: "open" }, + ) expect(private_category_channel.reload.open?).to eq(true) expect( @@ -103,16 +101,14 @@ action: UserHistory.actions[:custom_staff], custom_type: "chat_channel_status_change", new_value: :open, - previous_value: :closed - ) + previous_value: :closed, + ), ).to eq(true) end end describe "#read_only!" do - before do - private_category_channel.update!(status: :open) - end + before { private_category_channel.update!(status: :open) } it "does nothing if user is not staff" do private_category_channel.read_only!(user1) @@ -121,19 +117,19 @@ it "marks the channel read_only, logs a staff action, and sends an event" do events = [] - messages = MessageBus.track_publish do - events = DiscourseEvent.track_events do - private_category_channel.read_only!(staff) + messages = + MessageBus.track_publish do + events = DiscourseEvent.track_events { private_category_channel.read_only!(staff) } end - end - expect(events).to include(event_name: :chat_channel_status_change, params: [{ - channel: private_category_channel, - old_status: :open, - new_status: :read_only - }]) + expect(events).to include( + event_name: :chat_channel_status_change, + params: [{ channel: private_category_channel, old_status: :open, new_status: :read_only }], + ) expect(messages.first.channel).to eq("/chat/channel-status") - expect(messages.first.data).to eq({ chat_channel_id: private_category_channel.id, status: "read_only" }) + expect(messages.first.data).to eq( + { chat_channel_id: private_category_channel.id, status: "read_only" }, + ) expect(private_category_channel.reload.read_only?).to eq(true) expect( @@ -142,22 +138,20 @@ action: UserHistory.actions[:custom_staff], custom_type: "chat_channel_status_change", new_value: :read_only, - previous_value: :open - ) + previous_value: :open, + ), ).to eq(true) end end describe ".public_channels" do - context 'a category used as chatable is destroyed' do + context "a category used as chatable is destroyed" do fab!(:category_channel_1) { Fabricate(:chat_channel, chatable: Fabricate(:category)) } fab!(:category_channel_2) { Fabricate(:chat_channel, chatable: Fabricate(:category)) } - before do - category_channel_1.chatable.destroy! - end + before { category_channel_1.chatable.destroy! } - it 'doesn’t list the channel' do + it "doesn’t list the channel" do ids = ChatChannel.public_channels.pluck(:chatable_id) expect(ids).to_not include(category_channel_1.chatable_id) expect(ids).to include(category_channel_2.chatable_id) @@ -166,9 +160,7 @@ end describe "#archived!" do - before do - private_category_channel.update!(status: :read_only) - end + before { private_category_channel.update!(status: :read_only) } it "does nothing if user is not staff" do private_category_channel.archived!(user1) @@ -192,19 +184,21 @@ it "marks the channel archived, logs a staff action, and sends an event" do events = [] - messages = MessageBus.track_publish do - events = DiscourseEvent.track_events do - private_category_channel.archived!(staff) + messages = + MessageBus.track_publish do + events = DiscourseEvent.track_events { private_category_channel.archived!(staff) } end - end - expect(events).to include(event_name: :chat_channel_status_change, params: [{ - channel: private_category_channel, - old_status: :read_only, - new_status: :archived - }]) + expect(events).to include( + event_name: :chat_channel_status_change, + params: [ + { channel: private_category_channel, old_status: :read_only, new_status: :archived }, + ], + ) expect(messages.first.channel).to eq("/chat/channel-status") - expect(messages.first.data).to eq({ chat_channel_id: private_category_channel.id, status: "archived" }) + expect(messages.first.data).to eq( + { chat_channel_id: private_category_channel.id, status: "archived" }, + ) expect(private_category_channel.reload.archived?).to eq(true) expect( @@ -213,38 +207,38 @@ action: UserHistory.actions[:custom_staff], custom_type: "chat_channel_status_change", new_value: :archived, - previous_value: :read_only - ) + previous_value: :read_only, + ), ).to eq(true) end end - it 'is valid if name is long enough' do + it "is valid if name is long enough" do SiteSetting.max_topic_title_length = 5 - channel = described_class.new(name: 'a') - channel = described_class.new(name: 'a' * SiteSetting.max_topic_title_length) + channel = described_class.new(name: "a") + channel = described_class.new(name: "a" * SiteSetting.max_topic_title_length) expect(channel).to be_valid end - it 'is invalid if name is too long' do - channel = described_class.new(name: 'a' * (SiteSetting.max_topic_title_length + 1)) + it "is invalid if name is too long" do + channel = described_class.new(name: "a" * (SiteSetting.max_topic_title_length + 1)) expect(channel).to be_invalid end - it 'is invalid if name is empty' do - channel = described_class.new(name: '') + it "is invalid if name is empty" do + channel = described_class.new(name: "") expect(channel).to be_invalid end - it 'is valid if name is nil' do + it "is valid if name is nil" do channel = described_class.new(name: nil) expect(channel).to be_valid end - describe '#join' do + describe "#join" do before { group.add(user1) } - it 'creates a membership for the user and updates the count' do + it "creates a membership for the user and updates the count" do initial_count = private_category_channel.user_count membership = private_category_channel.add(user1) @@ -255,9 +249,14 @@ expect(private_category_channel.reload.user_count).to eq(initial_count + 1) end - it 'updates an existing membership for the user and updates the count' do + it "updates an existing membership for the user and updates the count" do initial_count = private_category_channel.user_count - membership = UserChatChannelMembership.create!(chat_channel: private_category_channel, user: user1, following: false) + membership = + UserChatChannelMembership.create!( + chat_channel: private_category_channel, + user: user1, + following: false, + ) private_category_channel.add(user1) @@ -265,9 +264,14 @@ expect(private_category_channel.reload.user_count).to eq(initial_count + 1) end - it 'does nothing if the user is already a member' do + it "does nothing if the user is already a member" do initial_count = private_category_channel.user_count - membership = UserChatChannelMembership.create!(chat_channel: private_category_channel, user: user1, following: true) + membership = + UserChatChannelMembership.create!( + chat_channel: private_category_channel, + user: user1, + following: true, + ) private_category_channel.add(user1) @@ -275,14 +279,14 @@ end end - describe '#remove' do + describe "#remove" do before do group.add(user1) @membership = private_category_channel.add(user1) private_category_channel.reload end - it 'updates the membership for the user and decreases the count' do + it "updates the membership for the user and decreases the count" do initial_count = private_category_channel.user_count membership = private_category_channel.remove(user1) @@ -295,7 +299,7 @@ expect { private_category_channel.remove(user2) }.to raise_error(ActiveRecord::RecordNotFound) end - it 'does nothing if the user is not following the channel' do + it "does nothing if the user is not following the channel" do initial_count = private_category_channel.user_count @membership.update!(following: false) diff --git a/spec/models/chat_message_spec.rb b/spec/models/chat_message_spec.rb index 1fb9f7ffd..5a09c6a3a 100644 --- a/spec/models/chat_message_spec.rb +++ b/spec/models/chat_message_spec.rb @@ -1,35 +1,35 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe ChatMessage do fab!(:message) { Fabricate(:chat_message, message: "hey friend, what's up?!") } - describe '.cook' do - it 'does not support HTML tags' do + describe ".cook" do + it "does not support HTML tags" do cooked = ChatMessage.cook("

test

") expect(cooked).to eq("

<h1>test</h1>

") end - it 'does not support headings' do + it "does not support headings" do cooked = ChatMessage.cook("## heading 2") expect(cooked).to eq("

## heading 2

") end - it 'does not support horizontal rules' do + it "does not support horizontal rules" do cooked = ChatMessage.cook("---") expect(cooked).to eq("

---

") end - it 'supports backticks rule' do + it "supports backticks rule" do cooked = ChatMessage.cook("`test`") expect(cooked).to eq("

test

") end - it 'supports fence rule' do + it "supports fence rule" do cooked = ChatMessage.cook(<<~RAW) ``` something = test @@ -42,7 +42,7 @@ COOKED end - it 'supports fence rule with language support' do + it "supports fence rule with language support" do cooked = ChatMessage.cook(<<~RAW) ```ruby Widget.triangulate(argument: "no u") @@ -55,23 +55,24 @@ COOKED end - it 'supports code rule' do + it "supports code rule" do cooked = ChatMessage.cook(" something = test") expect(cooked).to eq("
something = test\n
") end - it 'supports blockquote rule' do + it "supports blockquote rule" do cooked = ChatMessage.cook("> a quote") expect(cooked).to eq("
\n

a quote

\n
") end - it 'supports quote bbcode' do + it "supports quote bbcode" do topic = Fabricate(:topic, title: "Some quotable topic") post = Fabricate(:post, topic: topic) SiteSetting.external_system_avatars_enabled = false - avatar_src = "//test.localhost#{User.system_avatar_template(post.user.username).gsub("{size}", "40")}" + avatar_src = + "//test.localhost#{User.system_avatar_template(post.user.username).gsub("{size}", "40")}" cooked = ChatMessage.cook(<<~RAW) [quote="#{post.user.username}, post:#{post.post_number}, topic:#{topic.id}"] @@ -96,16 +97,33 @@ chat_channel = Fabricate(:chat_channel, name: "testchannel") user = Fabricate(:user, username: "chatbbcodeuser") user2 = Fabricate(:user, username: "otherbbcodeuser") - avatar_src = "//test.localhost#{User.system_avatar_template(user.username).gsub("{size}", "40")}" - avatar_src2 = "//test.localhost#{User.system_avatar_template(user2.username).gsub("{size}", "40")}" - msg1 = Fabricate(:chat_message, chat_channel: chat_channel, message: "this is the first message", user: user) - msg2 = Fabricate(:chat_message, chat_channel: chat_channel, message: "and another cool one", user: user2) + avatar_src = + "//test.localhost#{User.system_avatar_template(user.username).gsub("{size}", "40")}" + avatar_src2 = + "//test.localhost#{User.system_avatar_template(user2.username).gsub("{size}", "40")}" + msg1 = + Fabricate( + :chat_message, + chat_channel: chat_channel, + message: "this is the first message", + user: user, + ) + msg2 = + Fabricate( + :chat_message, + chat_channel: chat_channel, + message: "and another cool one", + user: user2, + ) other_messages_to_quote = [msg1, msg2] - cooked = ChatMessage.cook( - ChatTranscriptService.new( - chat_channel, Fabricate(:user), messages_or_ids: other_messages_to_quote.map(&:id) - ).generate_markdown - ) + cooked = + ChatMessage.cook( + ChatTranscriptService.new( + chat_channel, + Fabricate(:user), + messages_or_ids: other_messages_to_quote.map(&:id), + ).generate_markdown, + ) expect(cooked).to eq(<<~COOKED.chomp)
@@ -144,25 +162,27 @@ COOKED end - it 'supports strikethrough rule' do + it "supports strikethrough rule" do cooked = ChatMessage.cook("~~test~~") expect(cooked).to eq("

test

") end - it 'supports emphasis rule' do + it "supports emphasis rule" do cooked = ChatMessage.cook("**bold**") expect(cooked).to eq("

bold

") end - it 'supports link markdown rule' do + it "supports link markdown rule" do chat_message = Fabricate(:chat_message, message: "[test link](https://www.example.com)") - expect(chat_message.cooked).to eq("

test link

") + expect(chat_message.cooked).to eq( + "

test link

", + ) end - it 'supports table markdown plugin' do + it "supports table markdown plugin" do cooked = ChatMessage.cook(<<~RAW) | Command | Description | | --- | --- | @@ -191,33 +211,39 @@ expect(cooked).to eq(expected.chomp) end - it 'supports onebox markdown plugin' do + it "supports onebox markdown plugin" do cooked = ChatMessage.cook("https://www.example.com") - expect(cooked).to eq("

https://www.example.com

") + expect(cooked).to eq( + "

https://www.example.com

", + ) end - it 'supports emoji plugin' do + it "supports emoji plugin" do cooked = ChatMessage.cook(":grin:") - expect(cooked).to eq("

\":grin:\"

") + expect(cooked).to eq( + "

\":grin:\"

", + ) end - it 'supports mentions plugin' do + it "supports mentions plugin" do cooked = ChatMessage.cook("@mention") expect(cooked).to eq("

@mention

") end - it 'supports category-hashtag plugin' do + it "supports category-hashtag plugin" do category = Fabricate(:category) cooked = ChatMessage.cook("##{category.slug}") - expect(cooked).to eq("

##{category.slug}

") + expect(cooked).to eq( + "

##{category.slug}

", + ) end - it 'supports censored plugin' do + it "supports censored plugin" do watched_word = Fabricate(:watched_word, action: WatchedWord.actions[:censor]) cooked = ChatMessage.cook(watched_word.word) @@ -226,18 +252,30 @@ end it "includes links in pretty text excerpt if the raw message is a single link and the PrettyText excerpt is blank" do - message = Fabricate.build(:chat_message, message: "https://twitter.com/EffinBirds/status/1518743508378697729") + message = + Fabricate.build( + :chat_message, + message: "https://twitter.com/EffinBirds/status/1518743508378697729", + ) expect(message.excerpt).to eq("https://twitter.com/EffinBirds/status/1518743508378697729") - message = Fabricate.build( - :chat_message, - message: "https://twitter.com/EffinBirds/status/1518743508378697729", - cooked: <<~COOKED + message = + Fabricate.build( + :chat_message, + message: "https://twitter.com/EffinBirds/status/1518743508378697729", + cooked: <<~COOKED, \n COOKED - ) + ) expect(message.excerpt).to eq("https://twitter.com/EffinBirds/status/1518743508378697729") - message = Fabricate.build(:chat_message, message: "wow check out these birbs https://twitter.com/EffinBirds/status/1518743508378697729") - expect(message.excerpt).to eq("wow check out these birbs https://twitter.com/Effi…") + message = + Fabricate.build( + :chat_message, + message: + "wow check out these birbs https://twitter.com/EffinBirds/status/1518743508378697729", + ) + expect(message.excerpt).to eq( + "wow check out these birbs https://twitter.com/Effi…", + ) end it "returns an empty string if PrettyText.excerpt returns empty string" do @@ -250,20 +288,23 @@ end it "excerpts upload file name if message is empty" do - gif = Fabricate(:upload, original_filename: "cat.gif", width: 400, height: 300, extension: "gif") + gif = + Fabricate(:upload, original_filename: "cat.gif", width: 400, height: 300, extension: "gif") message = Fabricate(:chat_message, message: "") ChatUpload.create(chat_message: message, upload: gif) expect(message.excerpt).to eq "cat.gif" end - it 'supports autolink with <>' do + it "supports autolink with <>" do cooked = ChatMessage.cook("") - expect(cooked).to eq("

https://github.com/discourse/discourse-chat/pull/468

") + expect(cooked).to eq( + "

https://github.com/discourse/discourse-chat/pull/468

", + ) end - it 'supports lists' do + it "supports lists" do cooked = ChatMessage.cook(<<~MSG) wow look it's a list @@ -280,25 +321,27 @@ HTML end - it 'supports inline emoji' do + it "supports inline emoji" do cooked = ChatMessage.cook(":D") expect(cooked).to eq(<<~HTML.chomp)

:smiley:

HTML end - it 'supports emoji shortcuts' do + it "supports emoji shortcuts" do cooked = ChatMessage.cook("this is a replace test :P :|") expect(cooked).to eq(<<~HTML.chomp)

this is a replace test :stuck_out_tongue: :expressionless:

HTML end - it 'supports spoilers' do + it "supports spoilers" do if SiteSetting.respond_to?(:spoiler_enabled) && SiteSetting.spoiler_enabled cooked = ChatMessage.cook("[spoiler]the planet of the apes was earth all along[/spoiler]") - expect(cooked).to eq("
\n

the planet of the apes was earth all along

\n
") + expect(cooked).to eq( + "
\n

the planet of the apes was earth all along

\n
", + ) end end end @@ -309,8 +352,16 @@ end it "renders the message with uploads" do - image = Fabricate(:upload, original_filename: "test_image.jpg", width: 400, height: 300, extension: "jpg") - image2 = Fabricate(:upload, original_filename: "meme.jpg", width: 10, height: 10, extension: "jpg") + image = + Fabricate( + :upload, + original_filename: "test_image.jpg", + width: 400, + height: 300, + extension: "jpg", + ) + image2 = + Fabricate(:upload, original_filename: "meme.jpg", width: 10, height: 10, extension: "jpg") ChatUpload.create(chat_message: message, upload: image) ChatUpload.create(chat_message: message, upload: image2) expect(message.to_markdown).to eq(<<~MSG.chomp) @@ -339,9 +390,7 @@ fab!(:user1) { Fabricate(:user) } fab!(:user2) { Fabricate(:user) } - before do - SiteSetting.chat_duplicate_message_sensitivity = 1 - end + before { SiteSetting.chat_duplicate_message_sensitivity = 1 } it "blocks duplicate messages for the message, channel user, and message age requirements" do Fabricate(:chat_message, message: "this is duplicate", chat_channel: channel, user: user1) @@ -351,8 +400,8 @@ end end - describe '#destroy' do - it 'nullify messages with in_reply_to_id to this destroyed message' do + describe "#destroy" do + it "nullify messages with in_reply_to_id to this destroyed message" do message_1 = Fabricate(:chat_message) message_2 = Fabricate(:chat_message, in_reply_to_id: message_1.id) message_3 = Fabricate(:chat_message, in_reply_to_id: message_2.id) @@ -365,7 +414,7 @@ expect(message_3.reload.in_reply_to_id).to eq(message_2.id) end - it 'destroys chat_message_revisions' do + it "destroys chat_message_revisions" do message_1 = Fabricate(:chat_message) revision_1 = Fabricate(:chat_message_revision, chat_message: message_1) @@ -374,7 +423,7 @@ expect { revision_1.reload }.to raise_error(ActiveRecord::RecordNotFound) end - it 'destroys chat_message_reactions' do + it "destroys chat_message_reactions" do message_1 = Fabricate(:chat_message) reaction_1 = Fabricate(:chat_message_reaction, chat_message: message_1) @@ -383,7 +432,7 @@ expect { reaction_1.reload }.to raise_error(ActiveRecord::RecordNotFound) end - it 'destroys chat_mention' do + it "destroys chat_mention" do message_1 = Fabricate(:chat_message) mention_1 = Fabricate(:chat_mention, chat_message: message_1) @@ -392,7 +441,7 @@ expect { mention_1.reload }.to raise_error(ActiveRecord::RecordNotFound) end - it 'destroys chat_webhook_event' do + it "destroys chat_webhook_event" do message_1 = Fabricate(:chat_message) webhook_1 = Fabricate(:chat_webhook_event, chat_message: message_1) @@ -401,7 +450,7 @@ expect { webhook_1.reload }.to raise_error(ActiveRecord::RecordNotFound) end - it 'destroys chat_uploads' do + it "destroys chat_uploads" do message_1 = Fabricate(:chat_message) chat_upload_1 = Fabricate(:chat_upload, chat_message: message_1) @@ -410,12 +459,10 @@ expect { chat_upload_1.reload }.to raise_error(ActiveRecord::RecordNotFound) end - context 'bookmarks' do - before do - Bookmark.register_bookmarkable(ChatMessageBookmarkable) - end + context "bookmarks" do + before { Bookmark.register_bookmarkable(ChatMessageBookmarkable) } - it 'destroys bookmarks' do + it "destroys bookmarks" do message_1 = Fabricate(:chat_message) bookmark_1 = Fabricate(:bookmark, bookmarkable: message_1) diff --git a/spec/models/direct_message_channel_spec.rb b/spec/models/direct_message_channel_spec.rb index 306f2abea..9d10352ed 100644 --- a/spec/models/direct_message_channel_spec.rb +++ b/spec/models/direct_message_channel_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe DirectMessageChannel do fab!(:user1) { Fabricate(:user, username: "chatdmfellow1") } @@ -15,23 +15,25 @@ expect(direct_message_channel.chat_channel_title_for_user(chat_channel, user1)).to eq( I18n.t( "chat.channel.dm_title.multi_user", - users: [user3, user2].map { |u| "@#{u.username}" }.join(", ") - ) + users: [user3, user2].map { |u| "@#{u.username}" }.join(", "), + ), ) end it "returns a nicely formatted truncated name if it's more than 5 users" do user3 = Fabricate.build(:user, username: "chatdmregent") - users = [user1, user2, user3].concat(5.times.map.with_index { |i| Fabricate(:user, username: "chatdmuser#{i}") }) + users = [user1, user2, user3].concat( + 5.times.map.with_index { |i| Fabricate(:user, username: "chatdmuser#{i}") }, + ) direct_message_channel = Fabricate(:direct_message_channel, users: users) expect(direct_message_channel.chat_channel_title_for_user(chat_channel, user1)).to eq( I18n.t( "chat.channel.dm_title.multi_user_truncated", users: users[1..5].sort_by(&:username).map { |u| "@#{u.username}" }.join(", "), - leftover: 2 - ) + leftover: 2, + ), ) end @@ -39,7 +41,7 @@ direct_message_channel = Fabricate(:direct_message_channel, users: [user1, user2]) expect(direct_message_channel.chat_channel_title_for_user(chat_channel, user1)).to eq( - I18n.t("chat.channel.dm_title.single_user", user: "@#{user2.username}") + I18n.t("chat.channel.dm_title.single_user", user: "@#{user2.username}"), ) end @@ -47,7 +49,7 @@ direct_message_channel = Fabricate(:direct_message_channel, users: [user1]) expect(direct_message_channel.chat_channel_title_for_user(chat_channel, user1)).to eq( - I18n.t("chat.channel.dm_title.single_user", user: "@#{user1.username}") + I18n.t("chat.channel.dm_title.single_user", user: "@#{user1.username}"), ) end @@ -56,7 +58,9 @@ user2.destroy! direct_message_channel.reload - expect(direct_message_channel.chat_channel_title_for_user(chat_channel, user1)).to eq chat_channel.id + expect( + direct_message_channel.chat_channel_title_for_user(chat_channel, user1), + ).to eq chat_channel.id end end end diff --git a/spec/models/reviewable_chat_message_spec.rb b/spec/models/reviewable_chat_message_spec.rb index 1c4c7cbae..8553bcd3b 100644 --- a/spec/models/reviewable_chat_message_spec.rb +++ b/spec/models/reviewable_chat_message_spec.rb @@ -1,13 +1,15 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" RSpec.describe ReviewableChatMessage, type: :model do fab!(:moderator) { Fabricate(:moderator) } fab!(:user) { Fabricate(:user) } fab!(:chat_channel) { Fabricate(:chat_channel) } fab!(:chat_message) { Fabricate(:chat_message, chat_channel: chat_channel, user: user) } - fab!(:reviewable) { Fabricate(:reviewable_chat_message, target: chat_message, created_by: moderator) } + fab!(:reviewable) do + Fabricate(:reviewable_chat_message, target: chat_message, created_by: moderator) + end it "agree_and_keep agrees with the flag and doesn't delete the message" do reviewable.perform(moderator, :agree_and_keep_message) @@ -58,25 +60,25 @@ it "silences the user for the correct time when the threshold is met" do SiteSetting.chat_auto_silence_from_flags_duration = 3 reviewable.update!(score: ReviewableChatMessage.score_to_silence_user + 1) - expect { - ReviewableChatMessage.on_score_updated(reviewable) - }.to change { user.reload.silenced? }.to be true + expect { ReviewableChatMessage.on_score_updated(reviewable) }.to change { + user.reload.silenced? + }.to be true end it "does nothing if the new score is less than the score to auto-silence" do SiteSetting.chat_auto_silence_from_flags_duration = 3 reviewable.update!(score: ReviewableChatMessage.score_to_silence_user - 1) - expect { - ReviewableChatMessage.on_score_updated(reviewable) - }.not_to change { user.reload.silenced? } + expect { ReviewableChatMessage.on_score_updated(reviewable) }.not_to change { + user.reload.silenced? + } end it "does nothing if the silence duration is set to 0" do SiteSetting.chat_auto_silence_from_flags_duration = 0 reviewable.update!(score: ReviewableChatMessage.score_to_silence_user + 1) - expect { - ReviewableChatMessage.on_score_updated(reviewable) - }.not_to change { user.reload.silenced? } + expect { ReviewableChatMessage.on_score_updated(reviewable) }.not_to change { + user.reload.silenced? + } end end end diff --git a/spec/plugin_spec.rb b/spec/plugin_spec.rb index ca8a890ca..509e7ba0b 100644 --- a/spec/plugin_spec.rb +++ b/spec/plugin_spec.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" -describe 'discourse-chat' do +describe "discourse-chat" do before do SiteSetting.clean_up_uploads = true SiteSetting.clean_orphan_uploads_grace_period_hours = 1 @@ -10,7 +10,7 @@ SiteSetting.chat_enabled = true end - describe 'register_upload_unused' do + describe "register_upload_unused" do fab!(:chat_channel) { Fabricate(:chat_channel, chatable: Fabricate(:category)) } fab!(:user) { Fabricate(:user) } fab!(:upload) { Fabricate(:upload, user: user, created_at: 1.month.ago) } @@ -22,11 +22,11 @@ user: user, in_reply_to_id: nil, content: "Hello world!", - upload_ids: [upload.id] + upload_ids: [upload.id], ) end - it 'marks uploads with ChatUpload in use' do + it "marks uploads with ChatUpload in use" do unused_upload expect { Jobs::CleanUpUploads.new.execute({}) }.to change { Upload.count }.by(-1) @@ -35,7 +35,7 @@ end end - describe 'register_upload_in_use' do + describe "register_upload_in_use" do fab!(:chat_channel) { Fabricate(:chat_channel, chatable: Fabricate(:category)) } fab!(:user) { Fabricate(:user) } fab!(:message_upload) { Fabricate(:upload, user: user, created_at: 1.month.ago) } @@ -48,7 +48,7 @@ user: user, in_reply_to_id: nil, content: "Hello world! #{message_upload.sha1}", - upload_ids: [] + upload_ids: [], ) end @@ -56,11 +56,12 @@ ChatDraft.create!( user: user, chat_channel: chat_channel, - data: "{\"value\":\"hello world \",\"uploads\":[\"#{draft_upload.sha1}\"],\"replyToMsg\":null}" + data: + "{\"value\":\"hello world \",\"uploads\":[\"#{draft_upload.sha1}\"],\"replyToMsg\":null}", ) end - it 'marks uploads with ChatUpload in use' do + it "marks uploads with ChatUpload in use" do draft_upload unused_upload @@ -79,9 +80,7 @@ fab!(:group) { Fabricate(:group) } context "when chat enabled" do - before do - SiteSetting.chat_enabled = true - end + before { SiteSetting.chat_enabled = true } it "returns true if the target user and the guardian user is in the DiscourseChat.allowed_group_ids" do SiteSetting.chat_allowed_groups = group.id @@ -120,9 +119,7 @@ end context "when chat not enabled" do - before do - SiteSetting.chat_enabled = false - end + before { SiteSetting.chat_enabled = false } it "returns false" do expect(serializer.can_chat_user).to eq(false) @@ -143,7 +140,7 @@ user: user, in_reply_to_id: nil, content: "Hello world!", - upload_ids: [] + upload_ids: [], ).chat_message end @@ -158,10 +155,13 @@ end it "renders messages" do - results = InlineOneboxer.new(["#{chat_url}?messageId=#{chat_message.id}"], skip_cache: true).process + results = + InlineOneboxer.new(["#{chat_url}?messageId=#{chat_message.id}"], skip_cache: true).process expect(results).to be_present expect(results[0][:url]).to eq("#{chat_url}?messageId=#{chat_message.id}") - expect(results[0][:title]).to eq("Message ##{chat_message.id} by #{chat_message.user.username} – ##{chat_channel.name}") + expect(results[0][:title]).to eq( + "Message ##{chat_message.id} by #{chat_message.user.username} – ##{chat_channel.name}", + ) end end @@ -187,7 +187,7 @@
1 member
@@ -223,7 +223,7 @@ end end - describe 'auto-joining users to a channel' do + describe "auto-joining users to a channel" do fab!(:chatters_group) { Fabricate(:group) } fab!(:user) { Fabricate(:user, last_seen_at: 15.minutes.ago) } let!(:channel) { Fabricate(:chat_channel, auto_join_users: true, chatable: category) } @@ -236,33 +236,33 @@ def assert_user_following_state(user, channel, following:) following ? (expect(membership.following).to eq(true)) : (expect(membership).to be_nil) end - describe 'when a user is added to a group with access to a channel through a category' do + describe "when a user is added to a group with access to a channel through a category" do let!(:category) { Fabricate(:private_category, group: chatters_group) } - it 'joins the user to the channel if auto-join is enabled' do + it "joins the user to the channel if auto-join is enabled" do chatters_group.add(user) assert_user_following_state(user, channel, following: true) end - it 'does nothing if auto-join is disabled' do + it "does nothing if auto-join is disabled" do channel.update!(auto_join_users: false) assert_user_following_state(user, channel, following: false) end end - describe 'when a user is created' do + describe "when a user is created" do fab!(:category) { Fabricate(:category) } let(:user) { Fabricate.build(:user) } - it 'queues a job to auto-join the user' do + it "queues a job to auto-join the user" do user.save! assert_user_following_state(user, channel, following: true) end - it 'does nothing if auto-join is disabled' do + it "does nothing if auto-join is disabled" do channel.update!(auto_join_users: false) user.save! @@ -271,15 +271,15 @@ def assert_user_following_state(user, channel, following:) end end - describe 'when category permissions change' do + describe "when category permissions change" do fab!(:category) { Fabricate(:category) } let(:chatters_group_permission) do { chatters_group.name => CategoryGroup.permission_types[:full] } end - describe 'given permissions to a new group' do - it 'adds the user to the channel' do + describe "given permissions to a new group" do + it "adds the user to the channel" do chatters_group.add(user) category.update!(permissions: chatters_group_permission) @@ -287,7 +287,7 @@ def assert_user_following_state(user, channel, following:) assert_user_following_state(user, channel, following: true) end - it 'does nothing if there is no channel for the category' do + it "does nothing if there is no channel for the category" do another_category = Fabricate(:category) another_category.update!(permissions: chatters_group_permission) diff --git a/spec/queries/chat_channel_memberships_query_spec.rb b/spec/queries/chat_channel_memberships_query_spec.rb index 0cdd27dad..1561c8e3a 100644 --- a/spec/queries/chat_channel_memberships_query_spec.rb +++ b/spec/queries/chat_channel_memberships_query_spec.rb @@ -1,33 +1,33 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe ChatChannelMembershipsQuery do - fab!(:user_1) { Fabricate(:user, username: 'Aline', name: 'Boetie') } - fab!(:user_2) { Fabricate(:user, username: 'Bertrand', name: 'Arlan') } + fab!(:user_1) { Fabricate(:user, username: "Aline", name: "Boetie") } + fab!(:user_2) { Fabricate(:user, username: "Bertrand", name: "Arlan") } before do SiteSetting.chat_enabled = true SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:everyone] end - context 'chatable exists' do - context 'chatable is public' do + context "chatable exists" do + context "chatable is public" do fab!(:channel_1) { Fabricate(:chat_channel) } - context 'no memberships exists' do - it 'returns an empty array' do + context "no memberships exists" do + it "returns an empty array" do expect(described_class.call(channel_1.id)).to eq([]) end end - context 'memberships exist' do + context "memberships exist" do before do UserChatChannelMembership.create(user: user_1, chat_channel: channel_1, following: true) UserChatChannelMembership.create(user: user_2, chat_channel: channel_1, following: true) end - it 'returns the memberships' do + it "returns the memberships" do memberships = described_class.call(channel_1.id) expect(memberships.pluck(:user_id)).to contain_exactly(user_1.id, user_2.id) @@ -35,23 +35,35 @@ end end - context 'chatable is direct channel' do + context "chatable is direct channel" do fab!(:chatable_1) { Fabricate(:direct_message_channel, users: [user_1, user_2]) } fab!(:channel_1) { Fabricate(:chat_channel, chatable: chatable_1) } - context 'no memberships exists' do - it 'returns an empty array' do + context "no memberships exists" do + it "returns an empty array" do expect(described_class.call(channel_1.id)).to eq([]) end end - context 'memberships exist' do + context "memberships exist" do before do - UserChatChannelMembership.create!(user: user_1, chat_channel: channel_1, following: true, desktop_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always], mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always]) - UserChatChannelMembership.create!(user: user_2, chat_channel: channel_1, following: true, desktop_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always], mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always]) + UserChatChannelMembership.create!( + user: user_1, + chat_channel: channel_1, + following: true, + desktop_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always], + mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always], + ) + UserChatChannelMembership.create!( + user: user_2, + chat_channel: channel_1, + following: true, + desktop_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always], + mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always], + ) end - it 'returns the memberships' do + it "returns the memberships" do memberships = described_class.call(channel_1.id) expect(memberships.pluck(:user_id)).to contain_exactly(user_1.id, user_2.id) @@ -59,7 +71,7 @@ end end - context 'pagination' do + context "pagination" do fab!(:channel_1) { Fabricate(:chat_channel) } before do @@ -67,16 +79,16 @@ UserChatChannelMembership.create(user: user_2, chat_channel: channel_1, following: true) end - describe 'offset param' do - it 'offsets the results' do + describe "offset param" do + it "offsets the results" do memberships = described_class.call(channel_1.id, offset: 1) expect(memberships.length).to eq(1) end end - describe 'limit param' do - it 'limits the results' do + describe "limit param" do + it "limits the results" do memberships = described_class.call(channel_1.id, limit: 1) expect(memberships.length).to eq(1) @@ -84,7 +96,7 @@ end end - describe 'username param' do + describe "username param" do fab!(:channel_1) { Fabricate(:chat_channel) } before do @@ -92,7 +104,7 @@ UserChatChannelMembership.create(user: user_2, chat_channel: channel_1, following: true) end - it 'filters the results' do + it "filters the results" do memberships = described_class.call(channel_1.id, username: user_1.username) expect(memberships.length).to eq(1) @@ -100,7 +112,7 @@ end end - describe 'memberships order' do + describe "memberships order" do fab!(:channel_1) { Fabricate(:chat_channel) } before do @@ -108,12 +120,10 @@ UserChatChannelMembership.create(user: user_2, chat_channel: channel_1, following: true) end - context 'prioritizes username in ux' do - before do - SiteSetting.prioritize_username_in_ux = true - end + context "prioritizes username in ux" do + before { SiteSetting.prioritize_username_in_ux = true } - it 'is using ascending order on username' do + it "is using ascending order on username" do memberships = described_class.call(channel_1.id) expect(memberships[0].user).to eq(user_1) @@ -121,24 +131,20 @@ end end - context 'doesn’t prioritize username in ux' do - before do - SiteSetting.prioritize_username_in_ux = false - end + context "doesn’t prioritize username in ux" do + before { SiteSetting.prioritize_username_in_ux = false } - it 'is using ascending order on name' do + it "is using ascending order on name" do memberships = described_class.call(channel_1.id) expect(memberships[0].user).to eq(user_2) expect(memberships[1].user).to eq(user_1) end - context 'enable names is disabled' do - before do - SiteSetting.enable_names = false - end + context "enable names is disabled" do + before { SiteSetting.enable_names = false } - it 'is using ascending order on username' do + it "is using ascending order on username" do memberships = described_class.call(channel_1.id) expect(memberships[0].user).to eq(user_1) @@ -149,7 +155,7 @@ end end - context 'user is staged' do + context "user is staged" do fab!(:channel_1) { Fabricate(:chat_channel) } fab!(:staged_user) { Fabricate(:staged) } @@ -157,35 +163,45 @@ UserChatChannelMembership.create(user: staged_user, chat_channel: channel_1, following: true) end - it 'doesn’t list staged users' do + it "doesn’t list staged users" do memberships = described_class.call(channel_1.id) expect(memberships).to be_blank end end - context 'user is suspended' do + context "user is suspended" do fab!(:channel_1) { Fabricate(:chat_channel) } - fab!(:suspended_user) { Fabricate(:user, suspended_at: Time.now, suspended_till: 5.days.from_now) } + fab!(:suspended_user) do + Fabricate(:user, suspended_at: Time.now, suspended_till: 5.days.from_now) + end before do - UserChatChannelMembership.create(user: suspended_user, chat_channel: channel_1, following: true) + UserChatChannelMembership.create( + user: suspended_user, + chat_channel: channel_1, + following: true, + ) end - it 'doesn’t list suspended users' do + it "doesn’t list suspended users" do memberships = described_class.call(channel_1.id) expect(memberships).to be_blank end end - context 'user is inactive' do + context "user is inactive" do fab!(:channel_1) { Fabricate(:chat_channel) } fab!(:inactive_user) { Fabricate(:inactive_user) } before do - UserChatChannelMembership.create(user: inactive_user, chat_channel: channel_1, following: true) + UserChatChannelMembership.create( + user: inactive_user, + chat_channel: channel_1, + following: true, + ) end - it 'doesn’t list inactive users' do + it "doesn’t list inactive users" do memberships = described_class.call(channel_1.id) expect(memberships).to be_blank end diff --git a/spec/requests/admin/admin_incoming_chat_webhooks_controller_spec.rb b/spec/requests/admin/admin_incoming_chat_webhooks_controller_spec.rb index 4485c1eb3..553b16372 100644 --- a/spec/requests/admin/admin_incoming_chat_webhooks_controller_spec.rb +++ b/spec/requests/admin/admin_incoming_chat_webhooks_controller_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" RSpec.describe DiscourseChat::AdminIncomingChatWebhooksController do fab!(:admin) { Fabricate(:admin) } @@ -8,9 +8,7 @@ fab!(:chat_channel1) { Fabricate(:chat_channel) } fab!(:chat_channel2) { Fabricate(:chat_channel) } - before do - SiteSetting.chat_enabled = true - end + before { SiteSetting.chat_enabled = true } describe "#index" do fab!(:existing1) { Fabricate(:incoming_chat_webhook) } @@ -26,17 +24,14 @@ sign_in(admin) get "/admin/plugins/chat.json" expect(response.status).to eq(200) - expect(response.parsed_body["incoming_chat_webhooks"].map { |webhook| webhook["id"] }).to match_array([existing1.id, existing2.id]) + expect( + response.parsed_body["incoming_chat_webhooks"].map { |webhook| webhook["id"] }, + ).to match_array([existing1.id, existing2.id]) end end describe "#create" do - let(:attrs) { - { - name: "Test1", - chat_channel_id: chat_channel1.id - } - } + let(:attrs) { { name: "Test1", chat_channel_id: chat_channel1.id } } it "blocks non-admin" do sign_in(user) @@ -58,15 +53,19 @@ it "errors when chat_channel isn't valid" do sign_in(admin) - post "/admin/plugins/chat/hooks.json", params: { name: "test1a", chat_channel_id: ChatChannel.last.id + 1 } + post "/admin/plugins/chat/hooks.json", + params: { + name: "test1a", + chat_channel_id: ChatChannel.last.id + 1, + } expect(response.status).to eq(404) end it "creates a new incoming_chat_webhook record" do sign_in(admin) - expect { - post "/admin/plugins/chat/hooks.json", params: attrs - }.to change { IncomingChatWebhook.count }.by(1) + expect { post "/admin/plugins/chat/hooks.json", params: attrs }.to change { + IncomingChatWebhook.count + }.by(1) expect(response.parsed_body["name"]).to eq(attrs[:name]) expect(response.parsed_body["chat_channel"]["id"]).to eq(attrs[:chat_channel_id]) expect(response.parsed_body["url"]).not_to be_nil @@ -75,15 +74,15 @@ describe "#update" do fab!(:existing) { Fabricate(:incoming_chat_webhook, chat_channel: chat_channel1) } - let(:attrs) { + let(:attrs) do { name: "update test", chat_channel_id: chat_channel2.id, emoji: ":slight_smile:", description: "It does stuff!", - username: "beep boop" + username: "beep boop", } - } + end it "errors for non-admin" do sign_in(user) @@ -129,9 +128,9 @@ it "destroys incoming_chat_webhook records" do sign_in(admin) - expect { - delete "/admin/plugins/chat/hooks/#{existing.id}.json" - }.to change { IncomingChatWebhook.count }.by(-1) + expect { delete "/admin/plugins/chat/hooks/#{existing.id}.json" }.to change { + IncomingChatWebhook.count + }.by(-1) end end end diff --git a/spec/requests/api/category_chatables_controller_spec.rb b/spec/requests/api/category_chatables_controller_spec.rb index 97e450a17..d1a0f88f1 100644 --- a/spec/requests/api/category_chatables_controller_spec.rb +++ b/spec/requests/api/category_chatables_controller_spec.rb @@ -1,23 +1,23 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe DiscourseChat::Api::CategoryChatablesController do - describe '#access_by_category' do + describe "#access_by_category" do fab!(:group) { Fabricate(:group) } fab!(:private_category) { Fabricate(:private_category, group: group) } - context 'signed in as an admin' do + context "signed in as an admin" do fab!(:admin) { Fabricate(:admin) } before { sign_in(admin) } - it 'returns a list with the group names that could access a chat channel' do + it "returns a list with the group names that could access a chat channel" do get "/chat/api/category-chatables/#{private_category.id}/permissions.json" - expect(response.parsed_body['allowed_groups']).to contain_exactly("@#{group.name}") - expect(response.parsed_body['members_count']).to eq(0) - expect(response.parsed_body['private']).to eq(true) + expect(response.parsed_body["allowed_groups"]).to contain_exactly("@#{group.name}") + expect(response.parsed_body["members_count"]).to eq(0) + expect(response.parsed_body["private"]).to eq(true) end it "doesn't return group names from other categories" do @@ -28,9 +28,9 @@ get "/chat/api/category-chatables/#{category_2.id}/permissions.json" - expect(response.parsed_body['allowed_groups']).to contain_exactly("@#{group_2.name}") - expect(response.parsed_body['members_count']).to eq(1) - expect(response.parsed_body['private']).to eq(true) + expect(response.parsed_body["allowed_groups"]).to contain_exactly("@#{group_2.name}") + expect(response.parsed_body["members_count"]).to eq(1) + expect(response.parsed_body["private"]).to eq(true) end it "returns the everyone group when a category is public" do @@ -40,45 +40,43 @@ get "/chat/api/category-chatables/#{category_2.id}/permissions.json" - expect(response.parsed_body['allowed_groups']).to contain_exactly("@#{everyone_group.name}") - expect(response.parsed_body['members_count']).to be_nil - expect(response.parsed_body['private']).to eq(false) + expect(response.parsed_body["allowed_groups"]).to contain_exactly("@#{everyone_group.name}") + expect(response.parsed_body["members_count"]).to be_nil + expect(response.parsed_body["private"]).to eq(false) end it "includes the number of users with access" do number_of_users = 3 - number_of_users.times do - group.add(Fabricate(:user)) - end + number_of_users.times { group.add(Fabricate(:user)) } get "/chat/api/category-chatables/#{private_category.id}/permissions.json" - expect(response.parsed_body['allowed_groups']).to contain_exactly("@#{group.name}") - expect(response.parsed_body['members_count']).to eq(number_of_users) - expect(response.parsed_body['private']).to eq(true) + expect(response.parsed_body["allowed_groups"]).to contain_exactly("@#{group.name}") + expect(response.parsed_body["members_count"]).to eq(number_of_users) + expect(response.parsed_body["private"]).to eq(true) end - it 'returns a 404 when passed an invalid category' do + it "returns a 404 when passed an invalid category" do get "/chat/api/category-chatables/-99/permissions.json" expect(response.status).to eq(404) end end - context 'as anon' do - it 'returns a 404' do + context "as anon" do + it "returns a 404" do get "/chat/api/category-chatables/#{private_category.id}/permissions.json" expect(response.status).to eq(404) end end - context 'signed in as a regular user' do + context "signed in as a regular user" do fab!(:user) { Fabricate(:user) } before { sign_in(user) } - it 'returns a 404' do + it "returns a 404" do get "/chat/api/category-chatables/#{private_category.id}/permissions.json" expect(response.status).to eq(404) diff --git a/spec/requests/api/chat_channel_memberships_spec.rb b/spec/requests/api/chat_channel_memberships_spec.rb index 9700444c5..a0561cca2 100644 --- a/spec/requests/api/chat_channel_memberships_spec.rb +++ b/spec/requests/api/chat_channel_memberships_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe DiscourseChat::Api::ChatChannelMembershipsController do fab!(:user_1) { Fabricate(:user) } @@ -11,21 +11,25 @@ SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:everyone] end - describe '#index' do - include_examples 'channel access example', :get, '/memberships.json' + describe "#index" do + include_examples "channel access example", :get, "/memberships.json" - context 'memberships exist' do + context "memberships exist" do before do UserChatChannelMembership.create(user: user_1, chat_channel: channel_1, following: true) - UserChatChannelMembership.create(user: Fabricate(:user), chat_channel: channel_1, following: false) + UserChatChannelMembership.create( + user: Fabricate(:user), + chat_channel: channel_1, + following: false, + ) sign_in(user_1) end - it 'lists followed memberships' do + it "lists followed memberships" do get "/chat/api/chat_channels/#{channel_1.id}/memberships.json" expect(response.parsed_body.length).to eq(1) - expect(response.parsed_body[0]['user']['id']).to eq(user_1.id) + expect(response.parsed_body[0]["user"]["id"]).to eq(user_1.id) end end end diff --git a/spec/requests/api/chat_channel_notifications_settings_controller_spec.rb b/spec/requests/api/chat_channel_notifications_settings_controller_spec.rb index ea7ba17f3..b0d7b5e20 100644 --- a/spec/requests/api/chat_channel_notifications_settings_controller_spec.rb +++ b/spec/requests/api/chat_channel_notifications_settings_controller_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe DiscourseChat::Api::ChatChannelNotificationsSettingsController do before do @@ -8,99 +8,108 @@ SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:everyone] end - describe '#update' do - include_examples 'channel access example', :put, '/notifications_settings.json' + describe "#update" do + include_examples "channel access example", :put, "/notifications_settings.json" - context 'invalid params' do + context "invalid params" do fab!(:chat_channel) { Fabricate(:chat_channel) } fab!(:user) { Fabricate(:user) } - fab!(:membership) { Fabricate(:user_chat_channel_membership, user: user, chat_channel: chat_channel) } - - before do - sign_in(user) + fab!(:membership) do + Fabricate(:user_chat_channel_membership, user: user, chat_channel: chat_channel) end - it 'doesn’t use invalid params' do - UserChatChannelMembership.any_instance.expects(:update!).with( - 'muted' => 'true', - ).once + before { sign_in(user) } + + it "doesn’t use invalid params" do + UserChatChannelMembership.any_instance.expects(:update!).with("muted" => "true").once - put "/chat/api/chat_channels/#{chat_channel.id}/notifications_settings.json", params: { - muted: true, - foo: 1 - } + put "/chat/api/chat_channels/#{chat_channel.id}/notifications_settings.json", + params: { + muted: true, + foo: 1, + } expect(response.status).to eq(200) end end - context 'valid params' do + context "valid params" do fab!(:chat_channel) { Fabricate(:chat_channel) } fab!(:user) { Fabricate(:user) } - fab!(:membership) { Fabricate(:user_chat_channel_membership, muted: false, user: user, chat_channel: chat_channel) } - - before do - sign_in(user) + fab!(:membership) do + Fabricate( + :user_chat_channel_membership, + muted: false, + user: user, + chat_channel: chat_channel, + ) end - it 'updates the notifications settings' do - put "/chat/api/chat_channels/#{chat_channel.id}/notifications_settings.json", params: { - muted: true, - desktop_notification_level: 'always', - mobile_notification_level: 'never', - } + before { sign_in(user) } + + it "updates the notifications settings" do + put "/chat/api/chat_channels/#{chat_channel.id}/notifications_settings.json", + params: { + muted: true, + desktop_notification_level: "always", + mobile_notification_level: "never", + } expect(response.status).to eq(200) - expect(response.parsed_body).to match_response_schema('user_chat_channel_membership') + expect(response.parsed_body).to match_response_schema("user_chat_channel_membership") membership.reload expect(membership.muted).to eq(true) - expect(membership.desktop_notification_level).to eq('always') - expect(membership.mobile_notification_level).to eq('never') + expect(membership.desktop_notification_level).to eq("always") + expect(membership.mobile_notification_level).to eq("never") end end - context 'membership doesn’t exist' do + context "membership doesn’t exist" do fab!(:chat_channel) { Fabricate(:chat_channel) } fab!(:user) { Fabricate(:user) } - before do - sign_in(user) - end + before { sign_in(user) } - it 'raises a 404' do + it "raises a 404" do put "/chat/api/chat_channels/#{chat_channel.id}/notifications_settings.json" expect(response.status).to eq(404) end end - context 'invalid params' do + context "invalid params" do fab!(:chatable) { Fabricate(:direct_message_channel) } fab!(:chat_channel) { Fabricate(:chat_channel, chatable: chatable) } - fab!(:membership) { - Fabricate(:user_chat_channel_membership, + fab!(:membership) do + Fabricate( + :user_chat_channel_membership, user: chatable.users[0], chat_channel: chat_channel, following: true, muted: false, desktop_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always], - mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always] + mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always], ) - } - - before do - sign_in(chatable.users[0]) end - it 'raises a 422' do - put "/chat/api/chat_channels/#{chat_channel.id}/notifications_settings.json", params: { - muted: true, - } + before { sign_in(chatable.users[0]) } + + it "raises a 422" do + put "/chat/api/chat_channels/#{chat_channel.id}/notifications_settings.json", + params: { + muted: true, + } expect(response.status).to eq(422) - expect(response.parsed_body['errors'][0]).to eq(I18n.t('activerecord.errors.format', attribute: 'Muted', message: I18n.t('activerecord.errors.messages.invalid'))) + expect(response.parsed_body["errors"][0]).to eq( + I18n.t( + "activerecord.errors.format", + attribute: "Muted", + message: I18n.t("activerecord.errors.messages.invalid"), + ), + ) end end end diff --git a/spec/requests/api/chat_channels_controller_spec.rb b/spec/requests/api/chat_channels_controller_spec.rb index b0a7c50e9..1aca9eeba 100644 --- a/spec/requests/api/chat_channels_controller_spec.rb +++ b/spec/requests/api/chat_channels_controller_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe DiscourseChat::Api::ChatChannelsController do before do @@ -8,70 +8,70 @@ SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:everyone] end - describe '#index' do - context 'anonymous user' do - it 'returns a 403' do - get '/chat/api/chat_channels.json' + describe "#index" do + context "anonymous user" do + it "returns a 403" do + get "/chat/api/chat_channels.json" expect(response.status).to eq(403) end end - describe 'params' do - fab!(:opened_channel) { Fabricate(:chat_channel, name: 'foo') } - fab!(:closed_channel) { Fabricate(:chat_channel, name: 'bar', status: :closed) } + describe "params" do + fab!(:opened_channel) { Fabricate(:chat_channel, name: "foo") } + fab!(:closed_channel) { Fabricate(:chat_channel, name: "bar", status: :closed) } before { sign_in(Fabricate(:user)) } - it 'returns all channels by default' do - get '/chat/api/chat_channels.json' + it "returns all channels by default" do + get "/chat/api/chat_channels.json" expect(response.status).to eq(200) expect(response.parsed_body.length).to eq(2) end - it 'returns serialized channels ' do - get '/chat/api/chat_channels.json' + it "returns serialized channels " do + get "/chat/api/chat_channels.json" expect(response.status).to eq(200) response.parsed_body.each do |channel| - expect(channel).to match_response_schema('category_chat_channel') + expect(channel).to match_response_schema("category_chat_channel") end end - describe 'filter' do - it 'returns channels filtered by name' do - get '/chat/api/chat_channels.json?filter=foo' + describe "filter" do + it "returns channels filtered by name" do + get "/chat/api/chat_channels.json?filter=foo" expect(response.status).to eq(200) results = response.parsed_body expect(results.length).to eq(1) - expect(results[0]['title']).to eq('foo') + expect(results[0]["title"]).to eq("foo") end end - describe 'status' do - it 'returns channels with the status' do - get '/chat/api/chat_channels.json?status=closed' + describe "status" do + it "returns channels with the status" do + get "/chat/api/chat_channels.json?status=closed" expect(response.status).to eq(200) results = response.parsed_body expect(results.length).to eq(1) - expect(results[0]['status']).to eq('closed') + expect(results[0]["status"]).to eq("closed") end end - describe 'limit' do - it 'returns a number of channel equal to the limit' do - get '/chat/api/chat_channels.json?limit=1' + describe "limit" do + it "returns a number of channel equal to the limit" do + get "/chat/api/chat_channels.json?limit=1" expect(response.status).to eq(200) results = response.parsed_body expect(results.length).to eq(1) end end - describe 'offset' do - it 'returns channels from the offset' do - get '/chat/api/chat_channels.json?offset=2' + describe "offset" do + it "returns channels from the offset" do + get "/chat/api/chat_channels.json?offset=2" expect(response.status).to eq(200) results = response.parsed_body @@ -81,7 +81,7 @@ end end - describe '#create' do + describe "#create" do fab!(:admin) { Fabricate(:admin) } fab!(:category) { Fabricate(:category) } @@ -89,15 +89,15 @@ { type: category.class.name, id: category.id, - name: 'channel name', - description: 'My new channel' + name: "channel name", + description: "My new channel", } end before { sign_in(admin) } - it 'creates a channel associated to a category' do - put '/chat/chat_channels.json', params: params + it "creates a channel associated to a category" do + put "/chat/chat_channels.json", params: params new_channel = ChatChannel.last @@ -107,23 +107,23 @@ expect(new_channel.chatable_id).to eq(category.id) end - it 'creates a channel sets auto_join_users to false by default' do - put '/chat/chat_channels.json', params: params + it "creates a channel sets auto_join_users to false by default" do + put "/chat/chat_channels.json", params: params new_channel = ChatChannel.last expect(new_channel.auto_join_users).to eq(false) end - it 'creates a channel with auto_join_users set to true' do - put '/chat/chat_channels.json', params: params.merge(auto_join_users: true) + it "creates a channel with auto_join_users set to true" do + put "/chat/chat_channels.json", params: params.merge(auto_join_users: true) new_channel = ChatChannel.last expect(new_channel.auto_join_users).to eq(true) end - describe 'triggers the auto-join process' do + describe "triggers the auto-join process" do fab!(:chatters_group) { Fabricate(:group) } fab!(:user) { Fabricate(:user, last_seen_at: 15.minute.ago) } @@ -133,148 +133,159 @@ chatters_group.add(user) end - it 'joins the user when auto_join_users is true' do - put '/chat/chat_channels.json', params: params.merge(auto_join_users: true) + it "joins the user when auto_join_users is true" do + put "/chat/chat_channels.json", params: params.merge(auto_join_users: true) - created_channel_id = response.parsed_body.dig('chat_channel', 'id') - membership_exists = UserChatChannelMembership.find_by( - user: user, chat_channel_id: created_channel_id, following: true - ) + created_channel_id = response.parsed_body.dig("chat_channel", "id") + membership_exists = + UserChatChannelMembership.find_by( + user: user, + chat_channel_id: created_channel_id, + following: true, + ) expect(membership_exists).to be_present end it "doesn't join the user when auto_join_users is false" do - put '/chat/chat_channels.json', params: params.merge(auto_join_users: false) + put "/chat/chat_channels.json", params: params.merge(auto_join_users: false) - created_channel_id = response.parsed_body.dig('chat_channel', 'id') - membership_exists = UserChatChannelMembership.find_by( - user: user, chat_channel_id: created_channel_id, following: true - ) + created_channel_id = response.parsed_body.dig("chat_channel", "id") + membership_exists = + UserChatChannelMembership.find_by( + user: user, + chat_channel_id: created_channel_id, + following: true, + ) expect(membership_exists).to be_nil end end end - describe '#update' do - include_examples 'channel access example', :put + describe "#update" do + include_examples "channel access example", :put - context 'user can’t edit channel' do + context "user can’t edit channel" do fab!(:chat_channel) { Fabricate(:chat_channel) } - before do - sign_in(Fabricate(:user)) - end + before { sign_in(Fabricate(:user)) } - it 'returns a 403' do + it "returns a 403" do put "/chat/api/chat_channels/#{chat_channel.id}.json" expect(response.status).to eq(403) end end - context 'user provided invalid params' do + context "user provided invalid params" do fab!(:chat_channel) { Fabricate(:chat_channel, user_count: 10) } - before do - sign_in(Fabricate(:admin)) - end + before { sign_in(Fabricate(:admin)) } - it 'doesn’t change invalid properties' do + it "doesn’t change invalid properties" do put "/chat/api/chat_channels/#{chat_channel.id}.json", params: { user_count: 40 } expect(chat_channel.reload.user_count).to eq(10) end end - context 'user provided an empty name' do + context "user provided an empty name" do fab!(:user) { Fabricate(:admin) } - fab!(:chat_channel) { Fabricate(:chat_channel, name: 'something', description: 'something else') } + fab!(:chat_channel) do + Fabricate(:chat_channel, name: "something", description: "something else") + end before { sign_in(user) } - it 'nullifies the field and doesn’t store an empty string' do - put "/chat/api/chat_channels/#{chat_channel.id}.json", params: { name: ' ' } + it "nullifies the field and doesn’t store an empty string" do + put "/chat/api/chat_channels/#{chat_channel.id}.json", params: { name: " " } expect(chat_channel.reload.name).to be_nil end - it 'doesn’t nullify the description' do - put "/chat/api/chat_channels/#{chat_channel.id}.json", params: { name: ' ' } + it "doesn’t nullify the description" do + put "/chat/api/chat_channels/#{chat_channel.id}.json", params: { name: " " } - expect(chat_channel.reload.description).to eq('something else') + expect(chat_channel.reload.description).to eq("something else") end end - context 'user provided an empty description' do + context "user provided an empty description" do fab!(:user) { Fabricate(:admin) } - fab!(:chat_channel) { Fabricate(:chat_channel, name: 'something else', description: 'something') } + fab!(:chat_channel) do + Fabricate(:chat_channel, name: "something else", description: "something") + end before { sign_in(user) } - it 'nullifies the field and doesn’t store an empty string' do - put "/chat/api/chat_channels/#{chat_channel.id}.json", params: { description: ' ' } + it "nullifies the field and doesn’t store an empty string" do + put "/chat/api/chat_channels/#{chat_channel.id}.json", params: { description: " " } expect(chat_channel.reload.description).to be_nil end - it 'doesn’t nullify the name' do - put "/chat/api/chat_channels/#{chat_channel.id}.json", params: { description: ' ' } + it "doesn’t nullify the name" do + put "/chat/api/chat_channels/#{chat_channel.id}.json", params: { description: " " } - expect(chat_channel.reload.name).to eq('something else') + expect(chat_channel.reload.name).to eq("something else") end end - context 'channel is a direct message channel' do + context "channel is a direct message channel" do fab!(:user) { Fabricate(:admin) } fab!(:chatable) { Fabricate(:direct_message_channel) } fab!(:chat_channel) { Fabricate(:chat_channel, chatable: chatable) } before { sign_in(user) } - it 'raises a 403' do + it "raises a 403" do put "/chat/api/chat_channels/#{chat_channel.id}.json" expect(response.status).to eq(403) end end - context 'user provided valid params' do + context "user provided valid params" do fab!(:user) { Fabricate(:admin) } fab!(:chat_channel) { Fabricate(:chat_channel) } before { sign_in(user) } - it 'sets properties' do - put "/chat/api/chat_channels/#{chat_channel.id}.json", params: { name: 'joffrey', description: 'cat owner' } + it "sets properties" do + put "/chat/api/chat_channels/#{chat_channel.id}.json", + params: { + name: "joffrey", + description: "cat owner", + } - expect(chat_channel.reload.name).to eq('joffrey') - expect(chat_channel.reload.description).to eq('cat owner') + expect(chat_channel.reload.name).to eq("joffrey") + expect(chat_channel.reload.description).to eq("cat owner") end - it 'publishes an update' do - messages = MessageBus.track_publish('/chat/channel-edits') do - put "/chat/api/chat_channels/#{chat_channel.id}.json" - end + it "publishes an update" do + messages = + MessageBus.track_publish("/chat/channel-edits") do + put "/chat/api/chat_channels/#{chat_channel.id}.json" + end expect(messages[0].data[:chat_channel_id]).to eq(chat_channel.id) end - it 'returns a valid chat channel' do + it "returns a valid chat channel" do put "/chat/api/chat_channels/#{chat_channel.id}.json" - expect(response.parsed_body).to match_response_schema('category_chat_channel') + expect(response.parsed_body).to match_response_schema("category_chat_channel") end - describe 'Updating a channel to add users automatically' do - it 'sets the channel to auto-update users automatically' do + describe "Updating a channel to add users automatically" do + it "sets the channel to auto-update users automatically" do put "/chat/api/chat_channels/#{chat_channel.id}.json", params: { auto_join_users: true } - expect(response.parsed_body['auto_join_users']).to eq(true) + expect(response.parsed_body["auto_join_users"]).to eq(true) end - it 'tells staff members to slow down when toggling auto-update multiple times' do + it "tells staff members to slow down when toggling auto-update multiple times" do RateLimiter.enable put "/chat/api/chat_channels/#{chat_channel.id}.json", params: { auto_join_users: true } @@ -284,7 +295,7 @@ expect(response.status).to eq(429) end - describe 'triggers the auto-join process' do + describe "triggers the auto-join process" do fab!(:chatters_group) { Fabricate(:group) } fab!(:another_user) { Fabricate(:user, last_seen_at: 15.minute.ago) } @@ -294,24 +305,33 @@ chatters_group.add(another_user) end - it 'joins the user when auto_join_users is true' do + it "joins the user when auto_join_users is true" do put "/chat/api/chat_channels/#{chat_channel.id}.json", params: { auto_join_users: true } - created_channel_id = response.parsed_body['id'] - membership_exists = UserChatChannelMembership.find_by( - user: another_user, chat_channel_id: created_channel_id, following: true - ) + created_channel_id = response.parsed_body["id"] + membership_exists = + UserChatChannelMembership.find_by( + user: another_user, + chat_channel_id: created_channel_id, + following: true, + ) expect(membership_exists).to be_present end it "doesn't join the user when auto_join_users is false" do - put "/chat/api/chat_channels/#{chat_channel.id}.json", params: { auto_join_users: false } - - created_channel_id = response.parsed_body['id'] - membership_exists = UserChatChannelMembership.find_by( - user: another_user, chat_channel_id: created_channel_id, following: true - ) + put "/chat/api/chat_channels/#{chat_channel.id}.json", + params: { + auto_join_users: false, + } + + created_channel_id = response.parsed_body["id"] + membership_exists = + UserChatChannelMembership.find_by( + user: another_user, + chat_channel_id: created_channel_id, + following: true, + ) expect(membership_exists).to be_nil end diff --git a/spec/requests/chat_channel_controller_spec.rb b/spec/requests/chat_channel_controller_spec.rb index 597d06f93..2e7a25190 100644 --- a/spec/requests/chat_channel_controller_spec.rb +++ b/spec/requests/chat_channel_controller_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" RSpec.describe DiscourseChat::ChatChannelsController do fab!(:user) { Fabricate(:user, username: "johndoe", name: "John Doe") } @@ -8,7 +8,9 @@ fab!(:admin) { Fabricate(:admin, username: "andyjones", name: "Andy Jones") } fab!(:category) { Fabricate(:category) } fab!(:chat_channel) { Fabricate(:chat_channel, chatable: category) } - fab!(:dm_chat_channel) { Fabricate(:chat_channel, chatable: Fabricate(:direct_message_channel, users: [user, admin])) } + fab!(:dm_chat_channel) do + Fabricate(:chat_channel, chatable: Fabricate(:direct_message_channel, users: [user, admin])) + end before do SiteSetting.chat_enabled = true @@ -26,9 +28,14 @@ describe "with memberships for all channels" do before do ChatChannel.all.each do |cc| - model = cc.direct_message_channel? ? - :user_chat_channel_membership_for_dm : - :user_chat_channel_membership + model = + ( + if cc.direct_message_channel? + :user_chat_channel_membership_for_dm + else + :user_chat_channel_membership + end + ) Fabricate(model, chat_channel: cc, user: user) Fabricate(model, chat_channel: cc, user: user_with_private_access) @@ -50,8 +57,9 @@ get "/chat/chat_channels.json" expect(response.status).to eq(200) - expect(response.parsed_body["public_channels"].map { |channel| channel["id"] }) - .to match_array([chat_channel.id]) + expect( + response.parsed_body["public_channels"].map { |channel| channel["id"] }, + ).to match_array([chat_channel.id]) end it "returns channels visible to user with private access" do @@ -59,11 +67,9 @@ get "/chat/chat_channels.json" expect(response.status).to eq(200) - expect(response.parsed_body["public_channels"].map { |channel| channel["id"] }) - .to match_array([ - chat_channel.id, - private_category_cc.id - ]) + expect( + response.parsed_body["public_channels"].map { |channel| channel["id"] }, + ).to match_array([chat_channel.id, private_category_cc.id]) end it "returns all channels for admin" do @@ -71,11 +77,9 @@ get "/chat/chat_channels.json" expect(response.status).to eq(200) - expect(response.parsed_body["public_channels"].map { |channel| channel["id"] }) - .to match_array([ - chat_channel.id, - private_category_cc.id, - ]) + expect( + response.parsed_body["public_channels"].map { |channel| channel["id"] }, + ).to match_array([chat_channel.id, private_category_cc.id]) end it "doesn't error when a chat channel's chatable is destroyed" do @@ -92,11 +96,11 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: chat_channel, user: user, - content: "Hi @#{admin.username}" + content: "Hi @#{admin.username}", ) - get "/chat/chat_channels.json" - cc = response.parsed_body["public_channels"].detect { |c| c["id"] == chat_channel.id } - expect(cc["unread_mentions"]).to eq(1) + get "/chat/chat_channels.json" + cc = response.parsed_body["public_channels"].detect { |c| c["id"] == chat_channel.id } + expect(cc["unread_mentions"]).to eq(1) end describe "direct messages" do @@ -107,7 +111,8 @@ before do @dm1 = DiscourseChat::DirectMessageChannelCreator.create!(target_users: [user1, user2]) @dm2 = DiscourseChat::DirectMessageChannelCreator.create!(target_users: [user1, user3]) - @dm3 = DiscourseChat::DirectMessageChannelCreator.create!(target_users: [user1, user2, user3]) + @dm3 = + DiscourseChat::DirectMessageChannelCreator.create!(target_users: [user1, user2, user3]) @dm4 = DiscourseChat::DirectMessageChannelCreator.create!(target_users: [user2, user3]) end @@ -115,24 +120,27 @@ sign_in(user1) get "/chat/chat_channels.json" - expect(response.parsed_body["direct_message_channels"].map { |c| c["id"] }) - .to match_array([@dm1.id, @dm2.id, @dm3.id]) + expect( + response.parsed_body["direct_message_channels"].map { |c| c["id"] }, + ).to match_array([@dm1.id, @dm2.id, @dm3.id]) end it "returns correct DMs for user2" do sign_in(user2) get "/chat/chat_channels.json" - expect(response.parsed_body["direct_message_channels"].map { |c| c["id"] }) - .to match_array([@dm1.id, @dm3.id, @dm4.id]) + expect( + response.parsed_body["direct_message_channels"].map { |c| c["id"] }, + ).to match_array([@dm1.id, @dm3.id, @dm4.id]) end it "returns correct DMs for user3" do sign_in(user3) get "/chat/chat_channels.json" - expect(response.parsed_body["direct_message_channels"].map { |c| c["id"] }) - .to match_array([@dm2.id, @dm3.id, @dm4.id]) + expect( + response.parsed_body["direct_message_channels"].map { |c| c["id"] }, + ).to match_array([@dm2.id, @dm3.id, @dm4.id]) end it "correctly set unread_count for DMs" do @@ -140,10 +148,11 @@ DiscourseChat::ChatMessageCreator.create( chat_channel: @dm2, user: user1, - content: "What's going on?!" + content: "What's going on?!", ) get "/chat/chat_channels.json" - dm2_response = response.parsed_body["direct_message_channels"].detect { |c| c["id"] == @dm2.id } + dm2_response = + response.parsed_body["direct_message_channels"].detect { |c| c["id"] == @dm2.id } expect(dm2_response["unread_count"]).to eq(1) end end @@ -153,9 +162,7 @@ describe "#follow" do it "creates a user_chat_channel_membership record if one doesn't exist" do sign_in(user) - expect { - post "/chat/chat_channels/#{chat_channel.id}/follow.json" - }.to change { + expect { post "/chat/chat_channels/#{chat_channel.id}/follow.json" }.to change { UserChatChannelMembership.where(user_id: user.id, following: true).count }.by(1) expect(response.status).to eq(200) @@ -163,15 +170,14 @@ it "updates 'following' to true for existing record" do sign_in(user) - membership_record = UserChatChannelMembership.create!( - chat_channel_id: chat_channel.id, - user_id: user.id, - following: false - ) + membership_record = + UserChatChannelMembership.create!( + chat_channel_id: chat_channel.id, + user_id: user.id, + following: false, + ) - expect { - post "/chat/chat_channels/#{chat_channel.id}/follow.json" - }.to change { + expect { post "/chat/chat_channels/#{chat_channel.id}/follow.json" }.to change { membership_record.reload.following }.to(true).from(false) expect(response.status).to eq(200) @@ -181,15 +187,14 @@ describe "#unfollow" do it "updates 'following' to false for existing record" do sign_in(user) - membership_record = UserChatChannelMembership.create!( - chat_channel_id: chat_channel.id, - user_id: user.id, - following: true - ) + membership_record = + UserChatChannelMembership.create!( + chat_channel_id: chat_channel.id, + user_id: user.id, + following: true, + ) - expect { - post "/chat/chat_channels/#{chat_channel.id}/unfollow.json" - }.to change { + expect { post "/chat/chat_channels/#{chat_channel.id}/unfollow.json" }.to change { membership_record.reload.following }.to(false).from(true) expect(response.status).to eq(200) @@ -197,13 +202,14 @@ it "allows to unfollow a direct_message_channel" do sign_in(user) - membership_record = UserChatChannelMembership.create!( - chat_channel_id: dm_chat_channel.id, - user_id: user.id, - following: true, - desktop_notification_level: 2, - mobile_notification_level: 2, - ) + membership_record = + UserChatChannelMembership.create!( + chat_channel_id: dm_chat_channel.id, + user_id: user.id, + following: true, + desktop_notification_level: 2, + mobile_notification_level: 2, + ) post "/chat/chat_channels/#{dm_chat_channel.id}/unfollow.json" expect(response.status).to eq(200) @@ -229,7 +235,11 @@ it "errors when the name is over SiteSetting.max_topic_title_length" do sign_in(admin) SiteSetting.max_topic_title_length = 10 - put "/chat/chat_channels.json", params: { id: category2.id, name: "Hi, this is over 10 characters" } + put "/chat/chat_channels.json", + params: { + id: category2.id, + name: "Hi, this is over 10 characters", + } expect(response.status).to eq(400) end @@ -301,7 +311,11 @@ sign_in(admin) new_name = "beep boop" new_description = "this is something" - post "/chat/chat_channels/#{chat_channel.id}.json", params: { name: new_name, description: new_description } + post "/chat/chat_channels/#{chat_channel.id}.json", + params: { + name: new_name, + description: new_description, + } expect(response.status).to eq(200) expect(chat_channel.reload.name).to eq(new_name) expect(chat_channel.description).to eq(new_description) @@ -309,7 +323,6 @@ end describe "#search" do - describe "without chat permissions" do it "errors errors for anon" do get "/chat/chat_channels/search.json", params: { filter: "so" } @@ -454,7 +467,9 @@ end describe "#show" do - fab!(:channel) { Fabricate(:chat_channel, chatable: category, name: "My Great Channel & Stuff") } + fab!(:channel) do + Fabricate(:chat_channel, chatable: category, name: "My Great Channel & Stuff") + end it "can find channel by id" do sign_in(user) @@ -491,7 +506,9 @@ describe "#archive" do fab!(:channel) { Fabricate(:chat_channel, chatable: category, name: "The English Channel") } - let(:new_topic_params) { { type: "newTopic", title: "This is a test archive topic", category_id: category.id } } + let(:new_topic_params) do + { type: "newTopic", title: "This is a test archive topic", category_id: category.id } + end let(:existing_topic_params) { { type: "existingTopic", topic_id: Fabricate(:topic).id } } it "returns error if user is not staff" do @@ -529,7 +546,14 @@ sign_in(admin) put "/chat/chat_channels/#{channel.id}/archive.json", params: new_topic_params channel_archive = ChatChannelArchive.find_by(chat_channel: channel) - expect(job_enqueued?(job: :chat_channel_archive, args: { chat_channel_archive_id: channel_archive.id })).to eq(true) + expect( + job_enqueued?( + job: :chat_channel_archive, + args: { + chat_channel_archive_id: channel_archive.id, + }, + ), + ).to eq(true) expect(channel.reload.status).to eq("read_only") end @@ -537,7 +561,14 @@ sign_in(admin) put "/chat/chat_channels/#{channel.id}/archive.json", params: existing_topic_params channel_archive = ChatChannelArchive.find_by(chat_channel: channel) - expect(job_enqueued?(job: :chat_channel_archive, args: { chat_channel_archive_id: channel_archive.id })).to eq(true) + expect( + job_enqueued?( + job: :chat_channel_archive, + args: { + chat_channel_archive_id: channel_archive.id, + }, + ), + ).to eq(true) expect(channel.reload.status).to eq("read_only") end @@ -553,19 +584,14 @@ describe "#retry_archive" do fab!(:channel) do - Fabricate( - :chat_channel, - chatable: category, - name: "The English Channel", - status: :read_only - ) + Fabricate(:chat_channel, chatable: category, name: "The English Channel", status: :read_only) end fab!(:archive) do ChatChannelArchive.create!( chat_channel: channel, destination_topic_title: "test archive topic title", archived_by: admin, - total_messages: 10 + total_messages: 10, ) end @@ -602,18 +628,15 @@ archive.update!(archive_error: "bad stuff", archived_messages: 1) put "/chat/chat_channels/#{channel.id}/retry_archive.json" expect(response.status).to eq(200) - expect(job_enqueued?(job: :chat_channel_archive, args: { chat_channel_archive_id: archive.id })).to eq(true) + expect( + job_enqueued?(job: :chat_channel_archive, args: { chat_channel_archive_id: archive.id }), + ).to eq(true) end end describe "#change_status" do fab!(:channel) do - Fabricate( - :chat_channel, - chatable: category, - name: "Channel Orange", - status: :open - ) + Fabricate(:chat_channel, chatable: category, name: "Channel Orange", status: :open) end it "returns error if user is not staff" do @@ -654,45 +677,54 @@ describe "#delete" do fab!(:channel) do - Fabricate( - :chat_channel, - chatable: category, - name: "Ambrose Channel", - status: :open - ) + Fabricate(:chat_channel, chatable: category, name: "Ambrose Channel", status: :open) end it "returns error if user is not staff" do sign_in(user) - delete "/chat/chat_channels/#{channel.id}.json", params: { channel_name_confirmation: "ambrose channel" } + delete "/chat/chat_channels/#{channel.id}.json", + params: { + channel_name_confirmation: "ambrose channel", + } expect(response.status).to eq(403) end it "returns a 404 if the channel does not exist" do channel.destroy! sign_in(admin) - delete "/chat/chat_channels/#{channel.id}.json", params: { channel_name_confirmation: "ambrose channel" } + delete "/chat/chat_channels/#{channel.id}.json", + params: { + channel_name_confirmation: "ambrose channel", + } expect(response.status).to eq(404) end it "returns a 400 if the channel_name_confirmation does not match the channel name" do sign_in(admin) - delete "/chat/chat_channels/#{channel.id}.json", params: { channel_name_confirmation: "some Other channel" } + delete "/chat/chat_channels/#{channel.id}.json", + params: { + channel_name_confirmation: "some Other channel", + } expect(response.status).to eq(400) end it "deletes the channel right away and enqueues the background job to delete all its chat messages and related content" do sign_in(admin) - delete "/chat/chat_channels/#{channel.id}.json", params: { channel_name_confirmation: "ambrose channel" } + delete "/chat/chat_channels/#{channel.id}.json", + params: { + channel_name_confirmation: "ambrose channel", + } expect(response.status).to eq(200) expect(channel.reload.trashed?).to eq(true) - expect(job_enqueued?(job: :chat_channel_delete, args: { chat_channel_id: channel.id })).to eq(true) + expect(job_enqueued?(job: :chat_channel_delete, args: { chat_channel_id: channel.id })).to eq( + true, + ) expect( UserHistory.exists?( acting_user_id: admin.id, action: UserHistory.actions[:custom_staff], - custom_type: "chat_channel_delete" - ) + custom_type: "chat_channel_delete", + ), ).to eq(true) end end diff --git a/spec/requests/chat_controller_spec.rb b/spec/requests/chat_controller_spec.rb index 2e9e022ed..92705a08e 100644 --- a/spec/requests/chat_controller_spec.rb +++ b/spec/requests/chat_controller_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" RSpec.describe DiscourseChat::ChatController do fab!(:user) { Fabricate(:user) } @@ -8,12 +8,24 @@ fab!(:admin) { Fabricate(:admin) } fab!(:category) { Fabricate(:category) } fab!(:chat_channel) { Fabricate(:chat_channel, chatable: category) } - fab!(:dm_chat_channel) { Fabricate(:chat_channel, chatable: Fabricate(:direct_message_channel, users: [user, other_user, admin])) } + fab!(:dm_chat_channel) do + Fabricate( + :chat_channel, + chatable: Fabricate(:direct_message_channel, users: [user, other_user, admin]), + ) + end fab!(:tag) { Fabricate(:tag) } MESSAGE_COUNT = 70 MESSAGE_COUNT.times do |n| - fab!("message_#{n}") { Fabricate(:chat_message, chat_channel: chat_channel, user: other_user, message: "message #{n}") } + fab!("message_#{n}") do + Fabricate( + :chat_message, + chat_channel: chat_channel, + user: other_user, + message: "message #{n}", + ) + end end before do @@ -24,9 +36,7 @@ describe "#messages" do let(:page_size) { 30 } - before do - sign_in(user) - end + before { sign_in(user) } it "errors for user when they are not allowed to chat" do SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:staff] @@ -90,7 +100,9 @@ reviewable_score = chat_message.add_flag(user) get "/chat/#{chat_channel.id}/messages.json", params: { page_size: page_size } - expect(response.parsed_body["chat_messages"].last["user_flag_status"]).to eq(reviewable_score.status) + expect(response.parsed_body["chat_messages"].last["user_flag_status"]).to eq( + reviewable_score.status, + ) expect(response.parsed_body["chat_messages"].second_to_last["user_flag_status"]).to be_nil end @@ -113,7 +125,9 @@ get "/chat/#{chat_channel.id}/messages.json", params: { page_size: page_size } expect(response.parsed_body["chat_messages"].last["reviewable_id"]).to eq(last_reviewable.id) - expect(response.parsed_body["chat_messages"].second_to_last["reviewable_id"]).to eq(second_to_last_reviewable.id) + expect(response.parsed_body["chat_messages"].second_to_last["reviewable_id"]).to eq( + second_to_last_reviewable.id, + ) end it "correctly marks reactions as 'reacted' for the current_user" do @@ -132,7 +146,12 @@ describe "scrolling to the past" do it "returns the correct messages in created_at, id order" do - get "/chat/#{chat_channel.id}/messages.json", params: { message_id: message_40.id, direction: described_class::PAST, page_size: page_size } + get "/chat/#{chat_channel.id}/messages.json", + params: { + message_id: message_40.id, + direction: described_class::PAST, + page_size: page_size, + } messages = response.parsed_body["chat_messages"] expect(messages.count).to eq(page_size) expect(messages.first["created_at"].to_time).to eq_time(message_10.created_at) @@ -140,13 +159,23 @@ end it "returns 'can_load...' properly when there are more past messages" do - get "/chat/#{chat_channel.id}/messages.json", params: { message_id: message_40.id, direction: described_class::PAST, page_size: page_size } + get "/chat/#{chat_channel.id}/messages.json", + params: { + message_id: message_40.id, + direction: described_class::PAST, + page_size: page_size, + } expect(response.parsed_body["meta"]["can_load_more_past"]).to be true expect(response.parsed_body["meta"]["can_load_more_future"]).to be_nil end it "returns 'can_load...' properly when there are no past messages" do - get "/chat/#{chat_channel.id}/messages.json", params: { message_id: message_3.id, direction: described_class::PAST, page_size: page_size } + get "/chat/#{chat_channel.id}/messages.json", + params: { + message_id: message_3.id, + direction: described_class::PAST, + page_size: page_size, + } expect(response.parsed_body["meta"]["can_load_more_past"]).to be false expect(response.parsed_body["meta"]["can_load_more_future"]).to be_nil end @@ -154,7 +183,12 @@ describe "scrolling to the future" do it "returns the correct messages in created_at, id order when there are many after" do - get "/chat/#{chat_channel.id}/messages.json", params: { message_id: message_10.id, direction: described_class::FUTURE, page_size: page_size } + get "/chat/#{chat_channel.id}/messages.json", + params: { + message_id: message_10.id, + direction: described_class::FUTURE, + page_size: page_size, + } messages = response.parsed_body["chat_messages"] expect(messages.count).to eq(page_size) expect(messages.first["created_at"].to_time).to eq_time(message_11.created_at) @@ -162,32 +196,42 @@ end it "return 'can_load..' properly when there are future messages" do - get "/chat/#{chat_channel.id}/messages.json", params: { message_id: message_10.id, direction: described_class::FUTURE, page_size: page_size } + get "/chat/#{chat_channel.id}/messages.json", + params: { + message_id: message_10.id, + direction: described_class::FUTURE, + page_size: page_size, + } expect(response.parsed_body["meta"]["can_load_more_past"]).to be_nil expect(response.parsed_body["meta"]["can_load_more_future"]).to be true end it "returns 'can_load..' properly when there are no future messages" do - get "/chat/#{chat_channel.id}/messages.json", params: { message_id: message_60.id, direction: described_class::FUTURE, page_size: page_size } + get "/chat/#{chat_channel.id}/messages.json", + params: { + message_id: message_60.id, + direction: described_class::FUTURE, + page_size: page_size, + } expect(response.parsed_body["meta"]["can_load_more_past"]).to be_nil expect(response.parsed_body["meta"]["can_load_more_future"]).to be false end end - describe 'without direction (latest messages)' do - it 'signals there are no future messages' do + describe "without direction (latest messages)" do + it "signals there are no future messages" do get "/chat/#{chat_channel.id}/messages.json", params: { page_size: page_size } expect(response.parsed_body["meta"]["can_load_more_future"]).to eq(false) end - it 'signals there are more messages in the past' do + it "signals there are more messages in the past" do get "/chat/#{chat_channel.id}/messages.json", params: { page_size: page_size } expect(response.parsed_body["meta"]["can_load_more_past"]).to eq(true) end - it 'signals there are no more messages' do + it "signals there are no more messages" do new_channel = Fabricate(:chat_channel) Fabricate(:chat_message, chat_channel: new_channel, user: other_user, message: "message") chat_messages_qty = 1 @@ -272,7 +316,7 @@ post "/chat/#{chat_channel.id}.json", params: { message: message } expect(response.status).to eq(422) expect(response.parsed_body["errors"]).to include( - I18n.t("chat.errors.channel_new_message_disallowed", status: chat_channel.status_name) + I18n.t("chat.errors.channel_new_message_disallowed", status: chat_channel.status_name), ) end @@ -288,7 +332,7 @@ post "/chat/#{chat_channel.id}.json", params: { message: message } expect(response.status).to eq(422) expect(response.parsed_body["errors"]).to include( - I18n.t("chat.errors.channel_new_message_disallowed", status: chat_channel.status_name) + I18n.t("chat.errors.channel_new_message_disallowed", status: chat_channel.status_name), ) end @@ -296,26 +340,38 @@ sign_in(user) UserChatChannelMembership.create(user: user, chat_channel: chat_channel, following: true) - expect { - post "/chat/#{chat_channel.id}.json", params: { message: message } - }.to change { ChatMessage.count }.by(1) + expect { post "/chat/#{chat_channel.id}.json", params: { message: message } }.to change { + ChatMessage.count + }.by(1) expect(response.status).to eq(200) expect(ChatMessage.last.message).to eq(message) end end - describe 'for direct message' do + describe "for direct message" do fab!(:user1) { Fabricate(:user) } fab!(:user2) { Fabricate(:user) } fab!(:chatable) { Fabricate(:direct_message_channel, users: [user1, user2]) } fab!(:direct_message_channel) { Fabricate(:chat_channel, chatable: chatable) } def create_memberships - UserChatChannelMembership.create!(user: user1, chat_channel: direct_message_channel, following: true, desktop_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always], mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always]) - UserChatChannelMembership.create!(user: user2, chat_channel: direct_message_channel, following: false, desktop_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always], mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always]) + UserChatChannelMembership.create!( + user: user1, + chat_channel: direct_message_channel, + following: true, + desktop_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always], + mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always], + ) + UserChatChannelMembership.create!( + user: user2, + chat_channel: direct_message_channel, + following: false, + desktop_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always], + mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always], + ) end - it 'forces users to follow the channel' do + it "forces users to follow the channel" do create_memberships expect(UserChatChannelMembership.find_by(user_id: user2.id).following).to be false @@ -328,7 +384,7 @@ def create_memberships expect(UserChatChannelMembership.find_by(user_id: user2.id).following).to be true end - it 'errors when the user is not part of the direct message channel' do + it "errors when the user is not part of the direct message channel" do create_memberships DirectMessageUser.find_by(user: user1, direct_message_channel: chatable).destroy! @@ -351,7 +407,12 @@ def create_memberships it "rebakes the post" do sign_in(Fabricate(:admin)) - expect_enqueued_with(job: :process_chat_message, args: { chat_message_id: chat_message.id }) do + expect_enqueued_with( + job: :process_chat_message, + args: { + chat_message_id: chat_message.id, + }, + ) do put "/chat/#{chat_channel.id}/#{chat_message.id}/rebake.json" expect(response.status).to eq(200) @@ -380,7 +441,13 @@ def create_memberships sign_in(Fabricate(:admin)) chat_message.update!(message: "new content") - expect_enqueued_with(job: :process_chat_message, args: { chat_message_id: chat_message.id, is_dirty: true }) do + expect_enqueued_with( + job: :process_chat_message, + args: { + chat_message_id: chat_message.id, + is_dirty: true, + }, + ) do put "/chat/#{chat_channel.id}/#{chat_message.id}/rebake.json" expect(response.status).to eq(200) @@ -436,14 +503,17 @@ def create_memberships sign_in(admin) new_message = "Vrroooom cars go fast" - put "/chat/#{chat_channel.id}/edit/#{chat_message.id}.json", params: { new_message: new_message } + put "/chat/#{chat_channel.id}/edit/#{chat_message.id}.json", + params: { + new_message: new_message, + } expect(response.status).to eq(403) end it "errors when the user is silenced" do UserSilencer.new(user).silence sign_in(user) - put "/chat/#{chat_channel.id}/edit/#{chat_message.id}.json", params: { new_message: 'Hi' } + put "/chat/#{chat_channel.id}/edit/#{chat_message.id}.json", params: { new_message: "Hi" } expect(response.status).to eq(403) end @@ -451,7 +521,10 @@ def create_memberships sign_in(user) new_message = "Wow markvanlan must be a good programmer" - put "/chat/#{chat_channel.id}/edit/#{chat_message.id}.json", params: { new_message: new_message } + put "/chat/#{chat_channel.id}/edit/#{chat_message.id}.json", + params: { + new_message: new_message, + } expect(response.status).to eq(200) expect(chat_message.reload.message).to eq(new_message) end @@ -476,9 +549,9 @@ def create_memberships it "Allows admin to delete others' messages" do sign_in(admin) - expect { - delete "/chat/#{chat_channel.id}/#{ChatMessage.last.id}.json" - }.to change { ChatMessage.count }.by(-1) + expect { delete "/chat/#{chat_channel.id}/#{ChatMessage.last.id}.json" }.to change { + ChatMessage.count + }.by(-1) expect(response.status).to eq(200) end @@ -486,9 +559,9 @@ def create_memberships sign_in(ChatMessage.last.user) chat_channel.update!(status: :read_only) - expect { - delete "/chat/#{chat_channel.id}/#{ChatMessage.last.id}.json" - }.not_to change { ChatMessage.count } + expect { delete "/chat/#{chat_channel.id}/#{ChatMessage.last.id}.json" }.not_to change { + ChatMessage.count + } expect(response.status).to eq(403) sign_in(admin) @@ -500,22 +573,24 @@ def create_memberships sign_in(admin) chat_channel.update!(status: :read_only) - expect { - delete "/chat/#{chat_channel.id}/#{ChatMessage.last.id}.json" - }.not_to change { ChatMessage.count } + expect { delete "/chat/#{chat_channel.id}/#{ChatMessage.last.id}.json" }.not_to change { + ChatMessage.count + } expect(response.status).to eq(403) chat_channel.update!(status: :closed) - expect { - delete "/chat/#{chat_channel.id}/#{ChatMessage.last.id}.json" - }.to change { ChatMessage.count }.by(-1) + expect { delete "/chat/#{chat_channel.id}/#{ChatMessage.last.id}.json" }.to change { + ChatMessage.count + }.by(-1) expect(response.status).to eq(200) end end describe "#delete" do fab!(:second_user) { Fabricate(:user) } - fab!(:second_user_message) { Fabricate(:chat_message, user: second_user, chat_channel: chat_channel) } + fab!(:second_user_message) do + Fabricate(:chat_message, user: second_user, chat_channel: chat_channel) + end before do ChatMessage.create(user: user, message: "this is a message", chat_channel: chat_channel) @@ -531,9 +606,9 @@ def create_memberships it "Allows users to delete their own messages" do sign_in(user) - expect { - delete "/chat/#{chat_channel.id}/#{ChatMessage.last.id}.json" - }.to change { ChatMessage.count }.by(-1) + expect { delete "/chat/#{chat_channel.id}/#{ChatMessage.last.id}.json" }.to change { + ChatMessage.count + }.by(-1) expect(response.status).to eq(200) end end @@ -601,7 +676,8 @@ def create_memberships fab!(:second_user) { Fabricate(:user) } before do - message = ChatMessage.create(user: user, message: "this is a message", chat_channel: chat_channel) + message = + ChatMessage.create(user: user, message: "this is a message", chat_channel: chat_channel) message.trash! end @@ -615,67 +691,68 @@ def create_memberships end describe "#update_user_last_read" do - before do - sign_in(user) - end + before { sign_in(user) } fab!(:message_1) { Fabricate(:chat_message, chat_channel: chat_channel, user: other_user) } fab!(:message_2) { Fabricate(:chat_message, chat_channel: chat_channel, user: other_user) } - it 'returns a 404 when the user is not a channel member' do + it "returns a 404 when the user is not a channel member" do put "/chat/#{chat_channel.id}/read/#{message_1.id}.json" expect(response.status).to eq(404) end - it 'returns a 404 when the user is not following the channel' do - Fabricate(:user_chat_channel_membership, chat_channel: chat_channel, user: user, following: false) + it "returns a 404 when the user is not following the channel" do + Fabricate( + :user_chat_channel_membership, + chat_channel: chat_channel, + user: user, + following: false, + ) put "/chat/#{chat_channel.id}/read/#{message_1.id}.json" expect(response.status).to eq(404) end - describe 'when the user is a channel member' do - fab!(:membership) { Fabricate(:user_chat_channel_membership, chat_channel: chat_channel, user: user) } + describe "when the user is a channel member" do + fab!(:membership) do + Fabricate(:user_chat_channel_membership, chat_channel: chat_channel, user: user) + end context "message_id param doesn't link to a message of the channel" do - it 'raises a not found' do + it "raises a not found" do put "/chat/#{chat_channel.id}/read/-999.json" expect(response.status).to eq(404) end end - context 'message_id param is inferior to existing last read' do - before do - membership.update!(last_read_message_id: message_2.id) - end + context "message_id param is inferior to existing last read" do + before { membership.update!(last_read_message_id: message_2.id) } - it 'raises an invalid request' do + it "raises an invalid request" do put "/chat/#{chat_channel.id}/read/#{message_1.id}.json" expect(response.status).to eq(400) - expect(response.parsed_body['errors'][0]).to match(/message_id/) + expect(response.parsed_body["errors"][0]).to match(/message_id/) end end - context 'message_id refers to deleted message' do - before do - message_1.trash!(Discourse.system_user) - end + context "message_id refers to deleted message" do + before { message_1.trash!(Discourse.system_user) } - it 'works' do + it "works" do put "/chat/#{chat_channel.id}/read/#{message_1.id}.json" expect(response.status).to eq(200) end end - it 'updates timing records' do - expect { - put "/chat/#{chat_channel.id}/read/#{message_1.id}.json" - }.to change { UserChatChannelMembership.count }.by(0) + it "updates timing records" do + expect { put "/chat/#{chat_channel.id}/read/#{message_1.id}.json" }.to change { + UserChatChannelMembership.count + }.by(0) membership.reload expect(membership.chat_channel_id).to eq(chat_channel.id) @@ -684,24 +761,26 @@ def create_memberships end def create_notification_and_mention_for(user, sender, msg) - Notification.create!( - notification_type: Notification.types[:chat_mention], - user: user, - high_priority: true, - read: false, - data: { - message: 'chat.mention_notification', - chat_message_id: msg.id, - chat_channel_id: msg.chat_channel_id, - chat_channel_title: msg.chat_channel.title(user), - mentioned_by_username: sender.username, - }.to_json - ).tap do |notification| - ChatMention.create!(user: user, chat_message: msg, notification: notification) - end + Notification + .create!( + notification_type: Notification.types[:chat_mention], + user: user, + high_priority: true, + read: false, + data: { + message: "chat.mention_notification", + chat_message_id: msg.id, + chat_channel_id: msg.chat_channel_id, + chat_channel_title: msg.chat_channel.title(user), + mentioned_by_username: sender.username, + }.to_json, + ) + .tap do |notification| + ChatMention.create!(user: user, chat_message: msg, notification: notification) + end end - it 'marks all mention notifications as read for the channel' do + it "marks all mention notifications as read for the channel" do notification = create_notification_and_mention_for(user, other_user, message_1) put "/chat/#{chat_channel.id}/read/#{message_2.id}.json" @@ -724,24 +803,42 @@ def create_notification_and_mention_for(user, sender, msg) describe "react" do fab!(:chat_channel) { Fabricate(:chat_channel) } fab!(:chat_message) { Fabricate(:chat_message, chat_channel: chat_channel, user: user) } - fab!(:user_membership) { Fabricate(:user_chat_channel_membership, chat_channel: chat_channel, user: user) } + fab!(:user_membership) do + Fabricate(:user_chat_channel_membership, chat_channel: chat_channel, user: user) + end - fab!(:private_chat_channel) { Fabricate(:chat_channel, chatable: Fabricate(:private_category, group: Fabricate(:group))) } - fab!(:private_chat_message) { Fabricate(:chat_message, chat_channel: private_chat_channel, user: admin) } - fab!(:private_user_membership) { Fabricate(:user_chat_channel_membership, chat_channel: private_chat_channel, user: user) } + fab!(:private_chat_channel) do + Fabricate(:chat_channel, chatable: Fabricate(:private_category, group: Fabricate(:group))) + end + fab!(:private_chat_message) do + Fabricate(:chat_message, chat_channel: private_chat_channel, user: admin) + end + fab!(:private_user_membership) do + Fabricate(:user_chat_channel_membership, chat_channel: private_chat_channel, user: user) + end fab!(:chat_channel_no_memberships) { Fabricate(:chat_channel) } - fab!(:chat_message_no_memberships) { Fabricate(:chat_message, chat_channel: chat_channel_no_memberships, user: user) } + fab!(:chat_message_no_memberships) do + Fabricate(:chat_message, chat_channel: chat_channel_no_memberships, user: user) + end it "errors with invalid emoji" do sign_in(user) - put "/chat/#{chat_channel.id}/react/#{chat_message.id}.json", params: { emoji: 12, react_action: "add" } + put "/chat/#{chat_channel.id}/react/#{chat_message.id}.json", + params: { + emoji: 12, + react_action: "add", + } expect(response.status).to eq(400) end it "errors with invalid action" do sign_in(user) - put "/chat/#{chat_channel.id}/react/#{chat_message.id}.json", params: { emoji: ":heart:", react_action: "sdf" } + put "/chat/#{chat_channel.id}/react/#{chat_message.id}.json", + params: { + emoji: ":heart:", + react_action: "sdf", + } expect(response.status).to eq(400) end @@ -749,14 +846,22 @@ def create_notification_and_mention_for(user, sender, msg) sign_in(user) expect { - put "/chat/#{chat_channel_no_memberships.id}/react/#{chat_message_no_memberships.id}.json", params: { emoji: ":heart:", react_action: "add" } + put "/chat/#{chat_channel_no_memberships.id}/react/#{chat_message_no_memberships.id}.json", + params: { + emoji: ":heart:", + react_action: "add", + } }.to change { UserChatChannelMembership.count }.by(1) expect(response.status).to eq(200) end it "errors when user tries to react to private channel they can't access" do sign_in(user) - put "/chat/#{private_chat_channel.id}/react/#{private_chat_message.id}.json", params: { emoji: ":heart:", react_action: "add" } + put "/chat/#{private_chat_channel.id}/react/#{private_chat_message.id}.json", + params: { + emoji: ":heart:", + react_action: "add", + } expect(response.status).to eq(403) end @@ -765,47 +870,71 @@ def create_notification_and_mention_for(user, sender, msg) sign_in(user) emoji = ":heart:" expect { - put "/chat/#{chat_channel.id}/react/#{chat_message.id}.json", params: { emoji: emoji, react_action: "add" } + put "/chat/#{chat_channel.id}/react/#{chat_message.id}.json", + params: { + emoji: emoji, + react_action: "add", + } }.not_to change { chat_message.reactions.where(user: user, emoji: emoji).count } expect(response.status).to eq(403) expect(response.parsed_body["errors"]).to include( - I18n.t("chat.errors.channel_modify_message_disallowed", status: chat_channel.status_name) + I18n.t("chat.errors.channel_modify_message_disallowed", status: chat_channel.status_name), ) end it "errors when user is silenced" do UserSilencer.new(user).silence sign_in(user) - put "/chat/#{chat_channel.id}/react/#{chat_message.id}.json", params: { emoji: ":heart:", react_action: "add" } + put "/chat/#{chat_channel.id}/react/#{chat_message.id}.json", + params: { + emoji: ":heart:", + react_action: "add", + } expect(response.status).to eq(403) end it "errors when max unique reactions limit is reached" do - Emoji.all.map(&:name).take(29).each do |emoji| - chat_message.reactions.create(user: user, emoji: emoji) - end + Emoji + .all + .map(&:name) + .take(29) + .each { |emoji| chat_message.reactions.create(user: user, emoji: emoji) } sign_in(user) - put "/chat/#{chat_channel.id}/react/#{chat_message.id}.json", params: { emoji: ":wink:", react_action: "add" } + put "/chat/#{chat_channel.id}/react/#{chat_message.id}.json", + params: { + emoji: ":wink:", + react_action: "add", + } expect(response.status).to eq(200) - put "/chat/#{chat_channel.id}/react/#{chat_message.id}.json", params: { emoji: ":wave:", react_action: "add" } + put "/chat/#{chat_channel.id}/react/#{chat_message.id}.json", + params: { + emoji: ":wave:", + react_action: "add", + } expect(response.status).to eq(403) expect(response.parsed_body["errors"]).to include( - I18n.t("chat.errors.max_reactions_limit_reached") + I18n.t("chat.errors.max_reactions_limit_reached"), ) end it "does not error on new duplicate reactions" do another_user = Fabricate(:user) - Emoji.all.map(&:name).take(29).each do |emoji| - chat_message.reactions.create(user: another_user, emoji: emoji) - end + Emoji + .all + .map(&:name) + .take(29) + .each { |emoji| chat_message.reactions.create(user: another_user, emoji: emoji) } emoji = ":wink:" chat_message.reactions.create(user: another_user, emoji: emoji) sign_in(user) - put "/chat/#{chat_channel.id}/react/#{chat_message.id}.json", params: { emoji: emoji, react_action: "add" } + put "/chat/#{chat_channel.id}/react/#{chat_message.id}.json", + params: { + emoji: emoji, + react_action: "add", + } expect(response.status).to eq(200) end @@ -813,7 +942,11 @@ def create_notification_and_mention_for(user, sender, msg) sign_in(user) emoji = ":heart:" expect { - put "/chat/#{chat_channel.id}/react/#{chat_message.id}.json", params: { emoji: emoji, react_action: "add" } + put "/chat/#{chat_channel.id}/react/#{chat_message.id}.json", + params: { + emoji: emoji, + react_action: "add", + } }.to change { chat_message.reactions.where(user: user, emoji: emoji).count }.by(1) expect(response.status).to eq(200) end @@ -823,7 +956,11 @@ def create_notification_and_mention_for(user, sender, msg) emoji = ":heart:" chat_message.reactions.create(user: user, emoji: emoji) expect { - put "/chat/#{chat_channel.id}/react/#{chat_message.id}.json", params: { emoji: emoji, react_action: "remove" } + put "/chat/#{chat_channel.id}/react/#{chat_message.id}.json", + params: { + emoji: emoji, + react_action: "remove", + } }.to change { chat_message.reactions.where(user: user, emoji: emoji).count }.by(-1) expect(response.status).to eq(200) end @@ -837,34 +974,43 @@ def create_notification_and_mention_for(user, sender, msg) before do sign_in(admin) - [user, user2].each do |u| - u.user_option.update(chat_enabled: true) - end + [user, user2].each { |u| u.user_option.update(chat_enabled: true) } end it "doesn't invite users who cannot chat" do SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:admin] expect { put "/chat/#{chat_channel.id}/invite.json", params: { user_ids: [user.id] } - }.to change { user.notifications.where(notification_type: Notification.types[:chat_invitation]).count }.by(0) + }.to change { + user.notifications.where(notification_type: Notification.types[:chat_invitation]).count + }.by(0) end it "creates an invitation notification for users who can chat" do expect { put "/chat/#{chat_channel.id}/invite.json", params: { user_ids: [user.id] } - }.to change { user.notifications.where(notification_type: Notification.types[:chat_invitation]).count }.by(1) + }.to change { + user.notifications.where(notification_type: Notification.types[:chat_invitation]).count + }.by(1) end it "creates multiple invitations" do expect { put "/chat/#{chat_channel.id}/invite.json", params: { user_ids: [user.id, user2.id] } }.to change { - Notification.where(notification_type: Notification.types[:chat_invitation], user_id: [user.id, user2.id]).count + Notification.where( + notification_type: Notification.types[:chat_invitation], + user_id: [user.id, user2.id], + ).count }.by(2) end it "adds chat_message_id when param is present" do - put "/chat/#{chat_channel.id}/invite.json", params: { user_ids: [user.id], chat_message_id: chat_message.id } + put "/chat/#{chat_channel.id}/invite.json", + params: { + user_ids: [user.id], + chat_message_id: chat_message.id, + } expect(JSON.parse(Notification.last.data)["chat_message_id"]).to eq(chat_message.id.to_s) end end @@ -897,7 +1043,10 @@ def create_notification_and_mention_for(user, sender, msg) it "sets `dismissed_dm_retention_reminder` to true" do sign_in(user) expect { - post "/chat/dismiss-retention-reminder.json", params: { chatable_type: "DirectMessageChannel" } + post "/chat/dismiss-retention-reminder.json", + params: { + chatable_type: "DirectMessageChannel", + } }.to change { user.user_option.reload.dismissed_dm_retention_reminder }.to (true) end @@ -905,12 +1054,15 @@ def create_notification_and_mention_for(user, sender, msg) sign_in(user) user.user_option.update( dismissed_channel_retention_reminder: true, - dismissed_dm_retention_reminder: true + dismissed_dm_retention_reminder: true, ) post "/chat/dismiss-retention-reminder.json", params: { chatable_type: "Category" } expect(response.status).to eq(200) - post "/chat/dismiss-retention-reminder.json", params: { chatable_type: "DirectMessageChannel" } + post "/chat/dismiss-retention-reminder.json", + params: { + chatable_type: "DirectMessageChannel", + } expect(response.status).to eq(200) end end @@ -918,34 +1070,55 @@ def create_notification_and_mention_for(user, sender, msg) describe "#quote_messages" do fab!(:channel) { Fabricate(:chat_channel, chatable: category, name: "Cool Chat") } let(:user2) { Fabricate(:user) } - let(:message1) { Fabricate(:chat_message, user: user, chat_channel: channel, message: "an extremely insightful response :)") } - let(:message2) { Fabricate(:chat_message, user: user2, chat_channel: channel, message: "says you!") } + let(:message1) do + Fabricate( + :chat_message, + user: user, + chat_channel: channel, + message: "an extremely insightful response :)", + ) + end + let(:message2) do + Fabricate(:chat_message, user: user2, chat_channel: channel, message: "says you!") + end let(:message3) { Fabricate(:chat_message, user: user, chat_channel: channel, message: "aw :(") } it "returns a 403 if the user can't chat" do SiteSetting.chat_allowed_groups = nil sign_in(user) - post "/chat/#{channel.id}/quote.json", params: { message_ids: [message1.id, message2.id, message3.id] } + post "/chat/#{channel.id}/quote.json", + params: { + message_ids: [message1.id, message2.id, message3.id], + } expect(response.status).to eq(403) end it "returns a 403 if the user can't see the channel" do category.update!(read_restricted: true) sign_in(user) - post "/chat/#{channel.id}/quote.json", params: { message_ids: [message1.id, message2.id, message3.id] } + post "/chat/#{channel.id}/quote.json", + params: { + message_ids: [message1.id, message2.id, message3.id], + } expect(response.status).to eq(403) end it "returns a 404 for a not found channel" do channel.destroy sign_in(user) - post "/chat/#{channel.id}/quote.json", params: { message_ids: [message1.id, message2.id, message3.id] } + post "/chat/#{channel.id}/quote.json", + params: { + message_ids: [message1.id, message2.id, message3.id], + } expect(response.status).to eq(404) end it "quotes the message ids provided" do sign_in(user) - post "/chat/#{channel.id}/quote.json", params: { message_ids: [message1.id, message2.id, message3.id] } + post "/chat/#{channel.id}/quote.json", + params: { + message_ids: [message1.id, message2.id, message3.id], + } expect(response.status).to eq(200) markdown = response.parsed_body["markdown"] expect(markdown).to eq(<<~EXPECTED) @@ -970,9 +1143,7 @@ def create_notification_and_mention_for(user, sender, msg) fab!(:admin_dm_message) { Fabricate(:chat_message, user: admin, chat_channel: dm_chat_channel) } - before do - sign_in(user) - end + before { sign_in(user) } it "creates reviewable" do expect { @@ -1014,16 +1185,16 @@ def create_notification_and_mention_for(user, sender, msg) describe "#set_draft" do fab!(:chat_channel) { Fabricate(:chat_channel) } - before do - sign_in(user) - end + before { sign_in(user) } it "can create and destroy chat drafts" do - expect { post "/chat/drafts.json", params: { chat_channel_id: chat_channel.id, data: "{}" } } - .to change { ChatDraft.count }.by(1) + expect { + post "/chat/drafts.json", params: { chat_channel_id: chat_channel.id, data: "{}" } + }.to change { ChatDraft.count }.by(1) - expect { post "/chat/drafts.json", params: { chat_channel_id: chat_channel.id } } - .to change { ChatDraft.count }.by(-1) + expect { post "/chat/drafts.json", params: { chat_channel_id: chat_channel.id } }.to change { + ChatDraft.count + }.by(-1) end it "cannot create chat drafts for a category channel the user cannot access" do @@ -1035,8 +1206,9 @@ def create_notification_and_mention_for(user, sender, msg) expect(response.status).to eq(403) GroupUser.create!(user: user, group: group) - expect { post "/chat/drafts.json", params: { chat_channel_id: chat_channel.id, data: "{}" } } - .to change { ChatDraft.count }.by(1) + expect { + post "/chat/drafts.json", params: { chat_channel_id: chat_channel.id, data: "{}" } + }.to change { ChatDraft.count }.by(1) end it "cannot create chat drafts for a direct message channel the user cannot access" do @@ -1046,8 +1218,9 @@ def create_notification_and_mention_for(user, sender, msg) expect(response.status).to eq(403) DirectMessageUser.create(user: user, direct_message_channel: chat_channel.chatable) - expect { post "/chat/drafts.json", params: { chat_channel_id: chat_channel.id, data: "{}" } } - .to change { ChatDraft.count }.by(1) + expect { + post "/chat/drafts.json", params: { chat_channel_id: chat_channel.id, data: "{}" } + }.to change { ChatDraft.count }.by(1) end end @@ -1069,9 +1242,7 @@ def create_notification_and_mention_for(user, sender, msg) let!(:chatable) { Fabricate(:direct_message_channel) } fab!(:user) { Fabricate(:user) } - before do - sign_in(user) - end + before { sign_in(user) } it "ensures message's channel can be seen" do Guardian.any_instance.expects(:can_see_chat_channel?).with(channel) @@ -1115,50 +1286,56 @@ def create_notification_and_mention_for(user, sender, msg) end describe "#move_messages_to_channel" do - fab!(:message_to_move1) { Fabricate(:chat_message, chat_channel: chat_channel, message: "some cool message") } - fab!(:message_to_move2) { Fabricate(:chat_message, chat_channel: chat_channel, message: "and another thing") } + fab!(:message_to_move1) do + Fabricate(:chat_message, chat_channel: chat_channel, message: "some cool message") + end + fab!(:message_to_move2) do + Fabricate(:chat_message, chat_channel: chat_channel, message: "and another thing") + end fab!(:destination_channel) { Fabricate(:chat_channel) } let(:message_ids) { [message_to_move1.id, message_to_move2.id] } context "when the user is not admin" do it "returns an access denied error" do sign_in(user) - put "/chat/#{chat_channel.id}/move_messages_to_channel.json", params: { - destination_channel_id: destination_channel.id, - message_ids: message_ids - } + put "/chat/#{chat_channel.id}/move_messages_to_channel.json", + params: { + destination_channel_id: destination_channel.id, + message_ids: message_ids, + } expect(response.status).to eq(403) end end context "when the user is admin" do - before do - sign_in(admin) - end + before { sign_in(admin) } it "shows an error if the source channel is not found" do chat_channel.trash! - put "/chat/#{chat_channel.id}/move_messages_to_channel.json", params: { - destination_channel_id: destination_channel.id, - message_ids: message_ids - } + put "/chat/#{chat_channel.id}/move_messages_to_channel.json", + params: { + destination_channel_id: destination_channel.id, + message_ids: message_ids, + } expect(response.status).to eq(404) end it "shows an error if the destination channel is not found" do destination_channel.trash! - put "/chat/#{chat_channel.id}/move_messages_to_channel.json", params: { - destination_channel_id: destination_channel.id, - message_ids: message_ids - } + put "/chat/#{chat_channel.id}/move_messages_to_channel.json", + params: { + destination_channel_id: destination_channel.id, + message_ids: message_ids, + } expect(response.status).to eq(404) end it "successfully moves the messages to the new channel" do - put "/chat/#{chat_channel.id}/move_messages_to_channel.json", params: { - destination_channel_id: destination_channel.id, - message_ids: message_ids - } + put "/chat/#{chat_channel.id}/move_messages_to_channel.json", + params: { + destination_channel_id: destination_channel.id, + message_ids: message_ids, + } expect(response.status).to eq(200) latest_destination_messages = destination_channel.chat_messages.last(2) expect(latest_destination_messages.first.message).to eq("some cool message") @@ -1168,25 +1345,33 @@ def create_notification_and_mention_for(user, sender, msg) end it "shows an error message when the destination channel is invalid" do - destination_channel.update!(chatable: Fabricate(:direct_message_channel, users: [admin, Fabricate(:user)])) - put "/chat/#{chat_channel.id}/move_messages_to_channel.json", params: { - destination_channel_id: destination_channel.id, - message_ids: message_ids - } + destination_channel.update!( + chatable: Fabricate(:direct_message_channel, users: [admin, Fabricate(:user)]), + ) + put "/chat/#{chat_channel.id}/move_messages_to_channel.json", + params: { + destination_channel_id: destination_channel.id, + message_ids: message_ids, + } expect(response.status).to eq(422) - expect(response.parsed_body["errors"]).to include(I18n.t("chat.errors.message_move_invalid_channel")) + expect(response.parsed_body["errors"]).to include( + I18n.t("chat.errors.message_move_invalid_channel"), + ) end it "shows an error when none of the messages can be found" do destroyed_message = Fabricate(:chat_message, chat_channel: chat_channel) destroyed_message.trash! - put "/chat/#{chat_channel.id}/move_messages_to_channel.json", params: { - destination_channel_id: destination_channel.id, - message_ids: [destroyed_message] - } + put "/chat/#{chat_channel.id}/move_messages_to_channel.json", + params: { + destination_channel_id: destination_channel.id, + message_ids: [destroyed_message], + } expect(response.status).to eq(422) - expect(response.parsed_body["errors"]).to include(I18n.t("chat.errors.message_move_no_messages_found")) + expect(response.parsed_body["errors"]).to include( + I18n.t("chat.errors.message_move_no_messages_found"), + ) end end end diff --git a/spec/requests/direct_messages_controller_spec.rb b/spec/requests/direct_messages_controller_spec.rb index d767322d3..10675c71a 100644 --- a/spec/requests/direct_messages_controller_spec.rb +++ b/spec/requests/direct_messages_controller_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" RSpec.describe DiscourseChat::DirectMessagesController do fab!(:user) { Fabricate(:user) } @@ -24,9 +24,7 @@ def create_dm_channel(user_ids) describe "#index" do context "user is not allowed to chat" do - before do - SiteSetting.chat_allowed_groups = nil - end + before { SiteSetting.chat_allowed_groups = nil } it "returns a forbidden error" do get "/chat/direct_messages.json", params: { usernames: user1.username } @@ -42,12 +40,12 @@ def create_dm_channel(user_ids) end context "channel exists" do - let!(:channel) { + let!(:channel) do direct_messages_channel = DirectMessageChannel.create! direct_messages_channel.direct_message_users.create!(user_id: user.id) direct_messages_channel.direct_message_users.create!(user_id: user1.id) ChatChannel.create!(chatable: direct_messages_channel) - } + end it "returns the channel" do get "/chat/direct_messages.json", params: { usernames: user1.username } @@ -57,12 +55,13 @@ def create_dm_channel(user_ids) context "with more than two users" do fab!(:user3) { Fabricate(:user) } - before do - channel.chatable.direct_message_users.create!(user_id: user3.id) - end + before { channel.chatable.direct_message_users.create!(user_id: user3.id) } it "returns the channel" do - get "/chat/direct_messages.json", params: { usernames: [user1.username, user.username, user3.username].join(",") } + get "/chat/direct_messages.json", + params: { + usernames: [user1.username, user.username, user3.username].join(","), + } expect(response.status).to eq(200) expect(response.parsed_body["chat_channel"]["id"]).to eq(channel.id) end @@ -76,8 +75,9 @@ def create_dm_channel(user_ids) expect { post "/chat/direct_messages/create.json", params: { usernames: [usernames] } }.to change { DirectMessageChannel.count }.by(1) - expect(DirectMessageChannel.last.direct_message_users.map(&:user_id)) - .to match_array(direct_message_user_ids) + expect(DirectMessageChannel.last.direct_message_users.map(&:user_id)).to match_array( + direct_message_user_ids, + ) end it "returns existing dm channel if one exists for username(s)" do diff --git a/spec/requests/email_controller_spec.rb b/spec/requests/email_controller_spec.rb index fd7158c72..5b227de75 100644 --- a/spec/requests/email_controller_spec.rb +++ b/spec/requests/email_controller_spec.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe EmailController do - describe 'unsubscribing from chat email settings' do + describe "unsubscribing from chat email settings" do fab!(:user) { Fabricate(:user) } - it 'updates an user chat summary frequency' do + it "updates an user chat summary frequency" do SiteSetting.chat_enabled = true - never_freq = 'never' - key = UnsubscribeKey.create_key_for(user, 'chat_summary') + never_freq = "never" + key = UnsubscribeKey.create_key_for(user, "chat_summary") user.user_option.when_away! post "/email/unsubscribe/#{key}.json", params: { chat_email_frequency: never_freq } diff --git a/spec/requests/incoming_chat_webhooks_controller_spec.rb b/spec/requests/incoming_chat_webhooks_controller_spec.rb index 887d727b4..3a238a1fe 100644 --- a/spec/requests/incoming_chat_webhooks_controller_spec.rb +++ b/spec/requests/incoming_chat_webhooks_controller_spec.rb @@ -1,18 +1,16 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" RSpec.describe DiscourseChat::IncomingChatWebhooksController do fab!(:chat_channel) { Fabricate(:chat_channel) } fab!(:webhook) { Fabricate(:incoming_chat_webhook, chat_channel: chat_channel) } - before do - SiteSetting.chat_debug_webhook_payloads = true - end + before { SiteSetting.chat_debug_webhook_payloads = true } describe "#create_message" do it "errors with invalid key" do - post '/chat/hooks/null.json' + post "/chat/hooks/null.json" expect(response.status).to eq(400) end @@ -43,7 +41,7 @@ }.to change { ChatMessage.where(chat_channel: chat_channel).count }.by(0) expect(response.status).to eq(422) expect(response.parsed_body["errors"]).to include( - "Sorry, you can't post the word '#{watched_word.word}'; it's not allowed." + "Sorry, you can't post the word '#{watched_word.word}'; it's not allowed.", ) end @@ -54,16 +52,14 @@ }.to change { ChatMessage.where(chat_channel: chat_channel).count }.by(0) expect(response.status).to eq(422) expect(response.parsed_body["errors"]).to include( - I18n.t("chat.errors.channel_new_message_disallowed", status: chat_channel.status_name) + I18n.t("chat.errors.channel_new_message_disallowed", status: chat_channel.status_name), ) end it "rate limits" do RateLimiter.enable RateLimiter.clear_all! - 10.times do - post "/chat/hooks/#{webhook.key}.json", params: { text: "A new signup woo!" } - end + 10.times { post "/chat/hooks/#{webhook.key}.json", params: { text: "A new signup woo!" } } expect(response.status).to eq(200) post "/chat/hooks/#{webhook.key}.json", params: { text: "A new signup woo!" } @@ -86,16 +82,20 @@ { color: "#F4511E", title: "New+alert:+#46353", - text: "\"[StatusCake]+https://www.test_notification.com+(StatusCake+Test+Alert):+Down,\"", - fallback: "New+alert:+\"[StatusCake]+https://www.test_notification.com+(StatusCake+Test+Alert):+Down,\"+\nTags:+", - title_link: "https://eu.opsg.in/a/i/test/blahguid" - } + text: + "\"[StatusCake]+https://www.test_notification.com+(StatusCake+Test+Alert):+Down,\"", + fallback: + "New+alert:+\"[StatusCake]+https://www.test_notification.com+(StatusCake+Test+Alert):+Down,\"+\nTags:+", + title_link: "https://eu.opsg.in/a/i/test/blahguid", + }, ], } - expect { - post "/chat/hooks/#{webhook.key}/slack.json", params: payload_data - }.to change { ChatMessage.where(chat_channel: chat_channel).count }.by(1) - expect(ChatMessage.last.message).to eq("New alert: \"[StatusCake] https://www.test_notification.com (StatusCake Test Alert): Down,\" [46353](https://eu.opsg.in/a/i/test/blahguid)\nTags: ") + expect { post "/chat/hooks/#{webhook.key}/slack.json", params: payload_data }.to change { + ChatMessage.where(chat_channel: chat_channel).count + }.by(1) + expect(ChatMessage.last.message).to eq( + "New alert: \"[StatusCake] https://www.test_notification.com (StatusCake Test Alert): Down,\" [46353](https://eu.opsg.in/a/i/test/blahguid)\nTags: ", + ) expect { post "/chat/hooks/#{webhook.key}/slack.json", params: { payload: payload_data } }.to change { ChatMessage.where(chat_channel: chat_channel).count }.by(1) @@ -107,16 +107,20 @@ { color: "#F4511E", title: "New+alert:+#46353", - text: "\"[StatusCake]+https://www.test_notification.com+(StatusCake+Test+Alert):+Down,\"", - fallback: "New+alert:+\"[StatusCake]+https://www.test_notification.com+(StatusCake+Test+Alert):+Down,\"+\nTags:+", - title_link: "https://eu.opsg.in/a/i/test/blahguid" - } + text: + "\"[StatusCake]+https://www.test_notification.com+(StatusCake+Test+Alert):+Down,\"", + fallback: + "New+alert:+\"[StatusCake]+https://www.test_notification.com+(StatusCake+Test+Alert):+Down,\"+\nTags:+", + title_link: "https://eu.opsg.in/a/i/test/blahguid", + }, ], } expect { post "/chat/hooks/#{webhook.key}/slack.json", params: { payload: payload_data.to_json } }.to change { ChatMessage.where(chat_channel: chat_channel).count }.by(1) - expect(ChatMessage.last.message).to eq("New alert: \"[StatusCake] https://www.test_notification.com (StatusCake Test Alert): Down,\" [46353](https://eu.opsg.in/a/i/test/blahguid)\nTags: ") + expect(ChatMessage.last.message).to eq( + "New alert: \"[StatusCake] https://www.test_notification.com (StatusCake Test Alert): Down,\" [46353](https://eu.opsg.in/a/i/test/blahguid)\nTags: ", + ) end end end diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb index ea8a4042e..72f466533 100644 --- a/spec/requests/users_controller_spec.rb +++ b/spec/requests/users_controller_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true describe UsersController do - describe '#perform_account_activation' do + describe "#perform_account_activation" do let(:user) { Fabricate(:user, active: false) } let(:email_token) { Fabricate(:email_token, user: user) } let!(:channel) { Fabricate(:chat_channel, auto_join_users: true) } @@ -13,7 +13,7 @@ SiteSetting.chat_enabled = true end - it 'triggers the auto-join process' do + it "triggers the auto-join process" do put "/u/activate-account/#{email_token.token}" expect(response.status).to eq(200) diff --git a/spec/serializer/chat_channel_serializer_spec.rb b/spec/serializer/chat_channel_serializer_spec.rb index 7ab4ab2ca..4a164ae87 100644 --- a/spec/serializer/chat_channel_serializer_spec.rb +++ b/spec/serializer/chat_channel_serializer_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe ChatChannelSerializer do fab!(:user) { Fabricate(:user) } @@ -8,9 +8,7 @@ fab!(:chat_channel) { Fabricate(:chat_channel) } let(:guardian_user) { user } let(:guardian) { Guardian.new(guardian_user) } - subject do - described_class.new(chat_channel, scope: guardian, root: nil) - end + subject { described_class.new(chat_channel, scope: guardian, root: nil) } describe "archive status" do context "when user is not staff" do @@ -34,7 +32,7 @@ chat_channel: chat_channel, archived_by: admin, destination_topic_title: "This will be the archive topic", - total_messages: 10 + total_messages: 10, ) chat_channel.reload expect(subject.as_json.key?(:archive_completed)).to eq(true) diff --git a/spec/serializer/chat_message_serializer.rb b/spec/serializer/chat_message_serializer.rb index 507b96a7f..c5dbd3999 100644 --- a/spec/serializer/chat_message_serializer.rb +++ b/spec/serializer/chat_message_serializer.rb @@ -1,30 +1,30 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe ChatMessageSerializer do fab!(:chat_channel) { Fabricate(:chat_channel) } fab!(:message_1) { Fabricate(:chat_message, user: Fabricate(:user), chat_channel: chat_channel) } let(:guardian) { Guardian.new(Fabricate(:user)) } - subject do - described_class.new(message_1, scope: guardian, root: nil) - end + subject { described_class.new(message_1, scope: guardian, root: nil) } - describe '#reactions' do - fab!(:custom_emoji) { CustomEmoji.create!(name: 'trout', upload: Fabricate(:upload)) } - fab!(:reaction_1) { Fabricate(:chat_message_reaction, chat_message: message_1, emoji: custom_emoji.name) } + describe "#reactions" do + fab!(:custom_emoji) { CustomEmoji.create!(name: "trout", upload: Fabricate(:upload)) } + fab!(:reaction_1) do + Fabricate(:chat_message_reaction, chat_message: message_1, emoji: custom_emoji.name) + end - context 'an emoji used in a reaction has been destroyed' do - it 'doesn’t return the reaction' do + context "an emoji used in a reaction has been destroyed" do + it "doesn’t return the reaction" do Emoji.clear_cache - expect(subject.as_json[:reactions]['trout']).to be_present + expect(subject.as_json[:reactions]["trout"]).to be_present custom_emoji.destroy! Emoji.clear_cache - expect(subject.as_json[:reactions]['trout']).to_not be_present + expect(subject.as_json[:reactions]["trout"]).to_not be_present end end end diff --git a/spec/serializer/direct_message_channel_serializer_spec.rb b/spec/serializer/direct_message_channel_serializer_spec.rb index 04ac1bff1..0c7795dde 100644 --- a/spec/serializer/direct_message_channel_serializer_spec.rb +++ b/spec/serializer/direct_message_channel_serializer_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe DirectMessageChannelSerializer do describe "#user" do @@ -9,7 +9,12 @@ you = Fabricate.build(:user) direct_message_channel = Fabricate.build(:direct_message_channel, users: [me, you]) - serializer = DirectMessageChannelSerializer.new(direct_message_channel, scope: Guardian.new(me), root: false) + serializer = + DirectMessageChannelSerializer.new( + direct_message_channel, + scope: Guardian.new(me), + root: false, + ) expect(serializer.users).to eq([you]) end @@ -20,7 +25,12 @@ other_you = Fabricate.build(:user) direct_message_channel = Fabricate.build(:direct_message_channel, users: [me, you, other_you]) - serializer = DirectMessageChannelSerializer.new(direct_message_channel, scope: Guardian.new(me), root: false) + serializer = + DirectMessageChannelSerializer.new( + direct_message_channel, + scope: Guardian.new(me), + root: false, + ) expect(serializer.users).to match_array([you, other_you]) end @@ -29,7 +39,12 @@ me = Fabricate.build(:user) direct_message_channel = Fabricate.build(:direct_message_channel, users: [me]) - serializer = DirectMessageChannelSerializer.new(direct_message_channel, scope: Guardian.new(me), root: false) + serializer = + DirectMessageChannelSerializer.new( + direct_message_channel, + scope: Guardian.new(me), + root: false, + ) expect(serializer.users).to eq([me]) end diff --git a/spec/services/chat_publisher_spec.rb b/spec/services/chat_publisher_spec.rb index 98bdd554e..c8dc0c35d 100644 --- a/spec/services/chat_publisher_spec.rb +++ b/spec/services/chat_publisher_spec.rb @@ -1,19 +1,17 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe ChatPublisher do fab!(:channel) { Fabricate(:chat_channel) } fab!(:message) { Fabricate(:chat_message, chat_channel: channel) } - describe '.publish_refresh!' do - it 'publishes the message' do - data = MessageBus.track_publish do - ChatPublisher.publish_refresh!(channel, message) - end[0].data + describe ".publish_refresh!" do + it "publishes the message" do + data = MessageBus.track_publish { ChatPublisher.publish_refresh!(channel, message) }[0].data - expect(data['chat_message']['id']).to eq(message.id) - expect(data['type']).to eq('refresh') + expect(data["chat_message"]["id"]).to eq(message.id) + expect(data["type"]).to eq("refresh") end end end diff --git a/spec/support/examples/channel_access_example.rb b/spec/support/examples/channel_access_example.rb index ea28c02cc..7bfe1fca3 100644 --- a/spec/support/examples/channel_access_example.rb +++ b/spec/support/examples/channel_access_example.rb @@ -1,37 +1,37 @@ # frozen_string_literal: true -RSpec.shared_examples 'channel access example' do |verb, endpoint| - endpoint ||= '.json' +RSpec.shared_examples "channel access example" do |verb, endpoint| + endpoint ||= ".json" - context 'channel is not found' do + context "channel is not found" do before { sign_in(Fabricate(:admin)) } - it 'returns a 404' do + it "returns a 404" do public_send(verb, "/chat/api/chat_channels/-999#{endpoint}") expect(response.status).to eq(404) end end - context 'anonymous user' do + context "anonymous user" do fab!(:chat_channel) { Fabricate(:chat_channel) } - it 'returns a 403' do + it "returns a 403" do public_send(verb, "/chat/api/chat_channels/#{chat_channel.id}#{endpoint}") expect(response.status).to eq(403) end end - context 'channel can’t be seen by current user' do + context "channel can’t be seen by current user" do fab!(:chatable) { Fabricate(:private_category, group: Fabricate(:group)) } fab!(:chat_channel) { Fabricate(:chat_channel, chatable: chatable) } fab!(:user) { Fabricate(:user) } - fab!(:membership) { Fabricate(:user_chat_channel_membership, user: user, chat_channel: chat_channel) } + fab!(:membership) do + Fabricate(:user_chat_channel_membership, user: user, chat_channel: chat_channel) + end - before { - sign_in(user) - } + before { sign_in(user) } - it 'returns a 403' do + it "returns a 403" do public_send(verb, "/chat/api/chat_channels/#{chat_channel.id}#{endpoint}") expect(response.status).to eq(403) end diff --git a/spec/validators/chat_default_channel_validator_spec.rb b/spec/validators/chat_default_channel_validator_spec.rb index 0fda697cc..2d0e4328c 100644 --- a/spec/validators/chat_default_channel_validator_spec.rb +++ b/spec/validators/chat_default_channel_validator_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" describe ChatDefaultChannelValidator do fab!(:public_channel) { Fabricate(:chat_channel) }