Skip to content
This repository was archived by the owner on Jul 22, 2025. It is now read-only.

Commit 2c609e1

Browse files
authored
FEATURE: Add user location info to spam scanner context (#1076)
This adds registration and last known IP information and email to scanning context. This provides another hint for spam scanner about possible malicious users. For example registered in India, replying from Australia or email is clearly a throwaway email address.
1 parent 7957796 commit 2c609e1

File tree

2 files changed

+63
-5
lines changed

2 files changed

+63
-5
lines changed

lib/ai_moderation/spam_scanner.rb

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -288,18 +288,50 @@ def self.build_context(post, topic = nil)
288288
end
289289

290290
context << "\nPost Author Information:"
291-
if post.user # during test we may not have a user
292-
context << "- Username: #{post.user.username}"
293-
context << "- Account age: #{(Time.current - post.user.created_at).to_i / 86_400} days"
294-
context << "- Total posts: #{post.user.post_count}"
295-
context << "- Trust level: #{post.user.trust_level}"
291+
if user = post.user # during test we may not have a user
292+
context << "- Username: #{user.username}\n"
293+
context << "- Email: #{user.email}\n"
294+
context << "- Account age: #{(Time.current - user.created_at).to_i / 86_400} days\n"
295+
context << "- Total posts: #{user.post_count}\n"
296+
context << "- Trust level: #{user.trust_level}\n"
297+
if info = location_info(user)
298+
context << "- Registration Location: #{info[:registration]}\n" if info[:registration]
299+
context << "- Last Location: #{info[:last]}\n" if info[:last]
300+
end
296301
end
297302

298303
context << "\nPost Content (first #{MAX_RAW_SCAN_LENGTH} chars):\n"
299304
context << post.raw[0..MAX_RAW_SCAN_LENGTH]
300305
context.join("\n")
301306
end
302307

308+
def self.location_info(user)
309+
registration, last = nil
310+
if user.ip_address.present?
311+
info = DiscourseIpInfo.get(user.ip_address, resolve_hostname: true)
312+
last = "#{info[:location]} (#{info[:organization]})" if info && info[:location].present?
313+
end
314+
if user.registration_ip_address.present?
315+
info = DiscourseIpInfo.get(user.registration_ip_address, resolve_hostname: true)
316+
registration = "#{info[:location]} (#{info[:organization]})" if info &&
317+
info[:location].present?
318+
end
319+
320+
rval = nil
321+
if registration || last
322+
rval = { registration: registration } if registration
323+
if last && last != registration
324+
rval ||= {}
325+
rval[:last] = last
326+
end
327+
end
328+
329+
rval
330+
rescue => e
331+
Discourse.warn_exception(e, message: "Failed to lookup location info")
332+
nil
333+
end
334+
303335
def self.build_system_prompt(custom_instructions)
304336
base_prompt = +<<~PROMPT
305337
You are a spam detection system. Analyze the following post content and context.

spec/lib/modules/ai_moderation/spam_scanner_spec.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,4 +289,30 @@
289289
expect(post.user.reload.silenced?).to eq(false)
290290
end
291291
end
292+
293+
it "includes location information and email in context" do
294+
user.update!(ip_address: "1.2.3.4", registration_ip_address: "5.6.7.8")
295+
296+
ip_info_registration = { location: "New York", organization: "ISP1" }
297+
ip_info_last = { location: "London", organization: "ISP2" }
298+
299+
DiscourseIpInfo
300+
.stubs(:get)
301+
.with("5.6.7.8", resolve_hostname: true)
302+
.returns(ip_info_registration)
303+
DiscourseIpInfo.stubs(:get).with("1.2.3.4", resolve_hostname: true).returns(ip_info_last)
304+
305+
prompts = nil
306+
DiscourseAi::Completions::Llm.with_prepared_responses(
307+
["spam", "just because"],
308+
) do |_, _, _prompts|
309+
prompts = _prompts
310+
described_class.test_post(post)
311+
end
312+
313+
context = prompts.first.messages[1][:content]
314+
expect(context).to include("Registration Location: New York (ISP1)")
315+
expect(context).to include("Last Location: London (ISP2)")
316+
expect(context).to include(user.email)
317+
end
292318
end

0 commit comments

Comments
 (0)