@@ -48,36 +48,19 @@ async def on_member_update(self, before: discord.Member, after: discord.Member):
4848 # Onboarding state is not changed or onboarding is not complete
4949 return
5050
51- await self .handle_onboarding (after )
51+ await self .process_onboarding_for_member (after )
5252
5353 @commands .Cog .listener ()
5454 async def on_ready (self ):
5555 """
5656 Listen for on_ready event.
57- When event fires, add onboarded role to members in all guilds who meet the following criteria:
58- - Not in `onboarded_users` list.
59- - Has completed onboarding.
60- - Does not have the onboarded role.
57+ When event fires, process onboarding for all eligible members.
6158 """
6259 # Wait until Red is fully ready and cache is populated
6360 await self .bot .wait_until_red_ready ()
6461
6562 for guild in self .bot .guilds :
66- onboarded_role_id = await self .config .guild (guild ).role ()
67- if not onboarded_role_id :
68- # No role configured for this guild
69- continue
70-
71- onboarded_role = guild .get_role (onboarded_role_id )
72- if not onboarded_role :
73- # Role not found
74- log .warning (f"Role ID { onboarded_role_id } not found in guild { guild .name } (ID { guild .id } )." )
75- continue
76-
77- onboarded_users = await self .config .guild (guild ).onboarded_users ()
78- for member in guild .members :
79- if member not in onboarded_users and member .flags .completed_onboarding and onboarded_role not in member .roles :
80- await self .handle_onboarding (member )
63+ await self .process_onboarding_for_guild (guild )
8164
8265 # Commands
8366
@@ -115,7 +98,7 @@ async def get_status(self, ctx: commands.GuildContext):
11598 embed = (
11699 discord .Embed (colour = (await ctx .embed_colour ()))
117100 .add_field (name = "Onboarded Role" , value = onboarded_role )
118- .add_field (name = "Log Channnel " , value = log_channel )
101+ .add_field (name = "Log Channel " , value = log_channel )
119102 .add_field (name = "Onboarded User Count" , value = num_onboarded_users , inline = False )
120103 )
121104
@@ -134,8 +117,12 @@ async def set_role(self, ctx: commands.GuildContext, role: discord.Role):
134117 - `[p]onboarding_role role 1253932390562590999`
135118 """
136119 await self .config .guild (ctx .guild ).role .set (role .id )
137- log .debug (f"Onboarded role set to { role .name } (ID { role .id } )" )
120+ log .debug (f"Onboarded role set to ' { role .name } ' (ID { role .id } )" )
138121 await ctx .tick ()
122+ await ctx .send (
123+ f"Onboarding role has been set to { role .mention } . All eligible members will be assigned this role "
124+ f"next time the bot starts, or it can be triggered now with `{ ctx .prefix } onboarding_role process`."
125+ )
139126
140127 @onboarding_role .command ("logchannel" )
141128 async def set_log_channel (self , ctx : commands .GuildContext , channel : discord .TextChannel ):
@@ -148,14 +135,78 @@ async def set_log_channel(self, ctx: commands.GuildContext, channel: discord.Tex
148135 """
149136 if channel .permissions_for (ctx .me ).send_messages and channel .permissions_for (ctx .me ).embed_links :
150137 await self .config .guild (ctx .guild ).log_channel .set (channel .id )
151- log .debug (f"Log channel set to { channel .name } (ID { channel .id } )" )
138+ log .debug (f"Log channel set to ' { channel .name } ' (ID { channel .id } )" )
152139 await ctx .tick ()
153140 else :
154141 await ctx .send (f"❌ I need the `Send Messages` and `Embed Links` permissions to send logs to { channel .mention } ." )
155142
143+ @onboarding_role .command ("process" )
144+ async def manual_onboarding (self , ctx : commands .GuildContext , dry_run : bool = False ):
145+ """
146+ Manually process onboarding for all eligible members in this guild.
147+
148+ This will check all members in the guild and assign the onboarding role
149+ to those who have completed onboarding but don't have the role yet.
150+
151+ Args:
152+ dry_run: If True, only show what would be done without making changes.
153+ """
154+ async with ctx .typing ():
155+ processed_count = await self .process_onboarding_for_guild (ctx .guild , dry_run = dry_run )
156+
157+ if dry_run :
158+ if processed_count == 0 :
159+ await ctx .send ("🔍 **Dry Run**: No members would need onboarding role assignment." )
160+ elif processed_count == 1 :
161+ await ctx .send ("🔍 **Dry Run**: 1 member would be processed for onboarding role assignment." )
162+ else :
163+ await ctx .send (f"🔍 **Dry Run**: { processed_count } members would be processed for onboarding role assignment." )
164+ elif processed_count == 0 :
165+ await ctx .send ("✅ No members needed onboarding role assignment." )
166+ elif processed_count == 1 :
167+ await ctx .send ("✅ Processed onboarding for 1 member." )
168+ else :
169+ await ctx .send (f"✅ Processed onboarding for { processed_count } members." )
170+
156171 # Helpers
157172
158- async def handle_onboarding (self , member : discord .Member ):
173+ async def process_onboarding_for_guild (self , guild : discord .Guild , dry_run : bool = False ) -> int :
174+ """
175+ Process onboarding for members in a specific guild who meet the following criteria:
176+ - Not in `onboarded_users` list.
177+ - Has completed onboarding.
178+ - Does not have the onboarded role.
179+
180+ Args:
181+ guild: The Discord guild to process onboarding for.
182+ dry_run: If True, only count eligible members without making changes.
183+
184+ Returns:
185+ int: Number of members processed (or would be processed in dry run).
186+ """
187+ onboarded_role_id = await self .config .guild (guild ).role ()
188+ if not onboarded_role_id :
189+ # No role configured for this guild
190+ return 0
191+
192+ onboarded_role = guild .get_role (onboarded_role_id )
193+ if not onboarded_role :
194+ # Role not found
195+ log .warning (f"Role ID { onboarded_role_id } not found in guild '{ guild .name } ' (ID { guild .id } )." )
196+ return 0
197+
198+ onboarded_users = await self .config .guild (guild ).onboarded_users ()
199+ processed_count = 0
200+
201+ for member in guild .members :
202+ if member .id not in onboarded_users and member .flags .completed_onboarding and onboarded_role not in member .roles :
203+ if not dry_run :
204+ await self .process_onboarding_for_member (member )
205+ processed_count += 1
206+
207+ return processed_count
208+
209+ async def process_onboarding_for_member (self , member : discord .Member ):
159210 """Handle onboarding completed event"""
160211 log .debug (f"User '{ member .name } ' (ID { member .id } ) completed onboarding" )
161212 guild = member .guild
@@ -171,7 +222,7 @@ async def handle_onboarding(self, member: discord.Member):
171222 # Welcome role is not found
172223 log .warning (
173224 f"Cannot grant onboarding role to '{ member .name } ' (ID { member .id } ): "
174- + f"Onboarding role set to ID { role_id } but could not found."
225+ + f"Onboarding role set to ID { role_id } but could not be found."
175226 )
176227 return
177228
@@ -186,7 +237,7 @@ async def handle_onboarding(self, member: discord.Member):
186237
187238 await self .send_log_message (member )
188239 except discord .Forbidden :
189- error_msg = f"Adding onboarding role to { member .name } (ID { member .id } ) was forbidden."
240+ error_msg = f"Adding onboarding role to ' { member .name } ' (ID { member .id } ) was forbidden."
190241 log .warning (error_msg )
191242 await self .send_log_message (member , error_msg )
192243
@@ -226,7 +277,7 @@ async def send_log_message(self, member: discord.Member, error_msg: str = ""):
226277 try :
227278 await log_channel .send (embed = embed )
228279 except discord .Forbidden :
229- log .warning (f"Sending onboarding log to { log_channel .name } (ID { log_channel_id } ) was forbidden." )
280+ log .warning (f"Sending onboarding log to ' { log_channel .name } ' (ID { log_channel_id } ) was forbidden." )
230281
231282
232283def humanise_timedelta (delta : timedelta ) -> str :
0 commit comments