Skip to content
This repository was archived by the owner on Jul 22, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion lib/ai_moderation/spam_scanner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,13 @@ def self.completion_prompt(post, context:, custom_instructions:)
end

def self.perform_scan(post)
return if !enabled?
return if !should_scan_post?(post)

perform_scan!(post)
end

def self.perform_scan!(post)
return if !enabled?
settings = AiModerationSetting.spam
return if !settings || !settings.llm_model

Expand Down
29 changes: 29 additions & 0 deletions lib/tasks/modules/ai_moderation/scan.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

desc "Scan first posts of topics from a date, end date is optional. Usage: rake ai:spam:scan_topics[2024-01-01,2024-02-31]"
task "ai:spam:scan_topics", %i[start_date end_date] => [:environment] do |_, args|
start_date = args[:start_date] ? DateTime.parse(args[:start_date]) : 1.day.ago
end_date = args[:end_date] ? DateTime.parse(args[:end_date]) : Time.current

scope = Topic.joins(:posts).where(created_at: start_date..end_date).where("posts.post_number = 1")
puts "Processing #{scope.count} topics from #{start_date} to #{end_date}"
scope
.select("topics.id, posts.id as post_id")
.find_each(batch_size: 500) do |record|
Jobs.enqueue(:ai_spam_scan, post_id: record.post_id)
print "."
end
end

desc "Scan posts from a date, end date is optional. Usage: rake ai:spam:scan_posts[2024-01-31,2024-02-01]"
task "ai:spam:scan_posts", %i[start_date end_date] => [:environment] do |_, args|
start_date = args[:start_date] ? DateTime.parse(args[:start_date]) : 1.day.ago
end_date = args[:end_date] ? DateTime.parse(args[:end_date]) : Time.current

scope = Post.where(created_at: start_date..end_date).select(:id)
puts "Processing #{scope.count} posts from #{start_date} to #{end_date}"
scope.find_each(batch_size: 500) do |post|
Jobs.enqueue(:ai_spam_scan, post_id: post.id)
print "."
end
end
31 changes: 31 additions & 0 deletions spec/lib/modules/ai_moderation/spam_scanner_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,37 @@
end
end

describe ".perform_scan" do
it "does nothing if post should not be scanned" do
post.user.trust_level = TrustLevel[2]

expect { described_class.perform_scan(post) }.not_to change { AiSpamLog.count }
end

it "scans when post should be scanned" do
expect do
DiscourseAi::Completions::Llm.with_prepared_responses(["spam"]) do
described_class.perform_scan!(post)
end
end.to change { AiSpamLog.count }.by(1)
end
end

describe ".perform_scan!" do
it "creates spam log entry when scanning post" do
expect do
DiscourseAi::Completions::Llm.with_prepared_responses(["spam"]) do
described_class.perform_scan!(post)
end
end.to change { AiSpamLog.count }.by(1)
end

it "does nothing when disabled" do
SiteSetting.ai_spam_detection_enabled = false
expect { described_class.perform_scan!(post) }.not_to change { AiSpamLog.count }
end
end

describe ".scanned_max_times?" do
it "returns true when post has been scanned 3 times" do
3.times do
Expand Down
38 changes: 38 additions & 0 deletions spec/tasks/scan_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# frozen_string_literal: true

RSpec.describe "ai:spam rake tasks" do
let!(:topic1) { Fabricate(:topic, created_at: 2.days.ago) }
let!(:post1) { Fabricate(:post, topic: topic1, created_at: 2.days.ago) }
let!(:topic2) { Fabricate(:topic, created_at: 1.hour.ago) }
let!(:post2) { Fabricate(:post, topic: topic2, created_at: 1.hour.ago) }

describe "ai:spam:scan_posts" do
it "enqueues posts within date range" do
freeze_time do
start_date = 1.day.ago.to_s
end_date = Time.now.to_s

expect_enqueued_with(job: :ai_spam_scan, args: { post_id: post2.id }) do
Rake::Task["ai:spam:scan_posts"].invoke(start_date, end_date)
end

expect_not_enqueued_with(job: :ai_spam_scan, args: { post_id: post1.id })
end
end
end

describe "ai:spam:scan_topics" do
it "enqueues first posts of topics within date range" do
freeze_time do
start_date = 1.day.ago.to_s
end_date = Time.now.to_s

expect_enqueued_with(job: :ai_spam_scan, args: { post_id: topic2.first_post.id }) do
Rake::Task["ai:spam:scan_topics"].invoke(start_date, end_date)
end

expect_not_enqueued_with(job: :ai_spam_scan, args: { post_id: topic1.first_post.id })
end
end
end
end
Loading