FIX: Do not inherit from classes defined by plugins (#6)
This commit is contained in:
parent
510c6487e3
commit
cbaa40edc5
|
@ -1,4 +1,149 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ReviewableAIChatMessage < ReviewableChatMessage
|
require_dependency "reviewable"
|
||||||
|
|
||||||
|
class ReviewableAIChatMessage < Reviewable
|
||||||
|
def self.action_aliases
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.score_to_silence_user
|
||||||
|
sensitivity_score(SiteSetting.chat_silence_user_sensitivity, scale: 0.6)
|
||||||
|
end
|
||||||
|
|
||||||
|
def chat_message
|
||||||
|
@chat_message ||= (target || ChatMessage.with_deleted.find_by(id: target_id))
|
||||||
|
end
|
||||||
|
|
||||||
|
def chat_message_creator
|
||||||
|
@chat_message_creator ||= chat_message.user
|
||||||
|
end
|
||||||
|
|
||||||
|
def flagged_by_user_ids
|
||||||
|
@flagged_by_user_ids ||= reviewable_scores.map(&:user_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def post
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
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")
|
||||||
|
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")
|
||||||
|
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",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
build_action(actions, :ignore, icon: "external-link-alt")
|
||||||
|
|
||||||
|
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)
|
||||||
|
agree
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform_agree_and_restore(performed_by, args)
|
||||||
|
agree { chat_message.recover! }
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform_agree_and_delete(performed_by, args)
|
||||||
|
agree { chat_message.trash!(performed_by) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform_disagree_and_restore(performed_by, args)
|
||||||
|
disagree { chat_message.recover! }
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform_disagree(performed_by, args)
|
||||||
|
disagree
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform_ignore(performed_by, args)
|
||||||
|
ignore
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform_delete_and_ignore(performed_by, args)
|
||||||
|
ignore { chat_message.trash!(performed_by) }
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def agree
|
||||||
|
yield if block_given?
|
||||||
|
create_result(:success, :approved) do |result|
|
||||||
|
result.update_flag_stats = { status: :agreed, user_ids: flagged_by_user_ids }
|
||||||
|
result.recalculate_score = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def disagree
|
||||||
|
yield if block_given?
|
||||||
|
|
||||||
|
UserSilencer.unsilence(chat_message_creator)
|
||||||
|
|
||||||
|
create_result(:success, :rejected) do |result|
|
||||||
|
result.update_flag_stats = { status: :disagreed, user_ids: flagged_by_user_ids }
|
||||||
|
result.recalculate_score = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def ignore
|
||||||
|
yield if block_given?
|
||||||
|
create_result(:success, :ignored) do |result|
|
||||||
|
result.update_flag_stats = { status: :ignored, user_ids: flagged_by_user_ids }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
action.button_class = button_class
|
||||||
|
action.label = "chat.#{prefix}.title"
|
||||||
|
action.description = "chat.#{prefix}.description"
|
||||||
|
action.client_action = client_action
|
||||||
|
action.confirm_message = "#{prefix}.confirm" if confirm
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
# frozen_string_literal:true
|
# frozen_string_literal:true
|
||||||
|
|
||||||
|
require_dependency "reviewable"
|
||||||
|
|
||||||
class ReviewableAIPost < Reviewable
|
class ReviewableAIPost < Reviewable
|
||||||
# Penalties are handled by the modal after the action is performed
|
# Penalties are handled by the modal after the action is performed
|
||||||
def self.action_aliases
|
def self.action_aliases
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AIChatChannelSerializer < ApplicationSerializer
|
||||||
|
attributes :id, :chatable, :chatable_type, :chatable_url, :title, :slug
|
||||||
|
end
|
|
@ -1,5 +1,19 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ReviewableAIChatMessageSerializer < ReviewableChatMessageSerializer
|
require_dependency "reviewable_serializer"
|
||||||
payload_attributes :accuracies
|
|
||||||
|
class ReviewableAIChatMessageSerializer < ReviewableSerializer
|
||||||
|
payload_attributes :accuracies, :message_cooked
|
||||||
|
target_attributes :cooked
|
||||||
|
attributes :target_id
|
||||||
|
|
||||||
|
has_one :chat_channel, serializer: AIChatChannelSerializer, root: false, embed: :objects
|
||||||
|
|
||||||
|
def chat_channel
|
||||||
|
object.chat_message.chat_channel
|
||||||
|
end
|
||||||
|
|
||||||
|
def target_id
|
||||||
|
object.target&.id
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require_dependency "reviewable_flagged_post_serializer"
|
||||||
|
|
||||||
class ReviewableAIPostSerializer < ReviewableFlaggedPostSerializer
|
class ReviewableAIPostSerializer < ReviewableFlaggedPostSerializer
|
||||||
payload_attributes :accuracies
|
payload_attributes :accuracies
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,13 @@
|
||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
|
||||||
export default class ReviewableAIChatMessage extends Component {}
|
export default class ReviewableAIChatMessage extends Component {
|
||||||
|
@service store;
|
||||||
|
|
||||||
|
get chatChannel() {
|
||||||
|
return this.store.createRecord(
|
||||||
|
"chat-channel",
|
||||||
|
this.args.reviewable.chat_channel
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,19 +18,19 @@ describe Jobs::ToxicityClassifyChatMessage do
|
||||||
|
|
||||||
subject.execute({ chat_message_id: chat_message.id })
|
subject.execute({ chat_message_id: chat_message.id })
|
||||||
|
|
||||||
expect(ReviewableChatMessage.where(target: chat_message).count).to be_zero
|
expect(ReviewableAIChatMessage.where(target: chat_message).count).to be_zero
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does nothing if there's no arg called post_id" do
|
it "does nothing if there's no arg called post_id" do
|
||||||
subject.execute({})
|
subject.execute({})
|
||||||
|
|
||||||
expect(ReviewableChatMessage.where(target: chat_message).count).to be_zero
|
expect(ReviewableAIChatMessage.where(target: chat_message).count).to be_zero
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does nothing if no post match the given id" do
|
it "does nothing if no post match the given id" do
|
||||||
subject.execute({ chat_message_id: nil })
|
subject.execute({ chat_message_id: nil })
|
||||||
|
|
||||||
expect(ReviewableChatMessage.where(target: chat_message).count).to be_zero
|
expect(ReviewableAIChatMessage.where(target: chat_message).count).to be_zero
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does nothing if the post content is blank" do
|
it "does nothing if the post content is blank" do
|
||||||
|
@ -38,7 +38,7 @@ describe Jobs::ToxicityClassifyChatMessage do
|
||||||
|
|
||||||
subject.execute({ chat_message_id: chat_message.id })
|
subject.execute({ chat_message_id: chat_message.id })
|
||||||
|
|
||||||
expect(ReviewableChatMessage.where(target: chat_message).count).to be_zero
|
expect(ReviewableAIChatMessage.where(target: chat_message).count).to be_zero
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ describe Jobs::ToxicityClassifyChatMessage do
|
||||||
|
|
||||||
subject.execute({ chat_message_id: chat_message.id })
|
subject.execute({ chat_message_id: chat_message.id })
|
||||||
|
|
||||||
expect(ReviewableChatMessage.where(target: chat_message).count).to eq(1)
|
expect(ReviewableAIChatMessage.where(target: chat_message).count).to eq(1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
RSpec.describe ReviewableAIChatMessage, 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) { described_class.needs_review!(target: chat_message, created_by: moderator) }
|
||||||
|
|
||||||
|
it "agree_and_keep agrees with the flag and doesn't delete the message" do
|
||||||
|
reviewable.perform(moderator, :agree_and_keep_message)
|
||||||
|
|
||||||
|
expect(reviewable).to be_approved
|
||||||
|
expect(chat_message.reload.deleted_at).not_to be_present
|
||||||
|
end
|
||||||
|
|
||||||
|
it "agree_and_delete agrees with the flag and deletes the message" do
|
||||||
|
chat_message_id = chat_message.id
|
||||||
|
reviewable.perform(moderator, :agree_and_delete)
|
||||||
|
|
||||||
|
expect(reviewable).to be_approved
|
||||||
|
expect(ChatMessage.with_deleted.find_by(id: chat_message_id).deleted_at).to be_present
|
||||||
|
end
|
||||||
|
|
||||||
|
it "agree_and_restore agrees with the flag and restores the message" do
|
||||||
|
chat_message.trash!(user)
|
||||||
|
reviewable.perform(moderator, :agree_and_restore)
|
||||||
|
|
||||||
|
expect(reviewable).to be_approved
|
||||||
|
expect(chat_message.reload.deleted_at).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "perform_disagree disagrees with the flag and does nothing" do
|
||||||
|
reviewable.perform(moderator, :disagree)
|
||||||
|
|
||||||
|
expect(reviewable).to be_rejected
|
||||||
|
end
|
||||||
|
|
||||||
|
it "perform_disagree_and_restore disagrees with the flag and restores the message" do
|
||||||
|
chat_message.trash!(user)
|
||||||
|
reviewable.perform(moderator, :disagree_and_restore)
|
||||||
|
|
||||||
|
expect(reviewable).to be_rejected
|
||||||
|
expect(chat_message.reload.deleted_at).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "perform_ignore ignores the flag and does nothing" do
|
||||||
|
reviewable.perform(moderator, :ignore)
|
||||||
|
|
||||||
|
expect(reviewable).to be_ignored
|
||||||
|
expect(chat_message.reload.deleted_at).not_to be_present
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the flagged message author is silenced" do
|
||||||
|
before do
|
||||||
|
UserSilencer.silence(
|
||||||
|
user,
|
||||||
|
Discourse.system_user,
|
||||||
|
silenced_till: 10.minutes.from_now,
|
||||||
|
reason: I18n.t("chat.errors.auto_silence_from_flags"),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "perform_disagree unsilences the user" do
|
||||||
|
reviewable.perform(moderator, :disagree)
|
||||||
|
|
||||||
|
expect(user.reload.silenced?).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "perform_disagree_and_restore unsilences the user" do
|
||||||
|
chat_message.trash!(user)
|
||||||
|
reviewable.perform(moderator, :disagree_and_restore)
|
||||||
|
|
||||||
|
expect(user.reload.silenced?).to eq(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue