From 2c99aa0979fd672f3c4478cc70439b9dd6b3aadb Mon Sep 17 00:00:00 2001 From: Keegan George Date: Wed, 8 Jan 2025 17:54:16 -0800 Subject: [PATCH 1/2] DEV: Add structure for errors in spam This PR adds some structure for handling errors in the spam config while also handling a specific error related to the spam scanning user not being an admin account. --- .../discourse_ai/admin/ai_spam_controller.rb | 8 +++ app/serializers/ai_spam_serializer.rb | 9 ++- .../discourse/components/ai-spam.gjs | 55 ++++++++++++++++++- .../stylesheets/modules/llms/common/spam.scss | 12 ++++ config/locales/client.en.yml | 5 ++ config/routes.rb | 1 + lib/ai_moderation/spam_scanner.rb | 5 ++ 7 files changed, 93 insertions(+), 2 deletions(-) diff --git a/app/controllers/discourse_ai/admin/ai_spam_controller.rb b/app/controllers/discourse_ai/admin/ai_spam_controller.rb index 138908eb4..808a8e4c5 100644 --- a/app/controllers/discourse_ai/admin/ai_spam_controller.rb +++ b/app/controllers/discourse_ai/admin/ai_spam_controller.rb @@ -86,6 +86,14 @@ def test render json: result end + def fix_errors + if params[:error] == "spam_scanner_not_admin" + DiscourseAi::AiModeration::SpamScanner.fix_spam_scanner_not_admin + end + + render json: success_json + end + private def allowed_params diff --git a/app/serializers/ai_spam_serializer.rb b/app/serializers/ai_spam_serializer.rb index 14f1c4748..022d1dac9 100644 --- a/app/serializers/ai_spam_serializer.rb +++ b/app/serializers/ai_spam_serializer.rb @@ -7,7 +7,8 @@ class AiSpamSerializer < ApplicationSerializer :available_llms, :stats, :flagging_username, - :spam_score_type + :spam_score_type, + :spam_scanning_user def is_enabled object[:enabled] @@ -47,4 +48,10 @@ def stats def settings object[:settings] end + + def spam_scanning_user + user = DiscourseAi::AiModeration::SpamScanner.flagging_user + + user.serializable_hash(only: %i[id username name admin]) if user.present? + end end diff --git a/assets/javascripts/discourse/components/ai-spam.gjs b/assets/javascripts/discourse/components/ai-spam.gjs index d7f7a9af1..b5425d2e3 100644 --- a/assets/javascripts/discourse/components/ai-spam.gjs +++ b/assets/javascripts/discourse/components/ai-spam.gjs @@ -13,6 +13,7 @@ import DTooltip from "discourse/components/d-tooltip"; import withEventValue from "discourse/helpers/with-event-value"; import { ajax } from "discourse/lib/ajax"; import { popupAjaxError } from "discourse/lib/ajax-error"; +import dIcon from "discourse-common/helpers/d-icon"; import i18n from "discourse-common/helpers/i18n"; import getURL from "discourse-common/lib/get-url"; import AdminConfigAreaCard from "admin/components/admin-config-area-card"; @@ -35,10 +36,51 @@ export default class AiSpam extends Component { @tracked isEnabled = false; @tracked selectedLLM = null; @tracked customInstructions = ""; + @tracked errors = []; constructor() { super(...arguments); this.initializeFromModel(); + + if (this.args.model?.spam_scanning_user?.admin === false) { + this.errors.push({ + message: i18n("discourse_ai.spam.errors.scan_not_admin.message"), + button: { + label: i18n("discourse_ai.spam.errors.scan_not_admin.action"), + action: this.fixScanUserNotAdmin, + }, + }); + } + } + + @action + async fixScanUserNotAdmin() { + const spamScanningUser = this.args.model.spam_scanning_user; + if (!spamScanningUser || spamScanningUser.admin) { + return; + } + try { + const response = await ajax( + `/admin/plugins/discourse-ai/ai-spam/fix-errors`, + { + type: "POST", + data: { + error: "spam_scanner_not_admin", + }, + } + ); + + if (response.success) { + this.toasts.success({ + data: { message: i18n("discourse_ai.spam.errors.resolved") }, + duration: 2000, + }); + } + } catch (error) { + popupAjaxError(error); + } finally { + window.location.reload(); + } } @action @@ -165,11 +207,22 @@ export default class AiSpam extends Component {