| 
									
										
										
										
											2024-08-09 22:28:02 +01:00
										 |  |  | """
 | 
					
						
							|  |  |  |     matchy.py - Discord bot that matches people into groups | 
					
						
							|  |  |  | """
 | 
					
						
							| 
									
										
										
										
											2024-08-08 23:32:52 +01:00
										 |  |  | import logging | 
					
						
							| 
									
										
										
										
											2024-08-09 22:28:02 +01:00
										 |  |  | import discord | 
					
						
							| 
									
										
										
										
											2024-08-08 00:09:30 +01:00
										 |  |  | from discord import app_commands | 
					
						
							| 
									
										
										
										
											2024-08-07 22:45:23 +01:00
										 |  |  | from discord.ext import commands | 
					
						
							| 
									
										
										
										
											2024-08-10 09:44:22 +01:00
										 |  |  | import matching | 
					
						
							| 
									
										
										
										
											2024-08-10 10:45:44 +01:00
										 |  |  | import history | 
					
						
							|  |  |  | import config | 
					
						
							| 
									
										
										
										
											2024-08-10 09:44:22 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-10 10:06:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-10 10:45:44 +01:00
										 |  |  | Config = config.load() | 
					
						
							|  |  |  | History = history.load() | 
					
						
							| 
									
										
										
										
											2024-08-07 23:23:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-08 23:32:52 +01:00
										 |  |  | logger = logging.getLogger("matchy") | 
					
						
							|  |  |  | logger.setLevel(logging.INFO) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-07 22:15:39 +01:00
										 |  |  | intents = discord.Intents.default() | 
					
						
							|  |  |  | intents.message_content = True | 
					
						
							| 
									
										
										
										
											2024-08-07 23:23:52 +01:00
										 |  |  | intents.members = True | 
					
						
							| 
									
										
										
										
											2024-08-09 22:12:58 +01:00
										 |  |  | bot = commands.Bot(command_prefix='$', | 
					
						
							|  |  |  |                    description="Matchy matches matchees", intents=intents) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-07 22:15:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-08 00:09:30 +01:00
										 |  |  | @bot.event | 
					
						
							|  |  |  | async def on_ready(): | 
					
						
							| 
									
										
										
										
											2024-08-09 22:08:05 +01:00
										 |  |  |     """Bot is ready and connected""" | 
					
						
							| 
									
										
										
										
											2024-08-08 23:32:52 +01:00
										 |  |  |     logger.info("Bot is up and ready!") | 
					
						
							| 
									
										
										
										
											2024-08-09 22:08:05 +01:00
										 |  |  |     activity = discord.Game("/match") | 
					
						
							|  |  |  |     await bot.change_presence(status=discord.Status.online, activity=activity) | 
					
						
							| 
									
										
										
										
											2024-08-07 23:23:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-09 22:12:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-10 10:06:13 +01:00
										 |  |  | def owner_only(ctx: commands.Context) -> bool: | 
					
						
							|  |  |  |     """Checks the author is an owner""" | 
					
						
							| 
									
										
										
										
											2024-08-10 10:45:44 +01:00
										 |  |  |     return ctx.message.author.id in Config.owners | 
					
						
							| 
									
										
										
										
											2024-08-10 10:06:13 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-08 19:45:53 +01:00
										 |  |  | @bot.command() | 
					
						
							| 
									
										
										
										
											2024-08-09 00:32:59 +01:00
										 |  |  | @commands.dm_only() | 
					
						
							| 
									
										
										
										
											2024-08-10 10:06:13 +01:00
										 |  |  | @commands.check(owner_only) | 
					
						
							| 
									
										
										
										
											2024-08-09 16:34:11 +01:00
										 |  |  | async def sync(ctx: commands.Context): | 
					
						
							| 
									
										
										
										
											2024-08-09 22:08:05 +01:00
										 |  |  |     """Handle sync command""" | 
					
						
							| 
									
										
										
										
											2024-08-09 16:34:11 +01:00
										 |  |  |     msg = await ctx.reply("Reloading config...", ephemeral=True) | 
					
						
							| 
									
										
										
										
											2024-08-10 10:55:09 +01:00
										 |  |  |     Config.reload() | 
					
						
							| 
									
										
										
										
											2024-08-09 22:28:02 +01:00
										 |  |  |     logger.info("Reloaded config") | 
					
						
							| 
									
										
										
										
											2024-08-09 16:34:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-09 22:08:05 +01:00
										 |  |  |     await msg.edit(content="Syncing commands...") | 
					
						
							| 
									
										
										
										
											2024-08-08 19:45:53 +01:00
										 |  |  |     synced = await bot.tree.sync() | 
					
						
							| 
									
										
										
										
											2024-08-09 22:28:02 +01:00
										 |  |  |     logger.info("Synced %s command(s)", len(synced)) | 
					
						
							| 
									
										
										
										
											2024-08-09 16:34:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     await msg.edit(content="Done!") | 
					
						
							| 
									
										
										
										
											2024-08-08 19:45:53 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-09 22:12:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-10 00:01:43 +01:00
										 |  |  | @bot.command() | 
					
						
							|  |  |  | @commands.dm_only() | 
					
						
							| 
									
										
										
										
											2024-08-10 10:06:13 +01:00
										 |  |  | @commands.check(owner_only) | 
					
						
							| 
									
										
										
										
											2024-08-10 00:01:43 +01:00
										 |  |  | async def close(ctx: commands.Context): | 
					
						
							|  |  |  |     """Handle restart command""" | 
					
						
							|  |  |  |     await ctx.reply("Closing bot...", ephemeral=True) | 
					
						
							|  |  |  |     logger.info("Closing down the bot") | 
					
						
							|  |  |  |     await bot.close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-09 22:12:58 +01:00
										 |  |  | @bot.tree.command(description="Match up matchees") | 
					
						
							| 
									
										
										
										
											2024-08-09 00:32:59 +01:00
										 |  |  | @commands.guild_only() | 
					
						
							| 
									
										
										
										
											2024-08-09 22:28:02 +01:00
										 |  |  | @app_commands.describe(group_min="Minimum matchees per match (defaults to 3)", | 
					
						
							|  |  |  |                        matchee_role="Role for matchees (defaults to @Matchee)") | 
					
						
							| 
									
										
										
										
											2024-08-09 22:08:05 +01:00
										 |  |  | async def match(interaction: discord.Interaction, group_min: int = None, matchee_role: str = None): | 
					
						
							| 
									
										
										
										
											2024-08-09 00:06:23 +01:00
										 |  |  |     """Match groups of channel members""" | 
					
						
							| 
									
										
										
										
											2024-08-09 22:12:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-09 22:28:02 +01:00
										 |  |  |     logger.info("Handling request '/match group_min=%s matchee_role=%s'", | 
					
						
							|  |  |  |                 group_min, matchee_role) | 
					
						
							|  |  |  |     logger.info("User %s from %s in #%s", interaction.user, | 
					
						
							|  |  |  |                 interaction.guild.name, interaction.channel.name) | 
					
						
							| 
									
										
										
										
											2024-08-09 22:08:05 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Sort out the defaults, if not specified they'll come in as None | 
					
						
							|  |  |  |     if not group_min: | 
					
						
							|  |  |  |         group_min = 3 | 
					
						
							|  |  |  |     if not matchee_role: | 
					
						
							|  |  |  |         matchee_role = "Matchee" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Grab the roles and verify the given role | 
					
						
							| 
									
										
										
										
											2024-08-10 09:44:22 +01:00
										 |  |  |     matcher = matching.get_role_from_guild(interaction.guild, "Matcher") | 
					
						
							| 
									
										
										
										
											2024-08-09 22:53:28 +01:00
										 |  |  |     matcher = matcher and matcher in interaction.user.roles | 
					
						
							| 
									
										
										
										
											2024-08-10 09:44:22 +01:00
										 |  |  |     matchee = matching.get_role_from_guild(interaction.guild, matchee_role) | 
					
						
							| 
									
										
										
										
											2024-08-09 22:50:22 +01:00
										 |  |  |     if not matchee: | 
					
						
							| 
									
										
										
										
											2024-08-09 22:08:05 +01:00
										 |  |  |         await interaction.response.send_message(f"Server is missing '{matchee_role}' role :(", ephemeral=True) | 
					
						
							| 
									
										
										
										
											2024-08-07 23:23:52 +01:00
										 |  |  |         return | 
					
						
							| 
									
										
										
										
											2024-08-09 14:12:28 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-09 22:08:05 +01:00
										 |  |  |     # Create our groups! | 
					
						
							| 
									
										
										
										
											2024-08-09 22:12:58 +01:00
										 |  |  |     matchees = list( | 
					
						
							| 
									
										
										
										
											2024-08-09 22:50:22 +01:00
										 |  |  |         m for m in interaction.channel.members if matchee in m.roles) | 
					
						
							| 
									
										
										
										
											2024-08-10 21:47:32 +01:00
										 |  |  |     groups = matching.members_to_groups(matchees, History, group_min, allow_fallback=True) | 
					
						
							| 
									
										
										
										
											2024-08-09 22:12:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-09 22:08:05 +01:00
										 |  |  |     # Post about all the groups with a button to send to the channel | 
					
						
							| 
									
										
										
										
											2024-08-10 09:44:22 +01:00
										 |  |  |     msg = '\n'.join(matching.group_to_message(g) for g in groups) | 
					
						
							| 
									
										
										
										
											2024-08-09 22:12:58 +01:00
										 |  |  |     if not matcher:  # Let a non-matcher know why they don't have the button | 
					
						
							| 
									
										
										
										
											2024-08-09 22:53:28 +01:00
										 |  |  |         msg += f"\nYou'll need the {matcher.mention if matcher else 'Matcher'}" | 
					
						
							| 
									
										
										
										
											2024-08-09 22:28:02 +01:00
										 |  |  |         msg += " role to send this to the channel, sorry!" | 
					
						
							|  |  |  |     await interaction.response.send_message(msg, ephemeral=True, silent=True, | 
					
						
							|  |  |  |                                             view=(GroupMessageButton(groups) if matcher else discord.utils.MISSING)) | 
					
						
							| 
									
										
										
										
											2024-08-09 22:08:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-09 22:28:02 +01:00
										 |  |  |     logger.info("Done. Matched %s matchees into %s groups.", | 
					
						
							|  |  |  |                 len(matchees), len(groups)) | 
					
						
							| 
									
										
										
										
											2024-08-09 22:12:58 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-09 22:08:05 +01:00
										 |  |  | class GroupMessageButton(discord.ui.View): | 
					
						
							|  |  |  |     """A button to press to send the groups to the channel""" | 
					
						
							| 
									
										
										
										
											2024-08-09 22:12:58 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, groups: list[list[discord.Member]], timeout: int = 180): | 
					
						
							| 
									
										
										
										
											2024-08-09 22:08:05 +01:00
										 |  |  |         self.groups = groups | 
					
						
							|  |  |  |         super().__init__(timeout=timeout) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @discord.ui.button(label="Send groups to channel", style=discord.ButtonStyle.green, emoji="📮") | 
					
						
							| 
									
										
										
										
											2024-08-10 10:25:11 +01:00
										 |  |  |     async def send_to_channel(self, interaction: discord.Interaction, _button: discord.ui.Button) -> None: | 
					
						
							| 
									
										
										
										
											2024-08-09 22:28:02 +01:00
										 |  |  |         """Send the groups to the channel with the button is pressed""" | 
					
						
							| 
									
										
										
										
											2024-08-10 09:44:22 +01:00
										 |  |  |         for msg in (matching.group_to_message(g) for g in self.groups): | 
					
						
							|  |  |  |             await interaction.channel.send(msg) | 
					
						
							|  |  |  |         await interaction.channel.send("That's all folks, happy matching and remember - DFTBA!") | 
					
						
							| 
									
										
										
										
											2024-08-09 22:28:02 +01:00
										 |  |  |         await interaction.response.edit_message(content="Groups sent to channel!", view=None) | 
					
						
							| 
									
										
										
										
											2024-08-10 10:55:09 +01:00
										 |  |  |         History.save_groups_to_history(self.groups) | 
					
						
							| 
									
										
										
										
											2024-08-10 10:06:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-09 22:12:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-09 23:14:42 +01:00
										 |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     handler = logging.StreamHandler() | 
					
						
							| 
									
										
										
										
											2024-08-10 10:45:44 +01:00
										 |  |  |     bot.run(Config.token, log_handler=handler, root_logger=True) |