Skip to content
This repository was archived by the owner on May 30, 2024. It is now read-only.

Commit 15efb45

Browse files
authored
AUTOMATION: FEATURE: Add script to modify user group memberships through badges (#206)
1 parent e8634f0 commit 15efb45

File tree

5 files changed

+395
-0
lines changed

5 files changed

+395
-0
lines changed

plugins/automation/config/locales/client.en.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,16 @@ en:
225225
info: Info
226226
success: Success
227227
error: Error
228+
user_group_membership_through_badge:
229+
fields:
230+
badge_name:
231+
label: Badge Name
232+
group:
233+
label: Group
234+
description: Target group. Users with the specified badge will be added to this group
235+
remove_members_without_badge:
236+
label: Remove existing members without badge
237+
description: Optional, Remove existing group members without the specified badge
228238
suspend_user_by_email:
229239
fields:
230240
suspend_until:

plugins/automation/config/locales/server.en.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ en:
9191
user_global_notice:
9292
title: User global notice
9393
description: Allows to display a global notice for a user
94+
user_group_membership_through_badge:
95+
title: User Group Membership through Badge
96+
description: Modify user group membership based on a badge
9497
suspend_user_by_email_with_api_call:
9598
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.
9699
user_global_notice_with_stalled_topic:
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# frozen_string_literal: true
2+
3+
DiscourseAutomation::Scriptable::USER_GROUP_MEMBERSHIP_THROUGH_BADGE =
4+
"user_group_membership_through_badge"
5+
DiscourseAutomation::Scriptable::USER_GROUP_MEMBERSHIP_THROUGH_BADGE_BULK_MODIFY_START_COUNT = 1000
6+
7+
DiscourseAutomation::Scriptable.add(
8+
DiscourseAutomation::Scriptable::USER_GROUP_MEMBERSHIP_THROUGH_BADGE,
9+
) do
10+
version 1
11+
12+
field :badge_name, component: :text, required: true
13+
field :group, component: :group, required: true
14+
field :remove_members_without_badge, component: :boolean
15+
16+
triggerables %i[recurring user_first_logged_in]
17+
18+
script do |context, fields|
19+
badge_name = fields.dig("badge_name", "value").strip
20+
group_id = fields.dig("group", "value")
21+
remove_members_without_badge = fields.dig("remove_members_without_badge", "value")
22+
current_user = context["user"]
23+
bulk_modify_start_count =
24+
DiscourseAutomation::Scriptable::USER_GROUP_MEMBERSHIP_THROUGH_BADGE_BULK_MODIFY_START_COUNT
25+
26+
badge = Badge.find_by(name: badge_name)
27+
unless badge
28+
Rails.logger.warn("[discourse-automation] Couldn’t find badge with name #{badge_name}")
29+
next
30+
end
31+
32+
group = Group.find_by(id: group_id)
33+
unless group
34+
Rails.logger.warn("[discourse-automation] Couldn’t find group with id #{group_id}")
35+
next
36+
end
37+
38+
query_options = { group_id: group.id, badge_id: badge.id }
39+
40+
# IDs of users who currently have badge but not members of target group
41+
user_ids_to_add_query = +<<~SQL
42+
SELECT u.id AS user_id
43+
FROM users u
44+
JOIN user_badges ub ON u.id = ub.user_id
45+
LEFT JOIN group_users gu ON u.id = gu.user_id AND gu.group_id = :group_id
46+
WHERE ub.badge_id = :badge_id AND gu.user_id IS NULL
47+
SQL
48+
49+
if current_user
50+
user_ids_to_add_query << " AND u.id = :user_id"
51+
query_options[:user_id] = current_user.id
52+
end
53+
54+
user_ids_to_add = DB.query_single(user_ids_to_add_query, query_options)
55+
56+
if user_ids_to_add.count < bulk_modify_start_count
57+
User
58+
.where(id: user_ids_to_add)
59+
.each do |user|
60+
group.add(user)
61+
GroupActionLogger.new(Discourse.system_user, group).log_add_user_to_group(user)
62+
end
63+
else
64+
group.bulk_add(user_ids_to_add)
65+
end
66+
67+
next unless remove_members_without_badge
68+
69+
# IDs of users who are currently target group members without the badge
70+
user_ids_to_remove_query = +<<~SQL
71+
SELECT u.id AS user_id
72+
FROM users u
73+
JOIN group_users gu ON u.id = gu.user_id
74+
LEFT JOIN user_badges ub ON u.id = ub.user_id AND ub.badge_id = :badge_id
75+
WHERE gu.group_id = :group_id AND ub.user_id IS NULL
76+
SQL
77+
78+
if current_user
79+
user_ids_to_remove_query << " AND u.id = :user_id"
80+
query_options[:user_id] ||= current_user.id
81+
end
82+
83+
user_ids_to_remove = DB.query_single(user_ids_to_remove_query, query_options)
84+
85+
if user_ids_to_remove.count < bulk_modify_start_count
86+
User
87+
.where(id: user_ids_to_remove)
88+
.each do |user|
89+
group.remove(user)
90+
GroupActionLogger.new(Discourse.system_user, group).log_remove_user_from_group(user)
91+
end
92+
else
93+
group.bulk_remove(user_ids_to_remove)
94+
end
95+
end
96+
end

plugins/automation/plugin.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ module ::DiscourseAutomation
5353
lib/discourse_automation/scripts/suspend_user_by_email
5454
lib/discourse_automation/scripts/topic_required_words
5555
lib/discourse_automation/scripts/user_global_notice
56+
lib/discourse_automation/scripts/user_group_membership_through_badge
5657
lib/discourse_automation/scripts/zapier_webhook
5758
lib/discourse_automation/triggers/after_post_cook
5859
lib/discourse_automation/triggers/api_call

0 commit comments

Comments
 (0)