FEATURE: allow re-scoping chat user search via a plugin (#26361)

This enables the following in Discourse AI

```
 plugin.register_modifier(:chat_allowed_bot_user_ids) do |user_ids, guardian|
  if guardian.user
    mentionables = AiPersona.mentionables(user: guardian.user)
    allowed_bot_ids = mentionables.map { |mentionable| mentionable[:user_id] }
    user_ids.concat(allowed_bot_ids)
  end
  user_ids
end
```

some bots that are id < 0 need to be discoverable in search otherwise people can not talk to them.

---------

Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
This commit is contained in:
Sam 2024-03-27 08:55:53 +11:00 committed by GitHub
parent 680f1ff19c
commit e3a0faefc5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 50 additions and 5 deletions

View File

@ -259,12 +259,19 @@ class User < ActiveRecord::Base
) )
end end
scope :human_users, -> { where("users.id > 0") } scope :human_users,
->(allowed_bot_user_ids: nil) do
if allowed_bot_user_ids.present?
where("users.id > 0 OR users.id IN (?)", allowed_bot_user_ids)
else
where("users.id > 0")
end
end
# excluding fake users like the system user or anonymous users # excluding fake users like the system user or anonymous users
scope :real, scope :real,
-> do ->(allowed_bot_user_ids: nil) do
human_users.where( human_users(allowed_bot_user_ids: allowed_bot_user_ids).where(
"NOT EXISTS( "NOT EXISTS(
SELECT 1 SELECT 1
FROM anonymous_users a FROM anonymous_users a

View File

@ -98,11 +98,17 @@ module Chat
user_search = ::UserSearch.new(context.term, limit: 10) user_search = ::UserSearch.new(context.term, limit: 10)
if context.term.blank? if context.term.blank?
user_search = user_search.scoped_users.real.includes(:user_option) user_search = user_search.scoped_users
else else
user_search = user_search.search.real.includes(:user_option) user_search = user_search.search
end end
allowed_bot_user_ids =
DiscoursePluginRegistry.apply_modifier(:chat_allowed_bot_user_ids, [], guardian)
user_search = user_search.real(allowed_bot_user_ids: allowed_bot_user_ids)
user_search = user_search.includes(:user_option)
if context.excluded_memberships_channel_id if context.excluded_memberships_channel_id
user_search = user_search =
user_search.where( user_search.where(

View File

@ -82,6 +82,27 @@ RSpec.describe Chat::SearchChatable do
expect(result.users).to_not include(current_user) expect(result.users).to_not include(current_user)
end end
context "when chat_allowed_bot_user_ids modifier exists" do
fab!(:bot_1) { Fabricate(:user, id: -500) }
fab!(:bot_2) { Fabricate(:user, id: -501) }
it "alters the users returned" do
modifier_block = Proc.new { [bot_2.id] }
plugin_instance = Plugin::Instance.new
plugin_instance.register_modifier(:chat_allowed_bot_user_ids, &modifier_block)
expect(result.users).to_not include(bot_1)
expect(result.users).to include(bot_2)
expect(result.users).to include(current_user, sam, charlie, alain)
ensure
DiscoursePluginRegistry.unregister_modifier(
plugin_instance,
:chat_allowed_bot_user_ids,
&modifier_block
)
end
end
end end
context "when not including users" do context "when not including users" do

View File

@ -1781,6 +1781,17 @@ RSpec.describe User do
end end
end end
describe "real users" do
it "should find system user if you allow it" do
ids =
User
.real(allowed_bot_user_ids: [Discourse.system_user.id])
.where(id: Discourse.system_user.id)
.pluck(:id)
expect(ids).to eq([Discourse.system_user.id])
end
end
describe "#purge_unactivated" do describe "#purge_unactivated" do
fab!(:user) { Fabricate(:user, refresh_auto_groups: true) } fab!(:user) { Fabricate(:user, refresh_auto_groups: true) }
fab!(:unactivated) { Fabricate(:user, active: false) } fab!(:unactivated) { Fabricate(:user, active: false) }