From bbc7bf6c8ee324b982d19ba8348538ee87ce58c8 Mon Sep 17 00:00:00 2001 From: thomasgonda3 Date: Wed, 6 Mar 2024 22:48:02 -0800 Subject: [PATCH 001/147] added mkwii branch --- cogs/SquadQueue.py | 102 +-------------------------------------- mkwii_sample_config.json | 34 +++++++++++++ mmr.py | 24 ++++----- mogi_objects.py | 83 ++++++++++++++++++------------- redeploy.sh | 8 +-- 5 files changed, 103 insertions(+), 148 deletions(-) create mode 100644 mkwii_sample_config.json diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 7abd1e9..bbb4c25 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -5,7 +5,7 @@ from datetime import datetime, timezone, timedelta import time import json -from mmr import mk8dx_150cc_mmr, get_mmr_from_discord_id, mk8dx_150cc_fc +from mmr import mk8dx_150cc_mmr, get_mmr_from_discord_id from mogi_objects import Mogi, Team, Player, Room, VoteView, JoinView, get_tier import asyncio @@ -248,48 +248,6 @@ async def sub(self, interaction: discord.Interaction): await self.SUB_CHANNEL.send(view=view, delete_after=self.SUB_MESSAGE_LIFETIME_SECONDS) await interaction.response.send_message("Sent out request for sub.") - @app_commands.command(name="l") - @app_commands.checks.cooldown(1, 120, key=lambda i: (i.channel.id)) - @app_commands.guild_only() - async def list(self, interaction: discord.Interaction): - """Display the list of confirmed players for a mogi""" - mogi = self.get_mogi(interaction) - if mogi is None: - await interaction.response.send_message("Queue has not started yet.") - return - if not await self.is_started(interaction, mogi): - return - mogi_list = mogi.confirmed_list() - if len(mogi_list) == 0: - await interaction.response.send_message(f"There are no players in the queue - type `/c` to join") - return - - sorted_mogi_list = sorted(mogi_list, reverse=True) - msg = "Current Mogi List:\n" - for i in range(len(sorted_mogi_list)): - msg += f"{i+1}) " - msg += ", ".join([p.lounge_name for p in sorted_mogi_list[i].players]) - msg += f" ({sorted_mogi_list[i].avg_mmr:.1f} MMR)\n" - if (len(sorted_mogi_list) % (12/mogi.size) != 0): - num_next = int(len(sorted_mogi_list) % (12/mogi.size)) - teams_per_room = int(12/mogi.size) - num_rooms = int(len(sorted_mogi_list) / (12/mogi.size))+1 - msg += f"[{num_next}/{teams_per_room}] players for {num_rooms} room(s)" - message = msg.split("\n") - bulk_msg = "" - for i in range(len(message)): - if len(bulk_msg + message[i] + "\n") > 2000: - await interaction.channel.send(bulk_msg) if interaction.response.is_done() else await interaction.response.send_message(bulk_msg) - bulk_msg = "" - bulk_msg += message[i] + "\n" - if len(bulk_msg) > 0: - await interaction.channel.send(bulk_msg) if interaction.response.is_done() else await interaction.response.send_message(bulk_msg) - - @list.error # Tell the user when they've got a cooldown - async def on_list_error(self, interaction: discord.Interaction, error: app_commands.AppCommandError): - if isinstance(error, app_commands.CommandOnCooldown): - await interaction.response.send_message("Wait before using `/l` command", ephemeral=True) - @tasks.loop(minutes=1) async def list_task(self): """Continually display the list of confirmed players for a mogi in the history channel""" @@ -378,37 +336,6 @@ async def on_message(self, message: discord.Message): if player: player.score = int(message.content) - @app_commands.command(name="scoreboard") - @app_commands.guild_only() - async def scoreboard(self, interaction: discord.Interaction): - """Displays the scoreboard of the room. Only works in thread channels for SQ rooms.""" - - mogi = discord.utils.find(lambda mogi: mogi.is_room_thread( - interaction.channel_id), self.ongoing_events.values()) - if not mogi: - mogi = discord.utils.find(lambda mogi: mogi.is_room_thread( - interaction.channel_id), self.old_events.values()) - if not mogi: - await interaction.response.send_message(f"The Mogi object cannot be found.", ephemeral=True) - return - - room = discord.utils.find( - lambda room: room.thread.id == interaction.channel_id, mogi.rooms) - - if not room: - await interaction.response.send_message(f"The Thread object cannot be found.", ephemeral=True) - return - - format = round(12/len(room.teams)) - - msg = f"!submit {format} {get_tier(room.mmr_average - 500)}\n" - for team in room.teams: - for player in team.players: - msg += f"{player.lounge_name} {player.score}\n" - if format != 1: - msg += "\n" - await interaction.response.send_message(msg) - @app_commands.command(name="remove_player") @app_commands.guild_only() async def remove_player(self, interaction: discord.Interaction, member: discord.Member): @@ -533,31 +460,6 @@ async def has_roles(self, member: discord.Member, guild_id: int, config): return True return False - # command to add staff to room thread channels; users can't add new users to private threads, - # so the bot has to with this command - @commands.command() - @commands.cooldown(1, 60, commands.BucketType.channel) - async def staff(self, ctx): - """Calls staff to the current channel. Only works in thread channels for SQ rooms.""" - is_room_thread = False - for mogi in self.ongoing_events.values(): - if mogi.is_room_thread(ctx.channel.id): - is_room_thread = True - break - for mogi in self.old_events.values(): - if mogi.is_room_thread(ctx.channel.id): - is_room_thread = True - break - if not is_room_thread: - return - if str(ctx.guild.id) not in ctx.bot.config["staff_roles"].keys(): - await ctx.send("There is no Lounge Staff role configured for this server") - return - lounge_staff_roles = ctx.bot.config["staff_roles"][str(ctx.guild.id)] - mentions = " ".join( - [ctx.guild.get_role(role).mention for role in lounge_staff_roles]) - await ctx.send(mentions) - async def end_voting(self): """Ends voting in all rooms with ongoing votes.""" try: @@ -669,7 +571,7 @@ async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=F room_msg = msg mentions += " ".join([m.mention for m in extra_members if m is not None]) room_msg += "\nVote for format FFA, 2v2, 3v3, or 4v4.\n" - room_msg += "\nIf you need staff's assistance, use the `!staff` command in this channel.\n" + # room_msg += "\nIf you need staff's assistance, use the `!staff` command in this channel.\n" room_msg += mentions try: curr_room = rooms[i] diff --git a/mkwii_sample_config.json b/mkwii_sample_config.json new file mode 100644 index 0000000..94cf7de --- /dev/null +++ b/mkwii_sample_config.json @@ -0,0 +1,34 @@ +{ + "token": "", + "guild_id": 387347467332485122, + + "admin_roles": {"387347467332485122": ["Administrator", "Lounge Staff"]}, + + "queue_join_channel": 1215174119767932979, + "queue_sub_channel": 1215174160234315806, + "queue_history_channel": 1215174057453162496, + + "members_for_channels": {"387347467332485122": [ + 235148962103951360, + 155149108183695360, + 815483289515196416, + 892420397570592768, + 775415990238314518, + 774812906830299136, + 745443073202323586, + 774866940950872095]}, + + "queue_messages": true, + "sec_between_queue_msgs": 2, + + "username": "username", + "password": "password", + "url": "https://www.mkwlounge.gg", + + "TIME_ADJUSTMENT": 0, + "QUEUE_OPEN_TIME": 60, + "JOINING_TIME": 55, + "EXTENSION_TIME": 3, + "MOGI_LIFETIME": 180, + "SUB_MESSAGE_LIFETIME_SECONDS": 1200 +} \ No newline at end of file diff --git a/mmr.py b/mmr.py index 3e61701..26828d2 100644 --- a/mmr.py +++ b/mmr.py @@ -6,7 +6,7 @@ async def mk8dx_150cc_mmr(url, members): - base_url = url + '/api/player?' + base_url = url + '/api/ladderplayer.php?ladder_id=9&' players = [] timeout = aiohttp.ClientTimeout(total=10) try: @@ -15,40 +15,42 @@ async def mk8dx_150cc_mmr(url, members): auth=aiohttp.BasicAuth( "username", "password")) as session: for member in members: - request_text = f"discordId={member.id}" + request_text = f"discord_user_id={member.id}" request_url = base_url + request_text async with session.get(request_url, headers=headers) as resp: if resp.status != 200: players.append(None) continue - player_data = await resp.json() - if 'mmr' not in player_data.keys(): + result = await resp.json() + player_data = result["results"][0] + if 'current_mmr' not in player_data.keys(): players.append( - Player(member, player_data['name'], None)) + Player(member, player_data['player_name'], None)) continue players.append( - Player(member, player_data['name'], player_data['mmr'])) + Player(member, player_data['player_name'], player_data['current_mmr'])) except: print(f"Fetch for player {members[0]} has failed.", flush=True) return players async def get_mmr_from_discord_id(discord_id): - base_url = "https://www.mk8dx-lounge.com" + '/api/player?' + base_url = "https://www.mkwlounge.gg" + '/api/ladderplayer.php?ladder_id=9&' timeout = aiohttp.ClientTimeout(total=10) async with aiohttp.ClientSession( timeout=timeout, auth=aiohttp.BasicAuth( "username", "password")) as session: - request_text = f"discordId={discord_id}" + request_text = f"discord_user_id={discord_id}" request_url = base_url + request_text async with session.get(request_url, headers=headers) as resp: if resp.status != 200: return "Player does not exist" - player_data = await resp.json() - if 'mmr' not in player_data.keys(): + result = await resp.json() + player_data = result["results"][0] + if 'current_mmr' not in player_data.keys(): return "Player has no mmr" - return player_data['mmr'] + return player_data['current_mmr'] async def get_mmr(config, members): diff --git a/mogi_objects.py b/mogi_objects.py index 7a9c1a8..d63b0fe 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -163,6 +163,7 @@ def __init__(self, players, thread, mogi): self.__setattr__("2v2", []) self.__setattr__("3v3", []) self.__setattr__("4v4", []) + self.__setattr__("6v6", []) def __getitem__(self, key): return getattr(self, key) @@ -177,11 +178,12 @@ async def make_teams(self, format): msg += f"2) 2v2 - {len(self['2v2'])}\n" msg += f"3) 3v3 - {len(self['3v3'])}\n" msg += f"4) 4v4 - {len(self['4v4'])}\n" + msg += f"5) 6v6 - {len(self['6v6'])}\n" msg += f"Winner: {format[1]}\n\n" room_mmr = round(sum([p.mmr for p in self.players]) / 12) room.mmr_average = room_mmr - self.header_text = f"**Room {room.room_num} MMR: {room_mmr} - Tier {get_tier(room_mmr - 500)}** " + self.header_text = f"**Room {room.room_num} MMR: {room_mmr} - {get_tier(room_mmr)}** " msg += self.header_text msg += "\n" @@ -204,11 +206,7 @@ async def make_teams(self, format): msg += team_text self.teams_text += team_text - msg += f"\nTable: `/scoreboard`\n" - - msg += f"RandomBot Scoreboard: `/scoreboard {teams_per_room} {', '.join(scoreboard_text)}`\n\n" - - msg += "Decide a host amongst yourselves; room open at :00, penalty at :06. Good luck!" + msg += "Decide a host amongst yourselves; room open at :00, penalty at :08. Good luck!" room.teams = teams @@ -227,6 +225,8 @@ async def find_winner(self): max = len(self["3v3"]) if len(self["4v4"]) > max: max = len(self["4v4"]) + if len(self["6v6"]) > max: + max = len(self["6v6"]) winners = [] @@ -238,6 +238,8 @@ async def find_winner(self): winners.append((3, "3v3")) if len(self["4v4"]) == max: winners.append((4, "4v4")) + if len(self["6v6"]) == max: + winners.append((6, "6v6")) winner = random.choice(winners) @@ -258,6 +260,8 @@ async def one_button_callback(self, interaction, button): self["3v3"].remove(interaction.user.id) if interaction.user.id in self["4v4"]: self["4v4"].remove(interaction.user.id) + if interaction.user.id in self["6v6"]: + self["6v6"].remove(interaction.user.id) self["FFA"].append(interaction.user.id) if len(self["FFA"]) == 6: await self.make_teams((1, "FFA")) @@ -279,6 +283,8 @@ async def two_button_callback(self, interaction, button): self["3v3"].remove(interaction.user.id) if interaction.user.id in self["4v4"]: self["4v4"].remove(interaction.user.id) + if interaction.user.id in self["6v6"]: + self["6v6"].remove(interaction.user.id) self["2v2"].append(interaction.user.id) if len(self["2v2"]) == 6: await self.make_teams((2, "2v2")) @@ -300,6 +306,8 @@ async def three_button_callback(self, interaction, button): self["2v2"].remove(interaction.user.id) if interaction.user.id in self["4v4"]: self["4v4"].remove(interaction.user.id) + if interaction.user.id in self["6v6"]: + self["6v6"].remove(interaction.user.id) self["3v3"].append(interaction.user.id) if len(self["3v3"]) == 6: await self.make_teams((3, "3v3")) @@ -321,6 +329,8 @@ async def four_button_callback(self, interaction, button): self["2v2"].remove(interaction.user.id) if interaction.user.id in self["3v3"]: self["3v3"].remove(interaction.user.id) + if interaction.user.id in self["6v6"]: + self["6v6"].remove(interaction.user.id) self["4v4"].append(interaction.user.id) if len(self["4v4"]) == 6: await self.make_teams((4, "4v4")) @@ -330,6 +340,29 @@ async def four_button_callback(self, interaction, button): curr_button.disabled = True await interaction.response.edit_message(view=self) + @discord.ui.button(label="6v6: 0", custom_id="6v6") + async def six_button_callback(self, interaction, button): + if not self.found_winner: + if interaction.user.id in self["6v6"]: + self["6v6"].remove(interaction.user.id) + else: + if interaction.user.id in self["FFA"]: + self["FFA"].remove(interaction.user.id) + if interaction.user.id in self["2v2"]: + self["2v2"].remove(interaction.user.id) + if interaction.user.id in self["3v3"]: + self["3v3"].remove(interaction.user.id) + if interaction.user.id in self["4v4"]: + self["4v4"].remove(interaction.user.id) + self["6v6"].append(interaction.user.id) + if len(self["6v6"]) == 6: + await self.make_teams((6, "6v6")) + for curr_button in self.children: + curr_button.label = f"{curr_button.custom_id}: {len(self[curr_button.custom_id])}" + if len(self["6v6"]) == 6: + curr_button.disabled = True + await interaction.response.edit_message(view=self) + class JoinView(View): def __init__(self, room, get_mmr): @@ -364,33 +397,17 @@ async def button_callback(self, interaction, button): def get_tier(mmr: int): - if mmr > 14000: - return 'X' - if mmr > 13000: - return 'S' - if mmr > 12000: - return 'A' if mmr > 11000: - return 'AB' - if mmr > 10000: - return 'B' - if mmr > 9000: - return 'BC' - if mmr > 8000: - return 'C' - if mmr > 7000: - return 'CD' - if mmr > 6000: - return 'D' + return 'T7' + if mmr > 9500: + return 'T6' + if mmr > 7500: + return 'T5' + if mmr > 6250: + return 'T4' if mmr > 5000: - return 'DE' - if mmr > 4000: - return 'E' - if mmr > 3000: - return 'EF' - if mmr > 2000: - return 'F' - if mmr > 1000: - return 'FG' + return 'T3' + if mmr > 3500: + return 'T2' else: - return 'G' + return 'T1' diff --git a/redeploy.sh b/redeploy.sh index 9db4c40..4cf9181 100644 --- a/redeploy.sh +++ b/redeploy.sh @@ -1,5 +1,5 @@ #!/bin/bash -docker build --tag mogi-queuebot . -docker stop mogi-queuebot -docker rm mogi-queuebot -docker run -d --name mogi-queuebot --restart unless-stopped mogi-queuebot \ No newline at end of file +docker build --tag mkwii-mogi-queuebot . +docker stop mkwii-mogi-queuebot +docker rm mkwii-mogi-queuebot +docker run -d --name mkwii-mogi-queuebot --restart unless-stopped mkwii-mogi-queuebot \ No newline at end of file From f3241d6591251efbbbbd100f0f895410b057dc82 Mon Sep 17 00:00:00 2001 From: thomasgonda3 Date: Mon, 8 Apr 2024 17:52:48 -0700 Subject: [PATCH 002/147] updated config file --- mkwii_sample_config.json | 65 +++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/mkwii_sample_config.json b/mkwii_sample_config.json index 94cf7de..a273c48 100644 --- a/mkwii_sample_config.json +++ b/mkwii_sample_config.json @@ -1,34 +1,37 @@ { - "token": "", - "guild_id": 387347467332485122, - - "admin_roles": {"387347467332485122": ["Administrator", "Lounge Staff"]}, - - "queue_join_channel": 1215174119767932979, + "token": "", + "guild_id": 387347467332485122, + "admin_roles": { + "387347467332485122": [ + "Administrator", + "Lounge Staff" + ] + }, + "queue_join_channel": 1215174119767932979, "queue_sub_channel": 1215174160234315806, - "queue_history_channel": 1215174057453162496, - - "members_for_channels": {"387347467332485122": [ - 235148962103951360, - 155149108183695360, - 815483289515196416, - 892420397570592768, - 775415990238314518, - 774812906830299136, - 745443073202323586, - 774866940950872095]}, - - "queue_messages": true, - "sec_between_queue_msgs": 2, - - "username": "username", - "password": "password", - "url": "https://www.mkwlounge.gg", - - "TIME_ADJUSTMENT": 0, - "QUEUE_OPEN_TIME": 60, - "JOINING_TIME": 55, - "EXTENSION_TIME": 3, - "MOGI_LIFETIME": 180, - "SUB_MESSAGE_LIFETIME_SECONDS": 1200 + "queue_list_channel": 1215174057453162496, + "queue_history_channel": 1225709377747685477, + "members_for_channels": { + "387347467332485122": [ + 235148962103951360, + 155149108183695360, + 815483289515196416, + 892420397570592768, + 775415990238314518, + 774812906830299136, + 745443073202323586, + 774866940950872095 + ] + }, + "queue_messages": true, + "sec_between_queue_msgs": 2, + "username": "username", + "password": "password", + "url": "https://www.mkwlounge.gg", + "TIME_ADJUSTMENT": 0, + "QUEUE_OPEN_TIME": 60, + "JOINING_TIME": 55, + "EXTENSION_TIME": 3, + "MOGI_LIFETIME": 180, + "SUB_MESSAGE_LIFETIME_SECONDS": 1200 } \ No newline at end of file From c0a1ac55cf5228a396a9dec0db7be260cf2c978d Mon Sep 17 00:00:00 2001 From: thomasgonda3 Date: Mon, 8 Apr 2024 17:53:06 -0700 Subject: [PATCH 003/147] changed method of fetching player mmr --- mmr.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mmr.py b/mmr.py index 26828d2..d5bd746 100644 --- a/mmr.py +++ b/mmr.py @@ -6,7 +6,7 @@ async def mk8dx_150cc_mmr(url, members): - base_url = url + '/api/ladderplayer.php?ladder_id=9&' + base_url = url + '/api/ladderplayer.php?ladder_type=rt&' players = [] timeout = aiohttp.ClientTimeout(total=10) try: @@ -35,7 +35,7 @@ async def mk8dx_150cc_mmr(url, members): async def get_mmr_from_discord_id(discord_id): - base_url = "https://www.mkwlounge.gg" + '/api/ladderplayer.php?ladder_id=9&' + base_url = "https://www.mkwlounge.gg" + '/api/ladderplayer.php?ladder_type=rt&' timeout = aiohttp.ClientTimeout(total=10) async with aiohttp.ClientSession( timeout=timeout, From 264cfcd8749a8a9df070c2f512bf2a57402b8a9c Mon Sep 17 00:00:00 2001 From: thomasgonda3 Date: Mon, 8 Apr 2024 17:53:29 -0700 Subject: [PATCH 004/147] separated list and history channels --- cogs/SquadQueue.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index bbb4c25..6780939 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -43,6 +43,8 @@ def __init__(self, bot): self.SUB_CHANNEL = None + self.LIST_CHANNEL = None + self.HISTORY_CHANNEL = None self.LOCK = asyncio.Lock() @@ -72,11 +74,14 @@ async def on_ready(self): self.bot.config["queue_join_channel"]) self.SUB_CHANNEL = self.bot.get_channel( self.bot.config["queue_sub_channel"]) + self.LIST_CHANNEL = self.bot.get_channel( + self.bot.config["queue_list_channel"]) self.HISTORY_CHANNEL = self.bot.get_channel( self.bot.config["queue_history_channel"]) print(f"Server - {self.GUILD}", flush=True) print(f"Join Channel - {self.MOGI_CHANNEL}", flush=True) print(f"Sub Channel - {self.SUB_CHANNEL}", flush=True) + print(f"List Channel - {self.LIST_CHANNEL}", flush=True) print(f"History Channel - {self.HISTORY_CHANNEL}", flush=True) print("Ready!", flush=True) @@ -84,10 +89,13 @@ async def lockdown(self, channel: discord.TextChannel): # everyone_perms = channel.permissions_for(channel.guild.default_role) # if not everyone_perms.send_messages: # return - overwrite = channel.overwrites_for(channel.guild.default_role) - overwrite.send_messages = False - await channel.set_permissions(channel.guild.default_role, overwrite=overwrite) - await channel.send("Locked down " + channel.mention) + try: + overwrite = channel.overwrites_for(channel.guild.default_role) + overwrite.send_messages = False + await channel.set_permissions(channel.guild.default_role, overwrite=overwrite) + await channel.send("Locked down " + channel.mention) + except Exception as e: + print(e, flush=True) async def unlockdown(self, channel: discord.TextChannel): # everyone_perms = channel.permissions_for(channel.guild.default_role) @@ -266,7 +274,7 @@ async def list_task(self): msg += ", ".join([p.lounge_name for p in sorted_mogi_list[i].players]) msg += f" ({sorted_mogi_list[i].players[0].mmr} MMR)\n" if ((i + 1) % 12 == 0): - msg += "\n" + msg += "ㅤ\n" if (len(sorted_mogi_list) % (12/mogi.size) != 0): num_next = int(len(sorted_mogi_list) % (12/mogi.size)) teams_per_room = int(12/mogi.size) @@ -292,12 +300,12 @@ async def list_task(self): old_message = self.list_messages[i] await old_message.edit(content=message) else: - new_message = await self.HISTORY_CHANNEL.send(message) + new_message = await self.LIST_CHANNEL.send(message) self.list_messages.append(new_message) except: await self.delete_list_messages(0) for i, message in enumerate(new_messages): - new_message = await self.HISTORY_CHANNEL.send(message) + new_message = await self.LIST_CHANNEL.send(message) self.list_messages.append(new_message) else: await self.delete_list_messages(0) @@ -307,8 +315,8 @@ async def delete_list_messages(self, new_list_size: int): messages_to_delete = [] while len(self.list_messages) > new_list_size: messages_to_delete.append(self.list_messages.pop()) - if self.HISTORY_CHANNEL and len(messages_to_delete) > 0: - await self.HISTORY_CHANNEL.delete_messages(messages_to_delete) + if self.LIST_CHANNEL and len(messages_to_delete) > 0: + await self.LIST_CHANNEL.delete_messages(messages_to_delete) except Exception as e: print(e, flush=True) From 142b2f50b424ced5a9754de5c1a5e02a534c10ee Mon Sep 17 00:00:00 2001 From: parappurame Date: Thu, 11 Apr 2024 12:34:03 -0700 Subject: [PATCH 005/147] Changed Sub Command Edited the message associated with the sub command --- cogs/SquadQueue.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 6780939..9ba6000 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -242,12 +242,12 @@ async def sub(self, interaction: discord.Interaction): if not is_room_thread: await interaction.response.send_message(f"More than {self.MOGI_LIFETIME} minutes have passed since mogi start, the Mogi Object has been deleted.", ephemeral=True) return - msg = "<@&682445864400453739> - " + # msg = "<@&682445864400453739> - " if room.room_num == 1: msg += f"Room {room.room_num} is looking for a sub with mmr >{room.mmr_low - 500}\n" else: - low = 0 if room.mmr_low < 500 else room.mmr_low - 500 - msg += f"Room {room.room_num} is looking for a sub with range {low}-{room.mmr_high + 500}\n" + # low = 0 if room.mmr_low < 500 else room.mmr_low - 500 + msg += f"Room {room.room_num} is looking for a sub with range {room.mmr_low - 500}-{room.mmr_high + 500}\n" message_delete_date = datetime.now( timezone.utc) + timedelta(seconds=self.SUB_MESSAGE_LIFETIME_SECONDS) msg += f"Message will auto-delete in {discord.utils.format_dt(message_delete_date, style='R')}" From 8afe115f9af332ba7e7a979fe9530143dfce356b Mon Sep 17 00:00:00 2001 From: parappurame Date: Thu, 11 Apr 2024 12:35:04 -0700 Subject: [PATCH 006/147] Removed @here ping from new queues --- cogs/SquadQueue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 9ba6000..5978382 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -677,7 +677,7 @@ async def scheduler_mogi_start(self): mogi.gathering = True self.ongoing_events[mogi.mogi_channel] = mogi await self.unlockdown(mogi.mogi_channel) - await mogi.mogi_channel.send(f"A queue is gathering for the mogi {discord.utils.format_dt(mogi.start_time, style='R')} - @here Type `/c` to join, and `/d` to drop.") + await mogi.mogi_channel.send(f"A queue is gathering for the mogi {discord.utils.format_dt(mogi.start_time, style='R')} - Type `/c` to join, and `/d` to drop.") for ind in reversed(to_remove): del guild[ind] From 0810ab1bc5f28ba931c8e1206d96376c9ca3910f Mon Sep 17 00:00:00 2001 From: emilp-595 Date: Thu, 25 Apr 2024 18:13:56 -0700 Subject: [PATCH 007/147] Essential Changes for MKW Lounge Changed Time for Queues and set MMR threshold for rooms. Other various changes include setting new variables and having existing variables grab data from config instead of being hard-coded. --- cogs/SquadQueue.py | 100 +++++++++++-------- mkwii_sample_config.json => prod_config.json | 11 +- sample_config.json | 31 ------ test_config.json | 38 +++++++ 4 files changed, 100 insertions(+), 80 deletions(-) rename mkwii_sample_config.json => prod_config.json (82%) delete mode 100644 sample_config.json create mode 100644 test_config.json diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 5978382..30e7d89 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -25,6 +25,8 @@ def __init__(self, bot): self.sq_times = [] + self.prev_start_time = None + self._que_scheduler = self.que_scheduler.start() self._scheduler_task = self.sqscheduler.start() self._msgqueue_task = self.send_queued_messages.start() @@ -55,6 +57,8 @@ def __init__(self, bot): self.SUB_MESSAGE_LIFETIME_SECONDS = bot.config["SUB_MESSAGE_LIFETIME_SECONDS"] + self.room_mmr_threshold = bot.config["ROOM_MMR_THRESHOLD"] + # number of minutes before scheduled time that queue should open self.QUEUE_OPEN_TIME = timedelta(minutes=bot.config["QUEUE_OPEN_TIME"]) @@ -78,6 +82,14 @@ async def on_ready(self): self.bot.config["queue_list_channel"]) self.HISTORY_CHANNEL = self.bot.get_channel( self.bot.config["queue_history_channel"]) + try: + await self.LIST_CHANNEL.purge() + except: + print("Purging List channel failed", flush=True) + try: + await self.SUB_CHANNEL.purge() + except: + print("Purging Sub channel failed", flush=True) print(f"Server - {self.GUILD}", flush=True) print(f"Join Channel - {self.MOGI_CHANNEL}", flush=True) print(f"Sub Channel - {self.SUB_CHANNEL}", flush=True) @@ -242,7 +254,7 @@ async def sub(self, interaction: discord.Interaction): if not is_room_thread: await interaction.response.send_message(f"More than {self.MOGI_LIFETIME} minutes have passed since mogi start, the Mogi Object has been deleted.", ephemeral=True) return - # msg = "<@&682445864400453739> - " + msg = "" if room.room_num == 1: msg += f"Room {room.room_num} is looking for a sub with mmr >{room.mmr_low - 500}\n" else: @@ -373,10 +385,6 @@ async def annul_current_mogi(self, interaction: discord.Interaction): """The mogi currently gathering will be deleted. The queue resumes at the next hour. Staff use only.""" self.scheduled_events = {} self.ongoing_events = {} - curr_time = datetime.now(timezone.utc) - truncated_time = curr_time.replace( - minute=0, second=0, microsecond=0) - self.QUEUE_TIME_BLOCKER = truncated_time + timedelta(hours=1) await self.lockdown(self.MOGI_CHANNEL) await interaction.response.send_message("The current Mogi has been canceled, the queue will resume at the next hour.") @@ -394,6 +402,7 @@ async def resume_mogi_scheduling(self, interaction: discord.Interaction): """Mogis will begin to be scheduled again. Staff use only.""" curr_time = datetime.now(timezone.utc) self.QUEUE_TIME_BLOCKER = curr_time + self.prev_start_time = None await interaction.response.send_message("Mogis will resume scheduling.") @app_commands.command(name="reset_bot") @@ -512,7 +521,10 @@ async def check_room_channels(self, mogi): if num_created_rooms >= num_rooms: return for i in range(num_created_rooms, num_rooms): - room_name = f"{mogi.start_time.month}/{mogi.start_time.day}, {mogi.start_time.hour}:00:00 - Room {i+1}" + minute = mogi.start_time.minute + if len(str(minute)) == 1: + minute = '0' + str(minute) + room_name = f"{mogi.start_time.month}/{mogi.start_time.day}, {mogi.start_time.hour}:{minute}:00 - Room {i+1}" try: room_channel = await mogi.mogi_channel.create_thread(name=room_name, auto_archive_duration=60, @@ -570,8 +582,7 @@ async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=F for j in range(teams_per_room): msg += f"`{j+1}.` " team = sorted_list[start_index+j] - player_list.append( - sorted_list[start_index+j].get_first_player()) + player_list.append(sorted_list[start_index+j].get_first_player()) msg += ", ".join([p.lounge_name for p in team.players]) msg += f" ({int(team.avg_mmr)} MMR)\n" mentions += " ".join([p.member.mention for p in team.players]) @@ -579,24 +590,27 @@ async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=F room_msg = msg mentions += " ".join([m.mention for m in extra_members if m is not None]) room_msg += "\nVote for format FFA, 2v2, 3v3, or 4v4.\n" - # room_msg += "\nIf you need staff's assistance, use the `!staff` command in this channel.\n" + room_msg += "\nIf you need staff's assistance, use the `!staff` command in this channel.\n" room_msg += mentions - try: - curr_room = rooms[i] - room_channel = curr_room.thread - curr_room.teams = sorted_list[start_index:start_index+teams_per_room] - await room_channel.send(room_msg) - view = VoteView(player_list, room_channel, mogi) - curr_room.view = view - curr_room.mmr_low = player_list[11].mmr - curr_room.mmr_high = player_list[0].mmr - await room_channel.send(view=view) - except Exception as e: - print(e, flush=True) - err_msg = f"\nAn error has occurred while creating the room channel; please contact your opponents in DM or another channel\n" - err_msg += mentions - msg += err_msg - room_channel = None + curr_room = rooms[i] + room_channel = curr_room.thread + curr_room.teams = sorted_list[start_index:start_index+teams_per_room] + curr_room.mmr_low = player_list[11].mmr + curr_room.mmr_high = player_list[0].mmr + if curr_room.mmr_high - curr_room.mmr_low > self.room_mmr_threshold: + msg += f"\nThe mmr gap in the room is higher than the allowed threshold of {self.room_mmr_threshold}MMR, this room has been cancelled." + else: + try: + await room_channel.send(room_msg) + view = VoteView(player_list, room_channel, mogi) + curr_room.view = view + await room_channel.send(view=view) + except Exception as e: + print(e, flush=True) + err_msg = f"\nAn error has occurred while creating the room channel; please contact your opponents in DM or another channel\n" + err_msg += mentions + msg += err_msg + room_channel = None try: await mogi.mogi_channel.send(msg) except Exception as e: @@ -709,35 +723,36 @@ async def schedule_que_event(self): if self.GUILD is not None: curr_time = datetime.now(timezone.utc) - truncated_time = curr_time.replace( - minute=0, second=0, microsecond=0) - next_hour = truncated_time + timedelta(hours=1) - if len(self.sq_times) > 0 and next_hour == self.sq_times[0]: + start_time = curr_time + timedelta(minutes=65) + start_time = start_time.replace(minute=0, second=0, microsecond=0) + start_time -= self.QUEUE_OPEN_TIME + if self.prev_start_time: + start_time = self.prev_start_time + next_mogi_start = start_time + self.QUEUE_OPEN_TIME + if len(self.sq_times) > 0 and next_mogi_start == self.sq_times[0]: self.sq_times.pop(0) - self.QUEUE_TIME_BLOCKER = next_hour + self.QUEUE_TIME_BLOCKER = next_mogi_start await self.MOGI_CHANNEL.send("Squad Queue is currently going on at this hour! The queue will remain closed.") if curr_time < self.QUEUE_TIME_BLOCKER: # print(f"Mogi had been blocked from starting before the time limit {self.QUEUE_TIME_BLOCKER}", flush=True) return - if datetime.now().minute >= self.bot.config["JOINING_TIME"]: - # print("Hourly Que is too late, starting Que at next hour", flush=True) - return for mogi in self.ongoing_events.values(): - if mogi.start_time == next_hour: + if mogi.start_time == next_mogi_start: return - event_start_time = next_hour.astimezone() - self.QUEUE_OPEN_TIME - if event_start_time < discord.utils.utcnow(): - event_start_time = discord.utils.utcnow() + timedelta(minutes=1) mogi = Mogi(1, 1, self.MOGI_CHANNEL, is_automated=True, - start_time=next_hour) + start_time=next_mogi_start) if self.GUILD not in self.scheduled_events.keys(): self.scheduled_events[self.GUILD] = [] self.scheduled_events[self.GUILD].append(mogi) - print(f"Started Queue for {next_hour}", flush=True) + self.QUEUE_TIME_BLOCKER = next_mogi_start + + self.prev_start_time = next_mogi_start + + print(f"Started Queue for {next_mogi_start}", flush=True) @tasks.loop(minutes=1) async def delete_old_mogis(self): @@ -825,15 +840,12 @@ async def debug_add_many_players(self, ctx, members: commands.Greedy[discord.Mem @commands.command(name="debug_start_rooms") @commands.is_owner() async def debug_start_rooms(self, ctx): - truncated_time = datetime.now(timezone.utc).replace( - minute=0, second=0, microsecond=0) - next_hour = truncated_time + timedelta(hours=1) for mogi in self.ongoing_events.values(): - if mogi.start_time == next_hour: + if mogi.start_time == self.prev_start_time: await self.add_teams_to_rooms(mogi, (mogi.start_time.minute) % 60, True) return for mogi in self.old_events.values(): - if mogi.start_time == next_hour: + if mogi.start_time == self.prev_start_time: await self.add_teams_to_rooms(mogi, (mogi.start_time.minute) % 60, True) return diff --git a/mkwii_sample_config.json b/prod_config.json similarity index 82% rename from mkwii_sample_config.json rename to prod_config.json index a273c48..52f34fc 100644 --- a/mkwii_sample_config.json +++ b/prod_config.json @@ -2,7 +2,7 @@ "token": "", "guild_id": 387347467332485122, "admin_roles": { - "387347467332485122": [ + "1228027010836594698": [ "Administrator", "Lounge Staff" ] @@ -12,7 +12,7 @@ "queue_list_channel": 1215174057453162496, "queue_history_channel": 1225709377747685477, "members_for_channels": { - "387347467332485122": [ + "1228027010836594698": [ 235148962103951360, 155149108183695360, 815483289515196416, @@ -29,9 +29,10 @@ "password": "password", "url": "https://www.mkwlounge.gg", "TIME_ADJUSTMENT": 0, - "QUEUE_OPEN_TIME": 60, - "JOINING_TIME": 55, + "QUEUE_OPEN_TIME": 80, + "JOINING_TIME": 72, "EXTENSION_TIME": 3, "MOGI_LIFETIME": 180, - "SUB_MESSAGE_LIFETIME_SECONDS": 1200 + "SUB_MESSAGE_LIFETIME_SECONDS": 1200, + "ROOM_MMR_THRESHOLD": 8000 } \ No newline at end of file diff --git a/sample_config.json b/sample_config.json deleted file mode 100644 index 6f8aaad..0000000 --- a/sample_config.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "token": "", - "application_id": 1183741625986465803, - "guild_id": 1139900310081507398, - - "admin_roles": {"1139900310081507398": ["Administrator", "Lounge Staff"]}, - "staff_roles": {"1139900310081507398": [910528103225389107]}, - - "queue_join_channel": 1139900310861660163, - "queue_sub_channel": 1186097268240162846, - "queue_history_channel": 1194143778156576880, - - "members_for_channels": {"1139900310081507398": [934740707934081035, 743532508343304242, 735910635082219570]}, - - "channel_category_position": 5, - "channels_per_category": 25, - - "queue_messages": true, - "sec_between_queue_msgs": 2, - - "username": "username", - "password": "password", - "url": "https://www.mk8dx-lounge.com", - - "TIME_ADJUSTMENT": 0, - "QUEUE_OPEN_TIME": 60, - "JOINING_TIME": 55, - "EXTENSION_TIME": 3, - "MOGI_LIFETIME": 180, - "SUB_MESSAGE_LIFETIME_SECONDS": 1200 -} \ No newline at end of file diff --git a/test_config.json b/test_config.json new file mode 100644 index 0000000..39bf0db --- /dev/null +++ b/test_config.json @@ -0,0 +1,38 @@ +{ + "token": "", + "guild_id": 1228027010836594698, + "admin_roles": { + "1228027010836594698": [ + "Administrator", + "Lounge Staff" + ] + }, + "queue_join_channel": 1228046535321849907, + "queue_sub_channel": 1228046571447386173, + "queue_list_channel": 1228046584181293066, + "queue_history_channel": 1228046598777344161, + "members_for_channels": { + "1228027010836594698": [ + 235148962103951360, + 155149108183695360, + 815483289515196416, + 892420397570592768, + 775415990238314518, + 774812906830299136, + 745443073202323586, + 774866940950872095 + ] + }, + "queue_messages": true, + "sec_between_queue_msgs": 2, + "username": "username", + "password": "password", + "url": "https://www.mkwlounge.gg", + "TIME_ADJUSTMENT": 0, + "QUEUE_OPEN_TIME": 80, + "JOINING_TIME": 72, + "EXTENSION_TIME": 3, + "MOGI_LIFETIME": 180, + "SUB_MESSAGE_LIFETIME_SECONDS": 1200, + "ROOM_MMR_THRESHOLD": 8000 +} \ No newline at end of file From 12948c9bd801727bec5aec20e7bc0240bec768d8 Mon Sep 17 00:00:00 2001 From: emilp-595 Date: Tue, 7 May 2024 20:37:58 -0700 Subject: [PATCH 008/147] Thread Messages Removed the message telling players to mention staff for an inconvenience. Also changed the pen message to accurately reflect the pen timing. --- cogs/SquadQueue.py | 1 - mogi_objects.py | 14 +++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 30e7d89..02cc6d4 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -590,7 +590,6 @@ async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=F room_msg = msg mentions += " ".join([m.mention for m in extra_members if m is not None]) room_msg += "\nVote for format FFA, 2v2, 3v3, or 4v4.\n" - room_msg += "\nIf you need staff's assistance, use the `!staff` command in this channel.\n" room_msg += mentions curr_room = rooms[i] room_channel = curr_room.thread diff --git a/mogi_objects.py b/mogi_objects.py index d63b0fe..c3fd290 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -1,5 +1,7 @@ import random import discord +from datetime import datetime, timezone, timedelta +import time from discord.ui import View @@ -206,7 +208,17 @@ async def make_teams(self, format): msg += team_text self.teams_text += team_text - msg += "Decide a host amongst yourselves; room open at :00, penalty at :08. Good luck!" + minutes = self.mogi.start_time.minute + pen = minutes + 10 + + if pen > 60: + pen -= 60 + + if pen < 10: + pen = '0' + str(pen) + if minutes < 10: + minutes = '0' + str(minutes) + msg += "Decide a host amongst yourselves; room open at :{}, penalty at :{}. Good luck!".format(minutes, pen) room.teams = teams From 144dea35ea81d0ab717f3787a27e3a80dc2d5189 Mon Sep 17 00:00:00 2001 From: emilp-595 Date: Wed, 15 May 2024 22:30:15 -0700 Subject: [PATCH 009/147] Lounge Test 1 Fixes Added new commands for staff to edit some config values. Changed the sub message. Changed the queue joining message to clarify joining/closing time. --- cogs/SquadQueue.py | 35 +++++++++++++++++++++++++++++++---- mogi_objects.py | 2 +- prod_config.json | 8 ++++---- test_config.json | 4 ++-- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 02cc6d4..173b200 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -205,7 +205,7 @@ async def can(self, interaction: discord.Interaction): squad = Team(players) mogi.teams.append(squad) - msg += f"{players[0].lounge_name} joined queue for mogi {discord.utils.format_dt(mogi.start_time, style='R')}, `[{mogi.count_registered()} players]`" + msg += f"{players[0].lounge_name} joined queue closing at {discord.utils.format_dt(mogi.start_time - timedelta(minutes=self.QUEUE_OPEN_TIME - self.JOINING_TIME))}, `[{mogi.count_registered()} players]`" await interaction.followup.send(msg) await self.check_room_channels(mogi) @@ -254,7 +254,7 @@ async def sub(self, interaction: discord.Interaction): if not is_room_thread: await interaction.response.send_message(f"More than {self.MOGI_LIFETIME} minutes have passed since mogi start, the Mogi Object has been deleted.", ephemeral=True) return - msg = "" + msg = "<@&1167985222533533817> - " if room.room_num == 1: msg += f"Room {room.room_num} is looking for a sub with mmr >{room.mmr_low - 500}\n" else: @@ -404,6 +404,33 @@ async def resume_mogi_scheduling(self, interaction: discord.Interaction): self.QUEUE_TIME_BLOCKER = curr_time self.prev_start_time = None await interaction.response.send_message("Mogis will resume scheduling.") + + @app_commands.command(name="change_event_time") + @app_commands.guild_only() + async def change_event_time(self, interaction: discord.Interaction, event_time: int): + """Change the amount of time for each event in the queue.""" + if event_time > 15: + self.QUEUE_OPEN_TIME = timedelta(minutes=event_time) + self.JOINING_TIME = timedelta(minutes=event_time - 5) + await interaction.response.send_message(f"The amount of time for each mogi has been changed to {event_time} minutes.") + else: + await interaction.response.send_message("Please enter a number of minutes greater than 15.") + + @app_commands.command(name="change_mmr_threshold") + @app_commands.guild_only() + async def change_mmr_threshold(self, interaction: discord.Interaction, mmr_threshold: int): + """Change the maximum MMR gap allowed for a room.""" + self.room_mmr_threshold = mmr_threshold + await interaction.response.send_message(f"MMR Threshold for Queue Rooms has been modified to {mmr_threshold} MMR.") + + @app_commands.command(name="peek_bot_config") + @app_commands.guild_only() + async def peek_bot_config(self, interaction: discord.Interaction): + """View the configured values for the Queue System.""" + msg = "" + msg += f"Time for Each Event: {self.QUEUE_OPEN_TIME}, \n" + msg += f"Room MMR Threshold: {self.room_mmr_threshold}" + await interaction.response.send_message(msg) @app_commands.command(name="reset_bot") @app_commands.guild_only() @@ -589,7 +616,7 @@ async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=F mentions += " " room_msg = msg mentions += " ".join([m.mention for m in extra_members if m is not None]) - room_msg += "\nVote for format FFA, 2v2, 3v3, or 4v4.\n" + room_msg += "\nVote for format FFA, 2v2, 3v3, 4v4, or 6v6.\n" room_msg += mentions curr_room = rooms[i] room_channel = curr_room.thread @@ -597,7 +624,7 @@ async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=F curr_room.mmr_low = player_list[11].mmr curr_room.mmr_high = player_list[0].mmr if curr_room.mmr_high - curr_room.mmr_low > self.room_mmr_threshold: - msg += f"\nThe mmr gap in the room is higher than the allowed threshold of {self.room_mmr_threshold}MMR, this room has been cancelled." + msg += f"\nThe mmr gap in the room is higher than the allowed threshold of {self.room_mmr_threshold} MMR, this room has been cancelled." else: try: await room_channel.send(room_msg) diff --git a/mogi_objects.py b/mogi_objects.py index c3fd290..834f596 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -209,7 +209,7 @@ async def make_teams(self, format): self.teams_text += team_text minutes = self.mogi.start_time.minute - pen = minutes + 10 + pen = minutes + 8 if pen > 60: pen -= 60 diff --git a/prod_config.json b/prod_config.json index 52f34fc..bfec4bd 100644 --- a/prod_config.json +++ b/prod_config.json @@ -2,7 +2,7 @@ "token": "", "guild_id": 387347467332485122, "admin_roles": { - "1228027010836594698": [ + "387347467332485122": [ "Administrator", "Lounge Staff" ] @@ -12,7 +12,7 @@ "queue_list_channel": 1215174057453162496, "queue_history_channel": 1225709377747685477, "members_for_channels": { - "1228027010836594698": [ + "387347467332485122": [ 235148962103951360, 155149108183695360, 815483289515196416, @@ -29,8 +29,8 @@ "password": "password", "url": "https://www.mkwlounge.gg", "TIME_ADJUSTMENT": 0, - "QUEUE_OPEN_TIME": 80, - "JOINING_TIME": 72, + "QUEUE_OPEN_TIME": 72, + "JOINING_TIME": 67, "EXTENSION_TIME": 3, "MOGI_LIFETIME": 180, "SUB_MESSAGE_LIFETIME_SECONDS": 1200, diff --git a/test_config.json b/test_config.json index 39bf0db..21431b8 100644 --- a/test_config.json +++ b/test_config.json @@ -29,8 +29,8 @@ "password": "password", "url": "https://www.mkwlounge.gg", "TIME_ADJUSTMENT": 0, - "QUEUE_OPEN_TIME": 80, - "JOINING_TIME": 72, + "QUEUE_OPEN_TIME": 72, + "JOINING_TIME": 67, "EXTENSION_TIME": 3, "MOGI_LIFETIME": 180, "SUB_MESSAGE_LIFETIME_SECONDS": 1200, From 9b598a168548deb68f4d6dff3a992e91df87a713 Mon Sep 17 00:00:00 2001 From: emilp-595 Date: Wed, 15 May 2024 23:16:20 -0700 Subject: [PATCH 010/147] Add roles to queue channels Adding RT Updater and Reporter to the Queue Channels --- prod_config.json | 4 +++- test_config.json | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/prod_config.json b/prod_config.json index bfec4bd..703203c 100644 --- a/prod_config.json +++ b/prod_config.json @@ -20,7 +20,9 @@ 775415990238314518, 774812906830299136, 745443073202323586, - 774866940950872095 + 774866940950872095, + 393600567781621761, + 389252697284542465 ] }, "queue_messages": true, diff --git a/test_config.json b/test_config.json index 21431b8..9da0032 100644 --- a/test_config.json +++ b/test_config.json @@ -20,7 +20,9 @@ 775415990238314518, 774812906830299136, 745443073202323586, - 774866940950872095 + 774866940950872095, + 393600567781621761, + 389252697284542465 ] }, "queue_messages": true, From f55f3c18d4442519b7af4317c30706afb587363f Mon Sep 17 00:00:00 2001 From: emilp-595 Date: Wed, 15 May 2024 23:35:21 -0700 Subject: [PATCH 011/147] Added Table Bot to Queue Rooms --- prod_config.json | 1 + test_config.json | 1 + 2 files changed, 2 insertions(+) diff --git a/prod_config.json b/prod_config.json index 703203c..e8dcb96 100644 --- a/prod_config.json +++ b/prod_config.json @@ -21,6 +21,7 @@ 774812906830299136, 745443073202323586, 774866940950872095, + 1019051989734273054, 393600567781621761, 389252697284542465 ] diff --git a/test_config.json b/test_config.json index 9da0032..5ad0ab4 100644 --- a/test_config.json +++ b/test_config.json @@ -21,6 +21,7 @@ 774812906830299136, 745443073202323586, 774866940950872095, + 1019051989734273054, 393600567781621761, 389252697284542465 ] From af572b73d40002240b941f662c97cd58b6c51c93 Mon Sep 17 00:00:00 2001 From: emilp-595 Date: Thu, 16 May 2024 16:16:58 -0700 Subject: [PATCH 012/147] Fixed "c" command --- cogs/SquadQueue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 173b200..6c4098c 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -205,7 +205,7 @@ async def can(self, interaction: discord.Interaction): squad = Team(players) mogi.teams.append(squad) - msg += f"{players[0].lounge_name} joined queue closing at {discord.utils.format_dt(mogi.start_time - timedelta(minutes=self.QUEUE_OPEN_TIME - self.JOINING_TIME))}, `[{mogi.count_registered()} players]`" + msg += f"{players[0].lounge_name} joined queue closing at {discord.utils.format_dt(mogi.start_time - (self.QUEUE_OPEN_TIME - self.JOINING_TIME))}, `[{mogi.count_registered()} players]`" await interaction.followup.send(msg) await self.check_room_channels(mogi) From cdc2231a9a973ca729edc84aef59382528579edd Mon Sep 17 00:00:00 2001 From: emilp-595 Date: Fri, 14 Jun 2024 19:00:05 -0700 Subject: [PATCH 013/147] Added Changes since previous test Changed the display of the queue list, added restrictions and cooldown to the sub command, changed the channel that threads are made in, and others. --- cogs/SquadQueue.py | 153 ++++++++++++++++++++++++++++++++++++--------- mogi_objects.py | 41 ++++++------ prod_config.json | 7 ++- test_config.json | 10 ++- 4 files changed, 157 insertions(+), 54 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 6c4098c..9a6e601 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -1,3 +1,4 @@ +import aiohttp import discord from discord.ext import commands, tasks from discord import app_commands @@ -8,9 +9,13 @@ from mmr import mk8dx_150cc_mmr, get_mmr_from_discord_id from mogi_objects import Mogi, Team, Player, Room, VoteView, JoinView, get_tier import asyncio +from collections import defaultdict + +headers = {'Content-type': 'application/json'} # Scheduled_Event = collections.namedtuple('Scheduled_Event', 'size time started mogi_channel') +cooldowns = defaultdict(int) class SquadQueue(commands.Cog): def __init__(self, bot): @@ -49,6 +54,8 @@ def __init__(self, bot): self.HISTORY_CHANNEL = None + self.GENERAL_CHANNEL = None + self.LOCK = asyncio.Lock() self.URL = bot.config["url"] @@ -59,6 +66,8 @@ def __init__(self, bot): self.room_mmr_threshold = bot.config["ROOM_MMR_THRESHOLD"] + self.TIER_INFO = [] + # number of minutes before scheduled time that queue should open self.QUEUE_OPEN_TIME = timedelta(minutes=bot.config["QUEUE_OPEN_TIME"]) @@ -82,6 +91,9 @@ async def on_ready(self): self.bot.config["queue_list_channel"]) self.HISTORY_CHANNEL = self.bot.get_channel( self.bot.config["queue_history_channel"]) + self.GENERAL_CHANNEL = self.bot.get_channel( + self.bot.config["queue_general_channel"]) + await self.get_ladder_info() try: await self.LIST_CHANNEL.purge() except: @@ -95,7 +107,9 @@ async def on_ready(self): print(f"Sub Channel - {self.SUB_CHANNEL}", flush=True) print(f"List Channel - {self.LIST_CHANNEL}", flush=True) print(f"History Channel - {self.HISTORY_CHANNEL}", flush=True) + print(f"General Channel - {self.GENERAL_CHANNEL}", flush=True) print("Ready!", flush=True) + async def lockdown(self, channel: discord.TextChannel): # everyone_perms = channel.permissions_for(channel.guild.default_role) @@ -182,24 +196,23 @@ async def can(self, interaction: discord.Interaction): player_team = mogi.check_player(member) - if player_team is not None: - await interaction.followup.send(f"{interaction.user.mention} is already signed up.") - return - - players = await mk8dx_150cc_mmr(self.URL, [member]) - - if len(players) == 0 or players[0] is None: - msg = f"{interaction.user.mention} fetch for MMR has failed and joining the queue was unsuccessful. " - msg += "Please try again. If the problem continues then contact a staff member for help." - await interaction.followup.send(msg) - return - + players = [] msg = "" - if players[0].mmr is None: - starting_player_mmr = 1500 - players[0].mmr = starting_player_mmr + placement_role_id = 723753340063842345 + if member.get_role(placement_role_id): + starting_player_mmr = 750 + player = Player(member, member.display_name, starting_player_mmr) + players.append(player) msg += f"{players[0].lounge_name} is assumed to be a new player and will be playing this mogi with a starting MMR of {starting_player_mmr}. " msg += "If you believe this is a mistake, please contact a staff member for help.\n" + else: + players = await mk8dx_150cc_mmr(self.URL, [member]) + + if len(players) == 0 or players[0] is None: + msg = f"{interaction.user.mention} fetch for MMR has failed and joining the queue was unsuccessful. " + msg += "Please try again. If the problem continues then contact a staff member for help." + await interaction.followup.send(msg) + return players[0].confirmed = True squad = Team(players) @@ -237,38 +250,56 @@ async def drop(self, interaction: discord.Interaction): @app_commands.command(name="sub") @app_commands.guild_only() + # @commands.cooldown(rate=1, per=120, type=commands.BucketType.user) async def sub(self, interaction: discord.Interaction): """Sends out a request for a sub in the sub channel. Only works in thread channels for SQ rooms.""" + current_time = time.time() + lastCommandTime = cooldowns.get(interaction.user.id) + print(lastCommandTime, flush=True) + if lastCommandTime == None: + lastCommandTime = 0 + + if (current_time - lastCommandTime) < 120: #Cooldown timer in seconds + await interaction.response.send_message(f"You are still on cooldown. Please wait for {int(2 * 60 - (current_time - lastCommandTime))} more seconds to use this command again.", ephemeral=True) + return + is_room_thread = False room = None + bottom_room_num = 1 for mogi in self.ongoing_events.values(): if mogi.is_room_thread(interaction.channel_id): room = mogi.get_room_from_thread(interaction.channel_id) + bottom_room_num = len(mogi.rooms) is_room_thread = True break for mogi in self.old_events.values(): if mogi.is_room_thread(interaction.channel.id): room = mogi.get_room_from_thread(interaction.channel.id) + bottom_room_num = len(mogi.rooms) is_room_thread = True break if not is_room_thread: await interaction.response.send_message(f"More than {self.MOGI_LIFETIME} minutes have passed since mogi start, the Mogi Object has been deleted.", ephemeral=True) return msg = "<@&1167985222533533817> - " - if room.room_num == 1: - msg += f"Room {room.room_num} is looking for a sub with mmr >{room.mmr_low - 500}\n" + if bottom_room_num == 1: + msg += f"Room 1 is looking for a sub with any mmr\n" + elif room.room_num == 1: + msg += f"Room 1 is looking for a sub with mmr >{room.mmr_low - 500}\n" + elif room.room_num == bottom_room_num: + msg += f"Room {room.room_num} is looking for a sub with mmr <{room.mmr_high + 500}\n" else: - # low = 0 if room.mmr_low < 500 else room.mmr_low - 500 msg += f"Room {room.room_num} is looking for a sub with range {room.mmr_low - 500}-{room.mmr_high + 500}\n" message_delete_date = datetime.now( timezone.utc) + timedelta(seconds=self.SUB_MESSAGE_LIFETIME_SECONDS) msg += f"Message will auto-delete in {discord.utils.format_dt(message_delete_date, style='R')}" await self.SUB_CHANNEL.send(msg, delete_after=self.SUB_MESSAGE_LIFETIME_SECONDS) - view = JoinView(room, get_mmr_from_discord_id) + view = JoinView(room, get_mmr_from_discord_id, bottom_room_num) await self.SUB_CHANNEL.send(view=view, delete_after=self.SUB_MESSAGE_LIFETIME_SECONDS) + cooldowns[interaction.user.id] = current_time #Updates cooldown await interaction.response.send_message("Sent out request for sub.") - @tasks.loop(minutes=1) + @tasks.loop(seconds=30) async def list_task(self): """Continually display the list of confirmed players for a mogi in the history channel""" if len(self.ongoing_events) > 0: @@ -279,19 +310,30 @@ async def list_task(self): mogi_list = mogi.confirmed_list() + # Remove late players from the list to display separately + full_list_length = len(mogi_list) + num_of_rooms = full_list_length // 12 + num_confirmed_players = num_of_rooms * 12 + num_late_players = full_list_length - num_confirmed_players + late_players = [] + for i in range(num_confirmed_players, full_list_length): + player = mogi_list.pop() + late_players.append(player) + sorted_mogi_list = sorted(mogi_list, reverse=True) - msg = "Current Mogi List:\n" + msg = f"**Last Updated:** {discord.utils.format_dt(datetime.now(timezone.utc), style='R')}\n\n" + msg += "**Current Mogi List:**\n" for i in range(len(sorted_mogi_list)): msg += f"{i+1}) " msg += ", ".join([p.lounge_name for p in sorted_mogi_list[i].players]) msg += f" ({sorted_mogi_list[i].players[0].mmr} MMR)\n" if ((i + 1) % 12 == 0): msg += "ㅤ\n" - if (len(sorted_mogi_list) % (12/mogi.size) != 0): - num_next = int(len(sorted_mogi_list) % (12/mogi.size)) - teams_per_room = int(12/mogi.size) - num_rooms = int(len(sorted_mogi_list) / (12/mogi.size))+1 - msg += f"[{num_next}/{teams_per_room}] players for {num_rooms} room(s)" + msg += "**Late Players:**\n" + for i in range(len(late_players)): + msg += f"{i+1}) " + msg += ", ".join([p.lounge_name for p in late_players[i].players]) + msg += f" ({late_players[i].players[0].mmr} MMR)\n" message = msg.split("\n") new_messages = [] @@ -301,7 +343,7 @@ async def list_task(self): new_messages.append(bulk_msg) bulk_msg = "" bulk_msg += message[i] + "\n" - if len(bulk_msg) > 0: + if len(bulk_msg) > 0 and bulk_msg != "\n": new_messages.append(bulk_msg) await self.delete_list_messages(len(new_messages)) @@ -494,6 +536,50 @@ async def clear_sq_times(self, interaction: discord.Interaction): self.sq_times = [] await interaction.response.send_message("Cleared list of Squad Queue Times.") + + @app_commands.command(name="update_tier_info") + @app_commands.guild_only() + async def update_tier_info(self, interaction: discord.Interaction): + """Updates the info on each tier""" + #await self.get_ladder_info() + msg = await self.get_ladder_info() + await interaction.response.send_message(msg) + + async def get_ladder_info(self): + timeout = aiohttp.ClientTimeout(total=10) + url = "https://mkwlounge.gg/api/ladderclass.php?ladder_type=rt" + msg = "" + try: + async with aiohttp.ClientSession( + timeout = timeout, + auth=aiohttp.BasicAuth( + "username", "password")) as session: + async with session.get(url, headers=headers) as resp: + if resp.status != 200: + raise Exception( + "Fetch for tier info has failed, bad status code") + result = await resp.json() + if result['status'] != "success": + raise Exception( + "Fetch for tier info has failed, Status: Failure") + self.TIER_INFO = result["results"] + msg += "Fetch for Tier Info Successful.\n" + for tier in self.TIER_INFO: + boundary = "" + if tier["minimum_mmr"]: + boundary += f"{tier['minimum_mmr']}-" + else: + boundary += "<" + if tier["maximum_mmr"]: + boundary += f"{tier['maximum_mmr']}" + else: + boundary = f">{tier['minimum_mmr']}" + msg += f"T{tier['ladder_order']} Boundary: {boundary}\n" + print(msg, flush=True) + return msg + except Exception as e: + print(e, flush=True) + # check if user has roles defined in config.json async def has_roles(self, member: discord.Member, guild_id: int, config): @@ -553,7 +639,7 @@ async def check_room_channels(self, mogi): minute = '0' + str(minute) room_name = f"{mogi.start_time.month}/{mogi.start_time.day}, {mogi.start_time.hour}:{minute}:00 - Room {i+1}" try: - room_channel = await mogi.mogi_channel.create_thread(name=room_name, + room_channel = await self.GENERAL_CHANNEL.create_thread(name=room_name, auto_archive_duration=60, invitable=False) except Exception as e: @@ -599,6 +685,11 @@ async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=F mogi.mogi_channel.guild.id)] for m in extra_members_ids: extra_members.append(mogi.mogi_channel.guild.get_member(m)) + if str(mogi.mogi_channel.guild.id) in self.bot.config["roles_for_channels"].keys(): + extra_roles_ids = self.bot.config["roles_for_channels"][str( + mogi.mogi_channel.guild.id)] + for r in extra_roles_ids: + extra_members.append(mogi.mogi_channel.guild.get_role(r)) rooms = mogi.rooms for i in range(num_rooms): @@ -628,7 +719,7 @@ async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=F else: try: await room_channel.send(room_msg) - view = VoteView(player_list, room_channel, mogi) + view = VoteView(player_list, room_channel, mogi, self.TIER_INFO) curr_room.view = view await room_channel.send(view=view) except Exception as e: @@ -878,3 +969,7 @@ async def debug_start_rooms(self, ctx): async def setup(bot): await bot.add_cog(SquadQueue(bot)) + + + + diff --git a/mogi_objects.py b/mogi_objects.py index 834f596..b2e792c 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -73,6 +73,8 @@ def __init__(self, teams, room_num: int, thread: discord.Thread): self.mmr_low = None self.view = None self.finished = False + def get_player_list(self): + return [player.member.id for team in self.teams for player in team.players] class Team: @@ -153,7 +155,7 @@ def __init__(self, member, lounge_name, mmr): class VoteView(View): - def __init__(self, players, thread, mogi): + def __init__(self, players, thread, mogi, tier_info): super().__init__() self.players = players self.thread = thread @@ -161,6 +163,7 @@ def __init__(self, players, thread, mogi): self.header_text = "" self.teams_text = "" self.found_winner = False + self.tier_info = tier_info self.__setattr__("FFA", []) self.__setattr__("2v2", []) self.__setattr__("3v3", []) @@ -185,7 +188,7 @@ async def make_teams(self, format): room_mmr = round(sum([p.mmr for p in self.players]) / 12) room.mmr_average = room_mmr - self.header_text = f"**Room {room.room_num} MMR: {room_mmr} - {get_tier(room_mmr)}** " + self.header_text = f"**Room {room.room_num} MMR: {room_mmr} - T{get_tier(room_mmr, self.tier_info)}** " msg += self.header_text msg += "\n" @@ -377,7 +380,7 @@ async def six_button_callback(self, interaction, button): class JoinView(View): - def __init__(self, room, get_mmr): + def __init__(self, room: Room, get_mmr, bottom_room_num): super().__init__(timeout=1200) self.room = room self.get_mmr = get_mmr @@ -385,10 +388,15 @@ def __init__(self, room, get_mmr): @discord.ui.button(label="Join Room") async def button_callback(self, interaction, button): await interaction.response.defer() - muted_role_id = 600495108999086090 - if interaction.user.get_role(muted_role_id): + muted_role_id = 434887701662007296 + restricted_role_id = 797208908153618452 + if interaction.user.get_role(muted_role_id) or interaction.user.get_role(restricted_role_id): await interaction.followup.send( - "Players with the muted role cannot use the sub button.", ephemeral=True) + "Players with the muted or restricted role cannot use the sub button.", ephemeral=True) + return + if interaction.user.id in self.room.get_player_list(): + await interaction.followup.send( + "You are already in this room.", ephemeral=True) return try: user_mmr = await self.get_mmr(interaction.user.id) @@ -398,6 +406,8 @@ async def button_callback(self, interaction, button): return if self.room.room_num == 1: self.room.mmr_high = 999999 + if self.room.room_num == bottom_room_num: + self.room.mmr_low = -999999 if isinstance(user_mmr, int) and user_mmr < self.room.mmr_high + 500 and user_mmr > self.room.mmr_low - 500: button.disabled = True await interaction.followup.edit_message(interaction.message.id, view=self) @@ -408,18 +418,7 @@ async def button_callback(self, interaction, button): "You do not meet room requirements", ephemeral=True) -def get_tier(mmr: int): - if mmr > 11000: - return 'T7' - if mmr > 9500: - return 'T6' - if mmr > 7500: - return 'T5' - if mmr > 6250: - return 'T4' - if mmr > 5000: - return 'T3' - if mmr > 3500: - return 'T2' - else: - return 'T1' +def get_tier(mmr: int, tier_info): + for tier in tier_info: + if (tier["minimum_mmr"] is None or mmr >= tier["minimum_mmr"]) and (tier["maximum_mmr"] is None or mmr <= tier["maximum_mmr"]): + return tier["ladder_order"] diff --git a/prod_config.json b/prod_config.json index e8dcb96..a385c20 100644 --- a/prod_config.json +++ b/prod_config.json @@ -11,6 +11,7 @@ "queue_sub_channel": 1215174160234315806, "queue_list_channel": 1215174057453162496, "queue_history_channel": 1225709377747685477, + "queue_general_channel": 1241842559429513361, "members_for_channels": { "387347467332485122": [ 235148962103951360, @@ -21,7 +22,11 @@ 774812906830299136, 745443073202323586, 774866940950872095, - 1019051989734273054, + 1019051989734273054 + ] + }, + "roles_for_channels": { + "387347467332485122": [ 393600567781621761, 389252697284542465 ] diff --git a/test_config.json b/test_config.json index 5ad0ab4..4ee7f5a 100644 --- a/test_config.json +++ b/test_config.json @@ -11,6 +11,7 @@ "queue_sub_channel": 1228046571447386173, "queue_list_channel": 1228046584181293066, "queue_history_channel": 1228046598777344161, + "queue_general_channel": 1250554989508825168, "members_for_channels": { "1228027010836594698": [ 235148962103951360, @@ -21,9 +22,12 @@ 774812906830299136, 745443073202323586, 774866940950872095, - 1019051989734273054, - 393600567781621761, - 389252697284542465 + 1019051989734273054 + ] + }, + "roles_for_channels": { + "1228027010836594698": [ + 1249808212547604561 ] }, "queue_messages": true, From ccc3d48366ad9cdfcef98d6a5ab1705324b9e560 Mon Sep 17 00:00:00 2001 From: emilp-595 Date: Sat, 15 Jun 2024 15:48:41 -0700 Subject: [PATCH 014/147] Canning + Sub Issues Fix Re-added the check for duplicate instances in the queue list and fixed the sub command to function properly. --- cogs/SquadQueue.py | 13 ++++++++++++- mogi_objects.py | 3 ++- prod_config.json | 8 ++++++++ test_config.json | 8 ++++++++ 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 9a6e601..9724a8f 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -170,6 +170,12 @@ def get_mogi(self, ctx): return self.ongoing_events[ctx.channel] return None + def is_staff(self, member): + for staff_role in self.bot.config["staff_roles"]: + if member.get_role(staff_role): + return True + return False + async def is_started(self, ctx, mogi): if not mogi.started: await ctx.send("Mogi has not been started yet... type !start") @@ -196,6 +202,10 @@ async def can(self, interaction: discord.Interaction): player_team = mogi.check_player(member) + if player_team is not None: + await interaction.followup.send(f"{interaction.user.mention} is already signed up.") + return + players = [] msg = "" placement_role_id = 723753340063842345 @@ -259,7 +269,7 @@ async def sub(self, interaction: discord.Interaction): if lastCommandTime == None: lastCommandTime = 0 - if (current_time - lastCommandTime) < 120: #Cooldown timer in seconds + if (current_time - lastCommandTime) < 120 and not self.is_staff(interaction.user): #Cooldown timer in seconds await interaction.response.send_message(f"You are still on cooldown. Please wait for {int(2 * 60 - (current_time - lastCommandTime))} more seconds to use this command again.", ephemeral=True) return @@ -965,6 +975,7 @@ async def debug_start_rooms(self, ctx): if mogi.start_time == self.prev_start_time: await self.add_teams_to_rooms(mogi, (mogi.start_time.minute) % 60, True) return + async def setup(bot): diff --git a/mogi_objects.py b/mogi_objects.py index b2e792c..6d3571f 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -384,6 +384,7 @@ def __init__(self, room: Room, get_mmr, bottom_room_num): super().__init__(timeout=1200) self.room = room self.get_mmr = get_mmr + self.bottom_room_num = bottom_room_num @discord.ui.button(label="Join Room") async def button_callback(self, interaction, button): @@ -406,7 +407,7 @@ async def button_callback(self, interaction, button): return if self.room.room_num == 1: self.room.mmr_high = 999999 - if self.room.room_num == bottom_room_num: + if self.room.room_num == self.bottom_room_num: self.room.mmr_low = -999999 if isinstance(user_mmr, int) and user_mmr < self.room.mmr_high + 500 and user_mmr > self.room.mmr_low - 500: button.disabled = True diff --git a/prod_config.json b/prod_config.json index a385c20..bb082c3 100644 --- a/prod_config.json +++ b/prod_config.json @@ -31,6 +31,14 @@ 389252697284542465 ] }, + "staff_roles": [ + 387347888935534593, + 399382503825211393, + 399384750923579392, + 521149807994208295, + 792891432301625364, + 877629421811540016 + ], "queue_messages": true, "sec_between_queue_msgs": 2, "username": "username", diff --git a/test_config.json b/test_config.json index 4ee7f5a..06c72cb 100644 --- a/test_config.json +++ b/test_config.json @@ -30,6 +30,14 @@ 1249808212547604561 ] }, + "staff_roles": [ + 387347888935534593, + 399382503825211393, + 399384750923579392, + 521149807994208295, + 792891432301625364, + 877629421811540016 + ], "queue_messages": true, "sec_between_queue_msgs": 2, "username": "username", From 5beafcc2229066020f647abafa4b2e899140b33a Mon Sep 17 00:00:00 2001 From: emilp-595 Date: Wed, 19 Jun 2024 21:58:20 -0700 Subject: [PATCH 015/147] Added Track Type to config Changed endpoint url's to reflect new track type variable in the configuration file of the bot. Also added the config details for the CT side of things. --- cogs/SquadQueue.py | 12 +++++----- ct_config.json | 55 ++++++++++++++++++++++++++++++++++++++++++++++ mmr.py | 10 +++++---- prod_config.json | 5 +++-- test_config.json | 5 +++-- 5 files changed, 74 insertions(+), 13 deletions(-) create mode 100644 ct_config.json diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 9724a8f..1c645aa 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -6,7 +6,7 @@ from datetime import datetime, timezone, timedelta import time import json -from mmr import mk8dx_150cc_mmr, get_mmr_from_discord_id +from mmr import mkw_mmr, get_mmr_from_discord_id from mogi_objects import Mogi, Team, Player, Room, VoteView, JoinView, get_tier import asyncio from collections import defaultdict @@ -66,6 +66,8 @@ def __init__(self, bot): self.room_mmr_threshold = bot.config["ROOM_MMR_THRESHOLD"] + self.TRACK_TYPE = bot.config["track_type"] + self.TIER_INFO = [] # number of minutes before scheduled time that queue should open @@ -216,7 +218,7 @@ async def can(self, interaction: discord.Interaction): msg += f"{players[0].lounge_name} is assumed to be a new player and will be playing this mogi with a starting MMR of {starting_player_mmr}. " msg += "If you believe this is a mistake, please contact a staff member for help.\n" else: - players = await mk8dx_150cc_mmr(self.URL, [member]) + players = await mkw_mmr(self.URL, [member], self.TRACK_TYPE) if len(players) == 0 or players[0] is None: msg = f"{interaction.user.mention} fetch for MMR has failed and joining the queue was unsuccessful. " @@ -557,7 +559,7 @@ async def update_tier_info(self, interaction: discord.Interaction): async def get_ladder_info(self): timeout = aiohttp.ClientTimeout(total=10) - url = "https://mkwlounge.gg/api/ladderclass.php?ladder_type=rt" + url = "https://mkwlounge.gg/api/ladderclass.php?ladder_type=" + self.TRACK_TYPE msg = "" try: async with aiohttp.ClientSession( @@ -928,7 +930,7 @@ async def debug_add_team(self, ctx, members: commands.Greedy[discord.Member]): check_players = [ctx.author] check_players.extend(members) - players = await mk8dx_150cc_mmr(self.URL, check_players) + players = await mkw_mmr(self.URL, check_players, self.TRACK_TYPE) for i in range(0, 12): player = Player( players[0].member, f"{players[0].lounge_name}{i + 1}", players[0].mmr + (10 * i)) @@ -952,7 +954,7 @@ async def debug_add_many_players(self, ctx, members: commands.Greedy[discord.Mem check_players = [ctx.author] check_players.extend(members) - players = await mk8dx_150cc_mmr(self.URL, check_players) + players = await mkw_mmr(self.URL, check_players, self.TRACK_TYPE) for i in range(0, 100): player = Player( players[0].member, f"{players[0].lounge_name}{i + 1}", players[0].mmr + (10 * i)) diff --git a/ct_config.json b/ct_config.json new file mode 100644 index 0000000..55cfb91 --- /dev/null +++ b/ct_config.json @@ -0,0 +1,55 @@ +{ + "token": "", + "guild_id": 387347467332485122, + "admin_roles": { + "387347467332485122": [ + "Administrator", + "Lounge Staff" + ] + }, + "queue_join_channel": 1253109700107567155, + "queue_sub_channel": 1253109752418930779, + "queue_list_channel": 1253109466027655338, + "queue_history_channel": 1253109800196243618, + "queue_general_channel": 1253109398193045534, + "members_for_channels": { + "387347467332485122": [ + 235148962103951360, + 155149108183695360, + 815483289515196416, + 892420397570592768, + 775415990238314518, + 774812906830299136, + 745443073202323586, + 774866940950872095, + 1019051989734273054 + ] + }, + "roles_for_channels": { + "387347467332485122": [ + 520808645252874240, + 520808674411937792 + ] + }, + "staff_roles": [ + 387347888935534593, + 399382503825211393, + 399384750923579392, + 521149807994208295, + 792891432301625364, + 877629421811540016 + ], + "queue_messages": true, + "sec_between_queue_msgs": 2, + "username": "username", + "password": "password", + "url": "https://www.mkwlounge.gg", + "track_type": "ct", + "TIME_ADJUSTMENT": 0, + "QUEUE_OPEN_TIME": 90, + "JOINING_TIME": 85, + "EXTENSION_TIME": 5, + "MOGI_LIFETIME": 180, + "SUB_MESSAGE_LIFETIME_SECONDS": 1200, + "ROOM_MMR_THRESHOLD": 9000 +} \ No newline at end of file diff --git a/mmr.py b/mmr.py index d5bd746..c938b55 100644 --- a/mmr.py +++ b/mmr.py @@ -5,8 +5,9 @@ headers = {'Content-type': 'application/json'} -async def mk8dx_150cc_mmr(url, members): - base_url = url + '/api/ladderplayer.php?ladder_type=rt&' +async def mkw_mmr(url, members, track_type): + tracks = track_type + base_url = url + '/api/ladderplayer.php?ladder_type=' + tracks + '&' players = [] timeout = aiohttp.ClientTimeout(total=10) try: @@ -34,8 +35,9 @@ async def mk8dx_150cc_mmr(url, members): return players -async def get_mmr_from_discord_id(discord_id): - base_url = "https://www.mkwlounge.gg" + '/api/ladderplayer.php?ladder_type=rt&' +async def get_mmr_from_discord_id(discord_id, track_type): + tracks = track_type + base_url = "https://www.mkwlounge.gg" + '/api/ladderplayer.php?ladder_type=' + tracks + '&' timeout = aiohttp.ClientTimeout(total=10) async with aiohttp.ClientSession( timeout=timeout, diff --git a/prod_config.json b/prod_config.json index bb082c3..3b4b9c7 100644 --- a/prod_config.json +++ b/prod_config.json @@ -44,11 +44,12 @@ "username": "username", "password": "password", "url": "https://www.mkwlounge.gg", + "track_type": "rt", "TIME_ADJUSTMENT": 0, "QUEUE_OPEN_TIME": 72, "JOINING_TIME": 67, - "EXTENSION_TIME": 3, + "EXTENSION_TIME": 5, "MOGI_LIFETIME": 180, "SUB_MESSAGE_LIFETIME_SECONDS": 1200, - "ROOM_MMR_THRESHOLD": 8000 + "ROOM_MMR_THRESHOLD": 9000 } \ No newline at end of file diff --git a/test_config.json b/test_config.json index 06c72cb..6264685 100644 --- a/test_config.json +++ b/test_config.json @@ -43,11 +43,12 @@ "username": "username", "password": "password", "url": "https://www.mkwlounge.gg", + "track_type": "rt", "TIME_ADJUSTMENT": 0, "QUEUE_OPEN_TIME": 72, "JOINING_TIME": 67, - "EXTENSION_TIME": 3, + "EXTENSION_TIME": 5, "MOGI_LIFETIME": 180, "SUB_MESSAGE_LIFETIME_SECONDS": 1200, - "ROOM_MMR_THRESHOLD": 8000 + "ROOM_MMR_THRESHOLD": 9000 } \ No newline at end of file From 4caf62bc787e77751d49e882c53307ef11a7b8f6 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Fri, 21 Jun 2024 20:24:34 -0500 Subject: [PATCH 016/147] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 8c60208..b73e9cf 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /.venv* /config.json /credentials.json +.idea/* From 513a484a6e062d43ae4235573e58e621a1e09137 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Fri, 21 Jun 2024 20:25:59 -0500 Subject: [PATCH 017/147] Type hints for reading clarity and IDE help --- mogi_objects.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index 6d3571f..484d699 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -3,6 +3,7 @@ from datetime import datetime, timezone, timedelta import time from discord.ui import View +from typing import List class Mogi: @@ -14,8 +15,8 @@ def __init__(self, sq_id: int, size: int, mogi_channel: discord.TextChannel, self.sq_id = sq_id self.size = size self.mogi_channel = mogi_channel - self.teams = [] - self.rooms = [] + self.teams: List[Team] = [] + self.rooms: List[Room] = [] self.is_automated = is_automated if not is_automated: self.start_time = None From ac33a099544d6941ba6e23fc619a04959ca49522 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Fri, 21 Jun 2024 20:26:21 -0500 Subject: [PATCH 018/147] Refactor for reading clarity --- mogi_objects.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index 484d699..ead98d5 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -18,10 +18,7 @@ def __init__(self, sq_id: int, size: int, mogi_channel: discord.TextChannel, self.teams: List[Team] = [] self.rooms: List[Room] = [] self.is_automated = is_automated - if not is_automated: - self.start_time = None - else: - self.start_time = start_time + self.start_time = start_time if is_automated else None def check_player(self, member): for team in self.teams: From d751a6bfc83722892172dd35812f26f0ec5704dc Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Fri, 21 Jun 2024 20:26:51 -0500 Subject: [PATCH 019/147] Refactor count_registered for readability/conciseness --- mogi_objects.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index ead98d5..cc152cc 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -27,11 +27,8 @@ def check_player(self, member): return None def count_registered(self): - count = 0 - for team in self.teams: - if team.is_registered(): - count += 1 - return count + """Returns the number of teams that are registered""" + return sum(1 for team in self.teams if team.is_registered()) def confirmed_list(self): confirmed = [] From d305d4a3178349133d0c21282ff2f9402ecb8252 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Fri, 21 Jun 2024 20:27:20 -0500 Subject: [PATCH 020/147] Refactor confirmed_list for readability/conciseness --- mogi_objects.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index cc152cc..a752c54 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -31,11 +31,7 @@ def count_registered(self): return sum(1 for team in self.teams if team.is_registered()) def confirmed_list(self): - confirmed = [] - for team in self.teams: - if team.is_registered(): - confirmed.append(team) - return confirmed + return [team for team in self.teams if team.is_registered()] def remove_id(self, squad_id: int): confirmed = self.confirmed_list() From f0de7d18fb3879a8ab9defeaa00d39f8bdd7ce81 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Fri, 21 Jun 2024 20:27:44 -0500 Subject: [PATCH 021/147] Refactor is_registered for readability/conciseness --- mogi_objects.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index a752c54..32e3f35 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -77,10 +77,8 @@ def recalc_avg(self): self.avg_mmr = sum([p.mmr for p in self.players]) / len(self.players) def is_registered(self): - for player in self.players: - if player.confirmed is False: - return False - return True + """Returns if all players on the team are registered""" + return all(player.confirmed for player in self.players) def has_player(self, member): for player in self.players: From f4e60efe4cc8087ef20bba4f411d4cb42be9bfc8 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Fri, 21 Jun 2024 20:28:10 -0500 Subject: [PATCH 022/147] Refactor has_player for readability/conciseness --- mogi_objects.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index 32e3f35..2bf5cc4 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -81,10 +81,7 @@ def is_registered(self): return all(player.confirmed for player in self.players) def has_player(self, member): - for player in self.players: - if player.member.id == member.id: - return True - return False + return any(player.member.id == member.id for player in self.players) def get_player(self, member): for player in self.players: From 2ab5a075d423515f450cf8cc976f17dc1d8eb72a Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Fri, 21 Jun 2024 20:28:24 -0500 Subject: [PATCH 023/147] Refactor num_confirmed for readability/conciseness --- mogi_objects.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index 2bf5cc4..0f5f1ee 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -100,11 +100,8 @@ def sub_player(self, sub_out, sub_in): return def num_confirmed(self): - count = 0 - for player in self.players: - if player.confirmed: - count += 1 - return count + """Returns the number of confirmed players in the team""" + return sum(1 for player in self.players if player.confirmed) def get_unconfirmed(self): unconfirmed = [] From 784d4637ae2a82098ce861cbbf989b4940c66344 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Fri, 21 Jun 2024 20:28:43 -0500 Subject: [PATCH 024/147] Refactor get_unconfirmed for readability/conciseness --- mogi_objects.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index 0f5f1ee..472084b 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -104,11 +104,8 @@ def num_confirmed(self): return sum(1 for player in self.players if player.confirmed) def get_unconfirmed(self): - unconfirmed = [] - for player in self.players: - if not player.confirmed: - unconfirmed.append(player) - return unconfirmed + """Returns a list of players on the team who have not confirmed yet.""" + return [player for player in self.players if not player.confirmed] def __lt__(self, other): if self.avg_mmr < other.avg_mmr: From aac7c2a8799727eb7388cc9d519c14dfa7cb732a Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Fri, 21 Jun 2024 20:28:59 -0500 Subject: [PATCH 025/147] Refactor __lt__ for readability/conciseness --- mogi_objects.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index 472084b..ab3ce39 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -108,10 +108,7 @@ def get_unconfirmed(self): return [player for player in self.players if not player.confirmed] def __lt__(self, other): - if self.avg_mmr < other.avg_mmr: - return True - if self.avg_mmr > other.avg_mmr: - return False + return self.avg_mmr < other.avg_mmr def __gt__(self, other): return other.__lt__(self) From 5adeec5930aa36cf6f3a11941efaade73b7e5e4b Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Fri, 21 Jun 2024 20:29:10 -0500 Subject: [PATCH 026/147] Refactor __gt__ for readability/conciseness --- mogi_objects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mogi_objects.py b/mogi_objects.py index ab3ce39..1acca60 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -111,7 +111,7 @@ def __lt__(self, other): return self.avg_mmr < other.avg_mmr def __gt__(self, other): - return other.__lt__(self) + return other < self # def __eq__(self, other): # if self.avg_mmr == other.avg_mmr: From c64066d4746b4ccf84f191ba4aee575b5f00d725 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 00:29:35 -0500 Subject: [PATCH 027/147] Minor code formatting --- mogi_objects.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index 1acca60..eccf5c3 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -37,7 +37,7 @@ def remove_id(self, squad_id: int): confirmed = self.confirmed_list() if squad_id < 1 or squad_id > len(confirmed): return None - squad = confirmed[squad_id-1] + squad = confirmed[squad_id - 1] self.teams.remove(squad) return squad @@ -64,6 +64,7 @@ def __init__(self, teams, room_num: int, thread: discord.Thread): self.mmr_low = None self.view = None self.finished = False + def get_player_list(self): return [player.member.id for team in self.teams for player in team.players] @@ -180,7 +181,7 @@ async def make_teams(self, format): scoreboard_text = [] for j in range(teams_per_room): - team_text = f"`{j+1}.` " + team_text = f"`{j + 1}.` " team_names = ", ".join([p.lounge_name for p in teams[j].players]) scoreboard_text.append(team_names) team_text += team_names @@ -398,5 +399,6 @@ async def button_callback(self, interaction, button): def get_tier(mmr: int, tier_info): for tier in tier_info: - if (tier["minimum_mmr"] is None or mmr >= tier["minimum_mmr"]) and (tier["maximum_mmr"] is None or mmr <= tier["maximum_mmr"]): + if (tier["minimum_mmr"] is None or mmr >= tier["minimum_mmr"]) and ( + tier["maximum_mmr"] is None or mmr <= tier["maximum_mmr"]): return tier["ladder_order"] From ecf4ab49bddac9788796c4ae6cc173d266513301 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 00:30:10 -0500 Subject: [PATCH 028/147] Bad practice to shadow global functions --- mogi_objects.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index eccf5c3..21d0cac 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -171,9 +171,9 @@ async def make_teams(self, format): msg += "\n" teams = [] - teams_per_room = int(12 / format[0]) + teams_per_room = int(12 / format_[0]) for j in range(teams_per_room): - team = Team(self.players[j*format[0]:(j+1)*format[0]]) + team = Team(self.players[j * format_[0]:(j + 1) * format_[0]]) teams.append(team) teams.sort(key=lambda team: team.avg_mmr, reverse=True) From 8f5a5ef185e36645c4075fa8ffb46be3f43b1130 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 00:30:32 -0500 Subject: [PATCH 029/147] Combine 2 lines of text into 1 --- mogi_objects.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index 21d0cac..5d885ea 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -167,8 +167,7 @@ async def make_teams(self, format): room_mmr = round(sum([p.mmr for p in self.players]) / 12) room.mmr_average = room_mmr self.header_text = f"**Room {room.room_num} MMR: {room_mmr} - T{get_tier(room_mmr, self.tier_info)}** " - msg += self.header_text - msg += "\n" + msg += self.header_text + "\n" teams = [] teams_per_room = int(12 / format_[0]) From 86ed77614b4be21e6fd8d8edcb237526c80f6d0a Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 00:31:19 -0500 Subject: [PATCH 030/147] Clean up messy code, make more concise --- mogi_objects.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index 5d885ea..5e0bd51 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -188,17 +188,9 @@ async def make_teams(self, format): msg += team_text self.teams_text += team_text - minutes = self.mogi.start_time.minute - pen = minutes + 8 - - if pen > 60: - pen -= 60 - - if pen < 10: - pen = '0' + str(pen) - if minutes < 10: - minutes = '0' + str(minutes) - msg += "Decide a host amongst yourselves; room open at :{}, penalty at :{}. Good luck!".format(minutes, pen) + penalty_time = self.mogi.start_time + timedelta(minutes=8) + room_open_time = self.mogi.start_time + msg += f"Decide a host amongst yourselves; room open at :{room_open_time.minute:02}, penalty at :{penalty_time.minute:02}. Good luck!" room.teams = teams From 3121bce4e8689553e72cf340c99afd0c8485e845 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 00:33:33 -0500 Subject: [PATCH 031/147] Overhaul of voting code In addition to addressing code duplication, this commit addresses poor practices of using variables that start with a number. It also addresses poor practices of shadowing global functions (max) which is why original developer couldn't determine why max wasn't working. --- mogi_objects.py | 175 ++++++++++++------------------------------------ 1 file changed, 44 insertions(+), 131 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index 5e0bd51..cd093bc 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -142,27 +142,28 @@ def __init__(self, players, thread, mogi, tier_info): self.teams_text = "" self.found_winner = False self.tier_info = tier_info - self.__setattr__("FFA", []) - self.__setattr__("2v2", []) - self.__setattr__("3v3", []) - self.__setattr__("4v4", []) - self.__setattr__("6v6", []) - - def __getitem__(self, key): - return getattr(self, key) - - async def make_teams(self, format): + self.votes = {"FFA": [], + "2v2": [], + "3v3": [], + "4v4": [], + "6v6": [] + } + + async def make_teams(self, format_): random.shuffle(self.players) room = self.mogi.get_room_from_thread(self.thread.id) - msg = "**Poll Ended!** \n\n" - msg += f"1) FFA - {len(self['FFA'])}\n" - msg += f"2) 2v2 - {len(self['2v2'])}\n" - msg += f"3) 3v3 - {len(self['3v3'])}\n" - msg += f"4) 4v4 - {len(self['4v4'])}\n" - msg += f"5) 6v6 - {len(self['6v6'])}\n" - msg += f"Winner: {format[1]}\n\n" + msg = f"""**Poll Ended!** + +1) FFA - {len(self.votes['FFA'])} +2) 2v2 - {len(self.votes['2v2'])} +3) 3v3 - {len(self.votes['3v3'])} +4) 4v4 - {len(self.votes['4v4'])} +5) 6v6 - {len(self.votes['6v6'])} +Winner: {format_[1]} + +""" room_mmr = round(sum([p.mmr for p in self.players]) / 12) room.mmr_average = room_mmr @@ -199,30 +200,19 @@ async def make_teams(self, format): async def find_winner(self): if not self.found_winner: - # for some reason max function wasnt working... - max = 0 - if len(self["FFA"]) > max: - max = len(self["FFA"]) - if len(self["2v2"]) > max: - max = len(self["2v2"]) - if len(self["3v3"]) > max: - max = len(self["3v3"]) - if len(self["4v4"]) > max: - max = len(self["4v4"]) - if len(self["6v6"]) > max: - max = len(self["6v6"]) + # for some reason max function wasnt working... # It's because you were replacing it. + most_votes = len(max(self.votes.values(), key=len)) winners = [] - - if len(self["FFA"]) == max: + if len(self.votes["FFA"]) == most_votes: winners.append((1, "FFA")) - if len(self["2v2"]) == max: + if len(self.votes["2v2"]) == most_votes: winners.append((2, "2v2")) - if len(self["3v3"]) == max: + if len(self.votes["3v3"]) == most_votes: winners.append((3, "3v3")) - if len(self["4v4"]) == max: + if len(self.votes["4v4"]) == most_votes: winners.append((4, "4v4")) - if len(self["6v6"]) == max: + if len(self.votes["6v6"]) == most_votes: winners.append((6, "6v6")) winner = random.choice(winners) @@ -234,116 +224,39 @@ async def find_winner(self): @discord.ui.button(label="FFA: 0", custom_id="FFA") async def one_button_callback(self, interaction, button): - if not self.found_winner: - if interaction.user.id in self["FFA"]: - self["FFA"].remove(interaction.user.id) - else: - if interaction.user.id in self["2v2"]: - self["2v2"].remove(interaction.user.id) - if interaction.user.id in self["3v3"]: - self["3v3"].remove(interaction.user.id) - if interaction.user.id in self["4v4"]: - self["4v4"].remove(interaction.user.id) - if interaction.user.id in self["6v6"]: - self["6v6"].remove(interaction.user.id) - self["FFA"].append(interaction.user.id) - if len(self["FFA"]) == 6: - await self.make_teams((1, "FFA")) - for curr_button in self.children: - curr_button.label = f"{curr_button.custom_id}: {len(self[curr_button.custom_id])}" - if len(self["FFA"]) == 6: - curr_button.disabled = True - await interaction.response.edit_message(view=self) + await self.general_vote_callback(interaction, 1, "FFA") @discord.ui.button(label="2v2: 0", custom_id="2v2") async def two_button_callback(self, interaction, button): - if not self.found_winner: - if interaction.user.id in self["2v2"]: - self["2v2"].remove(interaction.user.id) - else: - if interaction.user.id in self["FFA"]: - self["FFA"].remove(interaction.user.id) - if interaction.user.id in self["3v3"]: - self["3v3"].remove(interaction.user.id) - if interaction.user.id in self["4v4"]: - self["4v4"].remove(interaction.user.id) - if interaction.user.id in self["6v6"]: - self["6v6"].remove(interaction.user.id) - self["2v2"].append(interaction.user.id) - if len(self["2v2"]) == 6: - await self.make_teams((2, "2v2")) - for curr_button in self.children: - curr_button.label = f"{curr_button.custom_id}: {len(self[curr_button.custom_id])}" - if len(self["2v2"]) == 6: - curr_button.disabled = True - await interaction.response.edit_message(view=self) + await self.general_vote_callback(interaction, 2, "2v2") @discord.ui.button(label="3v3: 0", custom_id="3v3") async def three_button_callback(self, interaction, button): - if not self.found_winner: - if interaction.user.id in self["3v3"]: - self["3v3"].remove(interaction.user.id) - else: - if interaction.user.id in self["FFA"]: - self["FFA"].remove(interaction.user.id) - if interaction.user.id in self["2v2"]: - self["2v2"].remove(interaction.user.id) - if interaction.user.id in self["4v4"]: - self["4v4"].remove(interaction.user.id) - if interaction.user.id in self["6v6"]: - self["6v6"].remove(interaction.user.id) - self["3v3"].append(interaction.user.id) - if len(self["3v3"]) == 6: - await self.make_teams((3, "3v3")) - for curr_button in self.children: - curr_button.label = f"{curr_button.custom_id}: {len(self[curr_button.custom_id])}" - if len(self["3v3"]) == 6: - curr_button.disabled = True - await interaction.response.edit_message(view=self) + await self.general_vote_callback(interaction, 3, "3v3") @discord.ui.button(label="4v4: 0", custom_id="4v4") async def four_button_callback(self, interaction, button): - if not self.found_winner: - if interaction.user.id in self["4v4"]: - self["4v4"].remove(interaction.user.id) - else: - if interaction.user.id in self["FFA"]: - self["FFA"].remove(interaction.user.id) - if interaction.user.id in self["2v2"]: - self["2v2"].remove(interaction.user.id) - if interaction.user.id in self["3v3"]: - self["3v3"].remove(interaction.user.id) - if interaction.user.id in self["6v6"]: - self["6v6"].remove(interaction.user.id) - self["4v4"].append(interaction.user.id) - if len(self["4v4"]) == 6: - await self.make_teams((4, "4v4")) - for curr_button in self.children: - curr_button.label = f"{curr_button.custom_id}: {len(self[curr_button.custom_id])}" - if len(self["4v4"]) == 6: - curr_button.disabled = True - await interaction.response.edit_message(view=self) + await self.general_vote_callback(interaction, 4, "4v4") @discord.ui.button(label="6v6: 0", custom_id="6v6") async def six_button_callback(self, interaction, button): + await self.general_vote_callback(interaction, 6, "6v6") + + async def general_vote_callback(self, interaction: discord.Interaction, players_per_team: int, vote: str): if not self.found_winner: - if interaction.user.id in self["6v6"]: - self["6v6"].remove(interaction.user.id) - else: - if interaction.user.id in self["FFA"]: - self["FFA"].remove(interaction.user.id) - if interaction.user.id in self["2v2"]: - self["2v2"].remove(interaction.user.id) - if interaction.user.id in self["3v3"]: - self["3v3"].remove(interaction.user.id) - if interaction.user.id in self["4v4"]: - self["4v4"].remove(interaction.user.id) - self["6v6"].append(interaction.user.id) - if len(self["6v6"]) == 6: - await self.make_teams((6, "6v6")) + original_vote = None + for vote_option, voter_ids in self.votes.items(): + if interaction.user.id in voter_ids: + original_vote = vote_option + voter_ids.remove(interaction.user.id) + if original_vote != vote: # They changed their vote or are a new voter + self.votes[vote].append(interaction.user.id) + if len(self.votes[vote]) == 6: + self.found_winner = True # This fixes a race condition + await self.make_teams((players_per_team, vote)) for curr_button in self.children: - curr_button.label = f"{curr_button.custom_id}: {len(self[curr_button.custom_id])}" - if len(self["6v6"]) == 6: + curr_button.label = f"{curr_button.custom_id}: {len(self.votes[curr_button.custom_id])}" + if self.found_winner: curr_button.disabled = True await interaction.response.edit_message(view=self) From 3ebb3814badc6b5a835fb6f6283d43099e6a54de Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 02:37:35 -0500 Subject: [PATCH 032/147] New penalty/open rooms time variable --- mogi_objects.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index cd093bc..3cdb3d5 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -12,6 +12,7 @@ def __init__(self, sq_id: int, size: int, mogi_channel: discord.TextChannel, self.started = False self.gathering = False self.making_rooms_run = False + self.making_rooms_run_time = None self.sq_id = sq_id self.size = size self.mogi_channel = mogi_channel @@ -133,7 +134,7 @@ def __init__(self, member, lounge_name, mmr): class VoteView(View): - def __init__(self, players, thread, mogi, tier_info): + def __init__(self, players, thread, mogi: Mogi, tier_info): super().__init__() self.players = players self.thread = thread @@ -150,6 +151,7 @@ def __init__(self, players, thread, mogi, tier_info): } async def make_teams(self, format_): + self.mogi.making_rooms_run_time = datetime.now(timezone.utc) random.shuffle(self.players) room = self.mogi.get_room_from_thread(self.thread.id) @@ -189,8 +191,8 @@ async def make_teams(self, format_): msg += team_text self.teams_text += team_text - penalty_time = self.mogi.start_time + timedelta(minutes=8) - room_open_time = self.mogi.start_time + penalty_time = self.mogi.making_rooms_run_time + timedelta(minutes=8) + room_open_time = self.mogi.making_rooms_run_time msg += f"Decide a host amongst yourselves; room open at :{room_open_time.minute:02}, penalty at :{penalty_time.minute:02}. Good luck!" room.teams = teams From 5aadbee0e6ac87408ff0bb140a6e84a6ca39e3ee Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 03:17:52 -0500 Subject: [PATCH 033/147] New configuration --- ct_config.json | 3 ++- prod_config.json | 3 ++- test_config.json | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ct_config.json b/ct_config.json index 55cfb91..3a777d7 100644 --- a/ct_config.json +++ b/ct_config.json @@ -45,7 +45,8 @@ "password": "password", "url": "https://www.mkwlounge.gg", "track_type": "ct", - "TIME_ADJUSTMENT": 0, + "FIRST_EVENT_TIME": 0, + "FIRST_EVENT_TIME_DESCRIPTION": "First event time is the number of minutes from 0:00 UTC that the bot would run the first event of the day. As an example, if this were set to 5, and the QUEUE_OPEN_TIME were set to 70, and the bot were to reboot at 2:00 UTC, the next event would immediately start and create rooms at 2:25 UTC.", "QUEUE_OPEN_TIME": 90, "JOINING_TIME": 85, "EXTENSION_TIME": 5, diff --git a/prod_config.json b/prod_config.json index 3b4b9c7..bf37127 100644 --- a/prod_config.json +++ b/prod_config.json @@ -45,7 +45,8 @@ "password": "password", "url": "https://www.mkwlounge.gg", "track_type": "rt", - "TIME_ADJUSTMENT": 0, + "FIRST_EVENT_TIME": 0, + "FIRST_EVENT_TIME_DESCRIPTION": "First event time is the number of minutes from 0:00 UTC that the bot would run the first event of the day. As an example, if this were set to 5, and the QUEUE_OPEN_TIME were set to 70, and the bot were to reboot at 2:00 UTC, the next event would immediately start and create rooms at 2:25 UTC.", "QUEUE_OPEN_TIME": 72, "JOINING_TIME": 67, "EXTENSION_TIME": 5, diff --git a/test_config.json b/test_config.json index 6264685..588af53 100644 --- a/test_config.json +++ b/test_config.json @@ -44,7 +44,8 @@ "password": "password", "url": "https://www.mkwlounge.gg", "track_type": "rt", - "TIME_ADJUSTMENT": 0, + "FIRST_EVENT_TIME": 0, + "FIRST_EVENT_TIME_DESCRIPTION": "First event time is the number of minutes from 0:00 UTC that the bot would run the first event of the day. As an example, if this were set to 5, and the QUEUE_OPEN_TIME were set to 70, and the bot were to reboot at 2:00 UTC, the next event would immediately start and create rooms at 2:25 UTC.", "QUEUE_OPEN_TIME": 72, "JOINING_TIME": 67, "EXTENSION_TIME": 5, From eb329e66a35c5f7508389108b22ee4701fe54aa8 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 03:20:46 -0500 Subject: [PATCH 034/147] Don't print exception, print traceback --- cogs/SquadQueue.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 1c645aa..37ea9cf 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -100,10 +100,12 @@ async def on_ready(self): await self.LIST_CHANNEL.purge() except: print("Purging List channel failed", flush=True) + print(traceback.format_exc()) try: await self.SUB_CHANNEL.purge() except: print("Purging Sub channel failed", flush=True) + print(traceback.format_exc()) print(f"Server - {self.GUILD}", flush=True) print(f"Join Channel - {self.MOGI_CHANNEL}", flush=True) print(f"Sub Channel - {self.SUB_CHANNEL}", flush=True) @@ -123,7 +125,7 @@ async def lockdown(self, channel: discord.TextChannel): await channel.set_permissions(channel.guild.default_role, overwrite=overwrite) await channel.send("Locked down " + channel.mention) except Exception as e: - print(e, flush=True) + print(traceback.format_exc()) async def unlockdown(self, channel: discord.TextChannel): # everyone_perms = channel.permissions_for(channel.guild.default_role) @@ -165,7 +167,7 @@ async def send_queued_messages(self): for i in range(len(sentmsgs)-1, -1, -1): await channel.send(sentmsgs[i]) except Exception as e: - print(e) + print(traceback.format_exc()) def get_mogi(self, ctx): if ctx.channel in self.ongoing_events.keys(): @@ -384,7 +386,7 @@ async def delete_list_messages(self, new_list_size: int): if self.LIST_CHANNEL and len(messages_to_delete) > 0: await self.LIST_CHANNEL.delete_messages(messages_to_delete) except Exception as e: - print(e, flush=True) + print(traceback.format_exc()) @commands.Cog.listener() async def on_message(self, message: discord.Message): @@ -590,8 +592,7 @@ async def get_ladder_info(self): print(msg, flush=True) return msg except Exception as e: - print(e, flush=True) - + print(traceback.format_exc()) # check if user has roles defined in config.json async def has_roles(self, member: discord.Member, guild_id: int, config): @@ -615,7 +616,7 @@ async def end_voting(self): continue await room.view.find_winner() except Exception as e: - print(e, flush=True) + print(traceback.format_exc()) async def write_history(self): """Writes the teams, tier and average of each room per hour.""" @@ -635,7 +636,7 @@ async def write_history(self): msg += "ㅤ" await self.HISTORY_CHANNEL.send(msg) except Exception as e: - print(e, flush=True) + print(traceback.format_exc()) # make thread channels while the event is gathering instead of at the end, # since discord only allows 50 thread channels to be created per 5 minutes. @@ -655,7 +656,7 @@ async def check_room_channels(self, mogi): auto_archive_duration=60, invitable=False) except Exception as e: - print(e, flush=True) + print(traceback.format_exc()) err_msg = f"\nAn error has occurred while creating a room channel:\n{e}" await mogi.mogi_channel.send(err_msg) return @@ -735,11 +736,11 @@ async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=F curr_room.view = view await room_channel.send(view=view) except Exception as e: - print(e, flush=True) - err_msg = f"\nAn error has occurred while creating the room channel; please contact your opponents in DM or another channel\n" - err_msg += mentions - msg += err_msg - room_channel = None + print(traceback.format_exc()) + err_msg = f"\nAn error has occurred while creating the room channel; please contact your opponents in DM or another channel\n" + err_msg += mentions + msg += err_msg + room_channel = None try: await mogi.mogi_channel.send(msg) except Exception as e: @@ -833,11 +834,11 @@ async def sqscheduler(self): try: await self.scheduler_mogi_start() except Exception as e: - print(e) + print(traceback.format_exc()) try: await self.ongoing_mogi_checks() except Exception as e: - print(e) + print(traceback.format_exc()) @tasks.loop(minutes=1) async def que_scheduler(self): @@ -898,7 +899,7 @@ async def delete_old_mogis(self): f"Deleting {mogi.start_time} Mogi at {curr_time}", flush=True) del self.old_events[mogi.mogi_channel] except Exception as e: - print(e, flush=True) + print(traceback.format_exc()) def get_event_str(self, mogi): mogi_time = discord.utils.format_dt(mogi.start_time, style="F") From 68038eaea18c89b8956aec53a615b03c5057b83a Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 03:21:18 -0500 Subject: [PATCH 035/147] Add imports --- cogs/SquadQueue.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 37ea9cf..83e55e3 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import aiohttp import discord from discord.ext import commands, tasks @@ -10,6 +12,8 @@ from mogi_objects import Mogi, Team, Player, Room, VoteView, JoinView, get_tier import asyncio from collections import defaultdict +from typing import Dict, List +import traceback headers = {'Content-type': 'application/json'} From 5635b4b61cfc04eb9bc31e0ab6d803a5104025da Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 03:22:46 -0500 Subject: [PATCH 036/147] Minor refactor of is_staff --- cogs/SquadQueue.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 83e55e3..c11619b 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -178,11 +178,8 @@ def get_mogi(self, ctx): return self.ongoing_events[ctx.channel] return None - def is_staff(self, member): - for staff_role in self.bot.config["staff_roles"]: - if member.get_role(staff_role): - return True - return False + def is_staff(self, member: discord.Member): + return any(member.get_role(staff_role) for staff_role in self.bot.config["staff_roles"]) async def is_started(self, ctx, mogi): if not mogi.started: From 602e2440fc01087bdb7873976e247afa72d34670 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 03:24:32 -0500 Subject: [PATCH 037/147] Adjustable amount for debug_many_players --- cogs/SquadQueue.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index c11619b..647a653 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -947,6 +947,7 @@ async def debug_add_team(self, ctx, members: commands.Greedy[discord.Member]): @commands.command(name="debug_add_many_players") @commands.is_owner() async def debug_add_many_players(self, ctx, members: commands.Greedy[discord.Member]): + num_times = 25 mogi = self.get_mogi(ctx) if mogi is None: return @@ -957,13 +958,13 @@ async def debug_add_many_players(self, ctx, members: commands.Greedy[discord.Mem check_players = [ctx.author] check_players.extend(members) players = await mkw_mmr(self.URL, check_players, self.TRACK_TYPE) - for i in range(0, 100): + for i in range(0, num_times): player = Player( players[0].member, f"{players[0].lounge_name}{i + 1}", players[0].mmr + (10 * i)) player.confirmed = True squad = Team([player]) mogi.teams.append(squad) - msg = f"{players[0].lounge_name} added 100 times." + msg = f"{players[0].lounge_name} added {num_times} times." await self.queue_or_send(ctx, msg) await self.check_room_channels(mogi) await self.check_num_teams(mogi) From a912711f3c8751cfd47530ef8859c2ba5cd255fc Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 03:26:01 -0500 Subject: [PATCH 038/147] Move queue scheduler into sqscheduler This has numerous benefits, but most importantly, it will help with race conditions --- cogs/SquadQueue.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 647a653..7a325a2 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -36,7 +36,6 @@ def __init__(self, bot): self.prev_start_time = None - self._que_scheduler = self.que_scheduler.start() self._scheduler_task = self.sqscheduler.start() self._msgqueue_task = self.send_queued_messages.start() self._list_task = self.list_task.start() @@ -832,6 +831,12 @@ async def sqscheduler(self): # It may seem silly to do try/except Exception, but this coroutine **cannot** fail # This coroutine *silently* fails and stops if exceptions aren't caught - an annoying abtraction of asyncio # This is unacceptable considering people are relying on these mogis to run, so we will not allow this routine to stop + try: + print(f"{datetime.now()}: sqscheduler ran") + if self.ongoing_event is None: + await self.schedule_que_event() + except Exception as e: + print(traceback.format_exc()) try: await self.scheduler_mogi_start() except Exception as e: @@ -841,13 +846,6 @@ async def sqscheduler(self): except Exception as e: print(traceback.format_exc()) - @tasks.loop(minutes=1) - async def que_scheduler(self): - try: - if not self.scheduled_events or len(self.scheduled_events[self.GUILD]) == 0: - await self.schedule_que_event() - except Exception as e: - print(e) async def schedule_que_event(self): """Schedules queue for the next hour in the given channel.""" From 99361182baa694d7e6b8ec06e7d88a98ea724ef7 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 03:40:33 -0500 Subject: [PATCH 039/147] Minor items --- cogs/SquadQueue.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 7a325a2..17d481a 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -180,13 +180,13 @@ def get_mogi(self, ctx): def is_staff(self, member: discord.Member): return any(member.get_role(staff_role) for staff_role in self.bot.config["staff_roles"]) - async def is_started(self, ctx, mogi): + async def is_started(self, ctx, mogi: Mogi): if not mogi.started: await ctx.send("Mogi has not been started yet... type !start") return False return True - async def is_gathering(self, ctx, mogi): + async def is_gathering(self, ctx, mogi: Mogi): if not mogi.gathering: await ctx.send("Mogi is closed; players cannot join or drop from the event") return False @@ -261,6 +261,7 @@ async def drop(self, interaction: discord.Interaction): msg += f", `[{mogi.count_registered()} players]`" await interaction.followup.send(msg) + await self.check_num_teams(mogi) @app_commands.command(name="sub") @app_commands.guild_only() @@ -746,7 +747,7 @@ async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=F except Exception as e: print( f"Mogi Channel message for room {i+1} has failed to send.", flush=True) - print(e, flush=True) + print(traceback.format_exc()) if num_teams < mogi.count_registered(): missed_teams = mogi.confirmed_list( )[num_teams:mogi.count_registered()] @@ -759,10 +760,10 @@ async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=F await mogi.mogi_channel.send(msg) except Exception as e: print("Late Player message has failed to send.", flush=True) - print(e, flush=True) await asyncio.sleep(120) await self.end_voting() await self.write_history() + print(traceback.format_exc()) async def check_num_teams(self, mogi): if not mogi.gathering or not mogi.is_automated: @@ -832,7 +833,6 @@ async def sqscheduler(self): # This coroutine *silently* fails and stops if exceptions aren't caught - an annoying abtraction of asyncio # This is unacceptable considering people are relying on these mogis to run, so we will not allow this routine to stop try: - print(f"{datetime.now()}: sqscheduler ran") if self.ongoing_event is None: await self.schedule_que_event() except Exception as e: @@ -900,7 +900,7 @@ async def delete_old_mogis(self): except Exception as e: print(traceback.format_exc()) - def get_event_str(self, mogi): + def get_event_str(self, mogi: Mogi): mogi_time = discord.utils.format_dt(mogi.start_time, style="F") mogi_time_relative = discord.utils.format_dt( mogi.start_time, style="R") From f70151e44ca8e20a4b0d90f2f277e96238bf782f Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 03:42:24 -0500 Subject: [PATCH 040/147] Main overhaul This commit overhauls complex code surrounding: SquadQueue.scheduled_events SquadQueue.ongoing_events SquadQueue.old_events And scheduling events --- cogs/SquadQueue.py | 431 ++++++++++++++++++++++----------------------- 1 file changed, 213 insertions(+), 218 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 17d481a..b00508a 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -25,16 +25,13 @@ class SquadQueue(commands.Cog): def __init__(self, bot): self.bot = bot - # keys are discord.Guild objects, values are list of Mogi instances - self.scheduled_events = {} - # keys are discord.TextChannel objects, values are instances of Mogi - self.ongoing_events = {} + self.next_event: Mogi = None - self.old_events = {} + self.ongoing_event: Mogi = None - self.sq_times = [] + self.old_events: List[Mogi] = [] - self.prev_start_time = None + self.sq_times = [] self._scheduler_task = self.sqscheduler.start() self._msgqueue_task = self.send_queued_messages.start() @@ -45,7 +42,7 @@ def __init__(self, bot): self.list_messages = [] - self.QUEUE_TIME_BLOCKER = datetime.now(timezone.utc) + self.LAUNCH_NEW_EVENTS = True self.GUILD = None @@ -73,7 +70,7 @@ def __init__(self, bot): self.TIER_INFO = [] - # number of minutes before scheduled time that queue should open + # Time between each event queue opening self.QUEUE_OPEN_TIME = timedelta(minutes=bot.config["QUEUE_OPEN_TIME"]) # number of minutes after QUEUE_OPEN_TIME that teams can join the mogi @@ -82,6 +79,13 @@ def __init__(self, bot): # number of minutes after JOINING_TIME for any potential extra teams to join self.EXTENSION_TIME = timedelta(minutes=bot.config["EXTENSION_TIME"]) + assert (self.QUEUE_OPEN_TIME >= self.JOINING_TIME + self.EXTENSION_TIME) + + # This is the first event of the day's time. However, this isn't the first event the bot will run. This is literally the time of the first event in a daily schedule. + self.FIRST_EVENT_TIME = datetime.combine(datetime.utcnow().date(), datetime.min.time(), + tzinfo=timezone.utc) + timedelta( + minutes=bot.config["FIRST_EVENT_TIME"]) + with open('./timezones.json', 'r') as cjson: self.timezones = json.load(cjson) @@ -172,10 +176,11 @@ async def send_queued_messages(self): except Exception as e: print(traceback.format_exc()) - def get_mogi(self, ctx): - if ctx.channel in self.ongoing_events.keys(): - return self.ongoing_events[ctx.channel] - return None + def get_mogi(self, ctx: discord.Interaction | discord.ext.commands.context.Context) -> Mogi | None: + if self.ongoing_event is None: + return None + if self.ongoing_event.mogi_channel.id == ctx.channel.id: + return self.ongoing_event def is_staff(self, member: discord.Member): return any(member.get_role(staff_role) for staff_role in self.bot.config["staff_roles"]) @@ -232,7 +237,7 @@ async def can(self, interaction: discord.Interaction): squad = Team(players) mogi.teams.append(squad) - msg += f"{players[0].lounge_name} joined queue closing at {discord.utils.format_dt(mogi.start_time - (self.QUEUE_OPEN_TIME - self.JOINING_TIME))}, `[{mogi.count_registered()} players]`" + msg += f"{players[0].lounge_name} joined queue closing at {discord.utils.format_dt(mogi.start_time)}, `[{mogi.count_registered()} players]`" await interaction.followup.send(msg) await self.check_room_channels(mogi) @@ -273,28 +278,32 @@ async def sub(self, interaction: discord.Interaction): print(lastCommandTime, flush=True) if lastCommandTime == None: lastCommandTime = 0 - - if (current_time - lastCommandTime) < 120 and not self.is_staff(interaction.user): #Cooldown timer in seconds - await interaction.response.send_message(f"You are still on cooldown. Please wait for {int(2 * 60 - (current_time - lastCommandTime))} more seconds to use this command again.", ephemeral=True) + + if (current_time - lastCommandTime) < 120 and not self.is_staff(interaction.user): # Cooldown timer in seconds + await interaction.response.send_message( + f"You are still on cooldown. Please wait for {int(2 * 60 - (current_time - lastCommandTime))} more seconds to use this command again.", + ephemeral=True) return is_room_thread = False room = None bottom_room_num = 1 - for mogi in self.ongoing_events.values(): + mogi = self.ongoing_event + if mogi is not None: if mogi.is_room_thread(interaction.channel_id): room = mogi.get_room_from_thread(interaction.channel_id) bottom_room_num = len(mogi.rooms) is_room_thread = True - break - for mogi in self.old_events.values(): - if mogi.is_room_thread(interaction.channel.id): - room = mogi.get_room_from_thread(interaction.channel.id) - bottom_room_num = len(mogi.rooms) + for old_mogi in self.old_events: + if old_mogi.is_room_thread(interaction.channel.id): + room = old_mogi.get_room_from_thread(interaction.channel.id) + bottom_room_num = len(old_mogi.rooms) is_room_thread = True break if not is_room_thread: - await interaction.response.send_message(f"More than {self.MOGI_LIFETIME} minutes have passed since mogi start, the Mogi Object has been deleted.", ephemeral=True) + await interaction.response.send_message( + f"More than {self.MOGI_LIFETIME} minutes have passed since mogi start, the Mogi Object has been deleted.", + ephemeral=True) return msg = "<@&1167985222533533817> - " if bottom_room_num == 1: @@ -302,7 +311,7 @@ async def sub(self, interaction: discord.Interaction): elif room.room_num == 1: msg += f"Room 1 is looking for a sub with mmr >{room.mmr_low - 500}\n" elif room.room_num == bottom_room_num: - msg += f"Room {room.room_num} is looking for a sub with mmr <{room.mmr_high + 500}\n" + msg += f"Room {room.room_num} is looking for a sub with mmr <{room.mmr_high + 500}\n" else: msg += f"Room {room.room_num} is looking for a sub with range {room.mmr_low - 500}-{room.mmr_high + 500}\n" message_delete_date = datetime.now( @@ -311,71 +320,72 @@ async def sub(self, interaction: discord.Interaction): await self.SUB_CHANNEL.send(msg, delete_after=self.SUB_MESSAGE_LIFETIME_SECONDS) view = JoinView(room, get_mmr_from_discord_id, bottom_room_num) await self.SUB_CHANNEL.send(view=view, delete_after=self.SUB_MESSAGE_LIFETIME_SECONDS) - cooldowns[interaction.user.id] = current_time #Updates cooldown + cooldowns[interaction.user.id] = current_time # Updates cooldown await interaction.response.send_message("Sent out request for sub.") @tasks.loop(seconds=30) async def list_task(self): """Continually display the list of confirmed players for a mogi in the history channel""" - if len(self.ongoing_events) > 0: - for mogi in self.ongoing_events.values(): - if not mogi.gathering: - await self.delete_list_messages(0) - return + mogi = self.ongoing_event + if mogi is not None: + if not mogi.gathering: + await self.delete_list_messages(0) + return - mogi_list = mogi.confirmed_list() - - # Remove late players from the list to display separately - full_list_length = len(mogi_list) - num_of_rooms = full_list_length // 12 - num_confirmed_players = num_of_rooms * 12 - num_late_players = full_list_length - num_confirmed_players - late_players = [] - for i in range(num_confirmed_players, full_list_length): - player = mogi_list.pop() - late_players.append(player) - - sorted_mogi_list = sorted(mogi_list, reverse=True) - msg = f"**Last Updated:** {discord.utils.format_dt(datetime.now(timezone.utc), style='R')}\n\n" - msg += "**Current Mogi List:**\n" - for i in range(len(sorted_mogi_list)): - msg += f"{i+1}) " - msg += ", ".join([p.lounge_name for p in sorted_mogi_list[i].players]) - msg += f" ({sorted_mogi_list[i].players[0].mmr} MMR)\n" - if ((i + 1) % 12 == 0): - msg += "ㅤ\n" - msg += "**Late Players:**\n" - for i in range(len(late_players)): - msg += f"{i+1}) " - msg += ", ".join([p.lounge_name for p in late_players[i].players]) - msg += f" ({late_players[i].players[0].mmr} MMR)\n" - message = msg.split("\n") - - new_messages = [] - bulk_msg = "" - for i in range(len(message)): - if len(bulk_msg + message[i] + "\n") > 2000: - new_messages.append(bulk_msg) - bulk_msg = "" - bulk_msg += message[i] + "\n" - if len(bulk_msg) > 0 and bulk_msg != "\n": + mogi_list = mogi.confirmed_list() + + # Remove late players from the list to display separately + full_list_length = len(mogi_list) + num_of_rooms = full_list_length // 12 + num_confirmed_players = num_of_rooms * 12 + num_late_players = full_list_length - num_confirmed_players + late_players = [] + for i in range(num_confirmed_players, full_list_length): + player = mogi_list.pop() + late_players.append(player) + + sorted_mogi_list = sorted(mogi_list, reverse=True) + msg = f"**Last Updated:** {discord.utils.format_dt(datetime.now(timezone.utc), style='R')}\n\n" + msg += "**Current Mogi List:**\n\n" + msg += f"**Queue closing: {discord.utils.format_dt(mogi.start_time)}**\n" + for i in range(len(sorted_mogi_list)): + msg += f"{i + 1}) " + msg += ", ".join([p.lounge_name for p in sorted_mogi_list[i].players]) + msg += f" ({sorted_mogi_list[i].players[0].mmr} MMR)\n" + if ((i + 1) % 12 == 0): + msg += "ㅤ\n" + msg += "**Late Players:**\n" + for i in range(len(late_players)): + msg += f"{i + 1}) " + msg += ", ".join([p.lounge_name for p in late_players[i].players]) + msg += f" ({late_players[i].players[0].mmr} MMR)\n" + message = msg.split("\n") + + new_messages = [] + bulk_msg = "" + for i in range(len(message)): + if len(bulk_msg + message[i] + "\n") > 2000: new_messages.append(bulk_msg) + bulk_msg = "" + bulk_msg += message[i] + "\n" + if len(bulk_msg) > 0 and bulk_msg != "\n": + new_messages.append(bulk_msg) - await self.delete_list_messages(len(new_messages)) + await self.delete_list_messages(len(new_messages)) - try: - for i, message in enumerate(new_messages): - if i < len(self.list_messages): - old_message = self.list_messages[i] - await old_message.edit(content=message) - else: - new_message = await self.LIST_CHANNEL.send(message) - self.list_messages.append(new_message) - except: - await self.delete_list_messages(0) - for i, message in enumerate(new_messages): + try: + for i, message in enumerate(new_messages): + if i < len(self.list_messages): + old_message = self.list_messages[i] + await old_message.edit(content=message) + else: new_message = await self.LIST_CHANNEL.send(message) self.list_messages.append(new_message) + except: + await self.delete_list_messages(0) + for i, message in enumerate(new_messages): + new_message = await self.LIST_CHANNEL.send(message) + self.list_messages.append(new_message) else: await self.delete_list_messages(0) @@ -393,24 +403,21 @@ async def delete_list_messages(self, new_list_size: int): async def on_message(self, message: discord.Message): if message.author.bot or not (message.content.isdecimal() and 12 <= int(message.content) <= 180): return - mogi = discord.utils.find(lambda mogi: mogi.is_room_thread( - message.channel.id), self.ongoing_events.values()) - if not mogi: - mogi = discord.utils.find(lambda mogi: mogi.is_room_thread( - message.channel.id), self.old_events.values()) - if not mogi: - return - room = discord.utils.find( - lambda room: room.thread.id == message.channel.id, mogi.rooms) - if not room or not room.teams: + mogi = None + if self.ongoing_event is not None: + mogi = self.ongoing_event if self.ongoing_event.is_room_thread(message.channel.id) else None + if mogi is None: + mogi = discord.utils.find(lambda m: m.is_room_thread(message.channel.id), self.old_events) + if mogi is None: + return + room = discord.utils.find(lambda r: r.thread.id == message.channel.id, mogi.rooms) + if room is None or not room.teams: return - team = discord.utils.find( - lambda team: team.has_player(message.author), room.teams) - if not team: + team = discord.utils.find(lambda t: t.has_player(message.author), room.teams) + if team is None: return - player = discord.utils.find( - lambda player: player.member.id == message.author.id, team.players) - if player: + player = discord.utils.find(lambda p: p.member.id == message.author.id, team.players) + if player is not None: player.score = int(message.content) @app_commands.command(name="remove_player") @@ -440,8 +447,8 @@ async def remove_player(self, interaction: discord.Interaction, member: discord. @app_commands.guild_only() async def annul_current_mogi(self, interaction: discord.Interaction): """The mogi currently gathering will be deleted. The queue resumes at the next hour. Staff use only.""" - self.scheduled_events = {} - self.ongoing_events = {} + self.next_event = None + self.ongoing_event = None await self.lockdown(self.MOGI_CHANNEL) await interaction.response.send_message("The current Mogi has been canceled, the queue will resume at the next hour.") @@ -449,17 +456,14 @@ async def annul_current_mogi(self, interaction: discord.Interaction): @app_commands.guild_only() async def pause_mogi_scheduling(self, interaction: discord.Interaction): """The mogi that is currently gathering will continue to work. Future mogis cannot be scheduled. Staff use only.""" - curr_time = datetime.now(timezone.utc) - self.QUEUE_TIME_BLOCKER = curr_time + timedelta(weeks=52) + self.LAUNCH_NEW_EVENTS = False await interaction.response.send_message("Future Mogis will not be started.") @app_commands.command(name="resume_mogi_scheduling") @app_commands.guild_only() async def resume_mogi_scheduling(self, interaction: discord.Interaction): """Mogis will begin to be scheduled again. Staff use only.""" - curr_time = datetime.now(timezone.utc) - self.QUEUE_TIME_BLOCKER = curr_time - self.prev_start_time = None + self.LAUNCH_NEW_EVENTS = True await interaction.response.send_message("Mogis will resume scheduling.") @app_commands.command(name="change_event_time") @@ -468,8 +472,9 @@ async def change_event_time(self, interaction: discord.Interaction, event_time: """Change the amount of time for each event in the queue.""" if event_time > 15: self.QUEUE_OPEN_TIME = timedelta(minutes=event_time) - self.JOINING_TIME = timedelta(minutes=event_time - 5) - await interaction.response.send_message(f"The amount of time for each mogi has been changed to {event_time} minutes.") + self.JOINING_TIME = timedelta(minutes=event_time) - self.EXTENSION_TIME + await interaction.response.send_message( + f"The amount of time for each mogi has been changed to {event_time} minutes.") else: await interaction.response.send_message("Please enter a number of minutes greater than 15.") @@ -485,7 +490,7 @@ async def change_mmr_threshold(self, interaction: discord.Interaction, mmr_thres async def peek_bot_config(self, interaction: discord.Interaction): """View the configured values for the Queue System.""" msg = "" - msg += f"Time for Each Event: {self.QUEUE_OPEN_TIME}, \n" + msg += f"Time between each event: {self.QUEUE_OPEN_TIME}, \n" msg += f"Room MMR Threshold: {self.room_mmr_threshold}" await interaction.response.send_message(msg) @@ -493,11 +498,10 @@ async def peek_bot_config(self, interaction: discord.Interaction): @app_commands.guild_only() async def reset_bot(self, interaction: discord.Interaction): """Resets the bot. Staff use only.""" - self.scheduled_events = {} - self.ongoing_events = {} - self.old_events = {} - curr_time = datetime.now(timezone.utc) - self.QUEUE_TIME_BLOCKER = curr_time + self.next_event = None + self.ongoing_event = None + self.old_events = [] + self.LAUNCH_NEW_EVENTS = True await interaction.response.send_message("All events have been deleted. Queue will restart shortly.") @commands.command(name="schedule_sq_times") @@ -506,6 +510,9 @@ async def schedule_sq_times(self, ctx, timestamps: commands.Greedy[int]): """Saves a list of sq times to skip over. Input a list of unix utc timestamps. Staff use only.""" if not await self.has_roles(ctx.author, ctx.guild.id, ctx.bot.config): return + await self.queue_or_send(ctx, + "This command has been disabled as it's unclear how the new changes might affect this system.") + return msg = "List of new Dates:\n" curr_time = datetime.now(timezone.utc) @@ -566,9 +573,9 @@ async def get_ladder_info(self): msg = "" try: async with aiohttp.ClientSession( - timeout = timeout, - auth=aiohttp.BasicAuth( - "username", "password")) as session: + timeout=timeout, + auth=aiohttp.BasicAuth( + "username", "password")) as session: async with session.get(url, headers=headers) as resp: if resp.status != 200: raise Exception( @@ -604,38 +611,34 @@ async def has_roles(self, member: discord.Member, guild_id: int, config): return True return False - async def end_voting(self): + @staticmethod + async def end_voting(mogi: Mogi): """Ends voting in all rooms with ongoing votes.""" try: - index = 0 - for mogi in self.ongoing_events.values(): - index += 1 - for room in mogi.rooms: + if mogi is not None: + for index, room in enumerate(mogi.rooms, 1): if not room or not room.view: - print( - f"Skipping room {index} in function end_voting.", flush=True) + print(f"Skipping room {index} in function end_voting.", flush=True) continue await room.view.find_winner() except Exception as e: print(traceback.format_exc()) - async def write_history(self): + @staticmethod + async def write_history(mogi: Mogi, history_channel: discord.TextChannel): """Writes the teams, tier and average of each room per hour.""" try: - index = 0 - for mogi in self.ongoing_events.values(): - index += 1 - await self.HISTORY_CHANNEL.send(f"{discord.utils.format_dt(mogi.start_time)} Rooms") - for room in mogi.rooms: + if mogi is not None: + await history_channel.send(f"{discord.utils.format_dt(mogi.start_time)} Rooms") + for index, room in enumerate(mogi.rooms, 1): if not room or not room.view: - print( - f"Skipping room {index} in function write_history.", flush=True) + print(f"Skipping room {index} in function write_history.", flush=True) continue msg = room.view.header_text msg += f"{room.thread.jump_url}\n" msg += room.view.teams_text msg += "ㅤ" - await self.HISTORY_CHANNEL.send(msg) + await history_channel.send(msg) except Exception as e: print(traceback.format_exc()) @@ -654,8 +657,8 @@ async def check_room_channels(self, mogi): room_name = f"{mogi.start_time.month}/{mogi.start_time.day}, {mogi.start_time.hour}:{minute}:00 - Room {i+1}" try: room_channel = await self.GENERAL_CHANNEL.create_thread(name=room_name, - auto_archive_duration=60, - invitable=False) + auto_archive_duration=60, + invitable=False) except Exception as e: print(traceback.format_exc()) err_msg = f"\nAn error has occurred while creating a room channel:\n{e}" @@ -663,6 +666,12 @@ async def check_room_channels(self, mogi): return mogi.rooms.append(Room(None, i+1, room_channel)) + @staticmethod + async def handle_voting_and_history(mogi: Mogi, history_channel: discord.TextChannel): + await asyncio.sleep(120) + await SquadQueue.end_voting(mogi) + await SquadQueue.write_history(mogi, history_channel) + # add teams to the room threads that we have already created async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=False): if open_time >= 60 or open_time < 0: @@ -672,9 +681,8 @@ async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=F return num_rooms = int(mogi.count_registered() / (12/mogi.size)) if num_rooms == 0: + self.ongoing_event = None await mogi.mogi_channel.send(f"Not enough players to fill a single room! This mogi will be cancelled.") - self.scheduled_events = {} - self.ongoing_events = {} return await self.lockdown(mogi.mogi_channel) mogi.making_rooms_run = True @@ -760,44 +768,46 @@ async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=F await mogi.mogi_channel.send(msg) except Exception as e: print("Late Player message has failed to send.", flush=True) - await asyncio.sleep(120) - await self.end_voting() - await self.write_history() print(traceback.format_exc()) - async def check_num_teams(self, mogi): + asyncio.create_task(SquadQueue.handle_voting_and_history(self.ongoing_event, self.HISTORY_CHANNEL)) + self.old_events.append(self.ongoing_event) + self.ongoing_event = None + + async def check_num_teams(self, mogi: Mogi): if not mogi.gathering or not mogi.is_automated: return cur_time = datetime.now(timezone.utc) - if mogi.start_time - self.QUEUE_OPEN_TIME + self.JOINING_TIME <= cur_time: - numLeftoverTeams = mogi.count_registered() % int((12/mogi.size)) - if numLeftoverTeams == 0: + if mogi.start_time <= cur_time: + num_leftover_teams = mogi.count_registered() % int((12 / mogi.size)) + if num_leftover_teams == 0: mogi.gathering = False await self.lockdown(mogi.mogi_channel) - await mogi.mogi_channel.send("A sufficient amount of players has been reached, so the mogi has been closed to extra players. Rooms will be made within the next minute.") + await mogi.mogi_channel.send( + "A sufficient amount of players has been reached, so the mogi has been closed to extra players. Rooms will be made within the next minute.") async def ongoing_mogi_checks(self): - for mogi in self.ongoing_events.values(): + mogi = self.ongoing_event + if mogi is not None: # If it's not automated, not started, we've already started making the rooms, don't run this async with self.LOCK: if not mogi.is_automated or not mogi.started or mogi.making_rooms_run: return cur_time = datetime.now(timezone.utc) - if (mogi.start_time - self.QUEUE_OPEN_TIME + self.JOINING_TIME + self.EXTENSION_TIME) <= cur_time: + if (mogi.start_time + self.EXTENSION_TIME) <= cur_time: mogi.gathering = False - if mogi.start_time - self.QUEUE_OPEN_TIME + self.JOINING_TIME <= cur_time and mogi.gathering: + elif mogi.start_time <= cur_time and mogi.gathering: # check if there are an even amount of teams since we are past the queue time - numLeftoverTeams = mogi.count_registered() % int((12/mogi.size)) - if numLeftoverTeams == 0: + num_leftover_teams = mogi.count_registered() % int((12 / mogi.size)) + if num_leftover_teams == 0: mogi.gathering = False else: if int(cur_time.second / 20) == 0: - force_time = mogi.start_time - self.QUEUE_OPEN_TIME + \ - self.JOINING_TIME + self.EXTENSION_TIME - minutes_left = int( - (force_time - cur_time).seconds/60) - x_teams = int(int(12/mogi.size) - numLeftoverTeams) - await mogi.mogi_channel.send(f"Need {x_teams} more player(s) to start immediately. Starting in {minutes_left + 1} minute(s) regardless.") + force_time = mogi.start_time + self.EXTENSION_TIME + minutes_left = (force_time - cur_time).seconds // 60 + x_teams = int(int(12 / mogi.size) - num_leftover_teams) + await mogi.mogi_channel.send( + f"Need {x_teams} more player(s) to start immediately. Starting in {minutes_left + 1} minute(s) regardless.") if not mogi.gathering: await self.delete_list_messages(0) await mogi.mogi_channel.send("Mogi is now closed; players can no longer join or drop from the event") @@ -805,26 +815,25 @@ async def ongoing_mogi_checks(self): async def scheduler_mogi_start(self): cur_time = datetime.now(timezone.utc) - for guild in self.scheduled_events.values(): - to_remove = [] # Keep a list of indexes to remove - can't remove while iterating - for i, mogi in enumerate(guild): - if (mogi.start_time - self.QUEUE_OPEN_TIME) < cur_time: - if mogi.mogi_channel in self.ongoing_events.keys() and self.ongoing_events[mogi.mogi_channel].gathering: - to_remove.append(i) - await mogi.mogi_channel.send(f"Because there is an ongoing event right now, the following event has been removed:\n{self.get_event_str(mogi)}\n") - else: - if mogi.mogi_channel in self.ongoing_events.keys(): - if self.ongoing_events[mogi.mogi_channel].started: - self.old_events[mogi.mogi_channel] = self.ongoing_events[mogi.mogi_channel] - del self.ongoing_events[mogi.mogi_channel] - to_remove.append(i) - mogi.started = True - mogi.gathering = True - self.ongoing_events[mogi.mogi_channel] = mogi - await self.unlockdown(mogi.mogi_channel) - await mogi.mogi_channel.send(f"A queue is gathering for the mogi {discord.utils.format_dt(mogi.start_time, style='R')} - Type `/c` to join, and `/d` to drop.") - for ind in reversed(to_remove): - del guild[ind] + next_mogi = self.next_event + if next_mogi is not None and (next_mogi.start_time - self.JOINING_TIME) < cur_time: + self.next_event = None # We are trying to launch the event - fail or not, we set next event to None + # There is an ongoing event, but people are still queueing, so remove it and fail + if self.ongoing_event is not None and self.ongoing_event.gathering: + await next_mogi.mogi_channel.send( + f"Because there is an ongoing event right now, the following event has been removed:\n{self.get_event_str(next_mogi)}\n") + return + # There is an ongoing event, but it has already started, so add it to the old events + if self.ongoing_event is not None and self.ongoing_event.started: # This is potentially an issue - we should be checking if make rooms has been run, not if it's started...? + self.old_events.append(self.ongoing_event) + + # Put the next mogi as the current event and launch it + self.ongoing_event = next_mogi + self.ongoing_event.started = True + self.ongoing_event.gathering = True + await self.unlockdown(self.ongoing_event.mogi_channel) + await self.ongoing_event.mogi_channel.send( + f"A queue is gathering for the mogi {discord.utils.format_dt(self.ongoing_event.start_time, style='R')} - Type `/c` to join, and `/d` to drop.") @tasks.loop(seconds=20.0) async def sqscheduler(self): @@ -846,42 +855,34 @@ async def sqscheduler(self): except Exception as e: print(traceback.format_exc()) + def compute_next_event_open_time(self): + cur_time = datetime.now(timezone.utc) + time_elapsed = cur_time - self.FIRST_EVENT_TIME + num_launched_events = time_elapsed // self.QUEUE_OPEN_TIME + next_event_open_time = self.FIRST_EVENT_TIME + (self.QUEUE_OPEN_TIME * num_launched_events) + return next_event_open_time async def schedule_que_event(self): - """Schedules queue for the next hour in the given channel.""" - + """Schedules next queue in the SquadQueue mogi queueing channel.""" if self.GUILD is not None: - curr_time = datetime.now(timezone.utc) - start_time = curr_time + timedelta(minutes=65) - start_time = start_time.replace(minute=0, second=0, microsecond=0) - start_time -= self.QUEUE_OPEN_TIME - if self.prev_start_time: - start_time = self.prev_start_time - next_mogi_start = start_time + self.QUEUE_OPEN_TIME - if len(self.sq_times) > 0 and next_mogi_start == self.sq_times[0]: - self.sq_times.pop(0) - self.QUEUE_TIME_BLOCKER = next_mogi_start - await self.MOGI_CHANNEL.send("Squad Queue is currently going on at this hour! The queue will remain closed.") - if curr_time < self.QUEUE_TIME_BLOCKER: - # print(f"Mogi had been blocked from starting before the time limit {self.QUEUE_TIME_BLOCKER}", flush=True) + if not self.LAUNCH_NEW_EVENTS: + # (f"Not allowed to launch new events.", flush=True) return - for mogi in self.ongoing_events.values(): - if mogi.start_time == next_mogi_start: - return - - mogi = Mogi(1, 1, self.MOGI_CHANNEL, is_automated=True, - start_time=next_mogi_start) - - if self.GUILD not in self.scheduled_events.keys(): - self.scheduled_events[self.GUILD] = [] - - self.scheduled_events[self.GUILD].append(mogi) - - self.QUEUE_TIME_BLOCKER = next_mogi_start + next_event_open_time = self.compute_next_event_open_time() + next_event_start_time = next_event_open_time + self.JOINING_TIME + print(f"Next event open time: {next_event_open_time}", flush=True) + # We don't want to schedule the next event if it would open after it's joining period and during its extension period + if next_event_start_time < datetime.now(timezone.utc): + return + if len(self.sq_times) > 0 and next_event_open_time == self.sq_times[0]: + self.sq_times.pop(0) + await self.MOGI_CHANNEL.send( + "Squad Queue is currently going on at this hour! The queue will remain closed.") - self.prev_start_time = next_mogi_start + self.next_event = Mogi(1, 1, self.MOGI_CHANNEL, is_automated=True, + start_time=next_event_start_time) - print(f"Started Queue for {next_mogi_start}", flush=True) + print(f"Started Queue for {next_event_start_time}", flush=True) @tasks.loop(minutes=1) async def delete_old_mogis(self): @@ -889,14 +890,10 @@ async def delete_old_mogis(self): try: curr_time = datetime.now(timezone.utc) mogi_lifetime = timedelta(minutes=self.MOGI_LIFETIME) - delete_queue = [] - for mogi in self.old_events.values(): - if curr_time - mogi_lifetime > mogi.start_time: - delete_queue.append(mogi) + delete_queue = [m for m in self.old_events if (curr_time - mogi_lifetime) > m.start_time] for mogi in delete_queue: - print( - f"Deleting {mogi.start_time} Mogi at {curr_time}", flush=True) - del self.old_events[mogi.mogi_channel] + print(f"Deleting {mogi.start_time} Mogi at {curr_time}", flush=True) + self.old_events.remove(mogi) except Exception as e: print(traceback.format_exc()) @@ -970,15 +967,13 @@ async def debug_add_many_players(self, ctx, members: commands.Greedy[discord.Mem @commands.command(name="debug_start_rooms") @commands.is_owner() async def debug_start_rooms(self, ctx): - for mogi in self.ongoing_events.values(): - if mogi.start_time == self.prev_start_time: - await self.add_teams_to_rooms(mogi, (mogi.start_time.minute) % 60, True) - return - for mogi in self.old_events.values(): - if mogi.start_time == self.prev_start_time: - await self.add_teams_to_rooms(mogi, (mogi.start_time.minute) % 60, True) - return - + cur_mogi = self.ongoing_event + if cur_mogi is not None: + await self.add_teams_to_rooms(cur_mogi, (cur_mogi.start_time.minute) % 60, True) + return + for old_mogi in self.old_events: + await self.add_teams_to_rooms(old_mogi, (old_mogi.start_time.minute) % 60, True) + return async def setup(bot): From d72518b6841088a5c3eb6aaaaa825e4fa0e9d3e4 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 03:42:38 -0500 Subject: [PATCH 041/147] Minor refactor --- cogs/SquadQueue.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index b00508a..4f67e37 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -188,14 +188,12 @@ def is_staff(self, member: discord.Member): async def is_started(self, ctx, mogi: Mogi): if not mogi.started: await ctx.send("Mogi has not been started yet... type !start") - return False - return True + return mogi.started async def is_gathering(self, ctx, mogi: Mogi): if not mogi.gathering: await ctx.send("Mogi is closed; players cannot join or drop from the event") - return False - return True + return mogi.gathering @app_commands.command(name="c") @app_commands.guild_only() From e625337be5cb1d9405604a9e49146367ae0446bd Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 18:26:35 -0500 Subject: [PATCH 042/147] Add comments --- cogs/SquadQueue.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 4f67e37..a69882a 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -666,7 +666,7 @@ async def check_room_channels(self, mogi): @staticmethod async def handle_voting_and_history(mogi: Mogi, history_channel: discord.TextChannel): - await asyncio.sleep(120) + await asyncio.sleep(120) # We could have used asyncio.call_later(120, handle_voting_and_history) in the caller's code await SquadQueue.end_voting(mogi) await SquadQueue.write_history(mogi, history_channel) @@ -767,7 +767,9 @@ async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=F except Exception as e: print("Late Player message has failed to send.", flush=True) print(traceback.format_exc()) - + + # We could have used asyncio.call_later(120, handle_voting_and_history) + # and removed asyncio.sleep(120) in handle_voting_and_history asyncio.create_task(SquadQueue.handle_voting_and_history(self.ongoing_event, self.HISTORY_CHANNEL)) self.old_events.append(self.ongoing_event) self.ongoing_event = None From e8c1fa5522b81adb5b89c99135485c7099c8c747 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 19:53:33 -0500 Subject: [PATCH 043/147] Remove irrelevant code Variables were not used --- cogs/SquadQueue.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 4f67e37..1ccf819 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -688,12 +688,6 @@ async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=F mogi.gathering = False await mogi.mogi_channel.send("Mogi is now closed; players can no longer join or drop from the event") - pen_time = open_time + 5 - start_time = open_time + 10 - while pen_time >= 60: - pen_time -= 60 - while start_time >= 60: - start_time -= 60 teams_per_room = int(12/mogi.size) num_teams = int(num_rooms * teams_per_room) final_list = mogi.confirmed_list()[0:num_teams] From 428af11c2cecba9452c7388b733a632d349930f6 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 23:09:53 -0500 Subject: [PATCH 044/147] Mogi.size -> Mogi.players_per_team + add parameter to Mogi that keeps track of teams per room --- cogs/SquadQueue.py | 16 ++++++++++------ mogi_objects.py | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 1ccf819..7c91b70 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -644,7 +644,7 @@ async def write_history(mogi: Mogi, history_channel: discord.TextChannel): # since discord only allows 50 thread channels to be created per 5 minutes. async def check_room_channels(self, mogi): num_teams = mogi.count_registered() - num_rooms = int(num_teams / (12/mogi.size)) + num_rooms = int(num_teams / (12 / mogi.players_per_team)) num_created_rooms = len(mogi.rooms) if num_created_rooms >= num_rooms: return @@ -677,7 +677,7 @@ async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=F return if mogi.making_rooms_run and started_automatically: return - num_rooms = int(mogi.count_registered() / (12/mogi.size)) + num_rooms = int(mogi.count_registered() / (12 / mogi.players_per_team)) if num_rooms == 0: self.ongoing_event = None await mogi.mogi_channel.send(f"Not enough players to fill a single room! This mogi will be cancelled.") @@ -688,7 +688,7 @@ async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=F mogi.gathering = False await mogi.mogi_channel.send("Mogi is now closed; players can no longer join or drop from the event") - teams_per_room = int(12/mogi.size) + teams_per_room = int(12 / mogi.players_per_team) num_teams = int(num_rooms * teams_per_room) final_list = mogi.confirmed_list()[0:num_teams] sorted_list = sorted(final_list, reverse=True) @@ -790,14 +790,14 @@ async def ongoing_mogi_checks(self): mogi.gathering = False elif mogi.start_time <= cur_time and mogi.gathering: # check if there are an even amount of teams since we are past the queue time - num_leftover_teams = mogi.count_registered() % int((12 / mogi.size)) + num_leftover_teams = mogi.count_registered() % int((12 / mogi.players_per_team)) if num_leftover_teams == 0: mogi.gathering = False else: if int(cur_time.second / 20) == 0: force_time = mogi.start_time + self.EXTENSION_TIME minutes_left = (force_time - cur_time).seconds // 60 - x_teams = int(int(12 / mogi.size) - num_leftover_teams) + x_teams = int(int(12 / mogi.players_per_team) - num_leftover_teams) await mogi.mogi_channel.send( f"Need {x_teams} more player(s) to start immediately. Starting in {minutes_left + 1} minute(s) regardless.") if not mogi.gathering: @@ -871,7 +871,11 @@ async def schedule_que_event(self): await self.MOGI_CHANNEL.send( "Squad Queue is currently going on at this hour! The queue will remain closed.") - self.next_event = Mogi(1, 1, self.MOGI_CHANNEL, is_automated=True, + self.next_event = Mogi(sq_id=1, + players_per_team=1, + teams_per_room=12, + mogi_channel=self.MOGI_CHANNEL, + is_automated=True, start_time=next_event_start_time) print(f"Started Queue for {next_event_start_time}", flush=True) diff --git a/mogi_objects.py b/mogi_objects.py index 3cdb3d5..b17593a 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -7,14 +7,14 @@ class Mogi: - def __init__(self, sq_id: int, size: int, mogi_channel: discord.TextChannel, + def __init__(self, sq_id: int, players_per_team: int, teams_per_room: int, mogi_channel: discord.TextChannel, is_automated=False, start_time=None): self.started = False self.gathering = False self.making_rooms_run = False self.making_rooms_run_time = None self.sq_id = sq_id - self.size = size + self.players_per_team = players_per_team self.mogi_channel = mogi_channel self.teams: List[Team] = [] self.rooms: List[Room] = [] From 31ea0919f49cce59a98b1378acd3acd1699cae40 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 23:10:09 -0500 Subject: [PATCH 045/147] See last commit --- cogs/SquadQueue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 7c91b70..227c44f 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -771,7 +771,7 @@ async def check_num_teams(self, mogi: Mogi): return cur_time = datetime.now(timezone.utc) if mogi.start_time <= cur_time: - num_leftover_teams = mogi.count_registered() % int((12 / mogi.size)) + num_leftover_teams = mogi.count_registered() % int((12 / mogi.players_per_team)) if num_leftover_teams == 0: mogi.gathering = False await self.lockdown(mogi.mogi_channel) From 919d5f18f285e2e7463fffb0b65cadd600f6b2cf Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 23:10:23 -0500 Subject: [PATCH 046/147] See last commit --- mogi_objects.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mogi_objects.py b/mogi_objects.py index b17593a..2e00158 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -15,6 +15,7 @@ def __init__(self, sq_id: int, players_per_team: int, teams_per_room: int, mogi_ self.making_rooms_run_time = None self.sq_id = sq_id self.players_per_team = players_per_team + self.teams_per_room = teams_per_room self.mogi_channel = mogi_channel self.teams: List[Team] = [] self.rooms: List[Room] = [] From 2c704d343885628c5653d501e9ef69647320c1bf Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 23:11:53 -0500 Subject: [PATCH 047/147] Minor refactor for readability --- cogs/SquadQueue.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 227c44f..76d269b 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -213,23 +213,21 @@ async def can(self, interaction: discord.Interaction): await interaction.followup.send(f"{interaction.user.mention} is already signed up.") return - players = [] msg = "" placement_role_id = 723753340063842345 if member.get_role(placement_role_id): starting_player_mmr = 750 - player = Player(member, member.display_name, starting_player_mmr) - players.append(player) + players = [Player(member, member.display_name, starting_player_mmr)] msg += f"{players[0].lounge_name} is assumed to be a new player and will be playing this mogi with a starting MMR of {starting_player_mmr}. " msg += "If you believe this is a mistake, please contact a staff member for help.\n" else: players = await mkw_mmr(self.URL, [member], self.TRACK_TYPE) - if len(players) == 0 or players[0] is None: - msg = f"{interaction.user.mention} fetch for MMR has failed and joining the queue was unsuccessful. " - msg += "Please try again. If the problem continues then contact a staff member for help." - await interaction.followup.send(msg) - return + if len(players) == 0 or players[0] is None: + msg = f"{interaction.user.mention} fetch for MMR has failed and joining the queue was unsuccessful. " + msg += "Please try again. If the problem continues then contact a staff member for help." + await interaction.followup.send(msg) + return players[0].confirmed = True squad = Team(players) From 64b204bc6d0f3a17caaf8617344354a8fa54abde Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 23:12:05 -0500 Subject: [PATCH 048/147] Minor refactor for readability --- cogs/SquadQueue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 76d269b..f08b30d 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -895,7 +895,7 @@ def get_event_str(self, mogi: Mogi): mogi_time = discord.utils.format_dt(mogi.start_time, style="F") mogi_time_relative = discord.utils.format_dt( mogi.start_time, style="R") - return (f"`#{mogi.sq_id}` **{mogi.size}v{mogi.size}:** {mogi_time} - {mogi_time_relative}") + return f"`#{mogi.sq_id}` **{mogi.players_per_team}v{mogi.players_per_team}:** {mogi_time} - {mogi_time_relative}" @commands.command(name="sync") @commands.is_owner() From cd3499e0b444b56b678b0761e2bf22ec6556c299 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 23:12:22 -0500 Subject: [PATCH 049/147] Type hint minor refactor --- cogs/SquadQueue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index f08b30d..7362a97 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -669,7 +669,7 @@ async def handle_voting_and_history(mogi: Mogi, history_channel: discord.TextCha await SquadQueue.write_history(mogi, history_channel) # add teams to the room threads that we have already created - async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=False): + async def add_teams_to_rooms(self, mogi: Mogi, open_time: int, started_automatically=False): if open_time >= 60 or open_time < 0: await mogi.mogi_channel.send("Please specify a valid time (in minutes) for rooms to open (00-59)") return From 78551e3f7a46d9e050e4fad18b969ea56a4dbfbf Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 23:12:47 -0500 Subject: [PATCH 050/147] Added common properties to Mogi --- mogi_objects.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/mogi_objects.py b/mogi_objects.py index 2e00158..606fa53 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -22,6 +22,21 @@ def __init__(self, sq_id: int, players_per_team: int, teams_per_room: int, mogi_ self.is_automated = is_automated self.start_time = start_time if is_automated else None + @property + def num_players(self): + """Returns the total number of players in teams where all players have confirmed""" + return self.num_teams * self.players_per_team + + @property + def num_teams(self): + """Returns the total number of teams where all players have confirmed""" + return self.count_registered() + + @property + def num_rooms(self): + """Returns the number of rooms based on teams where all players have confirmed""" + return self.num_teams // self.teams_per_room + def check_player(self, member): for team in self.teams: if team.has_player(member): From b0bce80ab6317c768263bdec7e2860beea39f085 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 23:13:05 -0500 Subject: [PATCH 051/147] Added avg_mmr property to Team --- mogi_objects.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index 606fa53..a8bb3b0 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -89,10 +89,10 @@ def get_player_list(self): class Team: def __init__(self, players): self.players = players - self.avg_mmr = sum([p.mmr for p in self.players]) / len(self.players) - def recalc_avg(self): - self.avg_mmr = sum([p.mmr for p in self.players]) / len(self.players) + @property + def avg_mmr(self): + return sum([p.mmr for p in self.players]) / len(self.players) def is_registered(self): """Returns if all players on the team are registered""" @@ -114,7 +114,6 @@ def sub_player(self, sub_out, sub_in): for i, player in enumerate(self.players): if player == sub_out: self.players[i] = sub_in - self.recalc_avg() return def num_confirmed(self): From bdc430d77a19117dda87ff1766fb48d45c0d6028 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 23:13:37 -0500 Subject: [PATCH 052/147] int * int results in int - no need to cast --- cogs/SquadQueue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 7362a97..cec86d9 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -707,7 +707,7 @@ async def add_teams_to_rooms(self, mogi: Mogi, open_time: int, started_automatic for i in range(num_rooms): msg = f"`Room {i+1} - Player List`\n" mentions = "" - start_index = int(i*teams_per_room) + start_index = i*teams_per_room player_list = [] for j in range(teams_per_room): msg += f"`{j+1}.` " From 32d3e9dd785118464eb1ea336a7821a8b5bba15c Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 23:19:22 -0500 Subject: [PATCH 053/147] Addresses race condition Someone could queue or drop during execution of lockdown call, and, after it was finished, we might not actually have enough rooms, throwing the rest of code after into question. --- cogs/SquadQueue.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index cec86d9..ae95c6e 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -680,11 +680,12 @@ async def add_teams_to_rooms(self, mogi: Mogi, open_time: int, started_automatic self.ongoing_event = None await mogi.mogi_channel.send(f"Not enough players to fill a single room! This mogi will be cancelled.") return - await self.lockdown(mogi.mogi_channel) + mogi.making_rooms_run = True if mogi.gathering: mogi.gathering = False await mogi.mogi_channel.send("Mogi is now closed; players can no longer join or drop from the event") + await self.lockdown(mogi.mogi_channel) teams_per_room = int(12 / mogi.players_per_team) num_teams = int(num_rooms * teams_per_room) From 20a60be7938ebf06f2fb1183353d3a184f376fc4 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 22 Jun 2024 23:22:29 -0500 Subject: [PATCH 054/147] Modification of last commit to make lockdown run before message --- cogs/SquadQueue.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index ae95c6e..92622f1 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -681,11 +681,14 @@ async def add_teams_to_rooms(self, mogi: Mogi, open_time: int, started_automatic await mogi.mogi_channel.send(f"Not enough players to fill a single room! This mogi will be cancelled.") return + was_gathering = mogi.gathering mogi.making_rooms_run = True - if mogi.gathering: - mogi.gathering = False - await mogi.mogi_channel.send("Mogi is now closed; players can no longer join or drop from the event") + mogi.gathering = False + await self.lockdown(mogi.mogi_channel) + if was_gathering: + await mogi.mogi_channel.send("Mogi is now closed; players can no longer join or drop from the event") + teams_per_room = int(12 / mogi.players_per_team) num_teams = int(num_rooms * teams_per_room) From 9badb9d648f609671772607a1a0033cfbcf08585 Mon Sep 17 00:00:00 2001 From: emilp-595 Date: Mon, 24 Jun 2024 17:36:26 -0700 Subject: [PATCH 055/147] Reformatted List Message Changed the position of the Queue Closing Time in the List Channel. --- cogs/SquadQueue.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index a69882a..efe1164 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -343,9 +343,9 @@ async def list_task(self): late_players.append(player) sorted_mogi_list = sorted(mogi_list, reverse=True) - msg = f"**Last Updated:** {discord.utils.format_dt(datetime.now(timezone.utc), style='R')}\n\n" + msg = f"**Queue closing: {discord.utils.format_dt(mogi.start_time)}**\n\n" + msg += f"**Last Updated:** {discord.utils.format_dt(datetime.now(timezone.utc), style='R')}\n\n" msg += "**Current Mogi List:**\n\n" - msg += f"**Queue closing: {discord.utils.format_dt(mogi.start_time)}**\n" for i in range(len(sorted_mogi_list)): msg += f"{i + 1}) " msg += ", ".join([p.lounge_name for p in sorted_mogi_list[i].players]) From a5e423fb415d2d0b838506c766fcbd3b21673b2c Mon Sep 17 00:00:00 2001 From: emilp-595 Date: Tue, 25 Jun 2024 10:12:11 -0700 Subject: [PATCH 056/147] Added missing argument in sub command --- cogs/SquadQueue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index efe1164..6dcecf5 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -316,7 +316,7 @@ async def sub(self, interaction: discord.Interaction): timezone.utc) + timedelta(seconds=self.SUB_MESSAGE_LIFETIME_SECONDS) msg += f"Message will auto-delete in {discord.utils.format_dt(message_delete_date, style='R')}" await self.SUB_CHANNEL.send(msg, delete_after=self.SUB_MESSAGE_LIFETIME_SECONDS) - view = JoinView(room, get_mmr_from_discord_id, bottom_room_num) + view = JoinView(room, lambda discord_id: get_mmr_from_discord_id(discord_id, self.TRACK_TYPE), bottom_room_num) await self.SUB_CHANNEL.send(view=view, delete_after=self.SUB_MESSAGE_LIFETIME_SECONDS) cooldowns[interaction.user.id] = current_time # Updates cooldown await interaction.response.send_message("Sent out request for sub.") From 925e1ae39e14a85bf76f1117c14560010291eaf4 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Tue, 25 Jun 2024 19:56:25 -0500 Subject: [PATCH 057/147] Add saving mmr threshold --- cogs/SquadQueue.py | 13 +++++++++++++ requirements.txt | 1 + 2 files changed, 14 insertions(+) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 6dcecf5..421ee38 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -14,12 +14,15 @@ from collections import defaultdict from typing import Dict, List import traceback +import os +import dill headers = {'Content-type': 'application/json'} # Scheduled_Event = collections.namedtuple('Scheduled_Event', 'size time started mogi_channel') cooldowns = defaultdict(int) +MMR_THRESHOLD_PKL = "mmr_threshold.pkl" class SquadQueue(commands.Cog): def __init__(self, bot): @@ -65,6 +68,11 @@ def __init__(self, bot): self.SUB_MESSAGE_LIFETIME_SECONDS = bot.config["SUB_MESSAGE_LIFETIME_SECONDS"] self.room_mmr_threshold = bot.config["ROOM_MMR_THRESHOLD"] + if os.path.isfile(MMR_THRESHOLD_PKL): + try: + self.room_mmr_threshold = dill.load('name_model.pkl') + except: + print(traceback.format_exc()) self.TRACK_TYPE = bot.config["track_type"] @@ -481,6 +489,11 @@ async def change_event_time(self, interaction: discord.Interaction, event_time: async def change_mmr_threshold(self, interaction: discord.Interaction, mmr_threshold: int): """Change the maximum MMR gap allowed for a room.""" self.room_mmr_threshold = mmr_threshold + try: + with open(MMR_THRESHOLD_PKL, 'wb') as f: + dill.dump(self.room_mmr_threshold, f) + except: + print(traceback.format_exc()) await interaction.response.send_message(f"MMR Threshold for Queue Rooms has been modified to {mmr_threshold} MMR.") @app_commands.command(name="peek_bot_config") diff --git a/requirements.txt b/requirements.txt index 5724417..bfbed65 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ aiohttp==3.8.4 discord.py==2.3.2 python-dateutil==2.8.2 +dill~=0.3.8 From 1eaafc81275c7f033163a5ed963b5081eb411568 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Tue, 25 Jun 2024 20:04:29 -0500 Subject: [PATCH 058/147] Bug fix --- cogs/SquadQueue.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 421ee38..1ecd171 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -70,7 +70,8 @@ def __init__(self, bot): self.room_mmr_threshold = bot.config["ROOM_MMR_THRESHOLD"] if os.path.isfile(MMR_THRESHOLD_PKL): try: - self.room_mmr_threshold = dill.load('name_model.pkl') + with open(MMR_THRESHOLD_PKL, 'rb') as f: + self.room_mmr_threshold = dill.load(f) except: print(traceback.format_exc()) From 65db6d8dd5c6167a9d58ff520f8fcc5803775077 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Tue, 25 Jun 2024 22:59:41 -0500 Subject: [PATCH 059/147] Update configs with pingable help roles --- ct_config.json | 5 +++++ prod_config.json | 5 +++++ test_config.json | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/ct_config.json b/ct_config.json index 3a777d7..7b2759f 100644 --- a/ct_config.json +++ b/ct_config.json @@ -39,6 +39,11 @@ 792891432301625364, 877629421811540016 ], + "helper_staff_roles": [ + 520808674411937792, + 792891432301625364, + 521149807994208295 + ], "queue_messages": true, "sec_between_queue_msgs": 2, "username": "username", diff --git a/prod_config.json b/prod_config.json index bf37127..23bc531 100644 --- a/prod_config.json +++ b/prod_config.json @@ -39,6 +39,11 @@ 792891432301625364, 877629421811540016 ], + "helper_staff_roles": [ + 389252697284542465, + 399384750923579392, + 399382503825211393 + ], "queue_messages": true, "sec_between_queue_msgs": 2, "username": "username", diff --git a/test_config.json b/test_config.json index 588af53..3239a78 100644 --- a/test_config.json +++ b/test_config.json @@ -38,6 +38,11 @@ 792891432301625364, 877629421811540016 ], + "helper_staff_roles": [ + 389252697284542465, + 399384750923579392, + 399382503825211393 + ], "queue_messages": true, "sec_between_queue_msgs": 2, "username": "username", From 64d045c95b98c92fefc618cee214399f93f8d75d Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Wed, 26 Jun 2024 00:09:44 -0500 Subject: [PATCH 060/147] Add muted and restricted roles to configs --- ct_config.json | 2 ++ prod_config.json | 2 ++ test_config.json | 2 ++ 3 files changed, 6 insertions(+) diff --git a/ct_config.json b/ct_config.json index 7b2759f..7c0c6ca 100644 --- a/ct_config.json +++ b/ct_config.json @@ -44,6 +44,8 @@ 792891432301625364, 521149807994208295 ], + "restricted_role_id": 797208908153618452, + "muted_role_id": 434887701662007296, "queue_messages": true, "sec_between_queue_msgs": 2, "username": "username", diff --git a/prod_config.json b/prod_config.json index 23bc531..3aa7fd3 100644 --- a/prod_config.json +++ b/prod_config.json @@ -44,6 +44,8 @@ 399384750923579392, 399382503825211393 ], + "restricted_role_id": 797208908153618452, + "muted_role_id": 434887701662007296, "queue_messages": true, "sec_between_queue_msgs": 2, "username": "username", diff --git a/test_config.json b/test_config.json index 3239a78..cce5ecd 100644 --- a/test_config.json +++ b/test_config.json @@ -43,6 +43,8 @@ 399384750923579392, 399382503825211393 ], + "restricted_role_id": 797208908153618452, + "muted_role_id": 434887701662007296, "queue_messages": true, "sec_between_queue_msgs": 2, "username": "username", From 084095ac28d0d26e3780e225b0f2a43286363979 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Wed, 26 Jun 2024 00:11:05 -0500 Subject: [PATCH 061/147] Added type hints --- cogs/SquadQueue.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 1ecd171..c65dee1 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -26,7 +26,7 @@ class SquadQueue(commands.Cog): def __init__(self, bot): - self.bot = bot + self.bot: commands.Bot = bot self.next_event: Mogi = None @@ -47,7 +47,7 @@ def __init__(self, bot): self.LAUNCH_NEW_EVENTS = True - self.GUILD = None + self.GUILD: discord.Guild = None self.MOGI_CHANNEL = None @@ -231,7 +231,7 @@ async def can(self, interaction: discord.Interaction): players.append(player) msg += f"{players[0].lounge_name} is assumed to be a new player and will be playing this mogi with a starting MMR of {starting_player_mmr}. " msg += "If you believe this is a mistake, please contact a staff member for help.\n" - else: + else: players = await mkw_mmr(self.URL, [member], self.TRACK_TYPE) if len(players) == 0 or players[0] is None: @@ -291,7 +291,7 @@ async def sub(self, interaction: discord.Interaction): f"You are still on cooldown. Please wait for {int(2 * 60 - (current_time - lastCommandTime))} more seconds to use this command again.", ephemeral=True) return - + is_room_thread = False room = None bottom_room_num = 1 @@ -472,7 +472,7 @@ async def resume_mogi_scheduling(self, interaction: discord.Interaction): """Mogis will begin to be scheduled again. Staff use only.""" self.LAUNCH_NEW_EVENTS = True await interaction.response.send_message("Mogis will resume scheduling.") - + @app_commands.command(name="change_event_time") @app_commands.guild_only() async def change_event_time(self, interaction: discord.Interaction, event_time: int): @@ -570,7 +570,7 @@ async def clear_sq_times(self, interaction: discord.Interaction): self.sq_times = [] await interaction.response.send_message("Cleared list of Squad Queue Times.") - + @app_commands.command(name="update_tier_info") @app_commands.guild_only() async def update_tier_info(self, interaction: discord.Interaction): @@ -781,7 +781,7 @@ async def add_teams_to_rooms(self, mogi, open_time: int, started_automatically=F except Exception as e: print("Late Player message has failed to send.", flush=True) print(traceback.format_exc()) - + # We could have used asyncio.call_later(120, handle_voting_and_history) # and removed asyncio.sleep(120) in handle_voting_and_history asyncio.create_task(SquadQueue.handle_voting_and_history(self.ongoing_event, self.HISTORY_CHANNEL)) @@ -990,7 +990,7 @@ async def debug_start_rooms(self, ctx): return -async def setup(bot): +async def setup(bot: commands.Bot): await bot.add_cog(SquadQueue(bot)) From e552a1f4f43ab0c75e045fca8356f93cc981f1f5 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Wed, 26 Jun 2024 00:11:24 -0500 Subject: [PATCH 062/147] Added cooldown handler for slash commands --- lounge.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lounge.py b/lounge.py index 5fe2f82..b024cb4 100644 --- a/lounge.py +++ b/lounge.py @@ -50,6 +50,13 @@ async def on_command_error(ctx, error): raise error +@bot.tree.error +async def on_app_command_error(interaction: discord.Interaction, error: discord.app_commands.AppCommandError): + if isinstance(error, discord.app_commands.errors.CommandOnCooldown): + await interaction.response.send_message(f'You are on cooldown. Try again in {round(error.retry_after)}s', ephemeral=True) + else: + raise error + @bot.event async def setup_hook(): for extension in initial_extensions: From 8f8d202181b7aaac61b2b80def78e2f71d3da938 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Wed, 26 Jun 2024 00:12:57 -0500 Subject: [PATCH 063/147] More type hints --- mogi_objects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mogi_objects.py b/mogi_objects.py index 3cdb3d5..786f25e 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -271,7 +271,7 @@ def __init__(self, room: Room, get_mmr, bottom_room_num): self.bottom_room_num = bottom_room_num @discord.ui.button(label="Join Room") - async def button_callback(self, interaction, button): + async def button_callback(self, interaction: discord.Interaction, button): await interaction.response.defer() muted_role_id = 434887701662007296 restricted_role_id = 797208908153618452 From 4ef91a191371f5568c38c19600035c9044d0aaf9 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Wed, 26 Jun 2024 00:13:49 -0500 Subject: [PATCH 064/147] Changed restricted/muted role check for subs --- cogs/SquadQueue.py | 13 ++++++++++++- mogi_objects.py | 11 ++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index c65dee1..2e1acc1 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -24,6 +24,14 @@ cooldowns = defaultdict(int) MMR_THRESHOLD_PKL = "mmr_threshold.pkl" + +def is_restricted(user: discord.User | discord.Member, config: dict) -> bool: + muted_role_id = config.get("muted_role_id") + restricted_role_id = config.get("restricted_role_id") + return (muted_role_id is not None and user.get_role(muted_role_id)) \ + or (restricted_role_id is not None and user.get_role(restricted_role_id)) + + class SquadQueue(commands.Cog): def __init__(self, bot): self.bot: commands.Bot = bot @@ -325,7 +333,10 @@ async def sub(self, interaction: discord.Interaction): timezone.utc) + timedelta(seconds=self.SUB_MESSAGE_LIFETIME_SECONDS) msg += f"Message will auto-delete in {discord.utils.format_dt(message_delete_date, style='R')}" await self.SUB_CHANNEL.send(msg, delete_after=self.SUB_MESSAGE_LIFETIME_SECONDS) - view = JoinView(room, lambda discord_id: get_mmr_from_discord_id(discord_id, self.TRACK_TYPE), bottom_room_num) + view = JoinView(room, + lambda discord_id: get_mmr_from_discord_id(discord_id, self.TRACK_TYPE), + bottom_room_num, + lambda user: is_restricted(user, self.bot.config)) await self.SUB_CHANNEL.send(view=view, delete_after=self.SUB_MESSAGE_LIFETIME_SECONDS) cooldowns[interaction.user.id] = current_time # Updates cooldown await interaction.response.send_message("Sent out request for sub.") diff --git a/mogi_objects.py b/mogi_objects.py index 786f25e..0b0fac9 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -1,9 +1,11 @@ +from __future__ import annotations + import random import discord from datetime import datetime, timezone, timedelta import time from discord.ui import View -from typing import List +from typing import List, Callable class Mogi: @@ -264,18 +266,17 @@ async def general_vote_callback(self, interaction: discord.Interaction, players_ class JoinView(View): - def __init__(self, room: Room, get_mmr, bottom_room_num): + def __init__(self, room: Room, get_mmr, bottom_room_num, is_restricted: Callable[[discord.User | discord.Member], bool] | None = None): super().__init__(timeout=1200) self.room = room self.get_mmr = get_mmr self.bottom_room_num = bottom_room_num + self.is_restricted = is_restricted @discord.ui.button(label="Join Room") async def button_callback(self, interaction: discord.Interaction, button): await interaction.response.defer() - muted_role_id = 434887701662007296 - restricted_role_id = 797208908153618452 - if interaction.user.get_role(muted_role_id) or interaction.user.get_role(restricted_role_id): + if self.is_restricted is not None and self.is_restricted(interaction.user): await interaction.followup.send( "Players with the muted or restricted role cannot use the sub button.", ephemeral=True) return From 92ee1e6018010b950b01a7414cc2d431e8247076 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Wed, 26 Jun 2024 00:16:36 -0500 Subject: [PATCH 065/147] Added /ping_staff command and role refresher - Ping staff command added, which has a user cooldown and muted/restricted role restriction. This command pings the selected staff role, which should add them to the thread. - Updated role names are pulled every 24 hours for the role IDs in case they have changed. --- cogs/SquadQueue.py | 51 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 2e1acc1..1d63ff5 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -106,6 +106,11 @@ def __init__(self, bot): with open('./timezones.json', 'r') as cjson: self.timezones = json.load(cjson) + # The following dictionary will be populated using the config's staff_helper_roles list. Players can + # call these roles into their room/thread using the /staff role command + # These will be refreshed every 24 hours to ensure that the correct name displays for the options + self.helper_staff_roles: Dict[str, discord.Role] = {} + @commands.Cog.listener() async def on_ready(self): self.GUILD = self.bot.get_guild(self.bot.config["guild_id"]) @@ -138,6 +143,7 @@ async def on_ready(self): print(f"General Channel - {self.GENERAL_CHANNEL}", flush=True) print("Ready!", flush=True) + self.refresh_helper_roles.start() async def lockdown(self, channel: discord.TextChannel): # everyone_perms = channel.permissions_for(channel.guild.default_role) @@ -461,6 +467,29 @@ async def remove_player(self, interaction: discord.Interaction, member: discord. await interaction.followup.send(msg) + @app_commands.command(name="ping_staff") + @app_commands.guild_only() + @app_commands.checks.cooldown(1, 300, key=lambda i: i.user.id) + async def ping_staff(self, interaction: discord.Interaction, role: str): + """Pings the specified staff role for help.""" + if not isinstance(interaction.channel, discord.Thread): + await interaction.response.send_message(f"Cannot use this command here.", ephemeral=True) + return + if is_restricted(interaction.user, self.bot.config): + await interaction.response.send_message(f"You are restricted from using this command.", ephemeral=True) + return + if role not in self.helper_staff_roles: + await interaction.response.send_message(f"You are not allowed to ping this role for help. Valid roles to ping: `{', '.join(self.helper_staff_roles)}`", ephemeral=True) + return + await interaction.response.send_message(f"{self.helper_staff_roles[role].mention}, {interaction.user.mention} is requesting help.", allowed_mentions=discord.AllowedMentions(roles=True)) + + @ping_staff.autocomplete('role') + async def ping_staff_autocomplete(self, interaction: discord.Interaction, current: str) -> List[app_commands.Choice[str]]: + return [ + app_commands.Choice(name=role_name, value=role_name) + for role_name in self.helper_staff_roles if current.lower() in role_name.lower() + ] + @app_commands.command(name="annul_current_mogi") @app_commands.guild_only() async def annul_current_mogi(self, interaction: discord.Interaction): @@ -922,6 +951,28 @@ async def delete_old_mogis(self): except Exception as e: print(traceback.format_exc()) + @tasks.loop(hours=24) + async def refresh_helper_roles(self): + """Refreshes the helper staff role names for the /ping_staff command using the role IDs in the config""" + try: + helper_staff_role_ids = self.bot.config["helper_staff_roles"] + # In my experience, large servers experience caching issues. + # This is a forced API call which guarantees the role information will be up-to-date. + all_roles = await self.GUILD.fetch_roles() + updated_roles = {} + for role_id in helper_staff_role_ids: + needle: discord.Role = discord.utils.find(lambda n: role_id == n.id, all_roles) + if needle is not None: + updated_roles[needle.name] = needle + # As a fail safe, if we didn't add any new roles, either due to a misconfiguration or an internal discord + # issue, only update if we found one or more of the roles + if len(updated_roles) > 0: + # We use clear and update to ensure any *references* to the original dictionary are updated + self.helper_staff_roles.clear() + self.helper_staff_roles.update(updated_roles) + except Exception as e: + print(traceback.format_exc()) + def get_event_str(self, mogi: Mogi): mogi_time = discord.utils.format_dt(mogi.start_time, style="F") mogi_time_relative = discord.utils.format_dt( From 362a8f6dc48721201faf80904457f2b8fb095310 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Wed, 26 Jun 2024 00:24:25 -0500 Subject: [PATCH 066/147] Removed annoying print --- cogs/SquadQueue.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 1d63ff5..3470e51 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -924,7 +924,6 @@ async def schedule_que_event(self): return next_event_open_time = self.compute_next_event_open_time() next_event_start_time = next_event_open_time + self.JOINING_TIME - print(f"Next event open time: {next_event_open_time}", flush=True) # We don't want to schedule the next event if it would open after it's joining period and during its extension period if next_event_start_time < datetime.now(timezone.utc): return From 640d4943cc4f4b9a76749941fcea666d0d86b6d2 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 16:54:44 -0500 Subject: [PATCH 067/147] Added server flag to configs --- ct_config.json | 2 ++ prod_config.json | 2 ++ test_config.json | 2 ++ 3 files changed, 6 insertions(+) diff --git a/ct_config.json b/ct_config.json index 7c0c6ca..0c2d970 100644 --- a/ct_config.json +++ b/ct_config.json @@ -1,5 +1,7 @@ { "token": "", + "lounge": "MKW", + "lounge_description": "Valid options are MKW or MK8DX", "guild_id": 387347467332485122, "admin_roles": { "387347467332485122": [ diff --git a/prod_config.json b/prod_config.json index 3aa7fd3..682f7fe 100644 --- a/prod_config.json +++ b/prod_config.json @@ -1,5 +1,7 @@ { "token": "", + "lounge": "MKW", + "lounge_description": "Valid options are MKW or MK8DX", "guild_id": 387347467332485122, "admin_roles": { "387347467332485122": [ diff --git a/test_config.json b/test_config.json index cce5ecd..3f78a68 100644 --- a/test_config.json +++ b/test_config.json @@ -1,5 +1,7 @@ { "token": "", + "lounge": "MKW", + "lounge_description": "Valid options are MKW or MK8DX", "guild_id": 1228027010836594698, "admin_roles": { "1228027010836594698": [ From 515997e66e8c79a32e5fbb3e23b0b69b974348bf Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 16:57:34 -0500 Subject: [PATCH 068/147] Add helper functions These functions help manipulate 2d arrays (lists) and chunk 1d array (list) into a 2d array (list) --- common.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 common.py diff --git a/common.py b/common.py new file mode 100644 index 0000000..cb65358 --- /dev/null +++ b/common.py @@ -0,0 +1,15 @@ +from typing import TypeVar, Type, List + +_T = TypeVar('T') + +def divide_chunks(list_: list, chunk_size: int): + # looping till length l + for i in range(0, len(list_), chunk_size): + yield list_[i:i + chunk_size] + + +def flatten(matrix: List[List[_T]]) -> List[_T]: + flat_list = [] + for row in matrix: + flat_list.extend(row) + return flat_list From 6039bc91d4f86efaa4345b753d6963be7c14df94 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 16:58:18 -0500 Subject: [PATCH 069/147] Load config into common file --- common.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common.py b/common.py index cb65358..9e6e508 100644 --- a/common.py +++ b/common.py @@ -1,7 +1,12 @@ +import json from typing import TypeVar, Type, List _T = TypeVar('T') +with open('./config.json', 'r') as cjson: + CONFIG = json.load(cjson) + + def divide_chunks(list_: list, chunk_size: int): # looping till length l for i in range(0, len(list_), chunk_size): From 9afe16bfd3f35a9f44b588e6688459212e002eb0 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 16:58:36 -0500 Subject: [PATCH 070/147] Server enumeration --- common.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/common.py b/common.py index 9e6e508..1c8ee9b 100644 --- a/common.py +++ b/common.py @@ -1,4 +1,5 @@ import json +from enum import Enum, auto from typing import TypeVar, Type, List _T = TypeVar('T') @@ -7,6 +8,19 @@ CONFIG = json.load(cjson) +class Server(Enum): + MKW = auto() + MK8DX = auto() + + +if CONFIG["lounge"] == "MKW": + SERVER = Server.MKW +elif CONFIG["lounge"] == "MK8DX": + SERVER = Server.MK8DX +else: + raise ValueError(f"{CONFIG['lounge']} is not a valid option for the 'lounge' attribute in the config.") + + def divide_chunks(list_: list, chunk_size: int): # looping till length l for i in range(0, len(list_), chunk_size): From 3b7861d5109e89dc78cc66944ab356852dadd140 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 16:59:30 -0500 Subject: [PATCH 071/147] Use config from common --- lounge.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lounge.py b/lounge.py index b024cb4..ff597be 100644 --- a/lounge.py +++ b/lounge.py @@ -1,3 +1,4 @@ +import common import discord from discord.ext import commands import json @@ -10,9 +11,7 @@ case_insensitive=True, intents=intents, help_command=None) initial_extensions = ['cogs.SquadQueue'] - -with open('./config.json', 'r') as cjson: - bot.config = json.load(cjson) +bot.config = common.config @bot.event From eaeea1c1f75a521cde9d08bc790a54280e3ffa00 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:01:16 -0500 Subject: [PATCH 072/147] Avoid using bare exception --- cogs/SquadQueue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index d1cbf80..fef0209 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -793,7 +793,7 @@ async def add_teams_to_rooms(self, mogi: Mogi, open_time: int, started_automatic view = VoteView(player_list, room_channel, mogi, self.TIER_INFO) curr_room.view = view await room_channel.send(view=view) - except Exception as e: + except discord.DiscordException as e: print(traceback.format_exc()) err_msg = f"\nAn error has occurred while creating the room channel; please contact your opponents in DM or another channel\n" err_msg += mentions From 4bc8e87ebd7bbb754e83c815a7e2dfd3b0019c23 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:01:29 -0500 Subject: [PATCH 073/147] Unnecessary code --- cogs/SquadQueue.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index fef0209..ee6fd70 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -795,10 +795,6 @@ async def add_teams_to_rooms(self, mogi: Mogi, open_time: int, started_automatic await room_channel.send(view=view) except discord.DiscordException as e: print(traceback.format_exc()) - err_msg = f"\nAn error has occurred while creating the room channel; please contact your opponents in DM or another channel\n" - err_msg += mentions - msg += err_msg - room_channel = None try: await mogi.mogi_channel.send(msg) except Exception as e: From e2464651a036bd08d06947bd4f6c95f86d1c6ebf Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:01:50 -0500 Subject: [PATCH 074/147] Avoid using bare exceptions --- cogs/SquadQueue.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index ee6fd70..ff9e050 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -797,9 +797,8 @@ async def add_teams_to_rooms(self, mogi: Mogi, open_time: int, started_automatic print(traceback.format_exc()) try: await mogi.mogi_channel.send(msg) - except Exception as e: - print( - f"Mogi Channel message for room {i+1} has failed to send.", flush=True) + except discord.DiscordException as e: + print(f"Mogi Channel message for room {i+1} has failed to send.", flush=True) print(traceback.format_exc()) if num_teams < mogi.count_registered(): missed_teams = mogi.confirmed_list( From d9fc9ec1f477d4b37f8798d778e1762687eaeac0 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:27:03 -0500 Subject: [PATCH 075/147] Avoid using bare exceptions --- cogs/SquadQueue.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index ff9e050..d5af09c 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -793,12 +793,12 @@ async def add_teams_to_rooms(self, mogi: Mogi, open_time: int, started_automatic view = VoteView(player_list, room_channel, mogi, self.TIER_INFO) curr_room.view = view await room_channel.send(view=view) - except discord.DiscordException as e: + except discord.DiscordException: print(traceback.format_exc()) try: await mogi.mogi_channel.send(msg) - except discord.DiscordException as e: print(f"Mogi Channel message for room {i+1} has failed to send.", flush=True) + except discord.DiscordException: print(traceback.format_exc()) if num_teams < mogi.count_registered(): missed_teams = mogi.confirmed_list( @@ -810,7 +810,7 @@ async def add_teams_to_rooms(self, mogi: Mogi, open_time: int, started_automatic msg += f" ({int(missed_teams[i].avg_mmr)} MMR)\n" try: await mogi.mogi_channel.send(msg) - except Exception as e: + except discord.DiscordException: print("Late Player message has failed to send.", flush=True) print(traceback.format_exc()) From 21028c711f359bad1099e4e43bc9a533278072b0 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:28:32 -0500 Subject: [PATCH 076/147] Import cleanup --- mogi_objects.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index 7dd8e5f..21ceaf3 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -1,11 +1,12 @@ from __future__ import annotations +import common +from common import flatten import random import discord from datetime import datetime, timezone, timedelta -import time from discord.ui import View -from typing import List, Callable +from typing import List, Tuple, Callable class Mogi: From 425ec55eff19e11ba48efc46c2ff33888808477c Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:28:46 -0500 Subject: [PATCH 077/147] Helper function to compute averages --- mogi_objects.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mogi_objects.py b/mogi_objects.py index 21ceaf3..b7af1ed 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -9,6 +9,11 @@ from typing import List, Tuple, Callable + +def average(list_: List[int | float]) -> float: + return sum(list_) / len(list_) + + class Mogi: def __init__(self, sq_id: int, players_per_team: int, teams_per_room: int, mogi_channel: discord.TextChannel, is_automated=False, start_time=None): From 27d33397364682d02d8f67bb0fa24e48e07fa1d5 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:31:28 -0500 Subject: [PATCH 078/147] Remove num_rooms property I decided that we should compute things differently --- mogi_objects.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index b7af1ed..90a1322 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -41,9 +41,6 @@ def num_teams(self): return self.count_registered() @property - def num_rooms(self): - """Returns the number of rooms based on teams where all players have confirmed""" - return self.num_teams // self.teams_per_room def check_player(self, member): for team in self.teams: From fd53843137a9a4c71eb161383e3a11dc47c4a01f Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:33:28 -0500 Subject: [PATCH 079/147] Team.is_registered -> Team.all_registered --- mogi_objects.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index 90a1322..41dbb42 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -50,10 +50,10 @@ def check_player(self, member): def count_registered(self): """Returns the number of teams that are registered""" - return sum(1 for team in self.teams if team.is_registered()) + return sum(1 for team in self.teams if team.all_registered()) def confirmed_list(self): - return [team for team in self.teams if team.is_registered()] + return [team for team in self.teams if team.all_registered()] def remove_id(self, squad_id: int): confirmed = self.confirmed_list() @@ -99,7 +99,7 @@ def __init__(self, players): def avg_mmr(self): return sum([p.mmr for p in self.players]) / len(self.players) - def is_registered(self): + def all_registered(self): """Returns if all players on the team are registered""" return all(player.confirmed for player in self.players) From 660f2249a37c7132e05744140c080dd2e2dda10b Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:39:13 -0500 Subject: [PATCH 080/147] Refactor of mmr_high, mmr_low --- mogi_objects.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index 41dbb42..4c9ab1f 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -81,12 +81,27 @@ def __init__(self, teams, room_num: int, thread: discord.Thread): self.teams = teams self.room_num = room_num self.thread = thread - self.mmr_average = 0 - self.mmr_high = None - self.mmr_low = None self.view = None self.finished = False + @property + def mmr_high(self) -> int: + if self.teams is None: + return None + return max(self.players, key=lambda p: p.mmr).mmr + + @property + def mmr_low(self) -> int: + if self.teams is None: + return None + return min(self.players, key=lambda p: p.mmr).mmr + + @property + def avg_mmr(self) -> float: + if self.teams is None: + return 0 + return average([p.mmr for p in self.players]) + def get_player_list(self): return [player.member.id for team in self.teams for player in team.players] From cfffc7502eecf3efaa05f51cb8725a2328c169a0 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:39:30 -0500 Subject: [PATCH 081/147] Add players property --- mogi_objects.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mogi_objects.py b/mogi_objects.py index 4c9ab1f..de836f8 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -102,6 +102,12 @@ def avg_mmr(self) -> float: return 0 return average([p.mmr for p in self.players]) + @property + def players(self) -> List[Player]: + if self.teams is None: + return [] + return flatten([t.players for t in self.teams]) + def get_player_list(self): return [player.member.id for team in self.teams for player in team.players] From 90acc6bfa4e69cfff8dfdf4246fa8067cc3fbcc4 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:40:13 -0500 Subject: [PATCH 082/147] Use average function --- mogi_objects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mogi_objects.py b/mogi_objects.py index de836f8..c7ccdf2 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -118,7 +118,7 @@ def __init__(self, players): @property def avg_mmr(self): - return sum([p.mmr for p in self.players]) / len(self.players) + return average([p.mmr for p in self.players]) def all_registered(self): """Returns if all players on the team are registered""" From 97ed0a459c641b16efe452fca7840e7615e0713d Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:40:47 -0500 Subject: [PATCH 083/147] Refactor: simplify comparison chain --- mogi_objects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mogi_objects.py b/mogi_objects.py index c7ccdf2..289a194 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -333,7 +333,7 @@ async def button_callback(self, interaction: discord.Interaction, button): self.room.mmr_high = 999999 if self.room.room_num == self.bottom_room_num: self.room.mmr_low = -999999 - if isinstance(user_mmr, int) and user_mmr < self.room.mmr_high + 500 and user_mmr > self.room.mmr_low - 500: + if isinstance(user_mmr, int) and self.room.mmr_high + 500 > user_mmr > self.room.mmr_low - 500: button.disabled = True await interaction.followup.edit_message(interaction.message.id, view=self) mention = interaction.user.mention From 6e259696820fb16e9aaa71892032f4a9b52b2966 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:42:16 -0500 Subject: [PATCH 084/147] Refactor of Player initialization This will allow the caller making the instance to immediately specify if the Player is confirmed or not. This does not change or break any *functionality* in our code. --- mogi_objects.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index 289a194..4df6a33 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -166,11 +166,11 @@ def __str__(self): class Player: - def __init__(self, member, lounge_name, mmr): + def __init__(self, member: discord.Member, lounge_name: str, mmr: int, confirmed=False): self.member = member self.lounge_name = lounge_name self.mmr = mmr - self.confirmed = False + self.confirmed = confirmed self.score = 0 From 00bf0b87d9ea977c6d51c408837ccb5591a972af Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:43:00 -0500 Subject: [PATCH 085/147] Add mention property for Plater --- mogi_objects.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mogi_objects.py b/mogi_objects.py index 4df6a33..f5696c5 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -173,6 +173,13 @@ def __init__(self, member: discord.Member, lounge_name: str, mmr: int, confirmed self.confirmed = confirmed self.score = 0 + @property + def mention(self): + """String that, when sent in a Discord message, will mention and ping the player.""" + if self.member is None: + return "<@!1>" + return self.member.mention + class VoteView(View): def __init__(self, players, thread, mogi: Mogi, tier_info): From 445c77d2a25d2110371dff2a14c046a33e4b180a Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:43:11 -0500 Subject: [PATCH 086/147] Add __repr__ for Player --- mogi_objects.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mogi_objects.py b/mogi_objects.py index f5696c5..d098fe4 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -180,6 +180,9 @@ def mention(self): return "<@!1>" return self.member.mention + def __repr__(self): + return f"{__class__.__name__}(member={self.member}, lounge_name={self.lounge_name}, mmr={self.mmr}, confirmed={self.confirmed})" + class VoteView(View): def __init__(self, players, thread, mogi: Mogi, tier_info): From 2167799af63ab1e0799914558e253279f210488f Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:43:39 -0500 Subject: [PATCH 087/147] Remove unused function This function is not used anywhere. --- mogi_objects.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index d098fe4..642fe27 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -136,12 +136,6 @@ def get_player(self, member): def get_first_player(self): return self.players[0] - def sub_player(self, sub_out, sub_in): - for i, player in enumerate(self.players): - if player == sub_out: - self.players[i] = sub_in - return - def num_confirmed(self): """Returns the number of confirmed players in the team""" return sum(1 for player in self.players if player.confirmed) From e34c2c09c99434f624dacca686cf8c82957872a1 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:45:06 -0500 Subject: [PATCH 088/147] Refactor: Add type hinting --- mogi_objects.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index 642fe27..ce9c814 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -78,7 +78,7 @@ def get_room_from_thread(self, channel_id: int): class Room: def __init__(self, teams, room_num: int, thread: discord.Thread): - self.teams = teams + self.teams: List["Team"] = teams self.room_num = room_num self.thread = thread self.view = None @@ -113,7 +113,7 @@ def get_player_list(self): class Team: - def __init__(self, players): + def __init__(self, players: List["Player"]): self.players = players @property @@ -127,7 +127,7 @@ def all_registered(self): def has_player(self, member): return any(player.member.id == member.id for player in self.players) - def get_player(self, member): + def get_player(self, member: discord.Member) -> Player | None: for player in self.players: if player.member.id == member.id: return player From f91df62150e8d062ef93ac43491f0242fe89e845 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:46:07 -0500 Subject: [PATCH 089/147] Remove unused function --- mogi_objects.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mogi_objects.py b/mogi_objects.py index ce9c814..e3121d4 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -116,6 +116,10 @@ class Team: def __init__(self, players: List["Player"]): self.players = players + def get_mentions(self): + """Return a string where all players on the team are discord @'d""" + return " ".join([p.member.mention for p in self.players]) + @property def avg_mmr(self): return average([p.mmr for p in self.players]) From bfe1f914eb84e89c6ac19dd472e62277bf11f0c0 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:46:25 -0500 Subject: [PATCH 090/147] Refactor: Type hint --- mogi_objects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mogi_objects.py b/mogi_objects.py index e3121d4..e3977dc 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -48,7 +48,7 @@ def check_player(self, member): return team return None - def count_registered(self): + def count_registered(self) -> int: """Returns the number of teams that are registered""" return sum(1 for team in self.teams if team.all_registered()) From 68ffca1612db0db4cc9b45d7013d0f242e36f4a3 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:47:34 -0500 Subject: [PATCH 091/147] Remove unused function --- mogi_objects.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index e3977dc..d9d6477 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -55,13 +55,6 @@ def count_registered(self) -> int: def confirmed_list(self): return [team for team in self.teams if team.all_registered()] - def remove_id(self, squad_id: int): - confirmed = self.confirmed_list() - if squad_id < 1 or squad_id > len(confirmed): - return None - squad = confirmed[squad_id - 1] - self.teams.remove(squad) - return squad def is_room_thread(self, channel_id: int): for room in self.rooms: From b7cbaa883cda8ef727b0c4fb50d88fe00242a385 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 17:52:48 -0500 Subject: [PATCH 092/147] Change how num_players is computed --- mogi_objects.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mogi_objects.py b/mogi_objects.py index d9d6477..b90803c 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -33,7 +33,7 @@ def __init__(self, sq_id: int, players_per_team: int, teams_per_room: int, mogi_ @property def num_players(self): """Returns the total number of players in teams where all players have confirmed""" - return self.num_teams * self.players_per_team + return sum(len(t.players) for t in self.teams if t.all_registered()) @property def num_teams(self): @@ -55,6 +55,8 @@ def count_registered(self) -> int: def confirmed_list(self): return [team for team in self.teams if team.all_registered()] + def players_on_confirmed_teams(self) -> List[Player]: + return flatten([team.players for team in self.confirmed_teams()]) def is_room_thread(self, channel_id: int): for room in self.rooms: From a61eb7dac1820fb7dc275dcfaa2cb7c6863482ac Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 18:00:41 -0500 Subject: [PATCH 093/147] Main one room algorithm Supports both MK8DX and MKW to implement differing algorithms and functionality. MK8DX is free to implement their own algorithm if they want to. --- cogs/SquadQueue.py | 135 ++++++++++++++++++++++++--------------------- mogi_objects.py | 86 ++++++++++++++++++++++++++--- 2 files changed, 152 insertions(+), 69 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index d5af09c..0e09e22 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -8,6 +8,9 @@ from datetime import datetime, timezone, timedelta import time import json + +import common +from common import divide_chunks from mmr import mkw_mmr, get_mmr_from_discord_id from mogi_objects import Mogi, Team, Player, Room, VoteView, JoinView, get_tier import asyncio @@ -33,6 +36,22 @@ def is_restricted(user: discord.User | discord.Member, config: dict) -> bool: or (restricted_role_id is not None and user.get_role(restricted_role_id)) +def basic_threshold_players_allowed(players: List[Player], threshold: int) -> bool: + """Returns True if the highest player's rating minus the lowest player's rating is below the given threshold""" + sorted_players = sorted(players, key=lambda p: p.mmr) + return (sorted_players[-1].mmr - sorted_players[0].mmr) <= threshold + + +def mkw_players_allowed(players: List[Player], threshold: int) -> bool: + """Returns true if the given list of players would be allowed to play together""" + return basic_threshold_players_allowed(players, threshold) + + +def mk8dx_players_allowed(players: List[Player], threshold: int) -> bool: + """Returns true if the given list of players would be allowed to play together""" + return basic_threshold_players_allowed(players, threshold) + + class SquadQueue(commands.Cog): def __init__(self, bot): self.bot: commands.Bot = bot @@ -355,7 +374,7 @@ async def list_task(self): await self.delete_list_messages(0) return - mogi_list = mogi.confirmed_list() + mogi_list = mogi.confirmed_teams() # Remove late players from the list to display separately full_list_length = len(mogi_list) @@ -696,12 +715,10 @@ async def write_history(mogi: Mogi, history_channel: discord.TextChannel): # make thread channels while the event is gathering instead of at the end, # since discord only allows 50 thread channels to be created per 5 minutes. async def check_room_channels(self, mogi): - num_teams = mogi.count_registered() - num_rooms = int(num_teams / (12 / mogi.players_per_team)) num_created_rooms = len(mogi.rooms) - if num_created_rooms >= num_rooms: + if num_created_rooms >= mogi.max_possible_rooms: return - for i in range(num_created_rooms, num_rooms): + for i in range(num_created_rooms, mogi.max_possible_rooms): minute = mogi.start_time.minute if len(str(minute)) == 1: minute = '0' + str(minute) @@ -730,8 +747,7 @@ async def add_teams_to_rooms(self, mogi: Mogi, open_time: int, started_automatic return if mogi.making_rooms_run and started_automatically: return - num_rooms = int(mogi.count_registered() / (12 / mogi.players_per_team)) - if num_rooms == 0: + if mogi.max_possible_rooms == 0: self.ongoing_event = None await mogi.mogi_channel.send(f"Not enough players to fill a single room! This mogi will be cancelled.") return @@ -744,70 +760,65 @@ async def add_teams_to_rooms(self, mogi: Mogi, open_time: int, started_automatic if was_gathering: await mogi.mogi_channel.send("Mogi is now closed; players can no longer join or drop from the event") - - teams_per_room = int(12 / mogi.players_per_team) - num_teams = int(num_rooms * teams_per_room) - final_list = mogi.confirmed_list()[0:num_teams] - sorted_list = sorted(final_list, reverse=True) - extra_members = [] - if str(mogi.mogi_channel.guild.id) in self.bot.config["members_for_channels"].keys(): - extra_members_ids = self.bot.config["members_for_channels"][str( - mogi.mogi_channel.guild.id)] - for m in extra_members_ids: - extra_members.append(mogi.mogi_channel.guild.get_member(m)) - if str(mogi.mogi_channel.guild.id) in self.bot.config["roles_for_channels"].keys(): - extra_roles_ids = self.bot.config["roles_for_channels"][str( - mogi.mogi_channel.guild.id)] - for r in extra_roles_ids: - extra_members.append(mogi.mogi_channel.guild.get_role(r)) - - rooms = mogi.rooms - for i in range(num_rooms): - msg = f"`Room {i+1} - Player List`\n" - mentions = "" - start_index = i*teams_per_room - player_list = [] - for j in range(teams_per_room): - msg += f"`{j+1}.` " - team = sorted_list[start_index+j] - player_list.append(sorted_list[start_index+j].get_first_player()) - msg += ", ".join([p.lounge_name for p in team.players]) - msg += f" ({int(team.avg_mmr)} MMR)\n" - mentions += " ".join([p.member.mention for p in team.players]) - mentions += " " - room_msg = msg - mentions += " ".join([m.mention for m in extra_members if m is not None]) - room_msg += "\nVote for format FFA, 2v2, 3v3, 4v4, or 6v6.\n" - room_msg += mentions - curr_room = rooms[i] - room_channel = curr_room.thread - curr_room.teams = sorted_list[start_index:start_index+teams_per_room] - curr_room.mmr_low = player_list[11].mmr - curr_room.mmr_high = player_list[0].mmr - if curr_room.mmr_high - curr_room.mmr_low > self.room_mmr_threshold: + + extra_members_ids = self.bot.config["members_for_channels"].get(str(mogi.mogi_channel.guild.id), []) + for m in extra_members_ids: + extra_members.append(mogi.mogi_channel.guild.get_member(m)) + extra_roles_ids = self.bot.config["roles_for_channels"].get(str(mogi.mogi_channel.guild.id), []) + for r in extra_roles_ids: + extra_members.append(mogi.mogi_channel.guild.get_role(r)) + + # Support for both server to implement their own algorithm for a player list being allowed + # For now, both servers have a simple rating range check that a list of players must meet, + # but each server could implement more complex checks on a given list of players. + if common.SERVER is common.Server.MKW: + def allowed_players_check(players): + return mkw_players_allowed(players, self.room_mmr_threshold) + elif common.SERVER is common.Server.MK8DX: + def allowed_players_check(players): + return mk8dx_players_allowed(players, self.room_mmr_threshold) + + all_confirmed_players = mogi.players_on_confirmed_teams() + regular_player_list = all_confirmed_players[:mogi.num_players//mogi.players_per_room] + late_player_list = all_confirmed_players[mogi.num_players//mogi.players_per_room:] + proposed_list = mogi.generate_proposed_list(allowed_players_check) + for room_number, room_players in enumerate(divide_chunks(proposed_list, mogi.players_per_room), 1): + msg = f"`Room {room_number} - Player List`\n" + for player_num, player in enumerate(room_players, 1): + added_str = " *(Added from late players)*" if player in late_player_list else "" + msg += f"""`{player_num}.` {player.lounge_name} ({player.mmr} MMR){added_str}\n""" + if allowed_players_check(room_players): msg += f"\nThe mmr gap in the room is higher than the allowed threshold of {self.room_mmr_threshold} MMR, this room has been cancelled." else: + curr_room = mogi.rooms[room_number - 1] + curr_room.teams = [Team([p]) for p in room_players] + player_mentions = " ".join([p.mention for p in room_players]) + extra_member_mentions = " ".join([m.mention for m in extra_members if m is not None]) + room_msg = f"""{msg} +Vote for format FFA, 2v2, 3v3, 4v4, or 6v6. +{player_mentions} {extra_member_mentions}""" try: - await room_channel.send(room_msg) - view = VoteView(player_list, room_channel, mogi, self.TIER_INFO) + await curr_room.thread.send(room_msg) + view = VoteView(room_players, curr_room.thread, mogi, self.TIER_INFO) curr_room.view = view - await room_channel.send(view=view) + await curr_room.thread.send(view=view) except discord.DiscordException: print(traceback.format_exc()) + try: await mogi.mogi_channel.send(msg) - print(f"Mogi Channel message for room {i+1} has failed to send.", flush=True) except discord.DiscordException: + print(f"Mogi Channel message for room {room_number} has failed to send.", flush=True) print(traceback.format_exc()) - if num_teams < mogi.count_registered(): - missed_teams = mogi.confirmed_list( - )[num_teams:mogi.count_registered()] + + # Compute the list of "late" players that didn't get into any room + not_in_proposed_list = [p for p in all_confirmed_players if p not in proposed_list] + if len(not_in_proposed_list) > 0: msg = "`Late players:`\n" - for i in range(len(missed_teams)): - msg += f"`{i+1}.` " - msg += ", ".join([p.lounge_name for p in missed_teams[i].players]) - msg += f" ({int(missed_teams[i].avg_mmr)} MMR)\n" + for i, player in enumerate(not_in_proposed_list, 1): + removed_str = " *(Removed from player list)*" if player in regular_player_list else "" + msg += f"`{i+1}.` {player.lounge_name} ({int(player.mmr)} MMR) {removed_str}\n" try: await mogi.mogi_channel.send(msg) except discord.DiscordException: @@ -924,8 +935,8 @@ async def schedule_que_event(self): await self.MOGI_CHANNEL.send( "Squad Queue is currently going on at this hour! The queue will remain closed.") self.next_event = Mogi(sq_id=1, - players_per_team=1, - teams_per_room=12, + max_players_per_team=1, + players_per_room=12, mogi_channel=self.MOGI_CHANNEL, is_automated=True, start_time=next_event_start_time) @@ -972,7 +983,7 @@ def get_event_str(self, mogi: Mogi): mogi_time = discord.utils.format_dt(mogi.start_time, style="F") mogi_time_relative = discord.utils.format_dt( mogi.start_time, style="R") - return f"`#{mogi.sq_id}` **{mogi.players_per_team}v{mogi.players_per_team}:** {mogi_time} - {mogi_time_relative}" + return f"`#{mogi.sq_id}` **{mogi.max_player_per_team}v{mogi.max_player_per_team}:** {mogi_time} - {mogi_time_relative}" @commands.command(name="sync") @commands.is_owner() diff --git a/mogi_objects.py b/mogi_objects.py index b90803c..cf0786f 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -15,15 +15,20 @@ def average(list_: List[int | float]) -> float: class Mogi: - def __init__(self, sq_id: int, players_per_team: int, teams_per_room: int, mogi_channel: discord.TextChannel, + ALGORITHM_STATUS_INSUFFICIENT_PLAYERS = 1 + ALGORITHM_STATUS_2_OR_MORE_ROOMS = 2 + ALGORITHM_STATUS_SUCCESS_FOUND = 3 + ALGORITHM_STATUS_SUCCESS_EMPTY = 4 + + def __init__(self, sq_id: int, max_players_per_team: int, players_per_room: int, mogi_channel: discord.TextChannel, is_automated=False, start_time=None): self.started = False self.gathering = False self.making_rooms_run = False self.making_rooms_run_time = None self.sq_id = sq_id - self.players_per_team = players_per_team - self.teams_per_room = teams_per_room + self.max_player_per_team = max_players_per_team + self.players_per_room = players_per_room self.mogi_channel = mogi_channel self.teams: List[Team] = [] self.rooms: List[Room] = [] @@ -41,6 +46,76 @@ def num_teams(self): return self.count_registered() @property + def max_possible_rooms(self) -> int: + """Returns the maximum possible number of rooms based on teams where all players have confirmed. Depending on range cutoffs or + the specifics of the algorithm used to make the actual rooms, this number could be higher than the final number of rooms.""" + return self.num_players // self.players_per_room + + @staticmethod + def _minimize_range(players: List[Player], num_players: int) -> List[Player] | None: + """Returns a collection of players (the number of players in the collection is the given num_players parameter) whose has the smallest + mmr spread. If the number of players in the given list is smaller than the request num_players collection size, or the num_players is less than 2 (doesn't make sense), None is returned.""" + # The number of players we were given is less than the collection size we are supposed to return, so return None + if len(players) < num_players: + return None + if num_players <= 1: + return None + # Sort the players so we easily know the player with the lowest rating and highest rating in any given collection + sorted_players = sorted(players, key=lambda p: p.mmr) + # In the beginning, the best found collection of players is the first 12 + best_collection = sorted_players[0:num_players] + cur_min = best_collection[-1].mmr - best_collection[0].mmr + # Find the collection of players with the least rating spread + for lowest_player_index, highest_player in enumerate(sorted_players[num_players:], 1): + lowest_player = sorted_players[lowest_player_index] + cur_range = highest_player.mmr - lowest_player.mmr + if cur_range < cur_min: + cur_min = cur_range + best_collection = sorted_players[lowest_player_index:lowest_player_index + num_players] + return best_collection + + def _one_room_final_list_algorithm(self, valid_players_check: Callable[[List[Player]], bool]) -> Tuple[ + List[Player], int]: + if self.max_possible_rooms == 0: + return [], Mogi.ALGORITHM_STATUS_INSUFFICIENT_PLAYERS + if self.max_possible_rooms > 1: + confirmed_players = self.players_on_confirmed_teams() + return confirmed_players[0:self.players_per_room * self.max_possible_rooms], Mogi.ALGORITHM_STATUS_2_OR_MORE_ROOMS + # At this point, we can only make one possible room, so our algorithm will be used + confirmed_players = self.players_on_confirmed_teams() + cur_check_list = list(confirmed_players[0:self.players_per_room]) + late_players = list(confirmed_players[self.players_per_room:]) + + while True: + best_collection = Mogi._minimize_range(cur_check_list, self.players_per_room) + if valid_players_check(best_collection): + return best_collection, Mogi.ALGORITHM_STATUS_SUCCESS_FOUND + if len(late_players) == 0: + break + cur_check_list.append(late_players.pop(0)) + # Even after checking the late players, we did not find + return [], Mogi.ALGORITHM_STATUS_SUCCESS_EMPTY + + def _mk8dx_generate_final_list(self) -> List[Player]: + confirmed_players = self.players_on_confirmed_teams() + return confirmed_players[0:self.players_per_room * self.max_possible_rooms] + + def _mkw_generate_final_list(self, valid_players_check: Callable[[List[Player]], bool]) -> List[Player]: + result, _ = self._one_room_final_list_algorithm(valid_players_check) + return result + + def generate_proposed_list(self, valid_players_check: Callable[[List[Player]], bool] = None) -> List[Player]: + """Algorithm that generates a proposed list of players that will play. This algorithm may differ between + MK8DX and MKW. The algorithm is allowed to propose any list of players it wants to. Among several possibilities, + this allows the algorithm to change the order of the players in the returned list, add or remove players, + and more. + + The algorithm may or may not enforce a hard check of the valid players. That is up to the implemented + algorithm.""" + if common.SERVER is common.Server.MK8DX: + return self._mk8dx_generate_final_list() + elif common.SERVER is common.Server.MKW: + return self._mkw_generate_final_list(valid_players_check) def check_player(self, member): for team in self.teams: @@ -52,7 +127,7 @@ def count_registered(self) -> int: """Returns the number of teams that are registered""" return sum(1 for team in self.teams if team.all_registered()) - def confirmed_list(self): + def confirmed_teams(self) -> List["Team"]: return [team for team in self.teams if team.all_registered()] def players_on_confirmed_teams(self) -> List[Player]: @@ -132,9 +207,6 @@ def get_player(self, member: discord.Member) -> Player | None: return player return None - def get_first_player(self): - return self.players[0] - def num_confirmed(self): """Returns the number of confirmed players in the team""" return sum(1 for player in self.players if player.confirmed) From a62007a11764082349274a42e69e5b74cd3f9854 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 18:01:17 -0500 Subject: [PATCH 094/147] Unit Testing for algorithm Unit testing to show correctness of algorithm --- unit_testing.py | 340 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 340 insertions(+) create mode 100644 unit_testing.py diff --git a/unit_testing.py b/unit_testing.py new file mode 100644 index 0000000..5c4f935 --- /dev/null +++ b/unit_testing.py @@ -0,0 +1,340 @@ +import unittest + +import common +from cogs import SquadQueue +from mogi_objects import Player, Mogi, Team +import random + + +class AlgorithmTests(unittest.TestCase): + + def test_empty_player_list(self): + """Test for empty player list""" + players = [] + num_players = 12 + result = Mogi._minimize_range(players, num_players) + self.assertIsNone(result) + + def test_not_enough_players(self): + """Test for when too many players are requested""" + players = [Player(None, None, None), Player(None, None, None)] + random.shuffle(players) + num_players = 3 + result = Mogi._minimize_range(players, num_players) + self.assertIsNone(result) + + def test_enough_players(self): + """Test for when there are enough requested players""" + players = [Player(None, "Joe", 100), Player(None, "Bob", 200)] + random.shuffle(players) + num_players = 2 + result = Mogi._minimize_range(players, num_players) + self.assertIsNotNone(result) + + def test_one_possible_collection(self): + """Test for when the number of players given is the number of players requested""" + p1 = Player(None, "Joe", 100) + p2 = Player(None, "Bob", 200) + players = [p1, p2] + random.shuffle(players) + num_players = 2 + result = Mogi._minimize_range(players, num_players) + self.assertListEqual([p1, p2], result) + + def test_correct_collection_1(self): + """Test #1 for correctness""" + p1 = Player(None, "Joe", 100) + p2 = Player(None, "Bob", 200) + p3 = Player(None, "Jane", 250) + players = [p1, p2, p3] + random.shuffle(players) + num_players = 2 + result = Mogi._minimize_range(players, num_players) + self.assertListEqual([p2, p3], result) + + def test_correct_collection_2(self): + """Test #2 for correctness""" + p1 = Player(None, "Joe", 100) + p2 = Player(None, "Bob", 200) + p3 = Player(None, "Jane", 350) + players = [p1, p2, p3] + random.shuffle(players) + num_players = 2 + result = Mogi._minimize_range(players, num_players) + self.assertListEqual([p1, p2], result) + + def test_correct_collection_3(self): + """Test #3 for correctness""" + p1 = Player(None, "Joe", 100) + p2 = Player(None, "Bob", 0) + p3 = Player(None, "Jane", 350) + players = [p1, p2, p3] + random.shuffle(players) + num_players = 2 + result = Mogi._minimize_range(players, num_players) + self.assertListEqual([p2, p1], result) + + def test_correct_collection_4(self): + """Test #4 for correctness""" + p1 = Player(None, "Joe", 0) + p2 = Player(None, "Bob", -100) + p3 = Player(None, "Jane", 1) + players = [p1, p2, p3] + random.shuffle(players) + num_players = 2 + result = Mogi._minimize_range(players, num_players) + self.assertListEqual([p1, p3], result) + + def test_correct_collection_5(self): + """Test #5 for correctness""" + p1 = Player(None, "Joe", 0) + p2 = Player(None, "Bob", 100) + p3 = Player(None, "Jane", 1) + players = [p1, p2, p3] + random.shuffle(players) + num_players = 2 + result = Mogi._minimize_range(players, num_players) + self.assertListEqual([p1, p3], result) + + def test_correct_collection_6(self): + """Test #6 for correctness, large player collection: + -300 + 0 + 100 + 200 + 500 + 501 + 502 + 600 + 750 + 800 + 1000 + 1500 : 1800 + 1501 : 1501 + 1550 : 1450 + 1800 : 1600 + 2000 : 1500""" + p1 = Player(None, "1", 1000) + p2 = Player(None, "2", 500) + p3 = Player(None, "3", 1500) + p4 = Player(None, "4", 750) + p5 = Player(None, "5", 800) + p6 = Player(None, "6", 2000) + p7 = Player(None, "7", 0) + p8 = Player(None, "8", -300) + p9 = Player(None, "9", 501) + p10 = Player(None, "10", 600) + p11 = Player(None, "11", 200) + p12 = Player(None, "12", 1550) + p13 = Player(None, "13", 1800) + p14 = Player(None, "14", 1501) + p15 = Player(None, "15", 502) + p16 = Player(None, "16", 100) + players = [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16] + random.shuffle(players) + num_players = 12 + result = Mogi._minimize_range(players, num_players) + self.assertListEqual([p16, p11, p2, p9, p15, p10, p4, p5, p1, p3, p14, p12], result) + + def test_correct_collection_7(self): + """Test #7 for large player collection where 12 players are given and 12 are requested""" + p1 = Player(None, "1", 1000) + p2 = Player(None, "2", 500) + p3 = Player(None, "3", 1500) + p4 = Player(None, "4", 750) + p5 = Player(None, "5", 800) + p6 = Player(None, "6", 2000) + p7 = Player(None, "7", 0) + p8 = Player(None, "8", -300) + p9 = Player(None, "9", 500) + p10 = Player(None, "10", 600) + p11 = Player(None, "11", 200) + p12 = Player(None, "12", 1550) + players = [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12] + random.shuffle(players) + num_players = 12 + result = Mogi._minimize_range(players, num_players) + self.assertSetEqual(set(players), set(result)) + + +class OneRoomAlgorithmTests(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.threshold = 65 + cls.threshold_function = None + if common.SERVER is common.Server.MKW: + cls.threshold_function = lambda players: SquadQueue.mkw_players_allowed(players, cls.threshold) + elif common.SERVER is common.Server.MK8DX: + cls.threshold_function = lambda players: SquadQueue.mk8dx_players_allowed(players, cls.threshold) + else: + raise Exception("Lounge parameter in config is invalid, unit tests will fail.") + + def _new_default_mogi(self) -> Mogi: + """Returns a default mogi with 12 players per room.""" + self # to hide annoying PyCharm IDE error - well aware it could be static, do not want it to be + return Mogi(sq_id=1, + max_players_per_team=1, + players_per_room=12, + mogi_channel=None + ) + + def test_no_players(self): + """Test when no players are queued""" + mogi = self._new_default_mogi() + results, status_code = mogi._one_room_final_list_algorithm(OneRoomAlgorithmTests.threshold_function) + self.assertListEqual([], results) + self.assertEqual(status_code, SquadQueue.Mogi.ALGORITHM_STATUS_INSUFFICIENT_PLAYERS) + + def test_one_player(self): + """Test when one player is queued""" + p1 = Player(None, "1", 1000, True) + mogi = self._new_default_mogi() + mogi.teams.append(Team([p1])) + results, status_code = mogi._one_room_final_list_algorithm(OneRoomAlgorithmTests.threshold_function) + self.assertListEqual([], results) + self.assertEqual(status_code, SquadQueue.Mogi.ALGORITHM_STATUS_INSUFFICIENT_PLAYERS) + + def test_two_players(self): + """Test when two players are queued""" + p1 = Player(None, "1", 1000, True) + p2 = Player(None, "2", 2000, True) + mogi = self._new_default_mogi() + mogi.teams.append(Team([p1])) + mogi.teams.append(Team([p2])) + results, status_code = mogi._one_room_final_list_algorithm(OneRoomAlgorithmTests.threshold_function) + self.assertListEqual([], results) + self.assertEqual(status_code, SquadQueue.Mogi.ALGORITHM_STATUS_INSUFFICIENT_PLAYERS) + + def test_large_max_players_per_team(self): + """This is to catch the Mogi class computes the number of confirmed players incorrectly""" + p1 = Player(None, "1", 1000, True) + mogi = self._new_default_mogi() + mogi.max_player_per_team = 24 + mogi.teams.append(Team([p1])) + results, status_code = mogi._one_room_final_list_algorithm(OneRoomAlgorithmTests.threshold_function) + self.assertListEqual([], results) + self.assertEqual(status_code, SquadQueue.Mogi.ALGORITHM_STATUS_INSUFFICIENT_PLAYERS) + + def test_2_rooms_correctness_1(self): + """Test if 24P for 12 player rooms generates 2 rooms with correct status code""" + players = [Player(None, f"{i}", i, True) for i in range(24)] + mogi = self._new_default_mogi() + for p in players: + mogi.teams.append(Team([p])) + results, status_code = mogi._one_room_final_list_algorithm(OneRoomAlgorithmTests.threshold_function) + self.assertSetEqual(set(players), set(results)) + self.assertEqual(status_code, SquadQueue.Mogi.ALGORITHM_STATUS_2_OR_MORE_ROOMS) + + def test_2_rooms_correctness_2(self): + """Test if 25P for 12 player rooms generates 2 rooms with correct players and correct status code""" + players = [Player(None, f"{i}", i, True) for i in range(25)] + random.shuffle(players) + mogi = self._new_default_mogi() + for p in players: + mogi.teams.append(Team([p])) + results, status_code = mogi._one_room_final_list_algorithm(OneRoomAlgorithmTests.threshold_function) + self.assertSetEqual(set(players[0:24]), set(results)) + self.assertEqual(status_code, SquadQueue.Mogi.ALGORITHM_STATUS_2_OR_MORE_ROOMS) + + def test_2_rooms_correctness_3(self): + """Test if 25P for 12 player rooms generates 2 rooms with correct players and correct status code even with extremely large range""" + players = [Player(None, f"{i*10000}", i*10000, True) for i in range(25)] + random.shuffle(players) + mogi = self._new_default_mogi() + for p in players: + mogi.teams.append(Team([p])) + results, status_code = mogi._one_room_final_list_algorithm(OneRoomAlgorithmTests.threshold_function) + self.assertSetEqual(set(players[0:24]), set(results)) + self.assertEqual(status_code, SquadQueue.Mogi.ALGORITHM_STATUS_2_OR_MORE_ROOMS) + + def test_1_room_correctness_1(self): + """Test if 15P for 12 player rooms generates 1 rooms with correct players and correct status code""" + assert self.threshold == 65 + players = [Player(None, f"{i}", abs(i-8)*(i-8), True) for i in range(17)] # -64, -49, -36, ... 0, 1, 4, ... 64 + players.pop(15) # Set 36 rating to not confirmed so only one possibility occurs + correct_result = players[2:14].copy() # Only one possibility that matches threshold + random.shuffle(players) + mogi = self._new_default_mogi() + for p in players: + mogi.teams.append(Team([p])) + results, status_code = mogi._one_room_final_list_algorithm(OneRoomAlgorithmTests.threshold_function) + self.assertEqual(status_code, SquadQueue.Mogi.ALGORITHM_STATUS_SUCCESS_FOUND) + self.assertSetEqual(set(correct_result), set(results)) + + def test_2_room_correctness_2(self): + """Test if 15P for 12 player rooms does not find any rooms when players are beyond range""" + assert self.threshold == 65 + players = [Player(None, f"{i}", (i-8)**3, True) for i in range(17)] # -512, -343, ... 0, 1, 8, ... 512 + correct_result = [] # No possible list of players + random.shuffle(players) + mogi = self._new_default_mogi() + for p in players: + mogi.teams.append(Team([p])) + results, status_code = mogi._one_room_final_list_algorithm(OneRoomAlgorithmTests.threshold_function) + self.assertEqual(status_code, SquadQueue.Mogi.ALGORITHM_STATUS_SUCCESS_EMPTY) + self.assertSetEqual(set(correct_result), set(results)) + + def test_2_room_correctness_3(self): + """Test if 15P for 12 player rooms finds the single correct solution""" + assert self.threshold == 65 + p1 = Player(None, "1", 10, True) + p2 = Player(None, "2", 5, True) + p3 = Player(None, "3", 14, True) + p4 = Player(None, "4", 75, True) + p5 = Player(None, "5", 8, True) + p6 = Player(None, "6", 20, True) + p7 = Player(None, "7", 0, True) + p8 = Player(None, "8", -3, True) + p9 = Player(None, "9", 4, True) + p10 = Player(None, "10", -105, True) + p11 = Player(None, "11", 2, True) + p12 = Player(None, "12", 15, True) + p13 = Player(None, "13", 16, True) + p14 = Player(None, "14", 74, True) + p15 = Player(None, "15", 9, True) + p16 = Player(None, "16", 100, True) + # p4 should be removed, p10 should be removed, p13 should be added, p15 should be added + players = [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16] + correct_result = [p1, p2, p3, p5, p6, p7, p8, p9, p11, p12, p13, p15] + mogi = self._new_default_mogi() + for p in players: + mogi.teams.append(Team([p])) + results, status_code = mogi._one_room_final_list_algorithm(OneRoomAlgorithmTests.threshold_function) + self.assertEqual(status_code, SquadQueue.Mogi.ALGORITHM_STATUS_SUCCESS_FOUND) + self.assertSetEqual(set(correct_result), set(results)) + + def test_2_room_correctness_4(self): + """Test if 15P for 12 player rooms finds no solution""" + assert self.threshold == 65 + p1 = Player(None, "1", 10, True) + p2 = Player(None, "2", 5, True) + p3 = Player(None, "3", 14, True) + p4 = Player(None, "4", 75, True) + p5 = Player(None, "5", 8, True) + p6 = Player(None, "6", 20, True) + p7 = Player(None, "7", 0, True) + p8 = Player(None, "8", -3, True) + p9 = Player(None, "9", 4, True) + p10 = Player(None, "10", -105, True) + p11 = Player(None, "11", 2, True) + p12 = Player(None, "12", 15, True) + p13 = Player(None, "13", 16, True) + p14 = Player(None, "14", 74, True) + p15 = Player(None, "15", 9, False) + p16 = Player(None, "16", 100, True) + players = [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16] + correct_result = [] # Same as previous test, but since p15 is not confirmed, no result is found + mogi = self._new_default_mogi() + for p in players: + mogi.teams.append(Team([p])) + results, status_code = mogi._one_room_final_list_algorithm(OneRoomAlgorithmTests.threshold_function) + self.assertEqual(status_code, SquadQueue.Mogi.ALGORITHM_STATUS_SUCCESS_EMPTY) + self.assertSetEqual(set(correct_result), set(results)) + + + +if __name__ == "__main__": + """If PyCharm, add --verbose flag to configuration. + Other IDEs, run from shell: python -m unittest ./unit_testing.py --verbose""" + unittest.main(verbosity=2) From 11a7794587166307d49b1ae93cc7f1cc1c6b28be Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 18:08:43 -0500 Subject: [PATCH 095/147] Bug fix --- lounge.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lounge.py b/lounge.py index ff597be..c9dd60e 100644 --- a/lounge.py +++ b/lounge.py @@ -11,7 +11,7 @@ case_insensitive=True, intents=intents, help_command=None) initial_extensions = ['cogs.SquadQueue'] -bot.config = common.config +bot.config = common.CONFIG @bot.event From 33b979372e320135c03f01b83e8222bc6c18c158 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 18:09:00 -0500 Subject: [PATCH 096/147] Added function for testing player ratings --- cogs/SquadQueue.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 0e09e22..eedcea2 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -1021,6 +1021,24 @@ async def debug_add_team(self, ctx, members: commands.Greedy[discord.Member]): await self.check_room_channels(mogi) await self.check_num_teams(mogi) + @commands.command(name="debug_add_many_ratings") + @commands.is_owner() + async def debug_add_many_ratings(self, ctx, ratings: str): + mogi = self.get_mogi(ctx) + if mogi is None: + return + if (not await self.is_started(ctx, mogi) + or not await self.is_gathering(ctx, mogi)): + return + for i, rating in enumerate(ratings.split(" ")): + if rating.isdecimal(): + player = Player(ctx.author, f"{ctx.author.display_name} {i}", int(rating), confirmed=True) + mogi.teams.append(Team([player])) + msg = f"Players added!" + await self.queue_or_send(ctx, msg) + await self.check_room_channels(mogi) + await self.check_num_teams(mogi) + @commands.command(name="debug_add_many_players") @commands.is_owner() async def debug_add_many_players(self, ctx, members: commands.Greedy[discord.Member]): From 7dbd37a54c7206fe0141cd557c184190899a5e67 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 18:25:56 -0500 Subject: [PATCH 097/147] Bug fixed for testing function --- cogs/SquadQueue.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index eedcea2..0274a5c 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -15,7 +15,7 @@ from mogi_objects import Mogi, Team, Player, Room, VoteView, JoinView, get_tier import asyncio from collections import defaultdict -from typing import Dict, List +from typing import Dict, List, Tuple import traceback import os import dill @@ -1023,18 +1023,18 @@ async def debug_add_team(self, ctx, members: commands.Greedy[discord.Member]): @commands.command(name="debug_add_many_ratings") @commands.is_owner() - async def debug_add_many_ratings(self, ctx, ratings: str): + async def debug_add_many_ratings(self, ctx, *ratings: str): mogi = self.get_mogi(ctx) if mogi is None: return if (not await self.is_started(ctx, mogi) or not await self.is_gathering(ctx, mogi)): return - for i, rating in enumerate(ratings.split(" ")): + for i, rating in enumerate(ratings): if rating.isdecimal(): player = Player(ctx.author, f"{ctx.author.display_name} {i}", int(rating), confirmed=True) mogi.teams.append(Team([player])) - msg = f"Players added!" + msg = f"Players added with the following ratings: {' '.join(['`' + r + '`' for r in ratings])}" await self.queue_or_send(ctx, msg) await self.check_room_channels(mogi) await self.check_num_teams(mogi) From 74237ad0cf409288350d301be10c50000b311ec8 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 18:34:37 -0500 Subject: [PATCH 098/147] Bug fix --- cogs/SquadQueue.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 0274a5c..1bb2024 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -780,8 +780,9 @@ def allowed_players_check(players): return mk8dx_players_allowed(players, self.room_mmr_threshold) all_confirmed_players = mogi.players_on_confirmed_teams() - regular_player_list = all_confirmed_players[:mogi.num_players//mogi.players_per_room] - late_player_list = all_confirmed_players[mogi.num_players//mogi.players_per_room:] + first_late_player_index = (mogi.num_players//mogi.players_per_room) * mogi.players_per_room + regular_player_list = all_confirmed_players[:first_late_player_index] + late_player_list = all_confirmed_players[first_late_player_index:] proposed_list = mogi.generate_proposed_list(allowed_players_check) for room_number, room_players in enumerate(divide_chunks(proposed_list, mogi.players_per_room), 1): msg = f"`Room {room_number} - Player List`\n" From ea59dd351792af94f8e8d36c7bd2c17469771943 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 18:34:58 -0500 Subject: [PATCH 099/147] Bug fix on display list --- cogs/SquadQueue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 1bb2024..2f9cb27 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -819,7 +819,7 @@ def allowed_players_check(players): msg = "`Late players:`\n" for i, player in enumerate(not_in_proposed_list, 1): removed_str = " *(Removed from player list)*" if player in regular_player_list else "" - msg += f"`{i+1}.` {player.lounge_name} ({int(player.mmr)} MMR) {removed_str}\n" + msg += f"`{i}.` {player.lounge_name} ({int(player.mmr)} MMR) {removed_str}\n" try: await mogi.mogi_channel.send(msg) except discord.DiscordException: From 712c3c66c7e724fa18762931c53acdfb9271aef5 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 18:35:12 -0500 Subject: [PATCH 100/147] Display modification --- cogs/SquadQueue.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 2f9cb27..78e4f6b 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -787,7 +787,7 @@ def allowed_players_check(players): for room_number, room_players in enumerate(divide_chunks(proposed_list, mogi.players_per_room), 1): msg = f"`Room {room_number} - Player List`\n" for player_num, player in enumerate(room_players, 1): - added_str = " *(Added from late players)*" if player in late_player_list else "" + added_str = ": **Added from late players**" if player in late_player_list else "" msg += f"""`{player_num}.` {player.lounge_name} ({player.mmr} MMR){added_str}\n""" if allowed_players_check(room_players): msg += f"\nThe mmr gap in the room is higher than the allowed threshold of {self.room_mmr_threshold} MMR, this room has been cancelled." @@ -818,7 +818,7 @@ def allowed_players_check(players): if len(not_in_proposed_list) > 0: msg = "`Late players:`\n" for i, player in enumerate(not_in_proposed_list, 1): - removed_str = " *(Removed from player list)*" if player in regular_player_list else "" + removed_str = ": **Removed from player list**" if player in regular_player_list else "" msg += f"`{i}.` {player.lounge_name} ({int(player.mmr)} MMR) {removed_str}\n" try: await mogi.mogi_channel.send(msg) From a4d0cdee70138a3f4db8312c1d42c93d3d9281c0 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 18:46:15 -0500 Subject: [PATCH 101/147] Bug fix --- cogs/SquadQueue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 78e4f6b..6a81d41 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -789,7 +789,7 @@ def allowed_players_check(players): for player_num, player in enumerate(room_players, 1): added_str = ": **Added from late players**" if player in late_player_list else "" msg += f"""`{player_num}.` {player.lounge_name} ({player.mmr} MMR){added_str}\n""" - if allowed_players_check(room_players): + if not allowed_players_check(room_players): msg += f"\nThe mmr gap in the room is higher than the allowed threshold of {self.room_mmr_threshold} MMR, this room has been cancelled." else: curr_room = mogi.rooms[room_number - 1] From a969b1650c5434ea5cf15617afc9d7c90382f388 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 18:57:31 -0500 Subject: [PATCH 102/147] Addresses #9 #9 --- cogs/SquadQueue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 6a81d41..f25108e 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -389,7 +389,7 @@ async def list_task(self): sorted_mogi_list = sorted(mogi_list, reverse=True) msg = f"**Queue closing: {discord.utils.format_dt(mogi.start_time)}**\n\n" msg += f"**Last Updated:** {discord.utils.format_dt(datetime.now(timezone.utc), style='R')}\n\n" - msg += "**Current Mogi List:**\n\n" + msg += "**Current Mogi List:**\n" for i in range(len(sorted_mogi_list)): msg += f"{i + 1}) " msg += ", ".join([p.lounge_name for p in sorted_mogi_list[i].players]) From 563bc6eaafaaf4894dbd112802101e8f05952193 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 18:57:51 -0500 Subject: [PATCH 103/147] Addresses optional part on #9 Optional part on #9 --- cogs/SquadQueue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index f25108e..6c72d37 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -388,7 +388,6 @@ async def list_task(self): sorted_mogi_list = sorted(mogi_list, reverse=True) msg = f"**Queue closing: {discord.utils.format_dt(mogi.start_time)}**\n\n" - msg += f"**Last Updated:** {discord.utils.format_dt(datetime.now(timezone.utc), style='R')}\n\n" msg += "**Current Mogi List:**\n" for i in range(len(sorted_mogi_list)): msg += f"{i + 1}) " @@ -401,6 +400,7 @@ async def list_task(self): msg += f"{i + 1}) " msg += ", ".join([p.lounge_name for p in late_players[i].players]) msg += f" ({late_players[i].players[0].mmr} MMR)\n" + msg += f"\n**Last Updated:** {discord.utils.format_dt(datetime.now(timezone.utc), style='R')}" message = msg.split("\n") new_messages = [] From 3093cc499d7efb783dd3544eed85cd4c3c1bf0dd Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 19:09:16 -0500 Subject: [PATCH 104/147] Bug fix on algorithm --- mogi_objects.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index cf0786f..a82bf88 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -79,7 +79,7 @@ def _one_room_final_list_algorithm(self, valid_players_check: Callable[[List[Pla if self.max_possible_rooms == 0: return [], Mogi.ALGORITHM_STATUS_INSUFFICIENT_PLAYERS if self.max_possible_rooms > 1: - confirmed_players = self.players_on_confirmed_teams() + confirmed_players = sorted(self.players_on_confirmed_teams()) return confirmed_players[0:self.players_per_room * self.max_possible_rooms], Mogi.ALGORITHM_STATUS_2_OR_MORE_ROOMS # At this point, we can only make one possible room, so our algorithm will be used confirmed_players = self.players_on_confirmed_teams() @@ -97,7 +97,7 @@ def _one_room_final_list_algorithm(self, valid_players_check: Callable[[List[Pla return [], Mogi.ALGORITHM_STATUS_SUCCESS_EMPTY def _mk8dx_generate_final_list(self) -> List[Player]: - confirmed_players = self.players_on_confirmed_teams() + confirmed_players = sorted(self.players_on_confirmed_teams()) return confirmed_players[0:self.players_per_room * self.max_possible_rooms] def _mkw_generate_final_list(self, valid_players_check: Callable[[List[Player]], bool]) -> List[Player]: From ba44c7605d450cd91ef4cce071eaa3610f8aa275 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 20:14:41 -0500 Subject: [PATCH 105/147] Make players comparable based on mmr --- mogi_objects.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index a82bf88..1ac927b 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -61,7 +61,7 @@ def _minimize_range(players: List[Player], num_players: int) -> List[Player] | N if num_players <= 1: return None # Sort the players so we easily know the player with the lowest rating and highest rating in any given collection - sorted_players = sorted(players, key=lambda p: p.mmr) + sorted_players = sorted(players, reverse=True) # In the beginning, the best found collection of players is the first 12 best_collection = sorted_players[0:num_players] cur_min = best_collection[-1].mmr - best_collection[0].mmr @@ -116,6 +116,8 @@ def generate_proposed_list(self, valid_players_check: Callable[[List[Player]], b return self._mk8dx_generate_final_list() elif common.SERVER is common.Server.MKW: return self._mkw_generate_final_list(valid_players_check) + else: + raise ValueError(f"Unknown server in config: {common.Server}") def check_player(self, member): for team in self.teams: @@ -158,13 +160,13 @@ def __init__(self, teams, room_num: int, thread: discord.Thread): def mmr_high(self) -> int: if self.teams is None: return None - return max(self.players, key=lambda p: p.mmr).mmr + return max(self.players).mmr @property def mmr_low(self) -> int: if self.teams is None: return None - return min(self.players, key=lambda p: p.mmr).mmr + return min(self.players).mmr @property def avg_mmr(self) -> float: @@ -248,6 +250,9 @@ def mention(self): def __repr__(self): return f"{__class__.__name__}(member={self.member}, lounge_name={self.lounge_name}, mmr={self.mmr}, confirmed={self.confirmed})" + def __lt__(self, other: Player): + return self.mmr < other.mmr + class VoteView(View): def __init__(self, players, thread, mogi: Mogi, tier_info): From 60d74a10d4fd5b7ccdc1f4c8d2d5ad20e14a2e1a Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 20:15:23 -0500 Subject: [PATCH 106/147] Bug fix --- cogs/SquadQueue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 6c72d37..6f42e6c 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -783,7 +783,7 @@ def allowed_players_check(players): first_late_player_index = (mogi.num_players//mogi.players_per_room) * mogi.players_per_room regular_player_list = all_confirmed_players[:first_late_player_index] late_player_list = all_confirmed_players[first_late_player_index:] - proposed_list = mogi.generate_proposed_list(allowed_players_check) + proposed_list = sorted(mogi.generate_proposed_list(allowed_players_check), reverse=True) for room_number, room_players in enumerate(divide_chunks(proposed_list, mogi.players_per_room), 1): msg = f"`Room {room_number} - Player List`\n" for player_num, player in enumerate(room_players, 1): From 341ec83a43b7dec86e0bd3716e0fd8c4b87bace6 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 20:15:36 -0500 Subject: [PATCH 107/147] Modify display of test function --- cogs/SquadQueue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 6f42e6c..64cc4c1 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -1031,7 +1031,7 @@ async def debug_add_many_ratings(self, ctx, *ratings: str): if (not await self.is_started(ctx, mogi) or not await self.is_gathering(ctx, mogi)): return - for i, rating in enumerate(ratings): + for i, rating in enumerate(ratings, 1): if rating.isdecimal(): player = Player(ctx.author, f"{ctx.author.display_name} {i}", int(rating), confirmed=True) mogi.teams.append(Team([player])) From c76df70591cbe6b4fce391b5d64e668eba440238 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 20:21:25 -0500 Subject: [PATCH 108/147] More bug fixes --- mogi_objects.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index 1ac927b..170bd59 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -74,12 +74,11 @@ def _minimize_range(players: List[Player], num_players: int) -> List[Player] | N best_collection = sorted_players[lowest_player_index:lowest_player_index + num_players] return best_collection - def _one_room_final_list_algorithm(self, valid_players_check: Callable[[List[Player]], bool]) -> Tuple[ - List[Player], int]: + def _one_room_final_list_algorithm(self, valid_players_check: Callable[[List[Player]], bool]) -> Tuple[List[Player], int]: if self.max_possible_rooms == 0: return [], Mogi.ALGORITHM_STATUS_INSUFFICIENT_PLAYERS if self.max_possible_rooms > 1: - confirmed_players = sorted(self.players_on_confirmed_teams()) + confirmed_players = self.players_on_confirmed_teams() return confirmed_players[0:self.players_per_room * self.max_possible_rooms], Mogi.ALGORITHM_STATUS_2_OR_MORE_ROOMS # At this point, we can only make one possible room, so our algorithm will be used confirmed_players = self.players_on_confirmed_teams() @@ -97,7 +96,7 @@ def _one_room_final_list_algorithm(self, valid_players_check: Callable[[List[Pla return [], Mogi.ALGORITHM_STATUS_SUCCESS_EMPTY def _mk8dx_generate_final_list(self) -> List[Player]: - confirmed_players = sorted(self.players_on_confirmed_teams()) + confirmed_players = self.players_on_confirmed_teams() return confirmed_players[0:self.players_per_room * self.max_possible_rooms] def _mkw_generate_final_list(self, valid_players_check: Callable[[List[Player]], bool]) -> List[Player]: From 819f1821174ca57eec953d3e42230adc73b1ca05 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 20:38:50 -0500 Subject: [PATCH 109/147] Another bug fix --- mogi_objects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mogi_objects.py b/mogi_objects.py index 170bd59..72362a0 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -61,7 +61,7 @@ def _minimize_range(players: List[Player], num_players: int) -> List[Player] | N if num_players <= 1: return None # Sort the players so we easily know the player with the lowest rating and highest rating in any given collection - sorted_players = sorted(players, reverse=True) + sorted_players = sorted(players) # In the beginning, the best found collection of players is the first 12 best_collection = sorted_players[0:num_players] cur_min = best_collection[-1].mmr - best_collection[0].mmr From 63d9a119ea337f2e3cb0fdb938d7cd261457103d Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 20:49:35 -0500 Subject: [PATCH 110/147] Fixes #13 + Clean refactor Fixes #13, plus a more clean refactor of this code. --- cogs/SquadQueue.py | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 64cc4c1..af206e2 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -373,33 +373,21 @@ async def list_task(self): if not mogi.gathering: await self.delete_list_messages(0) return + all_confirmed_players = mogi.players_on_confirmed_teams() + first_late_player_index = (mogi.num_players // mogi.players_per_room) * mogi.players_per_room + on_time_players = sorted(all_confirmed_players[:first_late_player_index], reverse=True) + late_players = all_confirmed_players[first_late_player_index:] - mogi_list = mogi.confirmed_teams() - - # Remove late players from the list to display separately - full_list_length = len(mogi_list) - num_of_rooms = full_list_length // 12 - num_confirmed_players = num_of_rooms * 12 - num_late_players = full_list_length - num_confirmed_players - late_players = [] - for i in range(num_confirmed_players, full_list_length): - player = mogi_list.pop() - late_players.append(player) - - sorted_mogi_list = sorted(mogi_list, reverse=True) msg = f"**Queue closing: {discord.utils.format_dt(mogi.start_time)}**\n\n" msg += "**Current Mogi List:**\n" - for i in range(len(sorted_mogi_list)): - msg += f"{i + 1}) " - msg += ", ".join([p.lounge_name for p in sorted_mogi_list[i].players]) - msg += f" ({sorted_mogi_list[i].players[0].mmr} MMR)\n" - if ((i + 1) % 12 == 0): + for i, player in enumerate(on_time_players, 1): + msg += f"{i}) {player.lounge_name} ({player.mmr} MMR)\n" + if i % mogi.players_per_room == 0: msg += "ㅤ\n" + msg += "**Late Players:**\n" - for i in range(len(late_players)): - msg += f"{i + 1}) " - msg += ", ".join([p.lounge_name for p in late_players[i].players]) - msg += f" ({late_players[i].players[0].mmr} MMR)\n" + for i, player in enumerate(late_players, 1): + msg += f"{i}) {player.lounge_name} ({player.mmr} MMR)\n" msg += f"\n**Last Updated:** {discord.utils.format_dt(datetime.now(timezone.utc), style='R')}" message = msg.split("\n") From 42c9cbb4c60e451c101a6e2d164ae6c7f58fc477 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 21:01:21 -0500 Subject: [PATCH 111/147] Minor text adjustment --- cogs/SquadQueue.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index af206e2..53368c6 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -384,7 +384,8 @@ async def list_task(self): msg += f"{i}) {player.lounge_name} ({player.mmr} MMR)\n" if i % mogi.players_per_room == 0: msg += "ㅤ\n" - + if len(on_time_players) == 0: # Text looks better this way + msg += "\n" msg += "**Late Players:**\n" for i, player in enumerate(late_players, 1): msg += f"{i}) {player.lounge_name} ({player.mmr} MMR)\n" From 7bf68ddb9040a253ef89156e2fcafbcc6ac8c7cc Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 21:23:49 -0500 Subject: [PATCH 112/147] Forgot to upload part of refactor --- cogs/SquadQueue.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 53368c6..5b68e4c 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -826,7 +826,7 @@ async def check_num_teams(self, mogi: Mogi): return cur_time = datetime.now(timezone.utc) if mogi.start_time <= cur_time: - num_leftover_teams = mogi.count_registered() % int((12 / mogi.players_per_team)) + num_leftover_teams = mogi.count_registered() % int((12 / mogi.max_player_per_team)) if num_leftover_teams == 0: mogi.gathering = False await self.lockdown(mogi.mogi_channel) @@ -845,14 +845,14 @@ async def ongoing_mogi_checks(self): mogi.gathering = False elif mogi.start_time <= cur_time and mogi.gathering: # check if there are an even amount of teams since we are past the queue time - num_leftover_teams = mogi.count_registered() % int((12 / mogi.players_per_team)) + num_leftover_teams = mogi.count_registered() % int((12 / mogi.max_player_per_team)) if num_leftover_teams == 0: mogi.gathering = False else: if int(cur_time.second / 20) == 0: force_time = mogi.start_time + self.EXTENSION_TIME minutes_left = (force_time - cur_time).seconds // 60 - x_teams = int(int(12 / mogi.players_per_team) - num_leftover_teams) + x_teams = int(int(12 / mogi.max_player_per_team) - num_leftover_teams) await mogi.mogi_channel.send( f"Need {x_teams} more player(s) to start immediately. Starting in {minutes_left + 1} minute(s) regardless.") if not mogi.gathering: From e7f2554f228ed3d80746baa86cdbf11de9ceca1d Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sat, 6 Jul 2024 21:33:39 -0500 Subject: [PATCH 113/147] Reverse part of commit, add error message back when an exception occurs with the room channel. --- cogs/SquadQueue.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 5b68e4c..83b7631 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -794,6 +794,9 @@ def allowed_players_check(players): curr_room.view = view await curr_room.thread.send(view=view) except discord.DiscordException: + err_msg = f"\nAn error has occurred while creating the room channel; please contact your opponents in DM or another channel\n" + err_msg += player_mentions + extra_member_mentions + msg += err_msg print(traceback.format_exc()) try: From e876296819f5c82d6ece0241d4e0cd89a46be83c Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sun, 7 Jul 2024 20:48:24 -0500 Subject: [PATCH 114/147] Extend discord command Just a shell. To test the command shows properly on Discord. --- cogs/SquadQueue.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 83b7631..381ba0f 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -238,6 +238,18 @@ async def is_gathering(self, ctx, mogi: Mogi): await ctx.send("Mogi is closed; players cannot join or drop from the event") return mogi.gathering + @app_commands.command(name="extend") + @app_commands.guild_only() + async def extend(self, interaction: discord.Interaction, minutes: int): + """Extend the queue + + Parameters + ----------- + minutes: int + The number of minutes to add to the extension time. Can be negative. + """ + await interaction.response.send_message(f"Extended queue by an additional {minutes} minute(s).") + @app_commands.command(name="c") @app_commands.guild_only() async def can(self, interaction: discord.Interaction): From 65639d22d8dee16a4aa2ae748e56fc1e70485e2d Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sun, 7 Jul 2024 21:11:30 -0500 Subject: [PATCH 115/147] Add additional extension time to Mogi object --- mogi_objects.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mogi_objects.py b/mogi_objects.py index 72362a0..0f022e3 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -21,7 +21,7 @@ class Mogi: ALGORITHM_STATUS_SUCCESS_EMPTY = 4 def __init__(self, sq_id: int, max_players_per_team: int, players_per_room: int, mogi_channel: discord.TextChannel, - is_automated=False, start_time=None): + is_automated=False, start_time=None, additional_extension_minutes=0): self.started = False self.gathering = False self.making_rooms_run = False @@ -34,6 +34,7 @@ def __init__(self, sq_id: int, max_players_per_team: int, players_per_room: int, self.rooms: List[Room] = [] self.is_automated = is_automated self.start_time = start_time if is_automated else None + self.additional_extension_minutes = additional_extension_minutes @property def num_players(self): From c8eae07ebb73ff16a9b7c127d3556ab4c5be2aa2 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sun, 7 Jul 2024 21:21:19 -0500 Subject: [PATCH 116/147] Change the extension functionality to... incorporate the additional extension minutes. --- cogs/SquadQueue.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 381ba0f..91b731c 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -856,7 +856,8 @@ async def ongoing_mogi_checks(self): if not mogi.is_automated or not mogi.started or mogi.making_rooms_run: return cur_time = datetime.now(timezone.utc) - if (mogi.start_time + self.EXTENSION_TIME) <= cur_time: + force_start_time = mogi.start_time + self.EXTENSION_TIME + mogi.additional_extension_minutes + if force_start_time <= cur_time: mogi.gathering = False elif mogi.start_time <= cur_time and mogi.gathering: # check if there are an even amount of teams since we are past the queue time @@ -865,8 +866,7 @@ async def ongoing_mogi_checks(self): mogi.gathering = False else: if int(cur_time.second / 20) == 0: - force_time = mogi.start_time + self.EXTENSION_TIME - minutes_left = (force_time - cur_time).seconds // 60 + minutes_left = (force_start_time - cur_time).seconds // 60 x_teams = int(int(12 / mogi.max_player_per_team) - num_leftover_teams) await mogi.mogi_channel.send( f"Need {x_teams} more player(s) to start immediately. Starting in {minutes_left + 1} minute(s) regardless.") From 591f8f94aa58e9ce5225132f519e03407f7367ae Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sun, 7 Jul 2024 21:23:39 -0500 Subject: [PATCH 117/147] Finish extend functionality --- cogs/SquadQueue.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 91b731c..b49d58b 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -248,6 +248,11 @@ async def extend(self, interaction: discord.Interaction, minutes: int): minutes: int The number of minutes to add to the extension time. Can be negative. """ + mogi = self.get_mogi(interaction) + if mogi is None or not mogi.started or not mogi.gathering: + await interaction.followup.send("Queue has not started yet.") + return + mogi.additional_extension_minutes += minutes await interaction.response.send_message(f"Extended queue by an additional {minutes} minute(s).") @app_commands.command(name="c") From 1ff190873ba2c5b860b71494461c4515f6a93fdf Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sun, 7 Jul 2024 21:28:05 -0500 Subject: [PATCH 118/147] Bug fixes: extension int should be timedelta --- cogs/SquadQueue.py | 2 +- mogi_objects.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index b49d58b..8986275 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -252,7 +252,7 @@ async def extend(self, interaction: discord.Interaction, minutes: int): if mogi is None or not mogi.started or not mogi.gathering: await interaction.followup.send("Queue has not started yet.") return - mogi.additional_extension_minutes += minutes + mogi.additional_extension_minutes += timedelta(minutes=minutes) await interaction.response.send_message(f"Extended queue by an additional {minutes} minute(s).") @app_commands.command(name="c") diff --git a/mogi_objects.py b/mogi_objects.py index 0f022e3..1a790d3 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -34,7 +34,7 @@ def __init__(self, sq_id: int, max_players_per_team: int, players_per_room: int, self.rooms: List[Room] = [] self.is_automated = is_automated self.start_time = start_time if is_automated else None - self.additional_extension_minutes = additional_extension_minutes + self.additional_extension = timedelta(minutes=additional_extension_minutes) @property def num_players(self): From 1f37c2c3e006ff3f5445f0c8526878b2afb7c351 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sun, 7 Jul 2024 21:30:29 -0500 Subject: [PATCH 119/147] Bug fix, did not rename class variable in all places --- cogs/SquadQueue.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 8986275..ba4d0dc 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -252,7 +252,7 @@ async def extend(self, interaction: discord.Interaction, minutes: int): if mogi is None or not mogi.started or not mogi.gathering: await interaction.followup.send("Queue has not started yet.") return - mogi.additional_extension_minutes += timedelta(minutes=minutes) + mogi.additional_extension += timedelta(minutes=minutes) await interaction.response.send_message(f"Extended queue by an additional {minutes} minute(s).") @app_commands.command(name="c") @@ -861,7 +861,7 @@ async def ongoing_mogi_checks(self): if not mogi.is_automated or not mogi.started or mogi.making_rooms_run: return cur_time = datetime.now(timezone.utc) - force_start_time = mogi.start_time + self.EXTENSION_TIME + mogi.additional_extension_minutes + force_start_time = mogi.start_time + self.EXTENSION_TIME + mogi.additional_extension if force_start_time <= cur_time: mogi.gathering = False elif mogi.start_time <= cur_time and mogi.gathering: From 9f516ff59094b1570e19b5458d3fd0b87a18f1c3 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sun, 7 Jul 2024 23:06:46 -0500 Subject: [PATCH 120/147] Fix bug with mmr check when subbing --- mogi_objects.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mogi_objects.py b/mogi_objects.py index 1a790d3..c4caa0d 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -414,6 +414,10 @@ async def button_callback(self, interaction: discord.Interaction, button): if self.room.room_num == self.bottom_room_num: self.room.mmr_low = -999999 if isinstance(user_mmr, int) and self.room.mmr_high + 500 > user_mmr > self.room.mmr_low - 500: + mmr_high = 999999 if self.room.room_num == 1 else self.room.mmr_high + mmr_low = -999999 if self.room.room_num == self.bottom_room_num else self.room.mmr_low + if isinstance(user_mmr, int) and mmr_high + 500 > user_mmr > mmr_low - 500: + self.room.subs.append(interaction.user.id) button.disabled = True await interaction.followup.edit_message(interaction.message.id, view=self) mention = interaction.user.mention From c634d6391218d54ee21701eaece1c26d79078e76 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sun, 7 Jul 2024 23:07:28 -0500 Subject: [PATCH 121/147] See last commit --- mogi_objects.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index c4caa0d..5f33df1 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -409,11 +409,6 @@ async def button_callback(self, interaction: discord.Interaction, button): await interaction.followup.send( "MMR lookup for player has failed, please try again.", ephemeral=True) return - if self.room.room_num == 1: - self.room.mmr_high = 999999 - if self.room.room_num == self.bottom_room_num: - self.room.mmr_low = -999999 - if isinstance(user_mmr, int) and self.room.mmr_high + 500 > user_mmr > self.room.mmr_low - 500: mmr_high = 999999 if self.room.room_num == 1 else self.room.mmr_high mmr_low = -999999 if self.room.room_num == self.bottom_room_num else self.room.mmr_low if isinstance(user_mmr, int) and mmr_high + 500 > user_mmr > mmr_low - 500: From 1a2efda107c987da79aa0330803c7ec391942e82 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sun, 7 Jul 2024 23:08:44 -0500 Subject: [PATCH 122/147] Prevent someone from subbing into room twice --- mogi_objects.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mogi_objects.py b/mogi_objects.py index 5f33df1..854b7a9 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -155,6 +155,7 @@ def __init__(self, teams, room_num: int, thread: discord.Thread): self.thread = thread self.view = None self.finished = False + self.subs: List["Player"] = [] @property def mmr_high(self) -> int: @@ -399,7 +400,7 @@ async def button_callback(self, interaction: discord.Interaction, button): await interaction.followup.send( "Players with the muted or restricted role cannot use the sub button.", ephemeral=True) return - if interaction.user.id in self.room.get_player_list(): + if interaction.user.id in self.room.get_player_list() + self.room.subs: await interaction.followup.send( "You are already in this room.", ephemeral=True) return @@ -409,6 +410,11 @@ async def button_callback(self, interaction: discord.Interaction, button): await interaction.followup.send( "MMR lookup for player has failed, please try again.", ephemeral=True) return + # Need a 2nd check to control the race condition introduced by "await self.get_mmr" + if interaction.user.id in self.room.get_player_list() + self.room.subs: + await interaction.followup.send( + "You are already in this room.", ephemeral=True) + return mmr_high = 999999 if self.room.room_num == 1 else self.room.mmr_high mmr_low = -999999 if self.room.room_num == self.bottom_room_num else self.room.mmr_low if isinstance(user_mmr, int) and mmr_high + 500 > user_mmr > mmr_low - 500: From b196cff63c8b774bb3ac817c98f0a7f4e29652fc Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sun, 7 Jul 2024 23:33:37 -0500 Subject: [PATCH 123/147] Structure to Player object to support hosts --- mogi_objects.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mogi_objects.py b/mogi_objects.py index 1a790d3..a1d71d5 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -233,12 +233,14 @@ def __str__(self): class Player: - def __init__(self, member: discord.Member, lounge_name: str, mmr: int, confirmed=False): + def __init__(self, member: discord.Member, lounge_name: str, mmr: int, confirmed=False, host=False): self.member = member self.lounge_name = lounge_name self.mmr = mmr self.confirmed = confirmed self.score = 0 + self.host = host + self.host_fc = None @property def mention(self): From 2201bccceadb63b766b58c647d9ff40c0ac70d55 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Sun, 7 Jul 2024 23:34:45 -0500 Subject: [PATCH 124/147] Can host command interface support --- cogs/SquadQueue.py | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index ba4d0dc..3b948a7 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -255,10 +255,19 @@ async def extend(self, interaction: discord.Interaction, minutes: int): mogi.additional_extension += timedelta(minutes=minutes) await interaction.response.send_message(f"Extended queue by an additional {minutes} minute(s).") + @app_commands.command(name="ch") + @app_commands.guild_only() + async def can_host(self, interaction: discord.Interaction): + """Join a mogi as a host""" + await self.join_queue(interaction, host=True) + @app_commands.command(name="c") @app_commands.guild_only() async def can(self, interaction: discord.Interaction): """Join a mogi""" + await self.join_queue(interaction) + + async def join_queue(self, interaction: discord.Interaction, host=False): await interaction.response.defer() async with self.LOCK: member = interaction.user @@ -268,9 +277,24 @@ async def can(self, interaction: discord.Interaction): return player_team = mogi.check_player(member) - - if player_team is not None: - await interaction.followup.send(f"{interaction.user.mention} is already signed up.") + player = None if player_team is None else player_team.get_player(member) + + if player is not None: + # The player is already signed up, but they might be changing to a host or non-host. Begin checks: + # The player was queued as host, and they queued again as a host + if player.host and host: + await interaction.followup.send(f"{interaction.user.mention} is already signed up as a host.") + # The player was queued as host, and they queued again as a non-host + elif player.host and not host: + await interaction.followup.send(f"{interaction.user.mention} has changed to a non-host.") + # The player was not queued as host, but they are changing to a host + elif not player.host and host: + await interaction.followup.send(f"{interaction.user.mention} has changed to a host.") + # The player was not queued as host and did not change to a host + elif not player.host and not host: + await interaction.followup.send(f"{interaction.user.mention} is already signed up.") + + player.host = host return msg = "" @@ -290,10 +314,11 @@ async def can(self, interaction: discord.Interaction): return players[0].confirmed = True + players[0].host = host squad = Team(players) mogi.teams.append(squad) - - msg += f"{players[0].lounge_name} joined queue closing at {discord.utils.format_dt(mogi.start_time)}, `[{mogi.count_registered()} players]`" + host_str = " as a host " if host else " " + msg += f"{players[0].lounge_name} joined queue{host_str}closing at {discord.utils.format_dt(mogi.start_time)}, `[{mogi.count_registered()} players]`" await interaction.followup.send(msg) await self.check_room_channels(mogi) From 8fb53c73e371e849a9ff71d4653105de42bbba79 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Mon, 8 Jul 2024 00:53:13 -0500 Subject: [PATCH 125/147] Create host_fcs.py --- host_fcs.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 host_fcs.py diff --git a/host_fcs.py b/host_fcs.py new file mode 100644 index 0000000..438c6f0 --- /dev/null +++ b/host_fcs.py @@ -0,0 +1,44 @@ +import common +from typing import Dict +from collections.abc import Iterable +import aiohttp + +MKW_HOST_API_URL = f"https://mkwlounge.gg/api/hostfc.php?discord_guild_id={common.CONFIG['guild_id']}&discord_user_id=" +MKW_HOST_API_URL = f"https://mkwlounge.gg/api/hostfc.php?discord_guild_id=387347467332485122&discord_user_id=" + +SESSION = None + + +def create_new_session(): + global SESSION + SESSION = aiohttp.ClientSession() + + +async def _get_mk8dx_hosts(discord_ids: Iterable[str]) -> Dict[str, str]: + return {} + + +async def _get_mkw_hosts(discord_ids: Iterable[str]) -> Dict[str, str]: + request_url = MKW_HOST_API_URL + ",".join(discord_ids) + async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=5)) as session: + async with session.get(request_url) as r: + if r.status != 200: + print(f"hostfc endpoint returned status {r.status}") + return {} + results = await r.json() + if "status" not in results or results["status"] != "success": + print(f"hostfc endpoint returned unsuccessful results {results}") + return {} + host_mapping = {} + for item in results["success"]: + host_mapping[item["discord_user_id"]] = item["fc"] + return host_mapping + + +async def get_hosts(discord_ids: Iterable[str]) -> Dict[str, str]: + if common.SERVER is common.Server.MKW: + return await _get_mkw_hosts(discord_ids) + elif common.SERVER is common.Server.MK8DX: + return await _get_mk8dx_hosts(discord_ids) + else: + raise ValueError("Bad config settings for server.") From a0ecb52c62bc337b2b81b70027795aa4da312890 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Mon, 8 Jul 2024 00:55:42 -0500 Subject: [PATCH 126/147] Mogi.populate_host_fcs --- host_fcs.py | 2 +- mogi_objects.py | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/host_fcs.py b/host_fcs.py index 438c6f0..fa21d4a 100644 --- a/host_fcs.py +++ b/host_fcs.py @@ -30,7 +30,7 @@ async def _get_mkw_hosts(discord_ids: Iterable[str]) -> Dict[str, str]: print(f"hostfc endpoint returned unsuccessful results {results}") return {} host_mapping = {} - for item in results["success"]: + for item in results["results"]: host_mapping[item["discord_user_id"]] = item["fc"] return host_mapping diff --git a/mogi_objects.py b/mogi_objects.py index a1d71d5..25bef0b 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -7,6 +7,7 @@ from datetime import datetime, timezone, timedelta from discord.ui import View from typing import List, Tuple, Callable +import host_fcs @@ -147,6 +148,14 @@ def get_room_from_thread(self, channel_id: int): return room return None + async def populate_host_fcs(self): + all_hosts = {str(plr.member.id): plr for plr in filter(lambda p: p.host, self.players_on_confirmed_teams())} + hosts = await host_fcs.get_hosts(all_hosts) + for host_discord_id, host_fc in hosts.items(): + player: Player = all_hosts.get(host_discord_id) + if player is not None: + player.host_fc = host_fc + class Room: def __init__(self, teams, room_num: int, thread: discord.Thread): From 36a73ddd2ba27a1cf1c659976a2cee290ea7ded7 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Mon, 8 Jul 2024 00:56:07 -0500 Subject: [PATCH 127/147] Room.host_list to keep track of the room's host order --- mogi_objects.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mogi_objects.py b/mogi_objects.py index 25bef0b..f8089fe 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -164,6 +164,7 @@ def __init__(self, teams, room_num: int, thread: discord.Thread): self.thread = thread self.view = None self.finished = False + self.host_list: List["Player"] = [] @property def mmr_high(self) -> int: From 1902da7e107406858e9773241a2a82e1124b079b Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Mon, 8 Jul 2024 00:57:13 -0500 Subject: [PATCH 128/147] Room.create_host_list Function to generate the host list depending on the rooms players who queued as host --- mogi_objects.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mogi_objects.py b/mogi_objects.py index f8089fe..8dc232e 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -193,6 +193,12 @@ def players(self) -> List[Player]: def get_player_list(self): return [player.member.id for team in self.teams for player in team.players] + def create_host_list(self): + all_hosts = list(filter(lambda p: p.host, self.players)) + random.shuffle(all_hosts) + self.host_list.clear() + self.host_list.extend(all_hosts) + class Team: def __init__(self, players: List["Player"]): From a487769be16b6d48da7ca9ca397b78f54ccdd3fe Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Mon, 8 Jul 2024 00:58:03 -0500 Subject: [PATCH 129/147] Room.get_host_str Returns a nicely formatted string with the host order --- mogi_objects.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/mogi_objects.py b/mogi_objects.py index 8dc232e..6a95da3 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -199,6 +199,21 @@ def create_host_list(self): self.host_list.clear() self.host_list.extend(all_hosts) + def get_host_str(self) -> str: + if len(self.host_list) == 0: + return "" + host_strs = [] + for i, player in enumerate(self.host_list, 1): + host_strs.append(f"{i}. {player.member.display_name}") + # First player on the list should be bold + if i == 1: + host_strs[0] = f"**{host_strs[0]}**" + result = f"Host: {', '.join(host_strs)}" + if common.SERVER is common.Server.MKW and self.host_list[0].host_fc is not None: + result += f"**Host ({self.host_list[0].member.display_name}) Friend Code: {self.host_list[0].host_fc}**" + return result + + class Team: def __init__(self, players: List["Player"]): From 85d5cb385238df7a09001f5f34a7dcb47ec8d606 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Mon, 8 Jul 2024 00:59:00 -0500 Subject: [PATCH 130/147] Add mogi_objects.Room to VoteView --- cogs/SquadQueue.py | 2 +- mogi_objects.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 3b948a7..9e58daf 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -832,7 +832,7 @@ def allowed_players_check(players): {player_mentions} {extra_member_mentions}""" try: await curr_room.thread.send(room_msg) - view = VoteView(room_players, curr_room.thread, mogi, self.TIER_INFO) + view = VoteView(room_players, curr_room.thread, mogi, curr_room, self.TIER_INFO) curr_room.view = view await curr_room.thread.send(view=view) except discord.DiscordException: diff --git a/mogi_objects.py b/mogi_objects.py index 6a95da3..c0f09c3 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -288,11 +288,12 @@ def __lt__(self, other: Player): class VoteView(View): - def __init__(self, players, thread, mogi: Mogi, tier_info): + def __init__(self, players, thread, mogi: Mogi, room: Room, tier_info): super().__init__() self.players = players self.thread = thread self.mogi = mogi + self.room = room self.header_text = "" self.teams_text = "" self.found_winner = False From 6ec8faf069b33c4007214c3e0f3fec5bfd56611f Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Mon, 8 Jul 2024 01:00:50 -0500 Subject: [PATCH 131/147] Populate host fcs and create the host list when queueing closes --- cogs/SquadQueue.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 9e58daf..acc0fda 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -815,6 +815,7 @@ def allowed_players_check(players): regular_player_list = all_confirmed_players[:first_late_player_index] late_player_list = all_confirmed_players[first_late_player_index:] proposed_list = sorted(mogi.generate_proposed_list(allowed_players_check), reverse=True) + await mogi.populate_host_fcs() for room_number, room_players in enumerate(divide_chunks(proposed_list, mogi.players_per_room), 1): msg = f"`Room {room_number} - Player List`\n" for player_num, player in enumerate(room_players, 1): @@ -825,6 +826,7 @@ def allowed_players_check(players): else: curr_room = mogi.rooms[room_number - 1] curr_room.teams = [Team([p]) for p in room_players] + curr_room.create_host_list() player_mentions = " ".join([p.mention for p in room_players]) extra_member_mentions = " ".join([m.mention for m in extra_members if m is not None]) room_msg = f"""{msg} From 037f7001e1bec1f6468f55365575e4383a1aa3a9 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Mon, 8 Jul 2024 01:01:51 -0500 Subject: [PATCH 132/147] When room begins, display the hosts appropriately --- mogi_objects.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mogi_objects.py b/mogi_objects.py index c0f09c3..29530a8 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -348,7 +348,11 @@ async def make_teams(self, format_): penalty_time = self.mogi.making_rooms_run_time + timedelta(minutes=8) room_open_time = self.mogi.making_rooms_run_time - msg += f"Decide a host amongst yourselves; room open at :{room_open_time.minute:02}, penalty at :{penalty_time.minute:02}. Good luck!" + potential_host_str = self.room.get_host_str() + if potential_host_str == "": + msg += f"Decide a host amongst yourselves; room open at :{room_open_time.minute:02}, penalty at :{penalty_time.minute:02}. Good luck!" + else: + msg += f"{potential_host_str}\n\nRoom open at :{room_open_time.minute:02}, penalty at :{penalty_time.minute:02}. Good luck!" room.teams = teams From 735ed34ec9fed95c7c759d593b96e6b9bc3077b7 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Mon, 8 Jul 2024 01:02:37 -0500 Subject: [PATCH 133/147] Textual bug fix --- mogi_objects.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index 29530a8..c717629 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -210,11 +210,10 @@ def get_host_str(self) -> str: host_strs[0] = f"**{host_strs[0]}**" result = f"Host: {', '.join(host_strs)}" if common.SERVER is common.Server.MKW and self.host_list[0].host_fc is not None: - result += f"**Host ({self.host_list[0].member.display_name}) Friend Code: {self.host_list[0].host_fc}**" + result += f"\n**Host ({self.host_list[0].member.display_name}) Friend Code: {self.host_list[0].host_fc}**" return result - class Team: def __init__(self, players: List["Player"]): self.players = players From eef42473536d4baaba0f3d3823edba1f4919b976 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Mon, 8 Jul 2024 01:07:04 -0500 Subject: [PATCH 134/147] Change back to guild id for production --- host_fcs.py | 1 - 1 file changed, 1 deletion(-) diff --git a/host_fcs.py b/host_fcs.py index fa21d4a..a107ab2 100644 --- a/host_fcs.py +++ b/host_fcs.py @@ -4,7 +4,6 @@ import aiohttp MKW_HOST_API_URL = f"https://mkwlounge.gg/api/hostfc.php?discord_guild_id={common.CONFIG['guild_id']}&discord_user_id=" -MKW_HOST_API_URL = f"https://mkwlounge.gg/api/hostfc.php?discord_guild_id=387347467332485122&discord_user_id=" SESSION = None From d34837a580f7e6e97372d65101e49cca7f17290c Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Mon, 8 Jul 2024 01:09:07 -0500 Subject: [PATCH 135/147] Add extra line --- mogi_objects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mogi_objects.py b/mogi_objects.py index c717629..c027293 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -349,7 +349,7 @@ async def make_teams(self, format_): room_open_time = self.mogi.making_rooms_run_time potential_host_str = self.room.get_host_str() if potential_host_str == "": - msg += f"Decide a host amongst yourselves; room open at :{room_open_time.minute:02}, penalty at :{penalty_time.minute:02}. Good luck!" + msg += f"\nDecide a host amongst yourselves; room open at :{room_open_time.minute:02}, penalty at :{penalty_time.minute:02}. Good luck!" else: msg += f"{potential_host_str}\n\nRoom open at :{room_open_time.minute:02}, penalty at :{penalty_time.minute:02}. Good luck!" From 07ade2d6754a6c972f0da8c243a7fa85d35a1086 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Mon, 8 Jul 2024 03:30:35 -0500 Subject: [PATCH 136/147] Thread name will update with tier in title --- mogi_objects.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mogi_objects.py b/mogi_objects.py index 823d772..8d9ca31 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -291,7 +291,7 @@ class VoteView(View): def __init__(self, players, thread, mogi: Mogi, room: Room, tier_info): super().__init__() self.players = players - self.thread = thread + self.thread:discord.Thread = thread self.mogi = mogi self.room = room self.header_text = "" @@ -358,6 +358,9 @@ async def make_teams(self, format_): self.found_winner = True await self.thread.send(msg) + if common.SERVER is common.Server.MKW: + new_thread_name = self.thread.name + f" - T{get_tier(room_mmr, self.tier_info)}" + await self.thread.edit(name=new_thread_name) async def find_winner(self): if not self.found_winner: From 64a75a0d88a4bd63c5e078debf499eeb20f6e20f Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Mon, 8 Jul 2024 14:37:53 -0500 Subject: [PATCH 137/147] Change mmr sub range to 700 --- mogi_objects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mogi_objects.py b/mogi_objects.py index 8d9ca31..574e4a6 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -457,7 +457,7 @@ async def button_callback(self, interaction: discord.Interaction, button): return mmr_high = 999999 if self.room.room_num == 1 else self.room.mmr_high mmr_low = -999999 if self.room.room_num == self.bottom_room_num else self.room.mmr_low - if isinstance(user_mmr, int) and mmr_high + 500 > user_mmr > mmr_low - 500: + if isinstance(user_mmr, int) and mmr_high + 700 > user_mmr > mmr_low - 700: self.room.subs.append(interaction.user.id) button.disabled = True await interaction.followup.edit_message(interaction.message.id, view=self) From 6e01bced9a35a05014f59c2ca295a7b988a9b364 Mon Sep 17 00:00:00 2001 From: Bad Wolf Date: Mon, 8 Jul 2024 14:40:09 -0500 Subject: [PATCH 138/147] Update sub range text from 500 to 700 --- cogs/SquadQueue.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index acc0fda..a1b93f2 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -390,11 +390,11 @@ async def sub(self, interaction: discord.Interaction): if bottom_room_num == 1: msg += f"Room 1 is looking for a sub with any mmr\n" elif room.room_num == 1: - msg += f"Room 1 is looking for a sub with mmr >{room.mmr_low - 500}\n" + msg += f"Room 1 is looking for a sub with mmr >{room.mmr_low - 700}\n" elif room.room_num == bottom_room_num: - msg += f"Room {room.room_num} is looking for a sub with mmr <{room.mmr_high + 500}\n" + msg += f"Room {room.room_num} is looking for a sub with mmr <{room.mmr_high + 700}\n" else: - msg += f"Room {room.room_num} is looking for a sub with range {room.mmr_low - 500}-{room.mmr_high + 500}\n" + msg += f"Room {room.room_num} is looking for a sub with range {room.mmr_low - 700}-{room.mmr_high + 700}\n" message_delete_date = datetime.now( timezone.utc) + timedelta(seconds=self.SUB_MESSAGE_LIFETIME_SECONDS) msg += f"Message will auto-delete in {discord.utils.format_dt(message_delete_date, style='R')}" From 5dc72aceee6bf7c772be20d178aee6a998b858b4 Mon Sep 17 00:00:00 2001 From: emilp-595 Date: Mon, 8 Jul 2024 22:13:02 -0700 Subject: [PATCH 139/147] Start the first event of the day 1 hour sooner Shifted the first event time from 0:00 UTC to 23:00 UTC --- cogs/SquadQueue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index a1b93f2..6b2ae1e 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -120,7 +120,7 @@ def __init__(self, bot): # This is the first event of the day's time. However, this isn't the first event the bot will run. This is literally the time of the first event in a daily schedule. self.FIRST_EVENT_TIME = datetime.combine(datetime.utcnow().date(), datetime.min.time(), - tzinfo=timezone.utc) + timedelta( + tzinfo=timezone.utc) - timedelta(hours=1) + timedelta( minutes=bot.config["FIRST_EVENT_TIME"]) with open('./timezones.json', 'r') as cjson: From 1caaf2727bc8e8aa8d1bcf3126edd0236912f599 Mon Sep 17 00:00:00 2001 From: emilp-595 Date: Tue, 9 Jul 2024 11:45:53 -0700 Subject: [PATCH 140/147] Revert "Start the first event of the day 1 hour sooner" This reverts commit 5dc72aceee6bf7c772be20d178aee6a998b858b4. --- cogs/SquadQueue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 6b2ae1e..a1b93f2 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -120,7 +120,7 @@ def __init__(self, bot): # This is the first event of the day's time. However, this isn't the first event the bot will run. This is literally the time of the first event in a daily schedule. self.FIRST_EVENT_TIME = datetime.combine(datetime.utcnow().date(), datetime.min.time(), - tzinfo=timezone.utc) - timedelta(hours=1) + timedelta( + tzinfo=timezone.utc) + timedelta( minutes=bot.config["FIRST_EVENT_TIME"]) with open('./timezones.json', 'r') as cjson: From 723e37c9c474f6838f1505d7946e4769fc3e5e0e Mon Sep 17 00:00:00 2001 From: thomasgonda3 Date: Sat, 13 Jul 2024 17:35:24 -0700 Subject: [PATCH 141/147] reimplemented all old mk8 queuebot features --- .gitignore | 1 + cogs/SquadQueue.py | 434 +++++++++++++----- example_configs/prod/mk8dx_prod_config.json | 46 ++ .../prod/mkw_ct_prod_config.json | 56 +-- .../prod/mkw_rt_prod_config.json | 58 +-- mmr.py | 88 +++- mogi_objects.py | 135 ++++-- test_config.json | 64 --- 8 files changed, 588 insertions(+), 294 deletions(-) create mode 100644 example_configs/prod/mk8dx_prod_config.json rename ct_config.json => example_configs/prod/mkw_ct_prod_config.json (60%) rename prod_config.json => example_configs/prod/mkw_rt_prod_config.json (59%) delete mode 100644 test_config.json diff --git a/.gitignore b/.gitignore index b73e9cf..aaa5a92 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /config.json /credentials.json .idea/* +/example_configs/test/* diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index a1b93f2..133b87b 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -11,8 +11,8 @@ import common from common import divide_chunks -from mmr import mkw_mmr, get_mmr_from_discord_id -from mogi_objects import Mogi, Team, Player, Room, VoteView, JoinView, get_tier +from mmr import mk8dx_mmr, mk8dx_get_mmr_from_discord_id, mkw_mmr, mkw_get_mmr_from_discord_id +from mogi_objects import Mogi, Team, Player, Room, VoteView, JoinView, get_tier, get_tier_mk8dx import asyncio from collections import defaultdict from typing import Dict, List, Tuple @@ -56,6 +56,8 @@ class SquadQueue(commands.Cog): def __init__(self, bot): self.bot: commands.Bot = bot + self.is_production = bot.config["is_production"] + self.next_event: Mogi = None self.ongoing_event: Mogi = None @@ -91,8 +93,14 @@ def __init__(self, bot): self.URL = bot.config["url"] + self.PLACEMENT_PLAYER_MMR = self.bot.config["PLACEMENT_PLAYER_MMR"] + + self.ROOM_JOIN_PENALTY_TIME = self.bot.config["ROOM_JOIN_PENALTY_TIME"] + self.MOGI_LIFETIME = bot.config["MOGI_LIFETIME"] + self.SUB_RANGE_MMR_ALLOWANCE = bot.config["SUB_RANGE_MMR_ALLOWANCE"] + self.SUB_MESSAGE_LIFETIME_SECONDS = bot.config["SUB_MESSAGE_LIFETIME_SECONDS"] self.room_mmr_threshold = bot.config["ROOM_MMR_THRESHOLD"] @@ -144,7 +152,8 @@ async def on_ready(self): self.bot.config["queue_history_channel"]) self.GENERAL_CHANNEL = self.bot.get_channel( self.bot.config["queue_general_channel"]) - await self.get_ladder_info() + if common.SERVER is common.Server.MKW: + await self.get_ladder_info() try: await self.LIST_CHANNEL.purge() except: @@ -162,7 +171,7 @@ async def on_ready(self): print(f"History Channel - {self.HISTORY_CHANNEL}", flush=True) print(f"General Channel - {self.GENERAL_CHANNEL}", flush=True) print("Ready!", flush=True) - + self.refresh_helper_roles.start() async def lockdown(self, channel: discord.TextChannel): @@ -226,7 +235,7 @@ def get_mogi(self, ctx: discord.Interaction | discord.ext.commands.context.Conte return self.ongoing_event def is_staff(self, member: discord.Member): - return any(member.get_role(staff_role) for staff_role in self.bot.config["staff_roles"]) + return any(member.get_role(staff_role) for staff_role in self.bot.config["admin_roles"]) async def is_started(self, ctx, mogi: Mogi): if not mogi.started: @@ -247,6 +256,8 @@ async def extend(self, interaction: discord.Interaction, minutes: int): ----------- minutes: int The number of minutes to add to the extension time. Can be negative. + + Staff use only. """ mogi = self.get_mogi(interaction) if mogi is None or not mogi.started or not mogi.gathering: @@ -271,13 +282,23 @@ async def join_queue(self, interaction: discord.Interaction, host=False): await interaction.response.defer() async with self.LOCK: member = interaction.user + url = self.URL + if not self.is_production: + if common.SERVER is common.Server.MK8DX: + # is actually a user and not a member + member = await self.bot.fetch_user(318637887597969419) + url = "https://www.mk8dx-lounge.com" + elif common.SERVER is common.Server.MKW: + member = await self.bot.fetch_user(82862780591378432) + url = "https://www.mkwlounge.gg" mogi = self.get_mogi(interaction) if mogi is None or not mogi.started or not mogi.gathering: await interaction.followup.send("Queue has not started yet.") return player_team = mogi.check_player(member) - player = None if player_team is None else player_team.get_player(member) + player = None if player_team is None else player_team.get_player( + member) if player is not None: # The player is already signed up, but they might be changing to a host or non-host. Begin checks: @@ -298,14 +319,18 @@ async def join_queue(self, interaction: discord.Interaction, host=False): return msg = "" - placement_role_id = 723753340063842345 - if member.get_role(placement_role_id): - starting_player_mmr = 750 - players = [Player(member, member.display_name, starting_player_mmr)] + placement_role_id = self.bot.config["placement_role_id"] + if self.is_production and member.get_role(placement_role_id): + starting_player_mmr = self.PLACEMENT_PLAYER_MMR + players = [ + Player(member, member.display_name, starting_player_mmr)] msg += f"{players[0].lounge_name} is assumed to be a new player and will be playing this mogi with a starting MMR of {starting_player_mmr}. " msg += "If you believe this is a mistake, please contact a staff member for help.\n" else: - players = await mkw_mmr(self.URL, [member], self.TRACK_TYPE) + if common.SERVER is common.Server.MKW: + players = await mkw_mmr(url, [member], self.TRACK_TYPE) + elif common.SERVER is common.Server.MK8DX: + players = await mk8dx_mmr(url, [member]) if len(players) == 0 or players[0] is None: msg = f"{interaction.user.mention} fetch for MMR has failed and joining the queue was unsuccessful. " @@ -338,7 +363,7 @@ async def drop(self, interaction: discord.Interaction): member = interaction.user squad = mogi.check_player(member) if squad is None: - await interaction.followup.send(f"{member.display_name} is not currently in this event; type `/c` to join") + await interaction.followup.send(f"{member.display_name} is not currently in this event; type `/c` or `/ch` to join") return mogi.teams.remove(squad) msg = "Removed " @@ -356,11 +381,12 @@ async def sub(self, interaction: discord.Interaction): """Sends out a request for a sub in the sub channel. Only works in thread channels for SQ rooms.""" current_time = time.time() lastCommandTime = cooldowns.get(interaction.user.id) - print(lastCommandTime, flush=True) + print(f"{interaction.user.name} requests a sub, previous sub command time: {lastCommandTime}", flush=True) if lastCommandTime == None: lastCommandTime = 0 - if (current_time - lastCommandTime) < 120 and not self.is_staff(interaction.user): # Cooldown timer in seconds + # Cooldown timer in seconds + if (current_time - lastCommandTime) < 120 and not self.is_staff(interaction.user): await interaction.response.send_message( f"You are still on cooldown. Please wait for {int(2 * 60 - (current_time - lastCommandTime))} more seconds to use this command again.", ephemeral=True) @@ -386,23 +412,35 @@ async def sub(self, interaction: discord.Interaction): f"More than {self.MOGI_LIFETIME} minutes have passed since mogi start, the Mogi Object has been deleted.", ephemeral=True) return - msg = "<@&1167985222533533817> - " + frequently_tagged_role_id = self.bot.config["frequently_tagged_role_id"] + msg = f"<@&{frequently_tagged_role_id}> - " if bottom_room_num == 1: msg += f"Room 1 is looking for a sub with any mmr\n" elif room.room_num == 1: - msg += f"Room 1 is looking for a sub with mmr >{room.mmr_low - 700}\n" + msg += f"Room 1 is looking for a sub with mmr >{room.mmr_low - self.SUB_RANGE_MMR_ALLOWANCE}\n" elif room.room_num == bottom_room_num: - msg += f"Room {room.room_num} is looking for a sub with mmr <{room.mmr_high + 700}\n" + msg += f"Room {room.room_num} is looking for a sub with mmr <{room.mmr_high + self.SUB_RANGE_MMR_ALLOWANCE}\n" else: - msg += f"Room {room.room_num} is looking for a sub with range {room.mmr_low - 700}-{room.mmr_high + 700}\n" + msg += f"Room {room.room_num} is looking for a sub with range {room.mmr_low - self.SUB_RANGE_MMR_ALLOWANCE}-{room.mmr_high + self.SUB_RANGE_MMR_ALLOWANCE}\n" message_delete_date = datetime.now( timezone.utc) + timedelta(seconds=self.SUB_MESSAGE_LIFETIME_SECONDS) msg += f"Message will auto-delete in {discord.utils.format_dt(message_delete_date, style='R')}" await self.SUB_CHANNEL.send(msg, delete_after=self.SUB_MESSAGE_LIFETIME_SECONDS) - view = JoinView(room, - lambda discord_id: get_mmr_from_discord_id(discord_id, self.TRACK_TYPE), - bottom_room_num, - lambda user: is_restricted(user, self.bot.config)) + view = None + if common.SERVER is common.Server.MKW: + view = JoinView(room, + lambda discord_id: mkw_get_mmr_from_discord_id( + discord_id, self.TRACK_TYPE, self.URL), + self.SUB_RANGE_MMR_ALLOWANCE, + bottom_room_num, + lambda user: is_restricted(user, self.bot.config)) + elif common.SERVER is common.Server.MK8DX: + view = JoinView(room, + lambda discord_id: mk8dx_get_mmr_from_discord_id( + discord_id, self.URL), + self.SUB_RANGE_MMR_ALLOWANCE, + bottom_room_num, + lambda user: is_restricted(user, self.bot.config)) await self.SUB_CHANNEL.send(view=view, delete_after=self.SUB_MESSAGE_LIFETIME_SECONDS) cooldowns[interaction.user.id] = current_time # Updates cooldown await interaction.response.send_message("Sent out request for sub.") @@ -416,21 +454,40 @@ async def list_task(self): await self.delete_list_messages(0) return all_confirmed_players = mogi.players_on_confirmed_teams() - first_late_player_index = (mogi.num_players // mogi.players_per_room) * mogi.players_per_room - on_time_players = sorted(all_confirmed_players[:first_late_player_index], reverse=True) - late_players = all_confirmed_players[first_late_player_index:] + first_late_player_index = ( + mogi.num_players // mogi.players_per_room) * mogi.players_per_room + if common.SERVER is common.Server.MK8DX: + for i, player in enumerate(all_confirmed_players, 1): + if i > first_late_player_index: + player.is_late = True + else: + player.is_late = False msg = f"**Queue closing: {discord.utils.format_dt(mogi.start_time)}**\n\n" msg += "**Current Mogi List:**\n" - for i, player in enumerate(on_time_players, 1): - msg += f"{i}) {player.lounge_name} ({player.mmr} MMR)\n" - if i % mogi.players_per_room == 0: - msg += "ㅤ\n" - if len(on_time_players) == 0: # Text looks better this way - msg += "\n" - msg += "**Late Players:**\n" - for i, player in enumerate(late_players, 1): - msg += f"{i}) {player.lounge_name} ({player.mmr} MMR)\n" + if common.SERVER is common.Server.MKW: + on_time_players = sorted( + all_confirmed_players[:first_late_player_index], reverse=True) + late_players = all_confirmed_players[first_late_player_index:] + for i, player in enumerate(on_time_players, 1): + msg += f"{i}) {player.lounge_name} ({player.mmr} MMR)\n" + if i % mogi.players_per_room == 0: + msg += "ㅤ\n" + if len(on_time_players) == 0: # Text looks better this way + msg += "\n" + msg += "**Late Players:**\n" + for i, player in enumerate(late_players, 1): + msg += f"{i}) {player.lounge_name} ({player.mmr} MMR)\n" + elif common.SERVER is common.Server.MK8DX: + all_confirmed_players.sort(reverse=True) + for i, player in enumerate(all_confirmed_players, 1): + msg += f"{i}) {player.lounge_name} ({player.mmr} MMR)" + if player.is_late: + msg += " (late)\n" + else: + msg += "\n" + if i % mogi.players_per_room == 0: + msg += "ㅤ\n" msg += f"\n**Last Updated:** {discord.utils.format_dt(datetime.now(timezone.utc), style='R')}" message = msg.split("\n") @@ -478,21 +535,60 @@ async def on_message(self, message: discord.Message): return mogi = None if self.ongoing_event is not None: - mogi = self.ongoing_event if self.ongoing_event.is_room_thread(message.channel.id) else None + mogi = self.ongoing_event if self.ongoing_event.is_room_thread( + message.channel.id) else None if mogi is None: - mogi = discord.utils.find(lambda m: m.is_room_thread(message.channel.id), self.old_events) + mogi = discord.utils.find(lambda m: m.is_room_thread( + message.channel.id), self.old_events) if mogi is None: return - room = discord.utils.find(lambda r: r.thread.id == message.channel.id, mogi.rooms) + room = discord.utils.find( + lambda r: r.thread.id == message.channel.id, mogi.rooms) if room is None or not room.teams: return - team = discord.utils.find(lambda t: t.has_player(message.author), room.teams) + team = discord.utils.find( + lambda t: t.has_player(message.author), room.teams) if team is None: return - player = discord.utils.find(lambda p: p.member.id == message.author.id, team.players) + player = discord.utils.find( + lambda p: p.member.id == message.author.id, team.players) if player is not None: player.score = int(message.content) + @app_commands.command(name="scoreboard") + @app_commands.guild_only() + async def scoreboard(self, interaction: discord.Interaction): + """Displays the scoreboard of the room. Only works in thread channels for SQ rooms.""" + if common.SERVER is not common.Server.MK8DX: + await interaction.response.send_message(f"Command is only usable for MK8DX.", ephemeral=True) + return + + if not isinstance(interaction.channel, discord.Thread): + await interaction.response.send_message(f"Cannot use this command here.", ephemeral=True) + return + + mogi = discord.utils.find(lambda mogi: mogi.is_room_thread( + interaction.channel_id), self.old_events) + if not mogi: + await interaction.response.send_message(f"The Mogi object cannot be found.", ephemeral=True) + return + + room = discord.utils.find( + lambda room: room.thread.id == interaction.channel_id, mogi.rooms) + if not room: + await interaction.response.send_message(f"The Thread object cannot be found.", ephemeral=True) + return + + format = round(12/len(room.teams)) + + msg = f"!submit {format} {get_tier_mk8dx(room.mmr_average - 500)}\n" + for team in room.teams: + for player in team.players: + msg += f"{player.lounge_name} {player.score}\n" + if format != 1: + msg += "\n" + await interaction.response.send_message(msg) + @app_commands.command(name="remove_player") @app_commands.guild_only() async def remove_player(self, interaction: discord.Interaction, member: discord.Member): @@ -506,7 +602,7 @@ async def remove_player(self, interaction: discord.Interaction, member: discord. squad = mogi.check_player(member) if squad is None: - await interaction.followup.send(f"{member.display_name} is not currently in this event; type `/c` to join") + await interaction.followup.send(f"{member.display_name} is not currently in this event") return mogi.teams.remove(squad) msg = "Staff has removed " @@ -539,14 +635,29 @@ async def ping_staff_autocomplete(self, interaction: discord.Interaction, curren for role_name in self.helper_staff_roles if current.lower() in role_name.lower() ] + def allow_new_mogis(self): + self.LAUNCH_NEW_EVENTS = True + @app_commands.command(name="annul_current_mogi") @app_commands.guild_only() - async def annul_current_mogi(self, interaction: discord.Interaction): - """The mogi currently gathering will be deleted. The queue resumes at the next hour. Staff use only.""" + async def annul_current_mogi(self, interaction: discord.Interaction, resume_mogis_after: bool): + """The mogi currently gathering will be deleted. resume_mogis_after determines whether future mogis will be scheduled. Staff use only.""" self.next_event = None self.ongoing_event = None + self.LAUNCH_NEW_EVENTS = False + msg = "The current mogi has been cancelled, " + if resume_mogis_after: + next_event_open_time = self.compute_next_event_open_time() + next_event_start_time = ( + next_event_open_time + self.QUEUE_OPEN_TIME) + delay = (next_event_start_time - + datetime.now(timezone.utc)).total_seconds() + asyncio.get_event_loop().call_later(delay, self.allow_new_mogis) + msg += f"the queue will resume at {next_event_start_time}." + else: + msg += "future mogis will not be started." await self.lockdown(self.MOGI_CHANNEL) - await interaction.response.send_message("The current Mogi has been canceled, the queue will resume at the next hour.") + await interaction.response.send_message(msg) @app_commands.command(name="pause_mogi_scheduling") @app_commands.guild_only() @@ -565,10 +676,11 @@ async def resume_mogi_scheduling(self, interaction: discord.Interaction): @app_commands.command(name="change_event_time") @app_commands.guild_only() async def change_event_time(self, interaction: discord.Interaction, event_time: int): - """Change the amount of time for each event in the queue.""" + """Change the amount of time for each event in the queue. Staff use only.""" if event_time > 15: self.QUEUE_OPEN_TIME = timedelta(minutes=event_time) - self.JOINING_TIME = timedelta(minutes=event_time) - self.EXTENSION_TIME + self.JOINING_TIME = timedelta( + minutes=event_time) - self.EXTENSION_TIME await interaction.response.send_message( f"The amount of time for each mogi has been changed to {event_time} minutes.") else: @@ -577,7 +689,7 @@ async def change_event_time(self, interaction: discord.Interaction, event_time: @app_commands.command(name="change_mmr_threshold") @app_commands.guild_only() async def change_mmr_threshold(self, interaction: discord.Interaction, mmr_threshold: int): - """Change the maximum MMR gap allowed for a room.""" + """Change the maximum MMR gap allowed for a room. Staff use only.""" self.room_mmr_threshold = mmr_threshold try: with open(MMR_THRESHOLD_PKL, 'wb') as f: @@ -589,10 +701,57 @@ async def change_mmr_threshold(self, interaction: discord.Interaction, mmr_thres @app_commands.command(name="peek_bot_config") @app_commands.guild_only() async def peek_bot_config(self, interaction: discord.Interaction): - """View the configured values for the Queue System.""" + """View the configured values for the Queue System. Staff use only.""" msg = "" - msg += f"Time between each event: {self.QUEUE_OPEN_TIME}, \n" - msg += f"Room MMR Threshold: {self.room_mmr_threshold}" + msg += f"LOUNGE TYPE: {self.bot.config['lounge']}\n" + if common.SERVER is common.Server.MKW: + msg += f"TRACK TYPE: {self.TRACK_TYPE}\n" + msg += f"CURRENT SERVER: {self.GUILD}\n" + msg += f"GENERAL CHANNEL: {self.GENERAL_CHANNEL}\n" + msg += f"LIST CHANNEL: {self.LIST_CHANNEL}\n" + msg += f"JOIN CHANNEL: {self.MOGI_CHANNEL}\n" + msg += f"SUB CHANNEL: {self.SUB_CHANNEL}\n" + msg += f"HISTORY CHANNEL: {self.HISTORY_CHANNEL}\n" + + admins = [] + for m in self.bot.config["admin_roles"]: + role = self.GUILD.get_role(m) + if role: + admins.append(role.name) + msg += f"ADMIN ROLES: {', '.join(admins)}\n" + + helper_staff = [] + for m in self.bot.config["helper_staff_roles"]: + role = self.GUILD.get_role(m) + if role: + helper_staff.append(role.name) + msg += f"HELPER STAFF ROLES: {', '.join(helper_staff)}\n" + + members_for_channels = [] + for m in self.bot.config["members_for_channels"]: + member = self.GUILD.get_member(m) + if member: + members_for_channels.append(member.name) + msg += f"MEMBERS ADDED TO EACH ROOM: {', '.join(members_for_channels)}\n" + + roles_for_channels = [] + for m in self.bot.config["roles_for_channels"]: + role = self.GUILD.get_role(m) + if role: + roles_for_channels.append(role.name) + msg += f"ROLES ADDED TO EACH ROOM: {', '.join(roles_for_channels)}\n" + + msg += f"PLACEMENT PLAYER STARTING MMR: {self.PLACEMENT_PLAYER_MMR}\n" + msg += f"SITE URL: {self.URL}\n" + msg += f"FIRST EVENT TIME: {self.FIRST_EVENT_TIME}\n" + msg += f"TIME BETWEEN EVENTS: {self.QUEUE_OPEN_TIME} minutes\n" + msg += f"EVENT JOINING TIME: {self.JOINING_TIME} minutes\n" + msg += f"EXTENSION TIME: {self.EXTENSION_TIME} minutes\n" + msg += f"ROOM JOIN PENALTY TIME: {self.ROOM_JOIN_PENALTY_TIME} minutes\n" + msg += f"EVENT LIFETIME: {self.MOGI_LIFETIME} minutes\n" + msg += f"EXTRA SUB RANGE ALLOWANCE: +- {self.SUB_RANGE_MMR_ALLOWANCE} MMR\n" + msg += f"SUB MESSAGE LIFETIME: {self.SUB_MESSAGE_LIFETIME_SECONDS} seconds\n" + msg += f"ROOM MMR THRESHOLD: {self.room_mmr_threshold}" await interaction.response.send_message(msg) @app_commands.command(name="reset_bot") @@ -609,13 +768,13 @@ async def reset_bot(self, interaction: discord.Interaction): @commands.guild_only() async def schedule_sq_times(self, ctx, timestamps: commands.Greedy[int]): """Saves a list of sq times to skip over. Input a list of unix utc timestamps. Staff use only.""" - if not await self.has_roles(ctx.author, ctx.guild.id, ctx.bot.config): + if not self.is_staff(ctx.author) and not await self.bot.is_owner(ctx.author): + self.queue_or_send( + ctx, "You do not have permission to use this command.") return - await self.queue_or_send(ctx, - "This command has been disabled as it's unclear how the new changes might affect this system.") - return - msg = "List of new Dates:\n" + msg = "The queue will skip events THAT START AT THE TIMESTAMP (not when the queue pops)\n" + msg += "List of new Dates:\n" curr_time = datetime.now(timezone.utc) new_sq_dates = [] @@ -663,8 +822,7 @@ async def clear_sq_times(self, interaction: discord.Interaction): @app_commands.command(name="update_tier_info") @app_commands.guild_only() async def update_tier_info(self, interaction: discord.Interaction): - """Updates the info on each tier""" - #await self.get_ladder_info() + """Updates the mmr divisions that denote each tier. Staff use only.""" msg = await self.get_ladder_info() await interaction.response.send_message(msg) @@ -703,15 +861,6 @@ async def get_ladder_info(self): except Exception as e: print(traceback.format_exc()) - # check if user has roles defined in config.json - async def has_roles(self, member: discord.Member, guild_id: int, config): - if str(guild_id) not in config["admin_roles"].keys(): - return True - for role in member.roles: - if role.name in config["admin_roles"][str(guild_id)]: - return True - return False - @staticmethod async def end_voting(mogi: Mogi): """Ends voting in all rooms with ongoing votes.""" @@ -719,7 +868,8 @@ async def end_voting(mogi: Mogi): if mogi is not None: for index, room in enumerate(mogi.rooms, 1): if not room or not room.view: - print(f"Skipping room {index} in function end_voting.", flush=True) + print( + f"Skipping room {index} in function end_voting.", flush=True) continue await room.view.find_winner() except Exception as e: @@ -730,10 +880,15 @@ async def write_history(mogi: Mogi, history_channel: discord.TextChannel): """Writes the teams, tier and average of each room per hour.""" try: if mogi is not None: - await history_channel.send(f"{discord.utils.format_dt(mogi.start_time)} Rooms") + # I want the time displayed to be at the hour but the current way reads as :55 minutes on each queue which looks bad... + room_display_time = mogi.start_time + if common.SERVER is common.Server.MK8DX: + room_display_time = mogi.start_time + timedelta(minutes=5) + await history_channel.send(f"{discord.utils.format_dt(room_display_time)} Rooms") for index, room in enumerate(mogi.rooms, 1): if not room or not room.view: - print(f"Skipping room {index} in function write_history.", flush=True) + print( + f"Skipping room {index} in function write_history.", flush=True) continue msg = room.view.header_text msg += f"{room.thread.jump_url}\n" @@ -750,10 +905,13 @@ async def check_room_channels(self, mogi): if num_created_rooms >= mogi.max_possible_rooms: return for i in range(num_created_rooms, mogi.max_possible_rooms): - minute = mogi.start_time.minute + start_time = mogi.start_time + if common.SERVER is common.Server.MK8DX: + start_time += timedelta(minutes=5) + minute = start_time.minute if len(str(minute)) == 1: minute = '0' + str(minute) - room_name = f"{mogi.start_time.month}/{mogi.start_time.day}, {mogi.start_time.hour}:{minute}:00 - Room {i+1}" + room_name = f"{start_time.month}/{start_time.day}, {start_time.hour}:{minute}:00 - Room {i+1}" try: room_channel = await self.GENERAL_CHANNEL.create_thread(name=room_name, auto_archive_duration=60, @@ -767,7 +925,8 @@ async def check_room_channels(self, mogi): @staticmethod async def handle_voting_and_history(mogi: Mogi, history_channel: discord.TextChannel): - await asyncio.sleep(120) # We could have used asyncio.call_later(120, handle_voting_and_history) in the caller's code + # We could have used asyncio.call_later(120, handle_voting_and_history) in the caller's code + await asyncio.sleep(120) await SquadQueue.end_voting(mogi) await SquadQueue.write_history(mogi, history_channel) @@ -793,11 +952,9 @@ async def add_teams_to_rooms(self, mogi: Mogi, open_time: int, started_automatic extra_members = [] - extra_members_ids = self.bot.config["members_for_channels"].get(str(mogi.mogi_channel.guild.id), []) - for m in extra_members_ids: + for m in self.bot.config["members_for_channels"]: extra_members.append(mogi.mogi_channel.guild.get_member(m)) - extra_roles_ids = self.bot.config["roles_for_channels"].get(str(mogi.mogi_channel.guild.id), []) - for r in extra_roles_ids: + for r in self.bot.config["roles_for_channels"]: extra_members.append(mogi.mogi_channel.guild.get_role(r)) # Support for both server to implement their own algorithm for a player list being allowed @@ -811,11 +968,14 @@ def allowed_players_check(players): return mk8dx_players_allowed(players, self.room_mmr_threshold) all_confirmed_players = mogi.players_on_confirmed_teams() - first_late_player_index = (mogi.num_players//mogi.players_per_room) * mogi.players_per_room + first_late_player_index = ( + mogi.num_players//mogi.players_per_room) * mogi.players_per_room regular_player_list = all_confirmed_players[:first_late_player_index] late_player_list = all_confirmed_players[first_late_player_index:] - proposed_list = sorted(mogi.generate_proposed_list(allowed_players_check), reverse=True) - await mogi.populate_host_fcs() + proposed_list = sorted(mogi.generate_proposed_list( + allowed_players_check), reverse=True) + if common.SERVER is common.Server.MKW: + await mogi.populate_host_fcs() for room_number, room_players in enumerate(divide_chunks(proposed_list, mogi.players_per_room), 1): msg = f"`Room {room_number} - Player List`\n" for player_num, player in enumerate(room_players, 1): @@ -828,13 +988,25 @@ def allowed_players_check(players): curr_room.teams = [Team([p]) for p in room_players] curr_room.create_host_list() player_mentions = " ".join([p.mention for p in room_players]) - extra_member_mentions = " ".join([m.mention for m in extra_members if m is not None]) - room_msg = f"""{msg} + extra_member_mentions = " ".join( + [m.mention for m in extra_members if m is not None]) + room_msg = "" + if common.SERVER is common.Server.MKW: + room_msg += f"""{msg} Vote for format FFA, 2v2, 3v3, 4v4, or 6v6. -{player_mentions} {extra_member_mentions}""" +{player_mentions} {extra_member_mentions} + +If you need staff's assistance, use the `/ping_staff` command in this channel.""" + elif common.SERVER is common.Server.MK8DX: + room_msg += f"""{msg} +Vote for format FFA, 2v2, 3v3, 4v4. +{player_mentions} {extra_member_mentions} + +If you need staff's assistance, use the `/ping_staff` command in this channel.""" try: await curr_room.thread.send(room_msg) - view = VoteView(room_players, curr_room.thread, mogi, curr_room, self.TIER_INFO) + view = VoteView(room_players, curr_room.thread, + mogi, curr_room, self.ROOM_JOIN_PENALTY_TIME, self.TIER_INFO) curr_room.view = view await curr_room.thread.send(view=view) except discord.DiscordException: @@ -846,11 +1018,13 @@ def allowed_players_check(players): try: await mogi.mogi_channel.send(msg) except discord.DiscordException: - print(f"Mogi Channel message for room {room_number} has failed to send.", flush=True) + print( + f"Mogi Channel message for room {room_number} has failed to send.", flush=True) print(traceback.format_exc()) # Compute the list of "late" players that didn't get into any room - not_in_proposed_list = [p for p in all_confirmed_players if p not in proposed_list] + not_in_proposed_list = [ + p for p in all_confirmed_players if p not in proposed_list] if len(not_in_proposed_list) > 0: msg = "`Late players:`\n" for i, player in enumerate(not_in_proposed_list, 1): @@ -864,7 +1038,8 @@ def allowed_players_check(players): # We could have used asyncio.call_later(120, handle_voting_and_history) # and removed asyncio.sleep(120) in handle_voting_and_history - asyncio.create_task(SquadQueue.handle_voting_and_history(self.ongoing_event, self.HISTORY_CHANNEL)) + asyncio.create_task(SquadQueue.handle_voting_and_history( + self.ongoing_event, self.HISTORY_CHANNEL)) self.old_events.append(self.ongoing_event) self.ongoing_event = None @@ -873,7 +1048,8 @@ async def check_num_teams(self, mogi: Mogi): return cur_time = datetime.now(timezone.utc) if mogi.start_time <= cur_time: - num_leftover_teams = mogi.count_registered() % int((12 / mogi.max_player_per_team)) + num_leftover_teams = mogi.count_registered() % int( + (12 / mogi.max_player_per_team)) if num_leftover_teams == 0: mogi.gathering = False await self.lockdown(mogi.mogi_channel) @@ -888,18 +1064,22 @@ async def ongoing_mogi_checks(self): if not mogi.is_automated or not mogi.started or mogi.making_rooms_run: return cur_time = datetime.now(timezone.utc) - force_start_time = mogi.start_time + self.EXTENSION_TIME + mogi.additional_extension + force_start_time = mogi.start_time + \ + self.EXTENSION_TIME + mogi.additional_extension if force_start_time <= cur_time: mogi.gathering = False elif mogi.start_time <= cur_time and mogi.gathering: # check if there are an even amount of teams since we are past the queue time - num_leftover_teams = mogi.count_registered() % int((12 / mogi.max_player_per_team)) + num_leftover_teams = mogi.count_registered() % int( + (12 / mogi.max_player_per_team)) if num_leftover_teams == 0: mogi.gathering = False else: if int(cur_time.second / 20) == 0: - minutes_left = (force_start_time - cur_time).seconds // 60 - x_teams = int(int(12 / mogi.max_player_per_team) - num_leftover_teams) + minutes_left = (force_start_time - + cur_time).seconds // 60 + x_teams = int( + int(12 / mogi.max_player_per_team) - num_leftover_teams) await mogi.mogi_channel.send( f"Need {x_teams} more player(s) to start immediately. Starting in {minutes_left + 1} minute(s) regardless.") if not mogi.gathering: @@ -911,14 +1091,16 @@ async def scheduler_mogi_start(self): cur_time = datetime.now(timezone.utc) next_mogi = self.next_event if next_mogi is not None and (next_mogi.start_time - self.JOINING_TIME) < cur_time: - self.next_event = None # We are trying to launch the event - fail or not, we set next event to None + # We are trying to launch the event - fail or not, we set next event to None + self.next_event = None # There is an ongoing event, but people are still queueing, so remove it and fail if self.ongoing_event is not None and self.ongoing_event.gathering: await next_mogi.mogi_channel.send( f"Because there is an ongoing event right now, the following event has been removed:\n{self.get_event_str(next_mogi)}\n") return # There is an ongoing event, but it has already started, so add it to the old events - if self.ongoing_event is not None and self.ongoing_event.started: # This is potentially an issue - we should be checking if make rooms has been run, not if it's started...? + # This is potentially an issue - we should be checking if make rooms has been run, not if it's started...? + if self.ongoing_event is not None and self.ongoing_event.started: self.old_events.append(self.ongoing_event) # Put the next mogi as the current event and launch it @@ -927,7 +1109,7 @@ async def scheduler_mogi_start(self): self.ongoing_event.gathering = True await self.unlockdown(self.ongoing_event.mogi_channel) await self.ongoing_event.mogi_channel.send( - f"A queue is gathering for the mogi {discord.utils.format_dt(self.ongoing_event.start_time, style='R')} - Type `/c` to join, and `/d` to drop.") + f"A queue is gathering for the mogi {discord.utils.format_dt(self.ongoing_event.start_time, style='R')} - Type `/c` to join, `/ch` to join and volunteer to host, and `/d` to drop.") @tasks.loop(seconds=20.0) async def sqscheduler(self): @@ -953,7 +1135,8 @@ def compute_next_event_open_time(self): cur_time = datetime.now(timezone.utc) time_elapsed = cur_time - self.FIRST_EVENT_TIME num_launched_events = time_elapsed // self.QUEUE_OPEN_TIME - next_event_open_time = self.FIRST_EVENT_TIME + (self.QUEUE_OPEN_TIME * num_launched_events) + next_event_open_time = self.FIRST_EVENT_TIME + \ + (self.QUEUE_OPEN_TIME * num_launched_events) return next_event_open_time async def schedule_que_event(self): @@ -969,8 +1152,18 @@ async def schedule_que_event(self): return if len(self.sq_times) > 0 and next_event_open_time == self.sq_times[0]: self.sq_times.pop(0) + self.next_event = None + self.ongoing_event = None + self.LAUNCH_NEW_EVENTS = False + next_event_start_time = next_event_open_time + self.QUEUE_OPEN_TIME + delay = (next_event_start_time - + datetime.now(timezone.utc)).total_seconds() + print(next_event_start_time, delay, flush=True) + asyncio.get_event_loop().call_later(delay, self.allow_new_mogis) + await self.lockdown(self.MOGI_CHANNEL) await self.MOGI_CHANNEL.send( - "Squad Queue is currently going on at this hour! The queue will remain closed.") + "Squad Queue is currently going on! The queue will remain closed.") + return self.next_event = Mogi(sq_id=1, max_players_per_team=1, players_per_room=12, @@ -986,9 +1179,11 @@ async def delete_old_mogis(self): try: curr_time = datetime.now(timezone.utc) mogi_lifetime = timedelta(minutes=self.MOGI_LIFETIME) - delete_queue = [m for m in self.old_events if (curr_time - mogi_lifetime) > m.start_time] + delete_queue = [m for m in self.old_events if ( + curr_time - mogi_lifetime) > m.start_time] for mogi in delete_queue: - print(f"Deleting {mogi.start_time} Mogi at {curr_time}", flush=True) + print( + f"Deleting {mogi.start_time} Mogi at {curr_time}", flush=True) self.old_events.remove(mogi) except Exception as e: print(traceback.format_exc()) @@ -1003,7 +1198,8 @@ async def refresh_helper_roles(self): all_roles = await self.GUILD.fetch_roles() updated_roles = {} for role_id in helper_staff_role_ids: - needle: discord.Role = discord.utils.find(lambda n: role_id == n.id, all_roles) + needle: discord.Role = discord.utils.find( + lambda n: role_id == n.id, all_roles) if needle is not None: updated_roles[needle.name] = needle # As a fail safe, if we didn't add any new roles, either due to a misconfiguration or an internal discord @@ -1015,7 +1211,6 @@ async def refresh_helper_roles(self): except Exception as e: print(traceback.format_exc()) - def get_event_str(self, mogi: Mogi): mogi_time = discord.utils.format_dt(mogi.start_time, style="F") mogi_time_relative = discord.utils.format_dt( @@ -1044,9 +1239,22 @@ async def debug_add_team(self, ctx, members: commands.Greedy[discord.Member]): or not await self.is_gathering(ctx, mogi)): return - check_players = [ctx.author] + member = ctx.author + url = self.URL + if not self.is_production: + if common.SERVER is common.Server.MK8DX: + member = await self.bot.fetch_user(318637887597969419) + url = "https://www.mk8dx-lounge.com" + elif common.SERVER is common.Server.MKW: + member = await self.bot.fetch_user(82862780591378432) + url = "https://www.mkwlounge.gg" + check_players = [member] check_players.extend(members) - players = await mkw_mmr(self.URL, check_players, self.TRACK_TYPE) + players = [] + if common.SERVER is common.Server.MKW: + players = await mkw_mmr(url, check_players, self.TRACK_TYPE) + elif common.SERVER is common.Server.MK8DX: + players = await mk8dx_mmr(url, check_players) for i in range(0, 12): player = Player( players[0].member, f"{players[0].lounge_name}{i + 1}", players[0].mmr + (10 * i)) @@ -1069,7 +1277,8 @@ async def debug_add_many_ratings(self, ctx, *ratings: str): return for i, rating in enumerate(ratings, 1): if rating.isdecimal(): - player = Player(ctx.author, f"{ctx.author.display_name} {i}", int(rating), confirmed=True) + player = Player(ctx.author, f"{ctx.author.display_name} {i}", int( + rating), confirmed=True) mogi.teams.append(Team([player])) msg = f"Players added with the following ratings: {' '.join(['`' + r + '`' for r in ratings])}" await self.queue_or_send(ctx, msg) @@ -1087,9 +1296,22 @@ async def debug_add_many_players(self, ctx, members: commands.Greedy[discord.Mem or not await self.is_gathering(ctx, mogi)): return - check_players = [ctx.author] + member = ctx.author + url = self.URL + if not self.is_production: + if common.SERVER is common.Server.MK8DX: + member = await self.bot.fetch_user(318637887597969419) + url = "https://www.mk8dx-lounge.com" + elif common.SERVER is common.Server.MKW: + member = await self.bot.fetch_user(82862780591378432) + url = "https://www.mkwlounge.gg" + check_players = [member] check_players.extend(members) - players = await mkw_mmr(self.URL, check_players, self.TRACK_TYPE) + players = [] + if common.SERVER is common.Server.MKW: + players = await mkw_mmr(url, check_players, self.TRACK_TYPE) + elif common.SERVER is common.Server.MK8DX: + players = await mk8dx_mmr(url, check_players) for i in range(0, num_times): player = Player( players[0].member, f"{players[0].lounge_name}{i + 1}", players[0].mmr + (10 * i)) @@ -1115,7 +1337,3 @@ async def debug_start_rooms(self, ctx): async def setup(bot: commands.Bot): await bot.add_cog(SquadQueue(bot)) - - - - diff --git a/example_configs/prod/mk8dx_prod_config.json b/example_configs/prod/mk8dx_prod_config.json new file mode 100644 index 0000000..94ac015 --- /dev/null +++ b/example_configs/prod/mk8dx_prod_config.json @@ -0,0 +1,46 @@ +{ + "token": "", + "is_production": true, + "lounge": "MK8DX", + "lounge_description": "Valid options are MKW or MK8DX", + "guild_id": 445404006177570829, + "queue_join_channel": 1186158671525318719, + "queue_sub_channel": 1186158767029620766, + "queue_list_channel": 1221665101904478340, + "queue_history_channel": 1194440106266865684, + "queue_general_channel": 1186158671525318719, + "admin_roles": [ + 464610815299944448, + 910528103225389107 + ], + "helper_staff_roles": [ + 910528103225389107 + ], + "members_for_channels": [ + 934740707934081035, + 743532508343304242, + 735910635082219570 + ], + "roles_for_channels": [], + "PLACEMENT_PLAYER_MMR": 1500, + "placement_role_id": 730980761322389504, + "frequently_tagged_role_id": 682445864400453739, + "restricted_role_id": 600495108999086090, + "muted_role_id": 600495108999086090, + "queue_messages": true, + "sec_between_queue_msgs": 2, + "username": "username", + "password": "password", + "url": "https://www.mk8dx-lounge.com", + "track_type": "rt", + "FIRST_EVENT_TIME": 0, + "FIRST_EVENT_TIME_DESCRIPTION": "First event time is the number of minutes from 0:00 UTC that the bot would run the first event of the day. As an example, if this were set to 5, and the QUEUE_OPEN_TIME were set to 70, and the bot were to reboot at 2:00 UTC, the next event would immediately start and create rooms at 2:25 UTC.", + "QUEUE_OPEN_TIME": 60, + "JOINING_TIME": 55, + "EXTENSION_TIME": 3, + "ROOM_JOIN_PENALTY_TIME": 5, + "MOGI_LIFETIME": 180, + "SUB_RANGE_MMR_ALLOWANCE": 500, + "SUB_MESSAGE_LIFETIME_SECONDS": 1200, + "ROOM_MMR_THRESHOLD": 9999999 +} \ No newline at end of file diff --git a/ct_config.json b/example_configs/prod/mkw_ct_prod_config.json similarity index 60% rename from ct_config.json rename to example_configs/prod/mkw_ct_prod_config.json index 0c2d970..91048a7 100644 --- a/ct_config.json +++ b/example_configs/prod/mkw_ct_prod_config.json @@ -1,51 +1,41 @@ { "token": "", + "is_production": true, "lounge": "MKW", "lounge_description": "Valid options are MKW or MK8DX", "guild_id": 387347467332485122, - "admin_roles": { - "387347467332485122": [ - "Administrator", - "Lounge Staff" - ] - }, "queue_join_channel": 1253109700107567155, "queue_sub_channel": 1253109752418930779, "queue_list_channel": 1253109466027655338, "queue_history_channel": 1253109800196243618, "queue_general_channel": 1253109398193045534, - "members_for_channels": { - "387347467332485122": [ - 235148962103951360, - 155149108183695360, - 815483289515196416, - 892420397570592768, - 775415990238314518, - 774812906830299136, - 745443073202323586, - 774866940950872095, - 1019051989734273054 - ] - }, - "roles_for_channels": { - "387347467332485122": [ - 520808645252874240, - 520808674411937792 - ] - }, - "staff_roles": [ - 387347888935534593, - 399382503825211393, - 399384750923579392, - 521149807994208295, - 792891432301625364, - 877629421811540016 + "admin_roles": [ + 387347888935534593, + 877629421811540016 ], "helper_staff_roles": [ 520808674411937792, 792891432301625364, 521149807994208295 ], + "members_for_channels": [ + 235148962103951360, + 155149108183695360, + 815483289515196416, + 892420397570592768, + 775415990238314518, + 774812906830299136, + 745443073202323586, + 774866940950872095, + 1019051989734273054 + ], + "roles_for_channels": [ + 520808645252874240, + 520808674411937792 + ], + "PLACEMENT_PLAYER_MMR": 750, + "placement_role_id": 723753340063842345, + "frequently_tagged_role_id": 1167985222533533817, "restricted_role_id": 797208908153618452, "muted_role_id": 434887701662007296, "queue_messages": true, @@ -59,7 +49,9 @@ "QUEUE_OPEN_TIME": 90, "JOINING_TIME": 85, "EXTENSION_TIME": 5, + "ROOM_JOIN_PENALTY_TIME": 8, "MOGI_LIFETIME": 180, + "SUB_RANGE_MMR_ALLOWANCE": 700, "SUB_MESSAGE_LIFETIME_SECONDS": 1200, "ROOM_MMR_THRESHOLD": 9000 } \ No newline at end of file diff --git a/prod_config.json b/example_configs/prod/mkw_rt_prod_config.json similarity index 59% rename from prod_config.json rename to example_configs/prod/mkw_rt_prod_config.json index 682f7fe..3b65358 100644 --- a/prod_config.json +++ b/example_configs/prod/mkw_rt_prod_config.json @@ -1,51 +1,41 @@ { "token": "", + "is_production": true, "lounge": "MKW", "lounge_description": "Valid options are MKW or MK8DX", "guild_id": 387347467332485122, - "admin_roles": { - "387347467332485122": [ - "Administrator", - "Lounge Staff" - ] - }, "queue_join_channel": 1215174119767932979, "queue_sub_channel": 1215174160234315806, "queue_list_channel": 1215174057453162496, "queue_history_channel": 1225709377747685477, "queue_general_channel": 1241842559429513361, - "members_for_channels": { - "387347467332485122": [ - 235148962103951360, - 155149108183695360, - 815483289515196416, - 892420397570592768, - 775415990238314518, - 774812906830299136, - 745443073202323586, - 774866940950872095, - 1019051989734273054 - ] - }, - "roles_for_channels": { - "387347467332485122": [ - 393600567781621761, - 389252697284542465 - ] - }, - "staff_roles": [ - 387347888935534593, - 399382503825211393, - 399384750923579392, - 521149807994208295, - 792891432301625364, - 877629421811540016 + "admin_roles": [ + 387347888935534593, + 877629421811540016 ], "helper_staff_roles": [ 389252697284542465, 399384750923579392, 399382503825211393 ], + "members_for_channels": [ + 235148962103951360, + 155149108183695360, + 815483289515196416, + 892420397570592768, + 775415990238314518, + 774812906830299136, + 745443073202323586, + 774866940950872095, + 1019051989734273054 + ], + "roles_for_channels": [ + 393600567781621761, + 389252697284542465 + ], + "PLACEMENT_PLAYER_MMR": 750, + "placement_role_id": 723753340063842345, + "frequently_tagged_role_id": 1167985222533533817, "restricted_role_id": 797208908153618452, "muted_role_id": 434887701662007296, "queue_messages": true, @@ -59,7 +49,9 @@ "QUEUE_OPEN_TIME": 72, "JOINING_TIME": 67, "EXTENSION_TIME": 5, + "ROOM_JOIN_PENALTY_TIME": 8, "MOGI_LIFETIME": 180, + "SUB_RANGE_MMR_ALLOWANCE": 700, "SUB_MESSAGE_LIFETIME_SECONDS": 1200, - "ROOM_MMR_THRESHOLD": 9000 + "ROOM_MMR_THRESHOLD": 8000 } \ No newline at end of file diff --git a/mmr.py b/mmr.py index c938b55..87269ac 100644 --- a/mmr.py +++ b/mmr.py @@ -5,6 +5,53 @@ headers = {'Content-type': 'application/json'} +# class LoungeData: +# def __init__(self): +# self._data = None + +# async def lounge_api_full(self): +# async with aiohttp.ClientSession() as session: +# async with session.get("https://www.mk8dx-lounge.com/api/player/list") as response: +# if response.status == 200: +# _data_full = await response.json() +# self._data = [ +# player for player in _data_full['players'] if "discordId" in player] + +# def data(self): +# return self._data + + +# lounge_data = LoungeData() + + +async def mk8dx_mmr(url, members): + base_url = url + '/api/player?' + players = [] + timeout = aiohttp.ClientTimeout(total=10) + try: + async with aiohttp.ClientSession( + timeout=timeout, + auth=aiohttp.BasicAuth( + "username", "password")) as session: + for member in members: + request_text = f"discordId={member.id}" + request_url = base_url + request_text + async with session.get(request_url, headers=headers) as resp: + if resp.status != 200: + players.append(None) + continue + player_data = await resp.json() + if 'mmr' not in player_data.keys(): + players.append( + Player(member, player_data['name'], None)) + continue + players.append( + Player(member, player_data['name'], player_data['mmr'])) + except: + print(f"Fetch for player {members[0]} has failed.", flush=True) + return players + + async def mkw_mmr(url, members, track_type): tracks = track_type base_url = url + '/api/ladderplayer.php?ladder_type=' + tracks + '&' @@ -35,9 +82,28 @@ async def mkw_mmr(url, members, track_type): return players -async def get_mmr_from_discord_id(discord_id, track_type): +async def mk8dx_get_mmr_from_discord_id(discord_id, url): + base_url = url + '/api/player?' + timeout = aiohttp.ClientTimeout(total=10) + async with aiohttp.ClientSession( + timeout=timeout, + auth=aiohttp.BasicAuth( + "username", "password")) as session: + request_text = f"discordId={discord_id}" + request_url = base_url + request_text + async with session.get(request_url, headers=headers) as resp: + if resp.status != 200: + return "Player does not exist" + player_data = await resp.json() + if 'mmr' not in player_data.keys(): + return "Player has no mmr" + return player_data['mmr'] + + +async def mkw_get_mmr_from_discord_id(discord_id, track_type, url): tracks = track_type - base_url = "https://www.mkwlounge.gg" + '/api/ladderplayer.php?ladder_type=' + tracks + '&' + base_url = url + \ + '/api/ladderplayer.php?ladder_type=' + tracks + '&' timeout = aiohttp.ClientTimeout(total=10) async with aiohttp.ClientSession( timeout=timeout, @@ -53,21 +119,3 @@ async def get_mmr_from_discord_id(discord_id, track_type): if 'current_mmr' not in player_data.keys(): return "Player has no mmr" return player_data['current_mmr'] - - -async def get_mmr(config, members): - return await mk8dx_150cc_mmr(config, members) - - -async def mk8dx_150cc_fc(config, name): - base_url = config["url"] + '/api/player?' - request_url = base_url + f'name={name}' - timeout = aiohttp.ClientTimeout(total=15) - async with aiohttp.ClientSession(timeout=timeout) as session: - async with session.get(request_url, headers=headers) as resp: - if resp.status != 200: - return None - player_data = await resp.json() - if 'switchFc' not in player_data.keys(): - return None - return player_data['switchFc'] diff --git a/mogi_objects.py b/mogi_objects.py index 574e4a6..d3b87dd 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -10,7 +10,6 @@ import host_fcs - def average(list_: List[int | float]) -> float: return sum(list_) / len(list_) @@ -35,7 +34,8 @@ def __init__(self, sq_id: int, max_players_per_team: int, players_per_room: int, self.rooms: List[Room] = [] self.is_automated = is_automated self.start_time = start_time if is_automated else None - self.additional_extension = timedelta(minutes=additional_extension_minutes) + self.additional_extension = timedelta( + minutes=additional_extension_minutes) @property def num_players(self): @@ -73,7 +73,8 @@ def _minimize_range(players: List[Player], num_players: int) -> List[Player] | N cur_range = highest_player.mmr - lowest_player.mmr if cur_range < cur_min: cur_min = cur_range - best_collection = sorted_players[lowest_player_index:lowest_player_index + num_players] + best_collection = sorted_players[lowest_player_index: + lowest_player_index + num_players] return best_collection def _one_room_final_list_algorithm(self, valid_players_check: Callable[[List[Player]], bool]) -> Tuple[List[Player], int]: @@ -88,7 +89,8 @@ def _one_room_final_list_algorithm(self, valid_players_check: Callable[[List[Pla late_players = list(confirmed_players[self.players_per_room:]) while True: - best_collection = Mogi._minimize_range(cur_check_list, self.players_per_room) + best_collection = Mogi._minimize_range( + cur_check_list, self.players_per_room) if valid_players_check(best_collection): return best_collection, Mogi.ALGORITHM_STATUS_SUCCESS_FOUND if len(late_players) == 0: @@ -149,7 +151,8 @@ def get_room_from_thread(self, channel_id: int): return None async def populate_host_fcs(self): - all_hosts = {str(plr.member.id): plr for plr in filter(lambda p: p.host, self.players_on_confirmed_teams())} + all_hosts = {str(plr.member.id): plr for plr in filter( + lambda p: p.host, self.players_on_confirmed_teams())} hosts = await host_fcs.get_hosts(all_hosts) for host_discord_id, host_fc in hosts.items(): player: Player = all_hosts.get(host_discord_id) @@ -272,6 +275,7 @@ def __init__(self, member: discord.Member, lounge_name: str, mmr: int, confirmed self.score = 0 self.host = host self.host_fc = None + self.is_late = False @property def mention(self): @@ -286,17 +290,24 @@ def __repr__(self): def __lt__(self, other: Player): return self.mmr < other.mmr + def set_is_late(self): + self.late = True + + def get_is_late(self): + return self.late + class VoteView(View): - def __init__(self, players, thread, mogi: Mogi, room: Room, tier_info): + def __init__(self, players, thread, mogi: Mogi, room: Room, penalty_time: int, tier_info): super().__init__() self.players = players - self.thread:discord.Thread = thread + self.thread: discord.Thread = thread self.mogi = mogi self.room = room self.header_text = "" self.teams_text = "" self.found_winner = False + self.penalty_time = penalty_time self.tier_info = tier_info self.votes = {"FFA": [], "2v2": [], @@ -305,8 +316,20 @@ def __init__(self, players, thread, mogi: Mogi, room: Room, tier_info): "6v6": [] } + self.add_button("FFA", self.general_vote_callback) + self.add_button("2v2", self.general_vote_callback) + self.add_button("3v3", self.general_vote_callback) + self.add_button("4v4", self.general_vote_callback) + + if common.SERVER is common.Server.MKW: + self.add_button("6v6", self.general_vote_callback) + async def make_teams(self, format_): - self.mogi.making_rooms_run_time = datetime.now(timezone.utc) + if common.SERVER is common.Server.MKW: + self.mogi.making_rooms_run_time = datetime.now(timezone.utc) + elif common.SERVER is common.Server.MK8DX: + self.mogi.making_rooms_run_time = self.mogi.start_time + \ + timedelta(minutes=5) random.shuffle(self.players) room = self.mogi.get_room_from_thread(self.thread.id) @@ -317,14 +340,18 @@ async def make_teams(self, format_): 2) 2v2 - {len(self.votes['2v2'])} 3) 3v3 - {len(self.votes['3v3'])} 4) 4v4 - {len(self.votes['4v4'])} -5) 6v6 - {len(self.votes['6v6'])} -Winner: {format_[1]} - """ + if common.SERVER is common.Server.MKW: + msg += f"5) 6v6 - {len(self.votes['6v6'])}\n" + msg += f"Winner: {format_[1]}\n\n" room_mmr = round(sum([p.mmr for p in self.players]) / 12) room.mmr_average = room_mmr - self.header_text = f"**Room {room.room_num} MMR: {room_mmr} - T{get_tier(room_mmr, self.tier_info)}** " + self.header_text = "" + if common.SERVER is common.Server.MKW: + self.header_text += f"**Room {room.room_num} MMR: {room_mmr} - T{get_tier(room_mmr, self.tier_info)}** " + elif common.SERVER is common.Server.MK8DX: + self.header_text += f"**Room {room.room_num} MMR: {room_mmr} - Tier {get_tier_mk8dx(room_mmr)}** " msg += self.header_text + "\n" teams = [] @@ -346,7 +373,13 @@ async def make_teams(self, format_): msg += team_text self.teams_text += team_text - penalty_time = self.mogi.making_rooms_run_time + timedelta(minutes=8) + if common.SERVER is common.Server.MK8DX: + msg += f"\nTable: `/scoreboard`\n" + + msg += f"RandomBot Scoreboard: `/scoreboard {teams_per_room} {', '.join(scoreboard_text)}`\n\n" + + penalty_time = self.mogi.making_rooms_run_time + \ + timedelta(minutes=self.penalty_time) room_open_time = self.mogi.making_rooms_run_time potential_host_str = self.room.get_host_str() if potential_host_str == "": @@ -359,7 +392,12 @@ async def make_teams(self, format_): self.found_winner = True await self.thread.send(msg) if common.SERVER is common.Server.MKW: - new_thread_name = self.thread.name + f" - T{get_tier(room_mmr, self.tier_info)}" + new_thread_name = self.thread.name + \ + f" - T{get_tier(room_mmr, self.tier_info)}" + await self.thread.edit(name=new_thread_name) + elif common.SERVER is common.Server.MK8DX: + new_thread_name = self.thread.name + \ + f" - Tier {get_tier_mk8dx(room_mmr)}" await self.thread.edit(name=new_thread_name) async def find_winner(self): @@ -376,7 +414,7 @@ async def find_winner(self): winners.append((3, "3v3")) if len(self.votes["4v4"]) == most_votes: winners.append((4, "4v4")) - if len(self.votes["6v6"]) == most_votes: + if common.SERVER is common.Server.MKW and len(self.votes["6v6"]) == most_votes: winners.append((6, "6v6")) winner = random.choice(winners) @@ -386,28 +424,17 @@ async def find_winner(self): await self.make_teams(winner) - @discord.ui.button(label="FFA: 0", custom_id="FFA") - async def one_button_callback(self, interaction, button): - await self.general_vote_callback(interaction, 1, "FFA") - - @discord.ui.button(label="2v2: 0", custom_id="2v2") - async def two_button_callback(self, interaction, button): - await self.general_vote_callback(interaction, 2, "2v2") + def add_button(self, label, callback): + button = discord.ui.Button(label=f"{label}: 0", custom_id=label) + button.callback = callback + self.add_item(button) - @discord.ui.button(label="3v3: 0", custom_id="3v3") - async def three_button_callback(self, interaction, button): - await self.general_vote_callback(interaction, 3, "3v3") - - @discord.ui.button(label="4v4: 0", custom_id="4v4") - async def four_button_callback(self, interaction, button): - await self.general_vote_callback(interaction, 4, "4v4") - - @discord.ui.button(label="6v6: 0", custom_id="6v6") - async def six_button_callback(self, interaction, button): - await self.general_vote_callback(interaction, 6, "6v6") - - async def general_vote_callback(self, interaction: discord.Interaction, players_per_team: int, vote: str): + async def general_vote_callback(self, interaction: discord.Interaction): if not self.found_winner: + vote = interaction.data['custom_id'] + players_per_team = 1 + if vote != "FFA": + players_per_team = int(vote[0]) original_vote = None for vote_option, voter_ids in self.votes.items(): if interaction.user.id in voter_ids: @@ -426,10 +453,11 @@ async def general_vote_callback(self, interaction: discord.Interaction, players_ class JoinView(View): - def __init__(self, room: Room, get_mmr, bottom_room_num, is_restricted: Callable[[discord.User | discord.Member], bool] | None = None): + def __init__(self, room: Room, get_mmr, sub_range_mmr_allowance, bottom_room_num, is_restricted: Callable[[discord.User | discord.Member], bool] | None = None): super().__init__(timeout=1200) self.room = room self.get_mmr = get_mmr + self.sub_range_mmr_allowance = sub_range_mmr_allowance self.bottom_room_num = bottom_room_num self.is_restricted = is_restricted @@ -457,7 +485,7 @@ async def button_callback(self, interaction: discord.Interaction, button): return mmr_high = 999999 if self.room.room_num == 1 else self.room.mmr_high mmr_low = -999999 if self.room.room_num == self.bottom_room_num else self.room.mmr_low - if isinstance(user_mmr, int) and mmr_high + 700 > user_mmr > mmr_low - 700: + if isinstance(user_mmr, int) and mmr_high + self.sub_range_mmr_allowance > user_mmr > mmr_low - self.sub_range_mmr_allowance: self.room.subs.append(interaction.user.id) button.disabled = True await interaction.followup.edit_message(interaction.message.id, view=self) @@ -473,3 +501,36 @@ def get_tier(mmr: int, tier_info): if (tier["minimum_mmr"] is None or mmr >= tier["minimum_mmr"]) and ( tier["maximum_mmr"] is None or mmr <= tier["maximum_mmr"]): return tier["ladder_order"] + + +def get_tier_mk8dx(mmr: int): + if mmr > 14000: + return 'X' + if mmr > 13000: + return 'S' + if mmr > 12000: + return 'A' + if mmr > 11000: + return 'AB' + if mmr > 10000: + return 'B' + if mmr > 9000: + return 'BC' + if mmr > 8000: + return 'C' + if mmr > 7000: + return 'CD' + if mmr > 6000: + return 'D' + if mmr > 5000: + return 'DE' + if mmr > 4000: + return 'E' + if mmr > 3000: + return 'EF' + if mmr > 2000: + return 'F' + if mmr > 1000: + return 'FG' + else: + return 'G' diff --git a/test_config.json b/test_config.json deleted file mode 100644 index 3f78a68..0000000 --- a/test_config.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "token": "", - "lounge": "MKW", - "lounge_description": "Valid options are MKW or MK8DX", - "guild_id": 1228027010836594698, - "admin_roles": { - "1228027010836594698": [ - "Administrator", - "Lounge Staff" - ] - }, - "queue_join_channel": 1228046535321849907, - "queue_sub_channel": 1228046571447386173, - "queue_list_channel": 1228046584181293066, - "queue_history_channel": 1228046598777344161, - "queue_general_channel": 1250554989508825168, - "members_for_channels": { - "1228027010836594698": [ - 235148962103951360, - 155149108183695360, - 815483289515196416, - 892420397570592768, - 775415990238314518, - 774812906830299136, - 745443073202323586, - 774866940950872095, - 1019051989734273054 - ] - }, - "roles_for_channels": { - "1228027010836594698": [ - 1249808212547604561 - ] - }, - "staff_roles": [ - 387347888935534593, - 399382503825211393, - 399384750923579392, - 521149807994208295, - 792891432301625364, - 877629421811540016 - ], - "helper_staff_roles": [ - 389252697284542465, - 399384750923579392, - 399382503825211393 - ], - "restricted_role_id": 797208908153618452, - "muted_role_id": 434887701662007296, - "queue_messages": true, - "sec_between_queue_msgs": 2, - "username": "username", - "password": "password", - "url": "https://www.mkwlounge.gg", - "track_type": "rt", - "FIRST_EVENT_TIME": 0, - "FIRST_EVENT_TIME_DESCRIPTION": "First event time is the number of minutes from 0:00 UTC that the bot would run the first event of the day. As an example, if this were set to 5, and the QUEUE_OPEN_TIME were set to 70, and the bot were to reboot at 2:00 UTC, the next event would immediately start and create rooms at 2:25 UTC.", - "QUEUE_OPEN_TIME": 72, - "JOINING_TIME": 67, - "EXTENSION_TIME": 5, - "MOGI_LIFETIME": 180, - "SUB_MESSAGE_LIFETIME_SECONDS": 1200, - "ROOM_MMR_THRESHOLD": 9000 -} \ No newline at end of file From f39be8d1e3685e3887260103c00d6adce04a4e63 Mon Sep 17 00:00:00 2001 From: thomasgonda3 Date: Mon, 15 Jul 2024 18:27:00 -0700 Subject: [PATCH 142/147] Pushed check for specific game mmr fetch into mmr.py file --- cogs/SquadQueue.py | 40 ++++++++++------------------------------ mmr.py | 19 +++++++++++++++++-- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 133b87b..8f0fe9d 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -11,7 +11,7 @@ import common from common import divide_chunks -from mmr import mk8dx_mmr, mk8dx_get_mmr_from_discord_id, mkw_mmr, mkw_get_mmr_from_discord_id +from mmr import get_mmr, get_mmr_from_discord_id from mogi_objects import Mogi, Team, Player, Room, VoteView, JoinView, get_tier, get_tier_mk8dx import asyncio from collections import defaultdict @@ -327,10 +327,7 @@ async def join_queue(self, interaction: discord.Interaction, host=False): msg += f"{players[0].lounge_name} is assumed to be a new player and will be playing this mogi with a starting MMR of {starting_player_mmr}. " msg += "If you believe this is a mistake, please contact a staff member for help.\n" else: - if common.SERVER is common.Server.MKW: - players = await mkw_mmr(url, [member], self.TRACK_TYPE) - elif common.SERVER is common.Server.MK8DX: - players = await mk8dx_mmr(url, [member]) + players = await get_mmr(url, [member], self.TRACK_TYPE) if len(players) == 0 or players[0] is None: msg = f"{interaction.user.mention} fetch for MMR has failed and joining the queue was unsuccessful. " @@ -426,21 +423,12 @@ async def sub(self, interaction: discord.Interaction): timezone.utc) + timedelta(seconds=self.SUB_MESSAGE_LIFETIME_SECONDS) msg += f"Message will auto-delete in {discord.utils.format_dt(message_delete_date, style='R')}" await self.SUB_CHANNEL.send(msg, delete_after=self.SUB_MESSAGE_LIFETIME_SECONDS) - view = None - if common.SERVER is common.Server.MKW: - view = JoinView(room, - lambda discord_id: mkw_get_mmr_from_discord_id( - discord_id, self.TRACK_TYPE, self.URL), - self.SUB_RANGE_MMR_ALLOWANCE, - bottom_room_num, - lambda user: is_restricted(user, self.bot.config)) - elif common.SERVER is common.Server.MK8DX: - view = JoinView(room, - lambda discord_id: mk8dx_get_mmr_from_discord_id( - discord_id, self.URL), - self.SUB_RANGE_MMR_ALLOWANCE, - bottom_room_num, - lambda user: is_restricted(user, self.bot.config)) + view = JoinView(room, + lambda discord_id: get_mmr_from_discord_id(self.URL, + discord_id, self.TRACK_TYPE), + self.SUB_RANGE_MMR_ALLOWANCE, + bottom_room_num, + lambda user: is_restricted(user, self.bot.config)) await self.SUB_CHANNEL.send(view=view, delete_after=self.SUB_MESSAGE_LIFETIME_SECONDS) cooldowns[interaction.user.id] = current_time # Updates cooldown await interaction.response.send_message("Sent out request for sub.") @@ -1250,11 +1238,7 @@ async def debug_add_team(self, ctx, members: commands.Greedy[discord.Member]): url = "https://www.mkwlounge.gg" check_players = [member] check_players.extend(members) - players = [] - if common.SERVER is common.Server.MKW: - players = await mkw_mmr(url, check_players, self.TRACK_TYPE) - elif common.SERVER is common.Server.MK8DX: - players = await mk8dx_mmr(url, check_players) + players = await get_mmr(url, check_players, self.TRACK_TYPE) for i in range(0, 12): player = Player( players[0].member, f"{players[0].lounge_name}{i + 1}", players[0].mmr + (10 * i)) @@ -1307,11 +1291,7 @@ async def debug_add_many_players(self, ctx, members: commands.Greedy[discord.Mem url = "https://www.mkwlounge.gg" check_players = [member] check_players.extend(members) - players = [] - if common.SERVER is common.Server.MKW: - players = await mkw_mmr(url, check_players, self.TRACK_TYPE) - elif common.SERVER is common.Server.MK8DX: - players = await mk8dx_mmr(url, check_players) + players = await get_mmr(url, check_players, self.TRACK_TYPE) for i in range(0, num_times): player = Player( players[0].member, f"{players[0].lounge_name}{i + 1}", players[0].mmr + (10 * i)) diff --git a/mmr.py b/mmr.py index 87269ac..f709a7a 100644 --- a/mmr.py +++ b/mmr.py @@ -1,6 +1,7 @@ import aiohttp import discord from mogi_objects import Player +import common headers = {'Content-type': 'application/json'} @@ -82,7 +83,14 @@ async def mkw_mmr(url, members, track_type): return players -async def mk8dx_get_mmr_from_discord_id(discord_id, url): +async def get_mmr(url, members, track_type): + if common.SERVER is common.Server.MK8DX: + return await mk8dx_mmr(url, members) + elif common.SERVER is common.Server.MKW: + return await mkw_mmr(url, members, track_type) + + +async def mk8dx_get_mmr_from_discord_id(url, discord_id): base_url = url + '/api/player?' timeout = aiohttp.ClientTimeout(total=10) async with aiohttp.ClientSession( @@ -100,7 +108,7 @@ async def mk8dx_get_mmr_from_discord_id(discord_id, url): return player_data['mmr'] -async def mkw_get_mmr_from_discord_id(discord_id, track_type, url): +async def mkw_get_mmr_from_discord_id(url, discord_id, track_type): tracks = track_type base_url = url + \ '/api/ladderplayer.php?ladder_type=' + tracks + '&' @@ -119,3 +127,10 @@ async def mkw_get_mmr_from_discord_id(discord_id, track_type, url): if 'current_mmr' not in player_data.keys(): return "Player has no mmr" return player_data['current_mmr'] + + +async def get_mmr_from_discord_id(url, discord_id, track_type): + if common.SERVER is common.Server.MK8DX: + return await mk8dx_get_mmr_from_discord_id(url, discord_id) + elif common.SERVER is common.Server.MKW: + return await mkw_get_mmr_from_discord_id(url, discord_id, track_type) From 4ff06800eb0913a7d89a56f6439019071b73aeff Mon Sep 17 00:00:00 2001 From: thomasgonda3 Date: Mon, 15 Jul 2024 19:34:45 -0700 Subject: [PATCH 143/147] reverted unnecessary check for game --- cogs/SquadQueue.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 8f0fe9d..a44cbff 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -962,8 +962,7 @@ def allowed_players_check(players): late_player_list = all_confirmed_players[first_late_player_index:] proposed_list = sorted(mogi.generate_proposed_list( allowed_players_check), reverse=True) - if common.SERVER is common.Server.MKW: - await mogi.populate_host_fcs() + await mogi.populate_host_fcs() for room_number, room_players in enumerate(divide_chunks(proposed_list, mogi.players_per_room), 1): msg = f"`Room {room_number} - Player List`\n" for player_num, player in enumerate(room_players, 1): From 2f65c81c3033b4ea9327c66acecf9fb285016234 Mon Sep 17 00:00:00 2001 From: thomasgonda3 Date: Mon, 15 Jul 2024 20:10:16 -0700 Subject: [PATCH 144/147] changed method of checking late player for mk8dx --- cogs/SquadQueue.py | 19 +++++-------------- mogi_objects.py | 7 ------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index a44cbff..13db0d0 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -444,19 +444,13 @@ async def list_task(self): all_confirmed_players = mogi.players_on_confirmed_teams() first_late_player_index = ( mogi.num_players // mogi.players_per_room) * mogi.players_per_room - if common.SERVER is common.Server.MK8DX: - for i, player in enumerate(all_confirmed_players, 1): - if i > first_late_player_index: - player.is_late = True - else: - player.is_late = False + on_time_players = sorted( + all_confirmed_players[:first_late_player_index], reverse=True) + late_players = all_confirmed_players[first_late_player_index:] msg = f"**Queue closing: {discord.utils.format_dt(mogi.start_time)}**\n\n" msg += "**Current Mogi List:**\n" if common.SERVER is common.Server.MKW: - on_time_players = sorted( - all_confirmed_players[:first_late_player_index], reverse=True) - late_players = all_confirmed_players[first_late_player_index:] for i, player in enumerate(on_time_players, 1): msg += f"{i}) {player.lounge_name} ({player.mmr} MMR)\n" if i % mogi.players_per_room == 0: @@ -469,11 +463,8 @@ async def list_task(self): elif common.SERVER is common.Server.MK8DX: all_confirmed_players.sort(reverse=True) for i, player in enumerate(all_confirmed_players, 1): - msg += f"{i}) {player.lounge_name} ({player.mmr} MMR)" - if player.is_late: - msg += " (late)\n" - else: - msg += "\n" + late_str = " (late)" if player in late_players else "" + msg += f"{i}) {player.lounge_name} ({player.mmr} MMR){late_str}\n" if i % mogi.players_per_room == 0: msg += "ㅤ\n" msg += f"\n**Last Updated:** {discord.utils.format_dt(datetime.now(timezone.utc), style='R')}" diff --git a/mogi_objects.py b/mogi_objects.py index d3b87dd..16fb033 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -275,7 +275,6 @@ def __init__(self, member: discord.Member, lounge_name: str, mmr: int, confirmed self.score = 0 self.host = host self.host_fc = None - self.is_late = False @property def mention(self): @@ -290,12 +289,6 @@ def __repr__(self): def __lt__(self, other: Player): return self.mmr < other.mmr - def set_is_late(self): - self.late = True - - def get_is_late(self): - return self.late - class VoteView(View): def __init__(self, players, thread, mogi: Mogi, room: Room, penalty_time: int, tier_info): From 573ad61305e91d0f430a67e56542e1a202296f62 Mon Sep 17 00:00:00 2001 From: thomasgonda3 Date: Mon, 15 Jul 2024 20:42:43 -0700 Subject: [PATCH 145/147] Added an explicit display_time variable for mogis to control how start time is presented --- cogs/SquadQueue.py | 22 ++++++++++---------- example_configs/prod/mk8dx_prod_config.json | 1 + example_configs/prod/mkw_ct_prod_config.json | 1 + example_configs/prod/mkw_rt_prod_config.json | 1 + mogi_objects.py | 3 ++- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/cogs/SquadQueue.py b/cogs/SquadQueue.py index 13db0d0..ba1f41f 100644 --- a/cogs/SquadQueue.py +++ b/cogs/SquadQueue.py @@ -121,6 +121,9 @@ def __init__(self, bot): # number of minutes after QUEUE_OPEN_TIME that teams can join the mogi self.JOINING_TIME = timedelta(minutes=bot.config["JOINING_TIME"]) + self.DISPLAY_OFFSET_MINUTES = timedelta( + minutes=bot.config["DISPLAY_OFFSET_MINUTES"]) + # number of minutes after JOINING_TIME for any potential extra teams to join self.EXTENSION_TIME = timedelta(minutes=bot.config["EXTENSION_TIME"]) @@ -724,6 +727,7 @@ async def peek_bot_config(self, interaction: discord.Interaction): msg += f"SITE URL: {self.URL}\n" msg += f"FIRST EVENT TIME: {self.FIRST_EVENT_TIME}\n" msg += f"TIME BETWEEN EVENTS: {self.QUEUE_OPEN_TIME} minutes\n" + msg += f"DISPLAY TIME OFFSET MINUTES FROM JOIN TIME END: {self.DISPLAY_OFFSET_MINUTES} minutes\n" msg += f"EVENT JOINING TIME: {self.JOINING_TIME} minutes\n" msg += f"EXTENSION TIME: {self.EXTENSION_TIME} minutes\n" msg += f"ROOM JOIN PENALTY TIME: {self.ROOM_JOIN_PENALTY_TIME} minutes\n" @@ -859,11 +863,7 @@ async def write_history(mogi: Mogi, history_channel: discord.TextChannel): """Writes the teams, tier and average of each room per hour.""" try: if mogi is not None: - # I want the time displayed to be at the hour but the current way reads as :55 minutes on each queue which looks bad... - room_display_time = mogi.start_time - if common.SERVER is common.Server.MK8DX: - room_display_time = mogi.start_time + timedelta(minutes=5) - await history_channel.send(f"{discord.utils.format_dt(room_display_time)} Rooms") + await history_channel.send(f"{discord.utils.format_dt(mogi.display_time)} Rooms") for index, room in enumerate(mogi.rooms, 1): if not room or not room.view: print( @@ -884,13 +884,11 @@ async def check_room_channels(self, mogi): if num_created_rooms >= mogi.max_possible_rooms: return for i in range(num_created_rooms, mogi.max_possible_rooms): - start_time = mogi.start_time - if common.SERVER is common.Server.MK8DX: - start_time += timedelta(minutes=5) - minute = start_time.minute + display_time = mogi.display_time + minute = display_time.minute if len(str(minute)) == 1: minute = '0' + str(minute) - room_name = f"{start_time.month}/{start_time.day}, {start_time.hour}:{minute}:00 - Room {i+1}" + room_name = f"{display_time.month}/{display_time.day}, {display_time.hour}:{minute}:00 - Room {i+1}" try: room_channel = await self.GENERAL_CHANNEL.create_thread(name=room_name, auto_archive_duration=60, @@ -1125,6 +1123,7 @@ async def schedule_que_event(self): return next_event_open_time = self.compute_next_event_open_time() next_event_start_time = next_event_open_time + self.JOINING_TIME + next_event_display_time = next_event_start_time + self.DISPLAY_OFFSET_MINUTES # We don't want to schedule the next event if it would open after it's joining period and during its extension period if next_event_start_time < datetime.now(timezone.utc): return @@ -1147,7 +1146,8 @@ async def schedule_que_event(self): players_per_room=12, mogi_channel=self.MOGI_CHANNEL, is_automated=True, - start_time=next_event_start_time) + start_time=next_event_start_time, + display_time=next_event_display_time) print(f"Started Queue for {next_event_start_time}", flush=True) diff --git a/example_configs/prod/mk8dx_prod_config.json b/example_configs/prod/mk8dx_prod_config.json index 94ac015..9430b3c 100644 --- a/example_configs/prod/mk8dx_prod_config.json +++ b/example_configs/prod/mk8dx_prod_config.json @@ -37,6 +37,7 @@ "FIRST_EVENT_TIME_DESCRIPTION": "First event time is the number of minutes from 0:00 UTC that the bot would run the first event of the day. As an example, if this were set to 5, and the QUEUE_OPEN_TIME were set to 70, and the bot were to reboot at 2:00 UTC, the next event would immediately start and create rooms at 2:25 UTC.", "QUEUE_OPEN_TIME": 60, "JOINING_TIME": 55, + "DISPLAY_OFFSET_MINUTES": 5, "EXTENSION_TIME": 3, "ROOM_JOIN_PENALTY_TIME": 5, "MOGI_LIFETIME": 180, diff --git a/example_configs/prod/mkw_ct_prod_config.json b/example_configs/prod/mkw_ct_prod_config.json index 91048a7..27d17c4 100644 --- a/example_configs/prod/mkw_ct_prod_config.json +++ b/example_configs/prod/mkw_ct_prod_config.json @@ -48,6 +48,7 @@ "FIRST_EVENT_TIME_DESCRIPTION": "First event time is the number of minutes from 0:00 UTC that the bot would run the first event of the day. As an example, if this were set to 5, and the QUEUE_OPEN_TIME were set to 70, and the bot were to reboot at 2:00 UTC, the next event would immediately start and create rooms at 2:25 UTC.", "QUEUE_OPEN_TIME": 90, "JOINING_TIME": 85, + "DISPLAY_OFFSET_MINUTES": 0, "EXTENSION_TIME": 5, "ROOM_JOIN_PENALTY_TIME": 8, "MOGI_LIFETIME": 180, diff --git a/example_configs/prod/mkw_rt_prod_config.json b/example_configs/prod/mkw_rt_prod_config.json index 3b65358..46515fe 100644 --- a/example_configs/prod/mkw_rt_prod_config.json +++ b/example_configs/prod/mkw_rt_prod_config.json @@ -48,6 +48,7 @@ "FIRST_EVENT_TIME_DESCRIPTION": "First event time is the number of minutes from 0:00 UTC that the bot would run the first event of the day. As an example, if this were set to 5, and the QUEUE_OPEN_TIME were set to 70, and the bot were to reboot at 2:00 UTC, the next event would immediately start and create rooms at 2:25 UTC.", "QUEUE_OPEN_TIME": 72, "JOINING_TIME": 67, + "DISPLAY_OFFSET_MINUTES": 0, "EXTENSION_TIME": 5, "ROOM_JOIN_PENALTY_TIME": 8, "MOGI_LIFETIME": 180, diff --git a/mogi_objects.py b/mogi_objects.py index 16fb033..0e67c24 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -21,7 +21,7 @@ class Mogi: ALGORITHM_STATUS_SUCCESS_EMPTY = 4 def __init__(self, sq_id: int, max_players_per_team: int, players_per_room: int, mogi_channel: discord.TextChannel, - is_automated=False, start_time=None, additional_extension_minutes=0): + is_automated=False, start_time=None, display_time=None, additional_extension_minutes=0): self.started = False self.gathering = False self.making_rooms_run = False @@ -34,6 +34,7 @@ def __init__(self, sq_id: int, max_players_per_team: int, players_per_room: int, self.rooms: List[Room] = [] self.is_automated = is_automated self.start_time = start_time if is_automated else None + self.display_time = display_time if is_automated else None self.additional_extension = timedelta( minutes=additional_extension_minutes) From 1584e4c115274ee4ed87e41ba189bba081d7feac Mon Sep 17 00:00:00 2001 From: thomasgonda3 Date: Tue, 16 Jul 2024 22:20:56 -0700 Subject: [PATCH 146/147] fixed way tier is calculated for rooms --- mogi_objects.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mogi_objects.py b/mogi_objects.py index 0e67c24..e066ce0 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -345,7 +345,7 @@ async def make_teams(self, format_): if common.SERVER is common.Server.MKW: self.header_text += f"**Room {room.room_num} MMR: {room_mmr} - T{get_tier(room_mmr, self.tier_info)}** " elif common.SERVER is common.Server.MK8DX: - self.header_text += f"**Room {room.room_num} MMR: {room_mmr} - Tier {get_tier_mk8dx(room_mmr)}** " + self.header_text += f"**Room {room.room_num} MMR: {room_mmr} - Tier {get_tier_mk8dx(room_mmr - 500)}** " msg += self.header_text + "\n" teams = [] @@ -391,7 +391,7 @@ async def make_teams(self, format_): await self.thread.edit(name=new_thread_name) elif common.SERVER is common.Server.MK8DX: new_thread_name = self.thread.name + \ - f" - Tier {get_tier_mk8dx(room_mmr)}" + f" - Tier {get_tier_mk8dx(room_mmr - 500)}" await self.thread.edit(name=new_thread_name) async def find_winner(self): From 4a3e0799e21c9d49396dcd9942e711aefe353eb8 Mon Sep 17 00:00:00 2001 From: thomasgonda3 Date: Tue, 16 Jul 2024 22:21:17 -0700 Subject: [PATCH 147/147] fixed bug with display of names for names with underscores in them --- mogi_objects.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mogi_objects.py b/mogi_objects.py index e066ce0..dba908f 100644 --- a/mogi_objects.py +++ b/mogi_objects.py @@ -384,6 +384,7 @@ async def make_teams(self, format_): room.teams = teams self.found_winner = True + msg = msg.replace("_", "\_") await self.thread.send(msg) if common.SERVER is common.Server.MKW: new_thread_name = self.thread.name + \