diff --git a/src/cogs/general.py b/src/cogs/general.py index 5a9a972..c6e4abf 100644 --- a/src/cogs/general.py +++ b/src/cogs/general.py @@ -1,12 +1,11 @@ import discord -from discord.ext import commands, bridge from discord.commands import option +from discord.ext import commands, bridge from src.func.String import String from src.func.Union import Union - class General(commands.Cog, name="general"): """ Contains source, avatar, qotd. @@ -47,17 +46,17 @@ class ModalCreator(discord.ui.Modal): def __init__(self) -> None: # fields = ["LABEL", "PLACEHOLDER", STYLE] super().__init__(title="QOTD Creator") - self.add_item(discord.ui.InputText(label="What is the question of the day?", placeholder="Enter the question here", style=discord.InputTextStyle.long)) - + self.add_item(discord.ui.InputText(label="What is the question of the day?", + placeholder="Enter the question here", + max_length=256, + style=discord.InputTextStyle.long)) async def callback(self, interaction: discord.Interaction): await interaction.response.send_message("The QOTD has been sent!") await String(string=self.children[0].value).qotd(ctx) - await ctx.send_modal(modal=ModalCreator()) - def setup(bot): bot.add_cog(General(bot)) diff --git a/src/cogs/guild.py b/src/cogs/guild.py index b093993..99471e7 100644 --- a/src/cogs/guild.py +++ b/src/cogs/guild.py @@ -6,7 +6,7 @@ from src.func.Integer import Integer from src.func.String import String from src.utils.consts import gvg_info_embed, requirements_embed, resident_embed -from src.utils.discord_utils import name_grabber +from src.utils.db_utils import get_db_uuid_username_from_discord_id class Guild(commands.Cog, name="guild"): @@ -27,9 +27,10 @@ def __init__(self, bot): async def gmember(self, ctx, name: str = None): """View the given user's guild experience over the past week!""" if not name: - name = await name_grabber(ctx.author) - - res = await String(string=name).gmember(ctx) + uuid, username = await get_db_uuid_username_from_discord_id(ctx.author.id) + res = await String(uuid=uuid, username=username).gmember(ctx) + else: + res = await String(string=name).gmember(ctx) if isinstance(res, discord.Embed): await ctx.respond(embed=res) if isinstance(res, str): @@ -91,8 +92,11 @@ async def invites(self, ctx, name: str = None): """View your invitation stats""" await ctx.defer() if not name: - name = await name_grabber(ctx.author) - await ctx.respond(embed=await String(name).invites()) + uuid, _ = await get_db_uuid_username_from_discord_id(ctx.author.id) + res = await String(string=uuid).invites() + else: + res = await String(string=name).invites() + await ctx.respond(embed=res) def setup(bot): bot.add_cog(Guild(bot)) diff --git a/src/cogs/hypixel.py b/src/cogs/hypixel.py index 3ecdc68..f6c2636 100644 --- a/src/cogs/hypixel.py +++ b/src/cogs/hypixel.py @@ -5,7 +5,7 @@ from src.func.General import General from src.func.String import String from src.func.Union import Union -from src.utils.discord_utils import name_grabber +from src.utils.db_utils import get_db_uuid_username_from_discord_id class Hypixel(commands.Cog, name="hypixel"): @@ -47,8 +47,11 @@ async def sync(self, ctx, name, tag: str = None): async def info(self, ctx, name: str = None): """View Hypixel stats of the given user!""" if not name: - name = await name_grabber(ctx.author) - await ctx.respond(embed=await String(string=name).info()) + uuid, username = await get_db_uuid_username_from_discord_id(ctx.author.id) + res = await String(uuid=uuid, username=username).info() + else: + res = await String(string=name).info() + await ctx.respond(embed=res) @bridge.bridge_command(aliases=['dnkladd', 'dnkla']) @commands.has_any_role("Staff") @@ -93,9 +96,11 @@ async def dnkl_list(self, ctx): async def dnkl_check(self, ctx, name: str = None): """Check whether you are eligible for the do-not-kick-list!""" if not name: - name = await name_grabber(ctx.author) + uuid, username = await get_db_uuid_username_from_discord_id(ctx.author.id) + res = await String(uuid=uuid, username=username).dnklcheck() + else: + res = await String(string=name).dnklcheck() - res = await String(string=name).dnklcheck() if isinstance(res, discord.Embed): await ctx.respond(embed=res) elif isinstance(res, str): diff --git a/src/cogs/staff.py b/src/cogs/staff.py index 4513d0c..9eb67fd 100644 --- a/src/cogs/staff.py +++ b/src/cogs/staff.py @@ -79,6 +79,12 @@ async def rolecheck(self, ctx, send_ping: bool = True): """Sync the names and roles of everyone in the discord!""" await General.rolecheck(ctx, send_ping) + @bridge.bridge_command() + @commands.has_role("new role") + async def add_members(self, ctx): + await General.add_players(ctx) + + def setup(bot): bot.add_cog(Staff(bot)) diff --git a/src/func/General.py b/src/func/General.py index e87be6d..93f5112 100644 --- a/src/func/General.py +++ b/src/func/General.py @@ -5,22 +5,22 @@ from __main__ import bot from datetime import datetime, timedelta -import aiohttp import discord import discord.ui from src.func.Union import Union from src.utils.calculation_utils import get_gexp_sorted, generate_lb_text from src.utils.consts import (accepted_staff_application_embed, active_req, - allies, error_color, guild_handle, + error_color, guild_handle, invalid_guild_embed, log_channel_id, member_req, milestone_emojis, milestones_channel, neg_color, neutral_color, pos_color, ticket_deleted_embed, registration_channel_id, registration_embed, staff_application_questions, ticket_categories, resident_req, dnkl_entries_not_found, - positive_responses) -from src.utils.db_utils import insert_new_giveaway, select_all + positive_responses, allies) +from src.utils.db_utils import insert_new_giveaway, select_all, get_db_username_from_uuid, \ + get_db_uuid_username_from_discord_id, insert_new_member, select_one from src.utils.discord_utils import (create_ticket, get_ticket_creator, log_event, name_grabber, has_tag_perms) @@ -68,68 +68,63 @@ async def rolecheck(ctx, send_ping: bool): discord_members = bot.guild.members + discord_member_uuids = [x[0] for x in await select_all("SELECT uuid FROM members")] # Define arrays for guild and ally uuids and names - guild_members = (await get_guild_by_name(guild_handle))['members'] + guild_members = (await get_guild_by_name(guild_handle))["members"] guild_uuids = await get_guild_uuids(guild_handle) - guild_usernames, ally_usernames, ally_uuids, ally_divisions = [], [], [], [] - # Appending UUIDs of members of all ally guilds into one array + # Allies dict; {uuid: {username, gtag}} + allies_dict = {} + + # Guild dict; {uuid: {username, gexp}} + guild = {} + for ally in allies: - await progress_message.edit(content=f"Fetching ally UUIDs - {ally}") - ally_uuids.extend(await get_guild_uuids(ally)) - req = await get_player_guild(ally_uuids[-1]) - gtag = " " if not req["tag"] or not req else req["tag"] - ally_divisions.append([len(ally_uuids), gtag]) - # Ally divisions marks the separation point of one guild - # from another in the ally_uuids array along with the guild's gtag - - # Limiting the maximum concurrency - async def gather_with_concurrency(n, *tasks): - semaphore = asyncio.Semaphore(n) - - async def sem_task(task): - async with semaphore: - return await task - - return await asyncio.gather(*(sem_task(task) for task in tasks)) - - # Get guild and ally names from their respective UUIDs - await progress_message.edit(content="Retrieving usernames...") - - for _set in [[guild_uuids, guild_usernames], [ally_uuids, ally_usernames]]: - draw, dump = _set - async with aiohttp.ClientSession(): - tasks = await gather_with_concurrency(2, - *[ - get_name_by_uuid(uuid) for uuid in draw - ]) # Gathering with a max concurrency of 2 - dump.extend(tasks) - # Loop through discord members + await progress_message.edit(content=f"Fetching ally UUIDs and usernames - {ally}") + ally_uuids = await get_guild_uuids(ally) + gtag = (await get_player_guild(ally_uuids[-1]))["tag"] + for uuid in ally_uuids: + if uuid not in discord_member_uuids: + continue + allies_dict[uuid] = {"username": await get_db_username_from_uuid(uuid), "tag": gtag} + await progress_message.edit(content=f"Fetching {guild_handle}'s UUIDs and usernames") + + for uuid in guild_uuids: + if uuid not in discord_member_uuids: + continue + for player in guild_members: + if player["uuid"] == uuid: + guild[uuid] = {"username": await get_db_username_from_uuid(uuid), + "gexp": sum(player["expHistory"].values())} await ctx.send("If you see the bot is stuck on a member along with an error message, " - "forcesync member the bot is stuck on.") + "forcesync member the bot is stuck on.") bot.admin_ids = [member.id for member in bot.admin.members] for discord_member in discord_members: # Do not check admins and bots if discord_member.id in bot.admin_ids or discord_member.bot: continue - username = await name_grabber(discord_member) + nick = await name_grabber(discord_member) + uuid_username = await get_db_uuid_username_from_discord_id(discord_member.id) + if not uuid_username: + await discord_member.remove_roles(bot.member_role, bot.ally, bot.guest, bot.active_role) + await discord_member.add_roles(bot.new_member_role) + continue + uuid, username = uuid_username has_tag_permission = await has_tag_perms(discord_member) - await progress_message.edit(content=f"Checking {username} - {discord_member}") + await progress_message.edit(content=f"Checking {nick} - {discord_member}") + # Member of guild - if username in guild_usernames: - for guild_member in guild_members: - if guild_uuids[guild_usernames.index(username)] == guild_member['uuid']: - # Finds a given UUID's corresponding hypixel data - - weekly_exp = sum(guild_member["expHistory"].values()) - if weekly_exp >= active_req: # Meets active req - await discord_member.add_roles(bot.active_role) - elif weekly_exp < active_req and bot.active_role in discord_member.roles: # Doesn't meet active req - await discord_member.remove_roles(bot.active_role) - - if not has_tag_permission: + if uuid in guild.keys(): + weekly_exp = guild[uuid]["gexp"] + if weekly_exp >= active_req: # Meets active req + await discord_member.add_roles(bot.active_role) + elif weekly_exp < active_req and bot.active_role in discord_member.roles: # Doesn't meet active req + await discord_member.remove_roles(bot.active_role) + + if (nick != username and bot.active_role not in discord_member.roles) or ( + nick != username and not has_tag_permission): await discord_member.edit(nick=username) # Edit roles @@ -137,33 +132,9 @@ async def sem_task(task): await discord_member.remove_roles(bot.new_member_role, bot.guest, bot.ally) continue - # Member of an ally guild - if username in ally_usernames: - # Get player gtag - position = ally_usernames.index(username) - dividers = [x[0] for x in ally_divisions] - tags = [x[1] for x in ally_divisions] - - start = 0 - for divider in dividers: - if start <= position < divider: - gtag = tags[dividers.index(divider)] - break - start = divider - - ''' - last_value = 1 - for guild_division in ally_divisions: - if last_value > 1: - if last_value < position < guild_division[0]: - gtag = guild_division[1] - - elif position < guild_division[0]: - gtag = guild_division[1] - last_value = guild_division[0] - ''' - # Set nick - if not discord_member.nick or f" [{gtag}]" not in discord_member.nick: + elif uuid in allies_dict.keys(): + gtag = allies_dict[uuid]["tag"] + if not discord_member.nick or nick != username or f"[{gtag}]" not in discord_member.nick: await discord_member.edit(nick=username + f' [{gtag}]') # Edit roles @@ -171,28 +142,157 @@ async def sem_task(task): await discord_member.remove_roles(bot.new_member_role, bot.member_role, bot.active_role) continue - # Get player data - username, uuid = await get_mojang_profile(username) - if not username: - # Edit roles and continue loop + elif not uuid: await discord_member.remove_roles(bot.member_role, bot.ally, bot.guest, bot.active_role) await discord_member.add_roles(bot.new_member_role) continue # Guests else: - if not has_tag_permission: + if not has_tag_permission and discord_member.nick != username: await discord_member.edit(nick=username) await discord_member.add_roles(bot.guest) await discord_member.remove_roles(bot.new_member_role, bot.member_role, bot.active_role, bot.ally) continue - - # Send ping to new member role in registration channel - if send_ping: + # Send ping to new mem + # ber role in registration channel + if not send_ping: await bot.get_channel(registration_channel_id).send(bot.new_member_role.mention, embed=registration_embed) await progress_message.edit(content="Rolecheck complete!") + # + # async def rolecheck(ctx, send_ping: bool): + # # Define a message for sending progress updates + # progress_message = await ctx.respond("Processing prerequisites...") + # + # discord_members = bot.guild.members + # + # # Define arrays for guild and ally uuids and names + # guild_members = (await get_guild_by_name(guild_handle))['members'] + # guild_uuids = await get_guild_uuids(guild_handle) + # guild_usernames, ally_usernames, ally_uuids, ally_divisions = [], [], [], [] + # + # # Appending UUIDs of members of all ally guilds into one array + # for ally in allies: + # await progress_message.edit(content=f"Fetching ally UUIDs - {ally}") + # ally_uuids.extend(await get_guild_uuids(ally)) + # req = await get_player_guild(ally_uuids[-1]) + # gtag = " " if not req["tag"] or not req else req["tag"] + # ally_divisions.append([len(ally_uuids), gtag]) + # # Ally divisions marks the separation point of one guild + # # from another in the ally_uuids array along with the guild's gtag + # + # # Limiting the maximum concurrency + # async def gather_with_concurrency(n, *tasks): + # semaphore = asyncio.Semaphore(n) + # + # async def sem_task(task): + # async with semaphore: + # return await task + # + # return await asyncio.gather(*(sem_task(task) for task in tasks)) + # + # # Get guild and ally names from their respective UUIDs + # await progress_message.edit(content="Retrieving usernames...") + # + # for _set in [[guild_uuids, guild_usernames], [ally_uuids, ally_usernames]]: + # draw, dump = _set + # async with aiohttp.ClientSession(): + # tasks = await gather_with_concurrency(2, + # *[ + # get_name_by_uuid(uuid) for uuid in draw + # ]) # Gathering with a max concurrency of 2 + # dump.extend(tasks) + # # Loop through discord members + # + # await ctx.send("If you see the bot is stuck on a member along with an error message, " + # "forcesync member the bot is stuck on.") + # bot.admin_ids = [member.id for member in bot.admin.members] + # for discord_member in discord_members: + # # Do not check admins and bots + # if discord_member.id in bot.admin_ids or discord_member.bot: + # continue + # + # username = await name_grabber(discord_member) + # has_tag_permission = await has_tag_perms(discord_member) + # await progress_message.edit(content=f"Checking {username} - {discord_member}") + # # Member of guild + # if username in guild_usernames: + # for guild_member in guild_members: + # if guild_uuids[guild_usernames.index(username)] == guild_member['uuid']: + # # Finds a given UUID's corresponding hypixel data + # + # weekly_exp = sum(guild_member["expHistory"].values()) + # if weekly_exp >= active_req: # Meets active req + # await discord_member.add_roles(bot.active_role) + # elif weekly_exp < active_req and bot.active_role in discord_member.roles: # Doesn't meet active req + # await discord_member.remove_roles(bot.active_role) + # + # if not has_tag_permission: + # await discord_member.edit(nick=username) + # + # # Edit roles + # await discord_member.add_roles(bot.member_role) + # await discord_member.remove_roles(bot.new_member_role, bot.guest, bot.ally) + # continue + # + # # Member of an ally guild + # if username in ally_usernames: + # # Get player gtag + # position = ally_usernames.index(username) + # dividers = [x[0] for x in ally_divisions] + # tags = [x[1] for x in ally_divisions] + # + # start = 0 + # for divider in dividers: + # if start <= position < divider: + # gtag = tags[dividers.index(divider)] + # break + # start = divider + # + # ''' + # last_value = 1 + # for guild_division in ally_divisions: + # if last_value > 1: + # if last_value < position < guild_division[0]: + # gtag = guild_division[1] + # + # elif position < guild_division[0]: + # gtag = guild_division[1] + # last_value = guild_division[0] + # ''' + # # Set nick + # if not discord_member.nick or f" [{gtag}]" not in discord_member.nick: + # await discord_member.edit(nick=username + f' [{gtag}]') + # + # # Edit roles + # await discord_member.add_roles(bot.guest, bot.ally) + # await discord_member.remove_roles(bot.new_member_role, bot.member_role, bot.active_role) + # continue + # + # # Get player data + # username, uuid = await get_mojang_profile(username) + # if not username: + # # Edit roles and continue loop + # await discord_member.remove_roles(bot.member_role, bot.ally, bot.guest, bot.active_role) + # await discord_member.add_roles(bot.new_member_role) + # continue + # + # # Guests + # else: + # if not has_tag_permission: + # await discord_member.edit(nick=username) + # await discord_member.add_roles(bot.guest) + # await discord_member.remove_roles(bot.new_member_role, bot.member_role, bot.active_role, bot.ally) + # continue + # + # # Send ping to new member role in registration channel + # if send_ping: + # await bot.get_channel(registration_channel_id).send(bot.new_member_role.mention, embed=registration_embed) + # + # await progress_message.edit(content="Rolecheck complete!") + async def delete(ctx): embed = discord.Embed(title="This ticket will be deleted in 10 seconds!", color=neg_color) @@ -801,3 +901,18 @@ async def compile_milestones(ctx): milestone_message = milestone_message + "\n**Congrats to everyone this week. If you wish to submit a milestone, look over at <#650248396480970782>!**" await bot.get_channel(milestones_channel).send(milestone_message) return f"{count} milestones have been compiled and sent in {bot.get_channel(milestones_channel)}" + + async def add_players(ctx): + for member in bot.guild.members: + if bot.new_member_role in member.roles or member.bot: + continue + record = await select_one("SELECT * FROM members where discord_id = (?)", (member.id,)) + if record: + continue + returned_name = await name_grabber(member) + name, uuid = await get_mojang_profile(returned_name) + if not (name and uuid): + continue + await insert_new_member(member.id, uuid, name) + await asyncio.sleep(15) + await ctx.send("Adding players complete.") diff --git a/src/func/String.py b/src/func/String.py index 4a0a468..2045dac 100644 --- a/src/func/String.py +++ b/src/func/String.py @@ -25,8 +25,10 @@ class String: - def __init__(self, string: str): + def __init__(self, string: str = None, uuid: str = None, username: str = None): self.string = string + self.uuid = uuid + self.username = username # Command from https://github.com/Rapptz/RoboDanny async def source(self): @@ -64,9 +66,13 @@ async def source(self): return f"Following is the source code for {self.string}\n{final_url}" async def gmember(self, ctx): - name, uuid = await get_mojang_profile(self.string) - if not name: - return unknown_ign_embed + if self.uuid and self.username: + uuid = self.uuid + name = self.username + else: + name, uuid = await get_mojang_profile(self.string) + if not name: + return unknown_ign_embed guild = await get_player_guild(uuid) @@ -150,7 +156,13 @@ async def gmember(self, ctx): return embed.set_image(url=chart.get_url()) async def info(self): - player_data = await get_hypixel_player(name=self.string) + if self.uuid and self.username: + uuid = self.uuid + else: + name, uuid = await get_mojang_profile(self.string) + if not name: + return unknown_ign_embed + player_data = await get_hypixel_player(name=uuid) # Player doesn't exist if not player_data: return unknown_ign_embed @@ -213,7 +225,13 @@ async def dnklremove(self): return f"{username} has been removed from the do-not-kick-list!" async def dnklcheck(self): - self.string, uuid = await get_mojang_profile(self.string) + if self.uuid and self.username: + uuid = self.uuid + name = self.username + else: + name, uuid = await get_mojang_profile(self.string) + if not name: + return unknown_ign_embed _, weeklygexp = await get_player_gexp(uuid) # Player is not in a guild @@ -222,13 +240,13 @@ async def dnklcheck(self): # Player is eligible if weeklygexp > dnkl_req: - embed = discord.Embed(title=self.string, color=pos_color) + embed = discord.Embed(title=name, color=pos_color) embed.add_field(name="This player is eligible to apply for the do-not-kick-list.", value=f"They have {weeklygexp}/{dnkl_req} weekly guild experience.", inline=True) # Player is not eligible else: - embed = discord.Embed(title=self.string, color=neg_color) + embed = discord.Embed(title=name, color=neg_color) embed.add_field(name="This player is not eligible to apply for the do-not-kick-list.", value=f"They have {weeklygexp}/{dnkl_req} weekly guild experience to be eligible.", inline=True) @@ -253,18 +271,24 @@ async def qotd(self, ctx): # 15th May 2022 was the 473rd QOTD day. It is used as a reference point to calculate the day number. day_number = 473 + (datetime.utcnow() - datetime.strptime("2022/05/15", "%Y/%m/%d")).days embed = discord.Embed( - title=f"Day {day_number}: {datetime.utcnow().day} {months[datetime.utcnow().month]} {datetime.utcnow().year}", - description=self.string, color=neutral_color) - embed.set_footer(text="Put your answers in #qotd-answer") + title=f"**{self.string}\n**", + description=f"You can respond to this qotd in: <#{qotd_ans_channel_id}>", color=neutral_color) + embed.set_author( + name=f"Day {day_number}: {datetime.utcnow().day} {months[datetime.utcnow().month]} {datetime.utcnow().year}") + embed.set_footer(text="- " + ctx.author.nick if ctx.author.nick else ctx.author.name) await bot.get_channel(qotd_channel_id).send("<@&923978802818871356>", embed=embed) await ctx.send(f"**The QOTD has been sent to <#{qotd_channel_id}>!**") await bot.get_channel(qotd_ans_channel_id).send(rainbow_separator) async def invites(self): - ign, uuid = await get_mojang_profile(self.string) - if not ign: - return unknown_ign_embed + if self.uuid and self.username: + uuid = self.uuid + name = self.username + else: + name, uuid = await get_mojang_profile(self.string) + if not name: + return unknown_ign_embed guild = await get_player_guild(uuid) if ("name" not in guild) or (guild["name"] != guild_handle): @@ -280,7 +304,7 @@ async def invites(self): weekly_invites = [await get_name_by_uuid(invitee) for invitee in weekly_invites] for invitee in weekly_invites: invites_text += f"**▸** {invitee}\n" - embed = discord.Embed(title=f"{ign}'s Invites", color=neutral_color) + embed = discord.Embed(title=f"{name}'s Invites", color=neutral_color) embed.add_field(name="Weekly Invites", value=None if not invites_text else invites_text, inline=False) embed.add_field(name="Total Invites", value=total_invites, inline=True) embed.add_field(name="Total Valid Invites", value=total_valid_invites, inline=True) diff --git a/src/utils/db_utils.py b/src/utils/db_utils.py index 8a7a970..9157238 100644 --- a/src/utils/db_utils.py +++ b/src/utils/db_utils.py @@ -7,6 +7,12 @@ async def connect_db(): bot.db = await aiosqlite.connect("database.db") + # Discord Member Table: + await bot.db.execute("""CREATE TABLE IF NOT EXISTS members ( + discord_id integer PRIMARY KEY NOT NULL, + uuid text NOT NULL, + username text)""") + # DNKL table: await bot.db.execute("""CREATE TABLE IF NOT EXISTS dnkl ( message_id integer NOT NULL, @@ -119,3 +125,25 @@ async def get_invites(inviter_uuid): return (await select_one( "SELECT current_invitee_uuids, total_invites, total_valid_invites FROM invites WHERE inviter_uuid = (?)", (inviter_uuid,))) + + +async def get_db_uuid_username_from_discord_id(discord_id: int): + return await select_one("SELECT uuid, username from members WHERE discord_id = (?)", (discord_id,)) + + +async def get_db_username_from_uuid(uuid: str): + return (await select_one("SELECT username from members WHERE uuid = (?)", (uuid,)))[0] + +async def insert_new_member(discord_id: int, uuid: str, username: str): + await bot.db.execute("INSERT INTO members VALUES (?, ?, ?)", (discord_id, uuid, username)) + await bot.db.commit() + + +async def update_member(discord_id: int, uuid: str, username: str): + discord_idExists = (await select_one("SELECT uuid from members WHERE discord_id = (?)", (discord_id,)))[0] + if discord_idExists: + await bot.db.execute("UPDATE members SET uuid = (?) and username = (?) WHERE discord_id = (?)", + (uuid, username, discord_id)) + await bot.db.commit() + else: + await insert_new_member(discord_id, uuid, username)