mirror of
https://github.com/discourse/discourse.git
synced 2024-11-24 21:50:24 +08:00
209 lines
6.7 KiB
Ruby
209 lines
6.7 KiB
Ruby
|
# frozen_string_literal: true
|
||
|
|
||
|
# Acceptable options:
|
||
|
# - message: Used when the flag type is notify_user or notify_moderators and we have to create
|
||
|
# a separate PM.
|
||
|
# - is_warning: Staff can send warnings when using the notify_user flag.
|
||
|
# - take_action: Automatically approves the created reviewable and deletes the chat message.
|
||
|
# - queue_for_review: Adds a special reason to the reviwable score and creates the reviewable using
|
||
|
# the force_review option.
|
||
|
|
||
|
class Chat::ChatReviewQueue
|
||
|
def flag_message(chat_message, guardian, flag_type_id, opts = {})
|
||
|
result = { success: false, errors: [] }
|
||
|
|
||
|
is_notify_type =
|
||
|
ReviewableScore.types.slice(:notify_user, :notify_moderators).values.include?(flag_type_id)
|
||
|
is_dm = chat_message.chat_channel.direct_message_channel?
|
||
|
|
||
|
raise Discourse::InvalidParameters.new(:flag_type) if is_dm && is_notify_type
|
||
|
|
||
|
guardian.ensure_can_flag_chat_message!(chat_message)
|
||
|
guardian.ensure_can_flag_message_as!(chat_message, flag_type_id, opts)
|
||
|
|
||
|
existing_reviewable = Reviewable.includes(:reviewable_scores).find_by(target: chat_message)
|
||
|
|
||
|
if !can_flag_again?(existing_reviewable, chat_message, guardian.user, flag_type_id)
|
||
|
result[:errors] << I18n.t("chat.reviewables.message_already_handled")
|
||
|
return result
|
||
|
end
|
||
|
|
||
|
payload = { message_cooked: chat_message.cooked }
|
||
|
|
||
|
if opts[:message].present? && !is_dm && is_notify_type
|
||
|
creator = companion_pm_creator(chat_message, guardian.user, flag_type_id, opts)
|
||
|
post = creator.create
|
||
|
|
||
|
if creator.errors.present?
|
||
|
creator.errors.full_messages.each { |msg| result[:errors] << msg }
|
||
|
return result
|
||
|
end
|
||
|
elsif is_dm
|
||
|
transcript = find_or_create_transcript(chat_message, guardian.user, existing_reviewable)
|
||
|
payload[:transcript_topic_id] = transcript.topic_id if transcript
|
||
|
end
|
||
|
|
||
|
queued_for_review = !!ActiveRecord::Type::Boolean.new.deserialize(opts[:queue_for_review])
|
||
|
|
||
|
reviewable =
|
||
|
ReviewableChatMessage.needs_review!(
|
||
|
created_by: guardian.user,
|
||
|
target: chat_message,
|
||
|
reviewable_by_moderator: true,
|
||
|
potential_spam: flag_type_id == ReviewableScore.types[:spam],
|
||
|
payload: payload,
|
||
|
)
|
||
|
reviewable.update(target_created_by: chat_message.user)
|
||
|
score =
|
||
|
reviewable.add_score(
|
||
|
guardian.user,
|
||
|
flag_type_id,
|
||
|
meta_topic_id: post&.topic_id,
|
||
|
take_action: opts[:take_action],
|
||
|
reason: queued_for_review ? "chat_message_queued_by_staff" : nil,
|
||
|
force_review: queued_for_review,
|
||
|
)
|
||
|
|
||
|
if opts[:take_action]
|
||
|
reviewable.perform(guardian.user, :agree_and_delete)
|
||
|
ChatPublisher.publish_delete!(chat_message.chat_channel, chat_message)
|
||
|
else
|
||
|
enforce_auto_silence_threshold(reviewable)
|
||
|
ChatPublisher.publish_flag!(chat_message, guardian.user, reviewable, score)
|
||
|
end
|
||
|
|
||
|
result.tap do |r|
|
||
|
r[:success] = true
|
||
|
r[:reviewable] = reviewable
|
||
|
end
|
||
|
end
|
||
|
|
||
|
private
|
||
|
|
||
|
def enforce_auto_silence_threshold(reviewable)
|
||
|
auto_silence_duration = SiteSetting.chat_auto_silence_from_flags_duration
|
||
|
return if auto_silence_duration.zero?
|
||
|
return if reviewable.score <= ReviewableChatMessage.score_to_silence_user
|
||
|
|
||
|
user = reviewable.target_created_by
|
||
|
return unless user
|
||
|
return if user.silenced?
|
||
|
|
||
|
UserSilencer.silence(
|
||
|
user,
|
||
|
Discourse.system_user,
|
||
|
silenced_till: auto_silence_duration.minutes.from_now,
|
||
|
reason: I18n.t("chat.errors.auto_silence_from_flags"),
|
||
|
)
|
||
|
end
|
||
|
|
||
|
def companion_pm_creator(chat_message, flagger, flag_type_id, opts)
|
||
|
notifying_user = flag_type_id == ReviewableScore.types[:notify_user]
|
||
|
|
||
|
i18n_key = notifying_user ? "notify_user" : "notify_moderators"
|
||
|
|
||
|
title =
|
||
|
I18n.t(
|
||
|
"reviewable_score_types.#{i18n_key}.chat_pm_title",
|
||
|
channel_name: chat_message.chat_channel.title(flagger),
|
||
|
locale: SiteSetting.default_locale,
|
||
|
)
|
||
|
|
||
|
body =
|
||
|
I18n.t(
|
||
|
"reviewable_score_types.#{i18n_key}.chat_pm_body",
|
||
|
message: opts[:message],
|
||
|
link: chat_message.full_url,
|
||
|
locale: SiteSetting.default_locale,
|
||
|
)
|
||
|
|
||
|
create_args = {
|
||
|
archetype: Archetype.private_message,
|
||
|
title: title.truncate(SiteSetting.max_topic_title_length, separator: /\s/),
|
||
|
raw: body,
|
||
|
}
|
||
|
|
||
|
if notifying_user
|
||
|
create_args[:subtype] = TopicSubtype.notify_user
|
||
|
create_args[:target_usernames] = chat_message.user.username
|
||
|
|
||
|
create_args[:is_warning] = opts[:is_warning] if flagger.staff?
|
||
|
else
|
||
|
create_args[:subtype] = TopicSubtype.notify_moderators
|
||
|
create_args[:target_group_names] = [Group[:moderators].name]
|
||
|
end
|
||
|
|
||
|
PostCreator.new(flagger, create_args)
|
||
|
end
|
||
|
|
||
|
def find_or_create_transcript(chat_message, flagger, existing_reviewable)
|
||
|
previous_message_ids =
|
||
|
ChatMessage
|
||
|
.where(chat_channel: chat_message.chat_channel)
|
||
|
.where("id < ?", chat_message.id)
|
||
|
.order("created_at DESC")
|
||
|
.limit(10)
|
||
|
.pluck(:id)
|
||
|
.reverse
|
||
|
|
||
|
return if previous_message_ids.empty?
|
||
|
|
||
|
service =
|
||
|
ChatTranscriptService.new(
|
||
|
chat_message.chat_channel,
|
||
|
Discourse.system_user,
|
||
|
messages_or_ids: previous_message_ids,
|
||
|
)
|
||
|
|
||
|
title =
|
||
|
I18n.t(
|
||
|
"chat.reviewables.direct_messages.transcript_title",
|
||
|
channel_name: chat_message.chat_channel.title(flagger),
|
||
|
locale: SiteSetting.default_locale,
|
||
|
)
|
||
|
|
||
|
body =
|
||
|
I18n.t(
|
||
|
"chat.reviewables.direct_messages.transcript_body",
|
||
|
transcript: service.generate_markdown,
|
||
|
locale: SiteSetting.default_locale,
|
||
|
)
|
||
|
|
||
|
create_args = {
|
||
|
archetype: Archetype.private_message,
|
||
|
title: title.truncate(SiteSetting.max_topic_title_length, separator: /\s/),
|
||
|
raw: body,
|
||
|
subtype: TopicSubtype.notify_moderators,
|
||
|
target_group_names: [Group[:moderators].name],
|
||
|
}
|
||
|
|
||
|
PostCreator.new(Discourse.system_user, create_args).create
|
||
|
end
|
||
|
|
||
|
def can_flag_again?(reviewable, message, flagger, flag_type_id)
|
||
|
return true if reviewable.blank?
|
||
|
|
||
|
flagger_has_pending_flags =
|
||
|
reviewable.reviewable_scores.any? { |rs| rs.user == flagger && rs.pending? }
|
||
|
|
||
|
if !flagger_has_pending_flags && flag_type_id == ReviewableScore.types[:notify_moderators]
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
flag_used =
|
||
|
reviewable.reviewable_scores.any? do |rs|
|
||
|
rs.reviewable_score_type == flag_type_id && rs.pending?
|
||
|
end
|
||
|
handled_recently =
|
||
|
!(
|
||
|
reviewable.pending? ||
|
||
|
reviewable.updated_at < SiteSetting.cooldown_hours_until_reflag.to_i.hours.ago
|
||
|
)
|
||
|
|
||
|
latest_revision = message.revisions.last
|
||
|
edited_since_last_review = latest_revision && latest_revision.updated_at > reviewable.updated_at
|
||
|
|
||
|
!flag_used && !flagger_has_pending_flags && (!handled_recently || edited_since_last_review)
|
||
|
end
|
||
|
end
|