def generate_signups_embed(bot, signups, event): embed = Embed(title=f"Signups - {event.title}", colour=Colour.dark_purple()) playing_signups = [] sub_signups = [] unregistered_signups = [signup for signup in signups if not Player.exists_discord_id(signup.user_id)] for signup in signups: if signup.can_play: playing_signups.append(signup) if signup.can_sub: sub_signups.append(signup) signups_tag_str = "" subs_tag_str = "" if len(playing_signups) > 0: for signup in playing_signups: user = bot.get_user(signup.user_id) player = Player.exists_discord_id(signup.user_id) signups_tag_str += f"@{user} ({player.minecraft_username if player else 'Unregistered'})\n" else: signups_tag_str = "Nobody :(" if len(sub_signups) > 0: for signup in sub_signups: user = bot.get_user(signup.user_id) subs_tag_str += f"@{user} \n" else: subs_tag_str = "Nobody :(" embed.add_field(name="Signed", value=f"```{signups_tag_str}```", inline=False) embed.add_field(name="Can Sub", value=f"```{subs_tag_str}```", inline=False) if unregistered_signups: tags = "\n".join([f"@{bot.get_user(signup.user_id)} " for signup in unregistered_signups]) embed.add_field(name="Unregistered:", value=f"```{tags}```", inline=False) return embed
async def on_raw_reaction_add(self, payload): request = get_register_request(payload.message_id) if payload.channel_id == REGISTER_REQUESTS_CHANNEL and bool( request) and payload.user_id != self.bot.user.id: channel = await self.bot.fetch_channel(REGISTER_REQUESTS_CHANNEL) message = await channel.fetch_message(payload.message_id) server = self.bot.get_guild(payload.guild_id) mod_member = server.get_member(payload.user_id) player_member = server.get_member(request[1]) required_role = get(server.roles, name=MOD_ROLE) if str( payload.emoji ) == "✅" and required_role.position <= mod_member.top_role.position: Player.add_player(request[0], request[1]) remove_register_request(payload.message_id) await message.clear_reactions() await message.edit( content= f"✅ {mod_member.name} accepted {player_member.mention}'s request for IGN" f" **{request[2]}**", embed=None) try: await success_embed( player_member, f"Your IGN request for **{request[2]}** was approved") except Forbidden: # This means the bot can't DM the user await channel.send( "This user has PMs off, failed to send DM.") elif str( payload.emoji ) == "❌" and required_role.position <= mod_member.top_role.position: remove_register_request(payload.message_id) await message.clear_reactions() await message.edit( content= f"❌ {mod_member.name} denied {player_member.mention}'s request for IGN" f" **{request[2]}**", embed=None) try: await player_member.send(embed=Embed( title="Denied IGN Request", description= f"Your request for IGN **{request[2]}** was denied.", color=Colour.dark_red())) except Forbidden: # This means the bot can't DM the user await channel.send( "This user has PMs off, failed to send DM.")
async def list(self, ctx, data_type): if not has_permissions(ctx, MOD_ROLE): await ctx.send( "You do not have sufficient permissions to perform this command", hidden=True) return False title = "List" info = [] if data_type == "players": players = sorted(Player.fetch_players_list(), key=lambda item: item.minecraft_username) title = "Registered Users" for player in players: player_string = f"**{player.minecraft_username}** ({self.bot.get_user(player.discord_id).mention if self.bot.get_user(player.discord_id) else '<@' + str(player.discord_id) + '> 🚫' })\n" for key in player.__dict__.keys(): player_string += f"> {key}: `{player.__dict__[key]}`\n" info.append(player_string) elif data_type == "register_requests": title = "IGN Registration Requests" requests = sorted(get_all_register_requests(), key=lambda item: item[2]) for request in requests: info.append( f"**{request[2]}** ({self.bot.get_user(request[1]).mention if self.bot.get_user(request[1]) else '<@' + str(request[1]) + '> 🚫'})" ) await create_list_pages( bot=self.bot, ctx=ctx, title=title, info=info, if_empty="There are no registration requests" if data_type == "register_requests" else "There are no registered players", elements_per_page=5)
def priority_rng_signups(playing_signups_list, size): """ Randomly generates a list of signups. To be used for PUG events. If a player is not registered / does not exist, they will not be included in the random list. Handle this accordingly - a list of unregistered players is returned by this function. :param playing_signups_list: A list of Signup objects :param size: The amount of players you would like to include in the output list :return: (selected_players, benched_players, unregistered_signups) """ seed() players = [] unregistered_signups = [] for signup in playing_signups_list: try: players.append(Player.from_discord_id(signup.user_id)) except PlayerDoesNotExistError: unregistered_signups.append(signup) shuffle(players) players = sorted(players, key=lambda item: item.priority, reverse=True) selected_players = players[:size] for player in selected_players: player.set_priority(0) benched_players = players[size:] for player in benched_players: player.change_priority(1) return selected_players, benched_players, unregistered_signups
async def event(event_id: int): try: event_from_id = Event.from_event_id(event_id) except EventDoesNotExistError: return await render_template("page_not_found.html"), 404 else: signups = Signup.fetch_signups_list(event_id) for signup in signups: signup.user = bot.get_user(signup.user_id) signup.player = Player.exists_discord_id(signup.user_id) event_from_id.time_est = get_embed_time_string( datetime.fromisoformat(event_from_id.time_est)) event_from_id.signup_deadline = get_embed_time_string( datetime.fromisoformat(event_from_id.signup_deadline)) event_from_id.description = markdown(event_from_id.description)\ if event_from_id.description else "No Description" return await render_template("event.html", event=event_from_id, signups=signups)
async def update_usernames(self): server = self.bot_channel.guild changes_list = [] for player in Player.fetch_players_list(): old_username = player.minecraft_username latest_username = player.update_minecraft_username() if latest_username != old_username and latest_username is not None: changes_list.append([player, old_username]) await async_sleep(1) if len(changes_list) > 0: embed = Embed(title="IGNs Updated", color=Colour.dark_purple()) for change in changes_list: player = change[0] member = server.get_member(player.discord_id) old_username = change[1] if not member.nick: member.nick = member.name team_list = re.findall(r"^\[(\w{1,4})\]", member.nick) alias_list = re.findall(r"\s\((.*)\)$", member.nick) new_nick = f"{'[' + team_list[0] + '] ' if team_list else ''}{player.minecraft_username}" + \ (f" ({alias_list[0]})" if alias_list else "") if UPDATE_NICKNAMES: try: await member.edit(nick=new_nick) except Forbidden: embed_value = f"🔴 Failed to update nickname to `{new_nick}` (Forbidden)" else: embed_value = f"Updated server nickname to `{new_nick}`" try: await success_embed( member, f"PUG server nickname updated to `{new_nick}`") embed_value += " (DM sent)" except Forbidden: embed_value += " (confirmation DM failed to send)" else: embed_value = "Nickname updates disabled in config." embed.add_field( name=f"{old_username} → {player.minecraft_username}", value=embed_value, inline=False) await self.bot_channel.send(embed=embed)
async def examine_members(self, ctx): """Examines the status and checks nicknames for all server members (for debug purposes)""" if not has_permissions(ctx, ADMIN_ROLE): await ctx.send( "You do not have sufficient permissions to perform this command", hidden=True) return False server = ctx.guild registered = [] unregistered = [] without_nick = [] for member in server.members: if not member.bot: if member.nick is None: without_nick.append(member.mention) else: try: player = Player.from_discord_id(member.id) except PlayerDoesNotExistError: unregistered.append(member.mention) else: team_list = re.findall(r"^\[(\w{1,4})\]", member.nick) alias_list = re.findall(r"\s\((.*)\)$", member.nick) new_nick = f"{'[' + team_list[0] + '] ' if team_list else ''}{player.minecraft_username}" + \ (f" ({alias_list[0]})" if alias_list else "") registered.append(f"{member.mention} → `{new_nick}`") await create_list_pages(self.bot, ctx, info=registered, title="Registered Users", elements_per_page=20) await create_list_pages(self.bot, ctx, info=unregistered, title="Unregistered Users", elements_per_page=20) await create_list_pages(self.bot, ctx, info=without_nick, title="Users without nicknames", elements_per_page=20)
async def on_member_join(self, member): if not SEND_JOIN_MESSAGE: return channel = self.bot.get_channel(PUBLIC_BOT_CHANNEL) if Player.exists_discord_id(member.id): return else: try: await member.send( f"Welcome {member.mention} to the PUG server, do not forget to use **/register**" f" in the PUG server.") logging.info( f"Sent registration reminder for {member.name} in DMs") except Forbidden: # This means the bot can't DM the user await channel.send( f"Welcome {member.mention} to the PUG server, do not forget to use **/register**." ) logging.info( f"Sent registration reminder for {member.name} in #{channel.name}" )
async def leaderboard(): player = None if await discord.authorized: user = await fetch_user_with_perms() player = Player.exists_discord_id(user["user"].id) else: user = False if player: player.leaderboard_position = False data = get_sorted_elo() position = 1 for item in data: data[position - 1] = (item[0], item[1], item[2], position) if player: if item[0] == player.minecraft_username: player.leaderboard_position = position position += 1 return await render_template("leaderboard.html", data=data, user=user, player=player)
async def leaderboard(self, ctx, role=None): player = Player.exists_discord_id(ctx.author.id) data = get_sorted_elo() leaderboard_entries = [] count = 1 if role: data = list( filter( lambda item: item[2] in [member.id for member in ctx.guild.members], data)) data = list( filter( lambda item: role in ctx.guild.get_member(item[2]).roles, data)) for item in data: if player: if player.minecraft_username == item[0]: leaderboard_entries.append( f"\n> **#{count}:** `{item[0]}` - **{item[1]}**\n") count += 1 continue leaderboard_entries.append( f"**#{count}:** `{item[0]}` - **{item[1]}**") count += 1 title = "Leaderboard" no_reg_desc = "There are no registered players" if role: title += f" | {role.name}" no_reg_desc = "There are no registered players with that role" await create_list_pages( self.bot, ctx, title, leaderboard_entries, no_reg_desc, elements_per_page=20, thumbnails=[ f"https://cravatar.eu/helmavatar/{data[0][0]}/128.png" ] if data else [], can_be_reversed=True)
async def prune_missing_players(self, ctx): """Removes registered players from the database who aren't in the server""" if not has_permissions(ctx, ADMIN_ROLE) and ctx.guild not in SLASH_COMMANDS_GUILDS: await ctx.send("You do not have sufficient permissions to perform this command", hidden=True) return False await ctx.defer() tz = timezone('US/Eastern') time = datetime.now(tz).strftime("%Y%m%d-%H%M%S") backup_filename = f"backups/database-{time}.db" copyfile("database/database.db", backup_filename) await success_embed(ctx, f"Created backup file: `{backup_filename}`") players = Player.fetch_players_list() guild_member_ids = [member.id for member in ctx.guild.members] deleted_player_names = [] for player in players: if player.discord_id not in guild_member_ids: deleted_player_names.append(player.minecraft_username) player.delete() if deleted_player_names: await success_embed(ctx, f"Removed players `{', '.join(deleted_player_names)}` from database.") else: await error_embed(ctx, "No players to prune.")
async def unregister(self, ctx, user: User = None): if not has_permissions(ctx, MOD_ROLE): await ctx.send( "You do not have sufficient permissions to perform this command", hidden=True) return False """ Allows a PUG Mod to unregister a discord user from the Minecraft account they registered to. Usage: unregister <user_mention> Example: unregister @Ninsanity """ if not user: await error_embed(ctx, "Missing Argument <input_user>") try: player = Player.from_discord_id(user.id) except PlayerDoesNotExistError: await error_embed(ctx, "Player is not registered in the database.") return await response_embed( ctx, "Confirm", f"""Are you sure you want to delete {user.mention} from the database? \nThis action is **permanent** and **irreversible**, and will remove their elo and priority **forever**. \nReply with yes or no.""") def check(m): return m.author == ctx.author and m.channel == ctx.channel response = await self.bot.wait_for('message', check=check) if response.content.lower() == "y" or response.content.lower( ) == "yes": player.delete() await success_embed(ctx, f"User {user.mention} has been unregistered.") else: await response_embed( ctx, "Stopped Deletion", f"User {user.mention} will not be deleted from the database.")
async def profile(self, ctx, user: User = None): """ Displays a user's profile """ if user: if not isinstance(user, int): player_id = user.id else: player_id = user else: player_id = ctx.author.id try: player = Player.from_discord_id(player_id) except PlayerDoesNotExistError: await error_embed(ctx, "Player does not exist") return #Position in leaderboard data = get_sorted_elo() count = 1 for item in data: if player: if player.minecraft_username == item[0]: leader_pos = count break count += 1 stats = f"**ELO:** {getattr(player, 'elo')}\n**Rank**: #{leader_pos}\n**Discord:** <@{getattr(player, 'discord_id')}>" #for key in player.__dict__.keys(): # stats += f"**{key}:** {getattr(player, key)}\n" embed = Embed(description=stats, color=Colour.dark_purple()) embed.set_author( name=f"User profile - {getattr(player, 'minecraft_username')}", icon_url= f"https://cravatar.eu/helmavatar/{getattr(player, 'minecraft_id')}/128.png" ) await ctx.send(embed=embed)
async def Bind(session: CommandSession): # 取一堆值 SenderQQNumber = session.ctx['user_id'] # 取发送者的qq号 SenderGamerName = session.current_arg_text.strip() # 去空格取命令参数 SenderGroupNumber = session.ctx['group_id'] DbSession = config.SESSION() if str(SenderGroupNumber) in config.SendGroup: pass else: if not SenderGamerName: await session.send('#bind后面必须跟上你的用户名嗷,例:#bind HelloWorld') else: player = DbSession.query(Player).filter( or_(Player.QQNumber == int(SenderQQNumber), Player.GamerName == SenderGamerName)).one_or_none() if player is not None: # 存在相同QQ号 SqlGamerName = player.GamerName SqlQQNumber = player.QQNumber if str(SqlQQNumber) == str(SenderQQNumber): await session.send( unescape('[CQ:at,qq=%s] 你已经绑定过%s了嗷' % (SenderQQNumber, SqlGamerName))) elif SqlGamerName == SenderGamerName: await session.send( unescape( '[CQ:at,qq=%s] %s已经被%s绑定了' % (SenderQQNumber, SenderGamerName, SqlQQNumber))) session.finish() add_player = Player(QQNumber=SenderQQNumber, GamerName=SenderGamerName, TpNumber='0') DbSession.add(add_player) DbSession.commit() await session.send('[CQ:at,qq=%s] 成功绑定%s!' % (SenderQQNumber, SenderGamerName))
async def user(self, ctx, discord_tag: User = None, action_type="get", variable_name=None, value=None): """ Allows a PUG Mod to edit information about a user. Usage: user @Tom <get/set> <variable_name> <value> Examples: user @Tom get returns user profile user @Tom set elo [elo] sets user ELO """ if not has_permissions(ctx, MOD_ROLE): await ctx.send( "You do not have sufficient permissions to perform this command", hidden=True) return False user = self.bot.get_user(discord_tag.id) if action_type == "get": try: player = Player.from_discord_id(user.id) except PlayerDoesNotExistError: await error_embed(ctx, "Player does not exist") return info = "" for key in player.__dict__.keys(): info += f"**{key}**: {getattr(player, key)}\n" embed = Embed(description=info, title=f"User Profile - {user.name}", color=Colour.dark_purple()) await ctx.send(embed=embed) elif action_type == "set": try: player = Player.from_discord_id(user.id) except PlayerDoesNotExistError: await error_embed(ctx, "Player does not exist") return if variable_name: if value: if variable_name == "username": old_username = player.update_minecraft_username() try: player.change_minecraft_username(value) await success_embed( ctx, f"Changed {discord_tag.mention}'s username: **{old_username}** -> **{value}**" ) except UsernameAlreadyExistsError: await error_embed( ctx, f"Username **{value}** is already in the database" ) except UsernameDoesNotExistError: await error_embed( ctx, f"Username **{value}** is not a valid username" ) elif variable_name == "discord": value = value[3:-1] if value.isdigit(): user = self.bot.get_user(int(value)) if user: try: player.change_discord_id(user.id) await success_embed( ctx, f"Changed discord user: {discord_tag.mention} -> {user.mention}" ) except DiscordAlreadyExistsError: await error_embed( ctx, f"User {user.mention} is already in the database" ) else: await error_embed(ctx, "Value must be a User") else: await error_embed(ctx, "Value must be a User") elif variable_name == "elo": old_elo = player.get_elo() if value.isdigit(): value = int(value) if player.set_elo(value): await success_embed( ctx, f"Set {discord_tag.mention}'s elo: **{old_elo}** -> **{value}**" ) else: await error_embed( ctx, f"Elo given (**{value}**) is below Elo floor (**{ELO_FLOOR}**)" ) else: await error_embed(ctx, "Value must be an int") else: old_priority = player.get_priority() if value.isdigit(): value = int(value) if player.set_priority(value): await success_embed( ctx, f"Set {discord_tag.mention}'s priority: **{old_priority}** -> **{value}**" ) else: await error_embed( ctx, f"Priority given (**{value}**) is negative" ) else: await error_embed(ctx, "Value must be an int") else: await error_embed(ctx, "No value inputted") else: await error_embed(ctx, "No variable name inputted") else: await error_embed(ctx, "Invalid action argument. Use 'get' or 'set'")
async def playerstats(self, ctx, ign=None, mode="competitive"): """Gets player stats using 915's stats website""" if not ctx.responded: await ctx.defer() get_player_id_url = "https://by48xt0cuf.execute-api.us-east-1.amazonaws.com/default/request-player?name={}" stats_from_id_url = "https://by48xt0cuf.execute-api.us-east-1.amazonaws.com/default/request-player?id={}" new_player_request_url = "https://qe824lieck.execute-api.us-east-1.amazonaws.com/default/new-player?id={}" if ign: username = ign else: player = Player.exists_discord_id(ctx.author.id) if player: username = player.minecraft_username else: username = None if not username: await error_embed(ctx, "Please input a player or `/register` to get your own stats") return response = await request_async_json(get_player_id_url.format(username), 'text/plain') if response: json = response[1] logging.info(json) if str(json).startswith("No player found"): await error_embed(ctx, f"Could not find player with name `{username}`") return if json["uuid"]: player_id = json["id"] else: await error_embed(ctx, f"The following player does not have a UUID in the API `{username}`") return else: await error_embed(ctx, f"Failed to get player ID for name `{username}`") return discord_message = await ctx.send(content="Grabbing your data") stats_response = await request_async_json(stats_from_id_url.format(player_id), 'text/plain') json_response = stats_response[1] if json_response["data"]: data = json_response["data"] else: logging.info(f"Player data for `{username}` is not loaded yet") await discord_message.edit(content="Your data is not loaded yet, hold tight") async with aiohttp.ClientSession() as session: async with session.get(new_player_request_url.format(player_id)) as r: if r.status == 200: text = await r.text() if text == "Success": logging.info("Successfully loaded new data") await discord_message.edit(content="Your data is being loaded.") await async_sleep(10) stats_response = await request_async_json(stats_from_id_url.format(player_id), 'text/plain') json_response = stats_response[1] data = json_response["data"] if not data: await discord_message.edit(content=f"Account `{username}` doesn't appear to have any" f" data, sorry. Try again later.") return else: await discord_message.edit(content="Data loaded") else: await error_embed(ctx, text) return else: await discord_message.edit(content="Request to load new player data failed") return class_stats_list = [] link = "https://www.nineonefive.xyz/stats/" for class_name in data[mode].keys(): class_stats = data[mode][class_name] class_stats_string = f"**{class_name.title()}**\n\n" + "\n"\ .join([f"**{stat_key.replace('_', ' ').title()}**: `{int(round(float(class_stats[stat_key]), 0))}`" for stat_key in class_stats.keys() if class_stats[stat_key] != "0"]) + "\n" # Damage per 20 minutes if float(class_stats["damage_dealt"]) > 1000 and float(class_stats["playtime"]) > 1000: dmg = float(class_stats["damage_dealt"]) n_20 = float(class_stats["playtime"]) / 1200 dmg_per_20 = int(round(dmg / n_20, 0)) class_stats_string += f"\n**Damage per 20m**: `{dmg_per_20}`" # KDR if float(class_stats["kills"]) > 0 and float(class_stats["deaths"]) > 0: kdr = round(float(class_stats["kills"])/float(class_stats["deaths"]), 3) class_stats_string += f"\n**KDR**: `{kdr}`" # Cap success if float(class_stats["flags_captured"]) > 0 and float(class_stats["flags_stolen"]) > 0: cap_eff = round(float(class_stats["flags_captured"]) / float(class_stats["flags_stolen"]) * 100, 2) class_stats_string += f"\n**Capture Success**: `{cap_eff}%`" class_stats_string += f"\n\n[Stats sourced from 915's brilliant website]({link})" class_stats_list.append(class_stats_string) await discord_message.edit(content="Plotting a beautiful graph") if not len(data[mode].keys()) > 0: await discord_message.edit(content=f"{username} has not played {mode}") return sizes = [int(data[mode][key]["playtime"]) for key in data[mode].keys()] avg_size = sum(sizes) / len(sizes) labels = [(key.title() if float(data[mode][key]["playtime"]) > avg_size else "") for key in data[mode].keys()] ##Sorting lists as tuples list_of_tuples = list(zip(labels, sizes)) sorted_list = sorted(list_of_tuples, key=lambda tup: tup[1]) unzipped_list = list(zip(*sorted_list)) new_labels, new_sizes = list(unzipped_list[0]), list(unzipped_list[1]) data_stream = pie_chart(new_labels, new_sizes, explode=[0.1 if label else 0 for label in labels], title="Playtime by class") data_stream.seek(0) chart_file = File(data_stream, filename="pie_chart.png") await discord_message.edit(content="Done! Sending results...") await ctx.send(file=chart_file) await create_list_pages(self.bot, ctx, info=class_stats_list, title=f"{mode.title()} stats | {username}", elements_per_page=1, thumbnails=[f"https://cravatar.eu/helmavatar/{username}/128.png"], can_be_reversed=True)
async def register(self, ctx, minecraft_username=""): """ Registers Minecraft username to Discord. Required to sign up for PUGs. Usage: register <minecraft_username> Example: register Ninsanity """ if not minecraft_username: embed = Embed(title="Error ❌", description="Missing argument <minecraft_username>", color=Colour.dark_red()) embed.add_field(name="Example", value="-register Ninsanity") await ctx.send(embed=embed) return uuid = MojangAPI.get_uuid(minecraft_username) if uuid: condition = Player.player_check(uuid, ctx.author.id) if not condition: if check_user_requests(ctx.author.id): await error_embed( ctx, "You have already submitted a register request") else: request_channel = self.bot.get_channel( REGISTER_REQUESTS_CHANNEL) embed = Embed( title=f"Register Request: {minecraft_username}", description= f"React below to verify {ctx.author.mention}", colour=Colour.dark_purple()) embed.set_thumbnail( url=f"https://cravatar.eu/helmavatar/{uuid}/128.png") message = await request_channel.send(embed=embed) await message.add_reaction("✅") await message.add_reaction("❌") if add_register_request(uuid, ctx.author.id, minecraft_username, message.id): await ctx.send(embed=Embed( title="Registration Pending", description= f"Requested to register **{minecraft_username}**" f" to {ctx.author.mention}", color=Colour.dark_purple())) else: await error_embed( ctx, "There was an error storing your register request. Contact a PUG Dev" ) elif condition == 1: await ctx.send(embed=Embed( title="Error ❌", description= f"**{minecraft_username}** is already registered", color=Colour.dark_red())) else: await ctx.send(embed=Embed( title="Error ❌", description=f"{ctx.author.mention} is already registered", color=Colour.dark_red())) else: await ctx.send(embed=Embed( title="Error ❌", description=f"**{minecraft_username}** does not exist", color=Colour.dark_red()))