Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

Commit

Permalink
AUTOMATION: FEATURE: Add script to modify user group memberships thro…
Browse files Browse the repository at this point in the history
…ugh badges (#206)
  • Loading branch information
s3lase committed Aug 29, 2023
1 parent e8634f0 commit 15efb45
Show file tree
Hide file tree
Showing 5 changed files with 395 additions and 0 deletions.
10 changes: 10 additions & 0 deletions plugins/automation/config/locales/client.en.yml
Expand Up @@ -225,6 +225,16 @@ en:
info: Info
success: Success
error: Error
user_group_membership_through_badge:
fields:
badge_name:
label: Badge Name
group:
label: Group
description: Target group. Users with the specified badge will be added to this group
remove_members_without_badge:
label: Remove existing members without badge
description: Optional, Remove existing group members without the specified badge
suspend_user_by_email:
fields:
suspend_until:
Expand Down
3 changes: 3 additions & 0 deletions plugins/automation/config/locales/server.en.yml
Expand Up @@ -91,6 +91,9 @@ en:
user_global_notice:
title: User global notice
description: Allows to display a global notice for a user
user_group_membership_through_badge:
title: User Group Membership through Badge
description: Modify user group membership based on a badge
suspend_user_by_email_with_api_call:
doc: When triggering `suspend_user_by_email` with an api call, the endpoint expects a valid `email` to be present in the params sent. `reasons` and `suspend_until (ISO 8601 format)` can also be used to override default fields values.
user_global_notice_with_stalled_topic:
Expand Down
@@ -0,0 +1,96 @@
# frozen_string_literal: true

DiscourseAutomation::Scriptable::USER_GROUP_MEMBERSHIP_THROUGH_BADGE =
"user_group_membership_through_badge"
DiscourseAutomation::Scriptable::USER_GROUP_MEMBERSHIP_THROUGH_BADGE_BULK_MODIFY_START_COUNT = 1000

DiscourseAutomation::Scriptable.add(
DiscourseAutomation::Scriptable::USER_GROUP_MEMBERSHIP_THROUGH_BADGE,
) do
version 1

field :badge_name, component: :text, required: true
field :group, component: :group, required: true
field :remove_members_without_badge, component: :boolean

triggerables %i[recurring user_first_logged_in]

script do |context, fields|
badge_name = fields.dig("badge_name", "value").strip
group_id = fields.dig("group", "value")
remove_members_without_badge = fields.dig("remove_members_without_badge", "value")
current_user = context["user"]
bulk_modify_start_count =
DiscourseAutomation::Scriptable::USER_GROUP_MEMBERSHIP_THROUGH_BADGE_BULK_MODIFY_START_COUNT

badge = Badge.find_by(name: badge_name)
unless badge
Rails.logger.warn("[discourse-automation] Couldn’t find badge with name #{badge_name}")
next
end

group = Group.find_by(id: group_id)
unless group
Rails.logger.warn("[discourse-automation] Couldn’t find group with id #{group_id}")
next
end

query_options = { group_id: group.id, badge_id: badge.id }

# IDs of users who currently have badge but not members of target group
user_ids_to_add_query = +<<~SQL
SELECT u.id AS user_id
FROM users u
JOIN user_badges ub ON u.id = ub.user_id
LEFT JOIN group_users gu ON u.id = gu.user_id AND gu.group_id = :group_id
WHERE ub.badge_id = :badge_id AND gu.user_id IS NULL
SQL

if current_user
user_ids_to_add_query << " AND u.id = :user_id"
query_options[:user_id] = current_user.id
end

user_ids_to_add = DB.query_single(user_ids_to_add_query, query_options)

if user_ids_to_add.count < bulk_modify_start_count
User
.where(id: user_ids_to_add)
.each do |user|
group.add(user)
GroupActionLogger.new(Discourse.system_user, group).log_add_user_to_group(user)
end
else
group.bulk_add(user_ids_to_add)
end

next unless remove_members_without_badge

# IDs of users who are currently target group members without the badge
user_ids_to_remove_query = +<<~SQL
SELECT u.id AS user_id
FROM users u
JOIN group_users gu ON u.id = gu.user_id
LEFT JOIN user_badges ub ON u.id = ub.user_id AND ub.badge_id = :badge_id
WHERE gu.group_id = :group_id AND ub.user_id IS NULL
SQL

if current_user
user_ids_to_remove_query << " AND u.id = :user_id"
query_options[:user_id] ||= current_user.id
end

user_ids_to_remove = DB.query_single(user_ids_to_remove_query, query_options)

if user_ids_to_remove.count < bulk_modify_start_count
User
.where(id: user_ids_to_remove)
.each do |user|
group.remove(user)
GroupActionLogger.new(Discourse.system_user, group).log_remove_user_from_group(user)
end
else
group.bulk_remove(user_ids_to_remove)
end
end
end
1 change: 1 addition & 0 deletions plugins/automation/plugin.rb
Expand Up @@ -53,6 +53,7 @@ module ::DiscourseAutomation
lib/discourse_automation/scripts/suspend_user_by_email
lib/discourse_automation/scripts/topic_required_words
lib/discourse_automation/scripts/user_global_notice
lib/discourse_automation/scripts/user_group_membership_through_badge
lib/discourse_automation/scripts/zapier_webhook
lib/discourse_automation/triggers/after_post_cook
lib/discourse_automation/triggers/api_call
Expand Down

0 comments on commit 15efb45

Please sign in to comment.