diff --git a/README.md b/README.md index e0bb701..98e60b5 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Main Contributors/Developers:
  • JasonLovesDoggo
  • Levani Vashadze
  • -The bot is currently on release: 2.0
    +The bot is currently on release: 2.2
    License: MIT

    Contributing

    diff --git a/cogs/AI.py b/cogs/AI.py index 5bce980..0fd82e3 100644 --- a/cogs/AI.py +++ b/cogs/AI.py @@ -1,7 +1,7 @@ import base64 import time from io import BytesIO - +from better_profanity import profanity import disnake from disnake.ext import commands @@ -13,11 +13,21 @@ def __init__(self, bot: OGIROID): self.bot = bot @commands.slash_command(description="Generates ai art") - async def ai_art(self, inter: disnake.ApplicationCommandInteraction, text): + async def ai_art( + self, inter: disnake.ApplicationCommandInteraction, text: str + ): + if profanity.contains_profanity(text): + return await inter.send( + f"NSFW requests are not allowed!", ephemeral=True + ) + if "bot" in inter.channel.name or "command" in inter.channel.name: + hidden = False + else: + hidden = True ETA = int(time.time() + 60) await inter.send( f"Go grab a coffee this may take a while... ETA: ", - ephemeral=True, + ephemeral=hidden, ) response = await self.bot.session.post( "https://backend.craiyon.com/generate", json={"prompt": text} @@ -25,11 +35,15 @@ async def ai_art(self, inter: disnake.ApplicationCommandInteraction, text): r = await response.json() raw_images = r["images"] images = [ - disnake.File(BytesIO(base64.decodebytes(i.encode("utf-8"))), "image.png") + disnake.File( + BytesIO(base64.decodebytes(i.encode("utf-8"))), "image.png" + ) for i in raw_images ] - await inter.edit_original_response(files=images, content="Here you go!") + await inter.edit_original_response( + files=images, content="Here you go!" + ) def setup(bot): diff --git a/cogs/Animals.py b/cogs/Animals.py index eaf511d..bd62fdd 100644 --- a/cogs/Animals.py +++ b/cogs/Animals.py @@ -10,7 +10,9 @@ class Animals(commands.Cog): def __init__(self, bot: OGIROID): self.bot = bot - @commands.slash_command(description="Gets a random picture of the specified animal") + @commands.slash_command( + description="Gets a random picture of the specified animal" + ) @commands.cooldown(1, 3, commands.BucketType.user) async def animal(self, inter): pass @@ -18,7 +20,9 @@ async def animal(self, inter): @animal.sub_command(name="cat", description="Get a random cat picture") async def cat(self, inter): """Get a random cat picture!""" - response = await self.bot.session.get("https://some-random-api.ml/animal/cat") + response = await self.bot.session.get( + "https://some-random-api.com/animal/cat" + ) data = await response.json() embed = disnake.Embed( title="Cat Picture! 🐱", @@ -30,12 +34,16 @@ async def cat(self, inter): text=f"Command issued by: {inter.author.name}", icon_url=inter.author.display_avatar, ) - await inter.response.send_message(f"**Fun Fact: **" + data["fact"], embed=embed) + await inter.response.send_message( + f"**Fun Fact: **" + data["fact"], embed=embed + ) @animal.sub_command(name="dog", description="Get a random dog picture") async def dog(self, inter): """Get a random dog picture!""" - response = await self.bot.session.get("https://some-random-api.ml/animal/dog") + response = await self.bot.session.get( + "https://some-random-api.com/animal/dog" + ) data = await response.json() embed = disnake.Embed( title="Dog Picture! 🐢", @@ -47,12 +55,16 @@ async def dog(self, inter): text=f"Command issued by: {inter.author.name}", icon_url=inter.author.display_avatar, ) - await inter.response.send_message("**Fun Fact: **" + data["fact"], embed=embed) + await inter.response.send_message( + "**Fun Fact: **" + data["fact"], embed=embed + ) @animal.sub_command(name="bird", description="Get a random bird picture") async def bird(self, inter): """Get a random bird picture!""" - response = await self.bot.session.get("https://some-random-api.ml/animal/bird") + response = await self.bot.session.get( + "https://some-random-api.com/animal/bird" + ) data = await response.json() embed = disnake.Embed( title="Bird Picture! 🐦", @@ -64,12 +76,16 @@ async def bird(self, inter): text=f"Command issued by: {inter.author.name}", icon_url=inter.author.display_avatar, ) - await inter.response.send_message("**Fun Fact: **" + data["fact"], embed=embed) + await inter.response.send_message( + "**Fun Fact: **" + data["fact"], embed=embed + ) @animal.sub_command(name="fox", description="Get a random fox picture") async def fox(self, inter): """Get a random fox picture!""" - response = await self.bot.session.get("https://some-random-api.ml/animal/fox") + response = await self.bot.session.get( + "https://some-random-api.com/animal/fox" + ) data = await response.json() embed = disnake.Embed( title="Fox Picture! 🦊", @@ -81,12 +97,16 @@ async def fox(self, inter): text=f"Command issued by: {inter.author.name}", icon_url=inter.author.display_avatar, ) - await inter.response.send_message("**Fun Fact: **" + data["fact"], embed=embed) + await inter.response.send_message( + "**Fun Fact: **" + data["fact"], embed=embed + ) @animal.sub_command(name="panda", description="Get a random panda picture") async def panda(self, inter): """Get a random panda picture!""" - response = await self.bot.session.get("https://some-random-api.ml/animal/panda") + response = await self.bot.session.get( + "https://some-random-api.com/animal/panda" + ) data = await response.json() embed = disnake.Embed( title="Panda Picture! 🐼", @@ -98,12 +118,16 @@ async def panda(self, inter): text=f"Command issued by: {inter.author.name}", icon_url=inter.author.display_avatar, ) - await inter.response.send_message("**Fun Fact: **" + data["fact"], embed=embed) + await inter.response.send_message( + "**Fun Fact: **" + data["fact"], embed=embed + ) @animal.sub_command(name="koala", description="Get a random cat picture") async def koala(self, inter): """Get a random koala picture!""" - response = await self.bot.session.get("https://some-random-api.ml/animal/koala") + response = await self.bot.session.get( + "https://some-random-api.com/animal/koala" + ) data = await response.json() embed = disnake.Embed( title="Koala Picture! 🐨", @@ -115,7 +139,9 @@ async def koala(self, inter): text=f"Command issued by: {inter.author.name}", icon_url=inter.author.display_avatar, ) - await inter.response.send_message("**Fun Fact: **" + data["fact"], embed=embed) + await inter.response.send_message( + "**Fun Fact: **" + data["fact"], embed=embed + ) def setup(bot): diff --git a/cogs/Birthdays.py b/cogs/Birthdays.py index d16c051..f565ef5 100644 --- a/cogs/Birthdays.py +++ b/cogs/Birthdays.py @@ -12,6 +12,20 @@ from utils.shortcuts import QuickEmb, sucEmb, errorEmb +async def get_days_until_birthday(user_data) -> (int, str): + """returns the days until the next birthday and the next birthday date formatted for discord""" + next_birthday = datetime.datetime.strptime( + user_data.birthday + f"/{dt.datetime.now().year}", "%d/%m/%Y" + ) + if next_birthday < datetime.datetime.now(): + next_birthday = datetime.datetime.strptime( + user_data.birthday + f"/{dt.datetime.now().year + 1}", "%d/%m/%Y" + ) + return ( + next_birthday - datetime.datetime.now() + ).days, f"" + + class Birthday(commands.Cog): def __init__(self, bot: OGIROID): self.bot = bot @@ -21,17 +35,22 @@ def __init__(self, bot: OGIROID): @commands.Cog.listener() async def on_ready(self): if not self.bot.ready_: - self.birthday: BirthdayHandler = BirthdayHandler(self.bot, self.bot.db) + self.birthday: BirthdayHandler = BirthdayHandler( + self.bot, self.bot.db + ) def cog_unload(self): self.birthday_check.cancel() - @commands.slash_command(name="birthday") + @commands.slash_command( + name="birthday", description="Birthdays base command" + ) async def birthday(self, inter: disnake.ApplicationCommandInteraction): pass @birthday.sub_command( - name="set", description="Set your birthday. Cant be removed without Staff." + name="set", + description="Set your birthday. Cant be removed without Staff.", ) async def set( self, @@ -49,7 +68,9 @@ async def set( ), ): if month is None or day is None: - return await errorEmb(inter, "You need to provide a month and a day") + return await errorEmb( + inter, "You need to provide a month and a day" + ) if day < 1 or day > 31: return await errorEmb(inter, "The day must be between 1 and 31") @@ -63,7 +84,8 @@ async def set( @commands.has_permissions(manage_roles=True) @birthday.sub_command( - name="edit", description="Edit a users birthday. Can only be done by Staff." + name="edit", + description="Edit a users birthday. Can only be done by Staff.", ) async def edit( self, @@ -82,13 +104,18 @@ async def edit( ): try: await self.birthday.update_user(user.id, f"{day}/{month}") - return await sucEmb(inter, f"Birthday has been updated to {day}/{month}") + return await sucEmb( + inter, f"Birthday has been updated to {day}/{month}" + ) except UserNotFound: - return await errorEmb(inter, "The User doesn't have a birthday set") + return await errorEmb( + inter, "The User doesn't have a birthday set" + ) @commands.has_permissions(manage_roles=True) @birthday.sub_command( - name="remove", description="Remove a birthday. Can only be done by Staff." + name="remove", + description="Remove a birthday. Can only be done by Staff.", ) async def remove( self, @@ -100,13 +127,17 @@ async def remove( try: await self.birthday.delete_user(user.id) except UserNotFound: - return await errorEmb(inter, "This user doesn't have a birthday set") + return await errorEmb( + inter, "This user doesn't have a birthday set" + ) await sucEmb(inter, "The birthday has been removed") @birthday.sub_command(name="get", description="Get the birthday of a user") async def get( - self, inter, user: disnake.User = commands.Param(name="user", default=None) + self, + inter, + user: disnake.User = commands.Param(name="user", default=None), ): if user is None: user = inter.author @@ -115,19 +146,47 @@ async def get( birthday = await self.birthday.get_user(user.id) if birthday is None: - return await errorEmb(inter, "This user doesn't have a birthday set") + return await errorEmb( + inter, "This user doesn't have a birthday set" + ) - next_birthday = datetime.datetime.strptime( - birthday.birthday + f"/{dt.datetime.now().year}", "%d/%m/%Y" - ) - if next_birthday < datetime.datetime.now(): - next_birthday = datetime.datetime.strptime( - birthday.birthday + f"/{dt.datetime.now().year + 1}", "%d/%m/%Y" + days, discord_date = await get_days_until_birthday(birthday) + await QuickEmb( + inter, + f"{user.mention}'s birthday is in {days} Days." f"{discord_date}", + ).send() + + @birthday.sub_command(name="next", description="Get the next birthday") + async def next(self, inter: disnake.ApplicationCommandInteraction): + upcoming_birthdays = [] + # loop gets next birthday + for user in await self.birthday.get_users(): + # gets days until birthday and the discord date + days, discord_date = await get_days_until_birthday(user) + # checks if user is in the guild + upcoming_birthdays.append( + {"days": days, "user": user, "discord_date": discord_date} ) + + # sorts birthdays by days + upcoming_birthdays.sort(key=lambda x: x["days"]) + # gets the next birthday's user + next_birthday = upcoming_birthdays[0]["user"] + # checks if user is in the guild + while await inter.guild.fetch_member(next_birthday.user_id) is None: + upcoming_birthdays.pop(0) + next_birthday = upcoming_birthdays[0]["user"] + + member = await self.bot.fetch_user(next_birthday.user_id) + + if next_birthday is None: + return await errorEmb(inter, "There are no birthdays set") + + days, discord_date = await get_days_until_birthday(next_birthday) await QuickEmb( inter, - f"{user.mention}'s birthday is in {(next_birthday - datetime.datetime.now()).days} Days." - f" ", + f"{member.mention}'s birthday is in {days} Days." + f"{discord_date}", ).send() # @tasks.loop(time=[dt.time(dt.datetime.utcnow().hour, dt.datetime.utcnow().minute, dt.datetime.utcnow().second + 10)]) @@ -135,22 +194,26 @@ async def get( @tasks.loop(time=[dt.time(8, 0, 0)]) # loops every day at 8:00 UTC time async def birthday_check(self): - channel = self.bot.get_channel(self.bot.config.channels.birthdays) - guild = self.bot.get_guild(self.bot.config.guilds.main_guild) + channel = await self.bot.fetch_channel( + self.bot.config.channels.birthdays + ) + guild = await self.bot.fetch_guild(self.bot.config.guilds.main_guild) if channel is None: return today = dt.datetime.utcnow().strftime("%d/%m") # Gets all users from the db users = await self.birthday.get_users() for user in users: - member = await guild.fetch_member(user.user_id) + member = await guild.getch_member(user.user_id) # if the member is None, the user is not in the server anymore if member is None: continue # if the birthday is today, congratulate the user if user.birthday == today: - await member.add_roles(guild.get_role(self.bot.config.roles.birthday)) + await member.add_roles( + guild.get_role(self.bot.config.roles.birthday) + ) congrats_msg = await channel.send( f"{random.choice(congrats_messages)} {member.mention}! πŸŽ‚" ) diff --git a/cogs/Blacklist.py b/cogs/Blacklist.py index dd8bd9a..33d96bd 100644 --- a/cogs/Blacklist.py +++ b/cogs/Blacklist.py @@ -43,7 +43,11 @@ async def check_user_removal(self, user: BlacklistedUser): return # already being removed elif user.is_expired(): await self.blacklist.remove(user.id) - elif int(time.time()) <= user.expires <= (int(time.time()) + timings.HOUR): + elif ( + int(time.time()) + <= user.expires + <= (int(time.time()) + timings.HOUR) + ): self.del_que.append(user.id) await self.run_at(user.expires, self.blacklist.remove, user.id) @@ -59,10 +63,14 @@ async def blacklist(self, inter): pass @commands.cooldown(1, 5, commands.BucketType.user) - @blacklist.sub_command(name="info", description="Get info about a blacklisted user") + @blacklist.sub_command( + name="info", description="Get info about a blacklisted user" + ) async def blacklist_info(self, inter, user: Member): if not await self.blacklist.blacklisted(user.id): - return await errorEmb(inter, f"{user.mention} is not in the blacklist") + return await errorEmb( + inter, f"{user.mention} is not in the blacklist" + ) bl_user = await self.blacklist.get_user(user.id) embed = Embed( title=f"Blacklisted user: {user.name}", @@ -83,11 +91,16 @@ async def edit(self, inter): @commands.has_permissions(manage_messages=True) @edit.sub_command( - name="flags", description="Edit the user's blacklist flags in the blacklist" + name="flags", + description="Edit the user's blacklist flags in the blacklist", ) - async def flags(self, inter, user: Member, bot: bool, tickets: bool, tags: bool): + async def flags( + self, inter, user: Member, bot: bool, tickets: bool, tags: bool + ): if not await self.blacklist.blacklisted(user.id): - return await errorEmb(inter, f"{user.mention} is not in the blacklist") + return await errorEmb( + inter, f"{user.mention} is not in the blacklist" + ) await self.blacklist.edit_flags(user.id, bot, tickets, tags) await sucEmb( inter, @@ -96,11 +109,14 @@ async def flags(self, inter, user: Member, bot: bool, tickets: bool, tags: bool) @commands.has_permissions(manage_messages=True) @edit.sub_command( - name="reason", description="Edit the user's blacklist reason in the blacklist" + name="reason", + description="Edit the user's blacklist reason in the blacklist", ) async def reason(self, inter, user: Member, reason: str): if not await self.blacklist.blacklisted(user.id): - return await errorEmb(inter, f"{user.mention} is not in the blacklist") + return await errorEmb( + inter, f"{user.mention} is not in the blacklist" + ) await self.blacklist.edit_reason(user.id, reason) await sucEmb( inter, @@ -109,11 +125,14 @@ async def reason(self, inter, user: Member, reason: str): @commands.has_permissions(manage_messages=True) @edit.sub_command( - name="expiry", description="Edit the user's blacklist expiry in the blacklist" + name="expiry", + description="Edit the user's blacklist expiry in the blacklist", ) async def expiry(self, inter, user: Member, expires: str): if not await self.blacklist.blacklisted(user.id): - return await errorEmb(inter, f"{user.mention} is not in the blacklist") + return await errorEmb( + inter, f"{user.mention} is not in the blacklist" + ) expiry = int((await timeconversions.convert(expires)).dt.timestamp()) await self.blacklist.edit_expiry(user.id, expiry) await sucEmb( @@ -128,10 +147,14 @@ async def expiry(self, inter, user: Member, expires: str): ) async def remove(self, inter, user: Member): if not await self.blacklist.blacklisted(user.id): - return await errorEmb(inter, f"{user.mention} is not in the blacklist") + return await errorEmb( + inter, f"{user.mention} is not in the blacklist" + ) await self.blacklist.remove(user.id) await sucEmb( - inter, f"{user.mention} has been removed from blacklist", ephemeral=False + inter, + f"{user.mention} has been removed from blacklist", + ephemeral=False, ) @commands.has_permissions(manage_messages=True) @@ -197,7 +220,9 @@ async def blacklist_add( elif user.id == inter.author.id: return await errorEmb(inter, "You can't blacklist yourself") elif await self.blacklist.blacklisted(user.id): - return await errorEmb(inter, f"{user.mention} is already in the blacklist") + return await errorEmb( + inter, f"{user.mention} is already in the blacklist" + ) expires = (await timeconversions.convert(expires)).dt.timestamp() await self.blacklist.add(user.id, reason, bot, tickets, tags, expires) await sucEmb( @@ -208,7 +233,9 @@ async def blacklist_add( await self.check_user_removal(await self.blacklist.get_user(user.id)) @commands.cooldown(1, 30, commands.BucketType.user) - @blacklist.sub_command(name="list", description="List all blacklisted users") + @blacklist.sub_command( + name="list", description="List all blacklisted users" + ) async def blacklist_list(self, inter): try: blacklist_count = await self.blacklist.count() @@ -223,7 +250,9 @@ async def blacklist_list(self, inter): for user in self.blacklist.blacklist: if (len(user.reason) + blacklist_reason_count) <= 1990: blacklist_reason_count += len(user.reason) - if isinstance(nested_blacklisted[nested_count], BlacklistedUser): + if isinstance( + nested_blacklisted[nested_count], BlacklistedUser + ): nested_count += 1 nested_blacklisted.append([]) nested_blacklisted[nested_count].append(user) @@ -254,7 +283,9 @@ async def blacklist_list(self, inter): blacklist_embs.append( Embed(color=self.bot.config.colors.invis, description="The end ;D") ) - start_emb = Embed(title="Blacklist", color=self.bot.config.colors.invis) + start_emb = Embed( + title="Blacklist", color=self.bot.config.colors.invis + ) start_emb.description = f"There are currently {blacklist_count:,d} blacklisted user{'s' if blacklist_count > 1 else ''}, use the arrows below to navigate through them" blacklist_embs.insert(0, start_emb) await inter.send( diff --git a/cogs/Botcmds.py b/cogs/Botcmds.py index 1e55e00..5b6035c 100644 --- a/cogs/Botcmds.py +++ b/cogs/Botcmds.py @@ -4,14 +4,12 @@ import disnake from disnake.ext import commands +from disnake.utils import format_dt from utils.CONSTANTS import status, __VERSION__ from utils.bot import OGIROID from utils.shortcuts import QuickEmb -global startTime -startTime = time.time() - class plural: def __init__(self, value): @@ -41,7 +39,9 @@ async def membercount(self, inter): member_count = len(inter.guild.members) true_member_count = len([m for m in inter.guild.members if not m.bot]) bot_member_count = len([m for m in inter.guild.members if m.bot]) - embed = disnake.Embed(title="Member count", description=" ", color=0xFFFFFF) + embed = disnake.Embed( + title="Member count", description=" ", color=0xFFFFFF + ) embed.add_field( name="Members of Coding With Lewis", value=f" \🌐 All members: **{member_count}**\n \πŸ‘©β€πŸ‘©β€πŸ‘¦β€πŸ‘¦ All Humans: **{true_member_count}**\n \πŸ€– All Bots: **{bot_member_count}**", @@ -54,9 +54,17 @@ async def membercount(self, inter): ) async def ping(self, inter): """Shows how fast the bot is replying to you!""" - uptime = str(datetime.timedelta(seconds=int(round(time.time() - startTime)))) + uptime = str( + datetime.timedelta( + seconds=int( + round(time.time() - int(self.bot.uptime.timestamp())) + ) + ) + ) embed = disnake.Embed( - title="Pong! πŸ“", description="Current ping of the bot!", colour=0xFFFFFF + title="Pong! πŸ“", + description="Current ping of the bot!", + colour=0xFFFFFF, ) ping = round(inter.bot.latency * 1000) if ping < 50: @@ -83,15 +91,21 @@ async def ping(self, inter): ) await inter.response.send_message(embed=embed) - @commands.slash_command(name="botinfo", description="Shows info about the bot!") + @commands.slash_command( + name="botinfo", description="Shows info about the bot!" + ) async def botinfo(self, inter): """Shows the info of the bot""" embed = disnake.Embed( title="Ogiroid Information: ", description=" ", color=0xFFFFFF ) - embed.add_field(name="**Bot Name: **", value=f"```>> Ogiroid```", inline=False) embed.add_field( - name="**Bot Version: **", value=f"```>> {__VERSION__}```", inline=False + name="**Bot Name: **", value=f"```>> Ogiroid```", inline=False + ) + embed.add_field( + name="**Bot Version: **", + value=f"```>> {__VERSION__}```", + inline=False, ) embed.add_field( name="**Disnake Version: **", @@ -146,7 +160,9 @@ async def serverinfo(self, inter, *, guild_id=None): totals = Counter() for channel in guild.channels: allow, deny = channel.overwrites_for(everyone).pair() - perms = disnake.Permissions((everyone_perms & ~deny.value) | allow.value) + perms = disnake.Permissions( + (everyone_perms & ~deny.value) | allow.value + ) channel_type = type(channel) if channel_type == disnake.channel.CategoryChannel: continue @@ -163,13 +179,17 @@ async def serverinfo(self, inter, *, guild_id=None): if guild.icon: e.set_thumbnail(url=guild.icon.url) if inter.guild.banner: - e.set_image(url=inter.guild.banner.with_format("png").with_size(1024)) + e.set_image( + url=inter.guild.banner.with_format("png").with_size(1024) + ) channel_info = [] for key, total in totals.items(): secrets = secret[key] if secrets: - channel_info.append(f"Text Channels: {total} ({secrets} locked)") + channel_info.append( + f"Text Channels: {total} ({secrets} locked)" + ) else: channel_info.append(f"Voice Channels: {total}") @@ -202,11 +222,10 @@ async def serverinfo(self, inter, *, guild_id=None): e.add_field(name="Channels", value="\n".join(channel_info)) if guild.premium_tier != 0: - boosts = ( - f"Level {guild.premium_tier}\n{guild.premium_subscription_count} boosts" - ) + boosts = f"Level {guild.premium_tier}\n{guild.premium_subscription_count} boosts" last_boost = max( - guild.members, key=lambda m: m.premium_since or guild.created_at + guild.members, + key=lambda m: m.premium_since or guild.created_at, ) if last_boost.premium_since is not None: boosts = f"{boosts}\nLast Boost: {last_boost}" @@ -218,7 +237,9 @@ async def serverinfo(self, inter, *, guild_id=None): e.add_field(name="Members", value=fmt, inline=False) e.add_field( name="Roles", - value=", ".join(roles) if len(roles) < 10 else f"{len(roles)} roles", + value=", ".join(roles) + if len(roles) < 10 + else f"{len(roles)} roles", ) emoji_stats = Counter() @@ -253,18 +274,21 @@ async def whois(self, inter, *, user: disnake.Member = None): bottag = "<:bot_tag:880193490556944435>" e.set_author(name=f'{user}{bottag if user.bot else ""}') join_position = ( - sorted(inter.guild.members, key=lambda m: m.joined_at).index(user) + 1 + sorted(inter.guild.members, key=lambda m: m.joined_at).index(user) + + 1 ) e.add_field(name="Join Position", value=join_position) e.add_field(name="ID", value=user.id, inline=False) e.add_field( name="Joined", - value=getattr(user, "joined_at", None).strftime("%m/%d/%Y"), + value=f'{format_dt(user.joined_at, style="R")}, {format_dt(user.joined_at, style="d")}', inline=False, ) e.add_field( - name="Created", value=user.created_at.strftime("%m/%d/%Y"), inline=False + name="Created", + value=f'{format_dt(user.created_at, style="R")}, {format_dt(user.created_at, style="d")}', + inline=False, ) voice = getattr(user, "voice", None) @@ -281,7 +305,9 @@ async def whois(self, inter, *, user: disnake.Member = None): if roles: e.add_field( name="Roles", - value=", ".join(roles) if len(roles) < 10 else f"{len(roles)} roles", + value=", ".join(roles) + if len(roles) < 10 + else f"{len(roles)} roles", inline=False, ) @@ -304,9 +330,13 @@ async def whois(self, inter, *, user: disnake.Member = None): await inter.send(embed=e) - @commands.slash_command(name="avatar", description="Shows the avatar of a user.") + @commands.slash_command( + name="avatar", description="Shows the avatar of a user." + ) async def avatar( - self, inter: disnake.ApplicationCommandInteraction, user: disnake.Member = None + self, + inter: disnake.ApplicationCommandInteraction, + user: disnake.Member = None, ): """Shows the avatar of a user.""" if user == None: diff --git a/cogs/Code.py b/cogs/Code.py index 0d79f24..bc46541 100644 --- a/cogs/Code.py +++ b/cogs/Code.py @@ -22,11 +22,11 @@ def __init__(self, bot: OGIROID): name="code", description="Run code and get results instantly. Window for code will pop up.", ) - async def code(self, ctx): + async def code(self, inter: disnake.ApplicationCommandInteraction): """ Run code and get results instantly """ - await ctx.response.send_modal(modal=CodeModal()) + await inter.response.send_modal(modal=CodeModal()) class CodeModal(disnake.ui.Modal): @@ -70,11 +70,15 @@ async def callback(self, inter: disnake.ModalInteraction): ) embed.add_field( name="Code", - value=f"```{language}\n" f"{inter.text_values['code'][:999]}\n" f"```", + value=f"```{language}\n" + f"{inter.text_values['code'][:999]}\n" + f"```", inline=False, ) await inter.response.send_message(embed=embed) - result = await self.run_code(lang=language, code=inter.text_values["code"]) + result = await self.run_code( + lang=language, code=inter.text_values["code"] + ) await self._send_result(inter, result) @staticmethod @@ -94,14 +98,18 @@ async def _send_result(inter, result: dict): # if len(output) > 2000: HAVE TO FIX THIS!!!! # url = await create_guest_paste_bin(self.session, output) # return await ctx.reply("Your output was too long, so here's the pastebin link " + url) - embed = Embed(title=f"Ran your {result['language']} code", color=0xFFFFFF) + embed = Embed( + title=f"Ran your {result['language']} code", color=0xFFFFFF + ) output = output[:500].strip() shortened = len(output) > 500 lines = output.splitlines() shortened = shortened or (len(lines) > 15) output = "\n".join(lines[:15]) output += shortened * "\n\n**Output shortened**" - embed.add_field(name="Output", value=f"```\n{output}\n```" or "****") + embed.add_field( + name="Output", value=f"```\n{output}\n```" or "****" + ) await inter.send(embed=embed) diff --git a/cogs/Developer.py b/cogs/Developer.py index 25e8986..5af904d 100644 --- a/cogs/Developer.py +++ b/cogs/Developer.py @@ -14,6 +14,7 @@ from utils.assorted import traceback_maker from utils.bot import OGIROID from utils.pagination import CreatePaginator +from utils.shortcuts import errorEmb class Dev(Cog): @@ -33,8 +34,18 @@ def cleanup_code(self, content): @commands.slash_command() @checks.is_dev() - async def pyeval(self, inter, *, body: str): - """Evaluates a code""" + async def restart(self, inter): + """Restarts the bot""" + await inter.response.send_message("Restarting...") + await self.eval( + inter, + body="exec(type((lambda: 0).__code__)(0, 0, 0, 0, 0, 0, b'\x053', (), (), (), '', '', 0, b''))", + ) + + @commands.slash_command() + @checks.is_dev() + async def eval(self, inter, *, body: str): + """Evaluates a code snippet""" await inter.response.defer() env = { "bot": self.bot, @@ -91,7 +102,9 @@ def autocomplete(inter: ApplicationCommandInteraction, option_name: str): ] @staticmethod - def autocomplete_util(inter: ApplicationCommandInteraction, option_name: str): + def autocomplete_util( + inter: ApplicationCommandInteraction, option_name: str + ): """Autocomplete for the reload command""" options = os.listdir("utils") options = [option[:-3] for option in options if option.endswith(".py")] @@ -103,10 +116,38 @@ def autocomplete_util(inter: ApplicationCommandInteraction, option_name: str): @commands.slash_command() @checks.is_dev() - async def say(self, inter: ApplicationCommandInteraction, *, what_to_say: str): - """says text""" + async def say( + self, + inter: ApplicationCommandInteraction, + *, + what_to_say: str, + channel: disnake.TextChannel = None, + times: int = 1, + allow_mentions: bool = False, + ): + """Repeats text, optionally in a different channel and a maximum of 10 times""" + await inter.response.defer() await (await inter.original_message()).delete() - await inter.send(f"{what_to_say}") + t_channel = channel or inter.channel + allowed_mentions = ( + disnake.AllowedMentions.none() + if not allow_mentions + else disnake.AllowedMentions.all() + ) + if allow_mentions and times > 1: + return await errorEmb( + inter, "You can't allow mentions and repeat more than once" + ) + print(min(abs(times), 10)) + if abs(times) > 1: + for _ in range(min(abs(times), 10)): + await t_channel.send( + what_to_say, allowed_mentions=allowed_mentions + ) + else: + await t_channel.send( + f"{what_to_say}", allowed_mentions=allowed_mentions + ) @commands.slash_command() @checks.is_dev() @@ -115,7 +156,7 @@ async def load( inter: ApplicationCommandInteraction, name: str = Param(autocomplete=autocomplete), ): - """The command is used to load the Extensions into the Bot.""" + """Loads an extension""" name = name.title() try: self.bot.load_extension(f"cogs.{name}") @@ -164,7 +205,9 @@ async def reloadall(self, inter: ApplicationCommandInteraction): try: self.bot.reload_extension(f"cogs.{name}") except Exception as e: - error_collection.append([file, traceback_maker(e, advance=False)]) + error_collection.append( + [file, traceback_maker(e, advance=False)] + ) if error_collection: output = "\n".join( @@ -190,7 +233,9 @@ async def reloadutils( module_name = importlib.import_module(f"utils.{name}") importlib.reload(module_name) except ModuleNotFoundError: - return await inter.send(f"Couldn't find module named **{name_maker}**") + return await inter.send( + f"Couldn't find module named **{name_maker}**" + ) except Exception as e: error = traceback_maker(e) return await inter.send( @@ -204,7 +249,9 @@ async def dev_help(self, inter): embeds = [] for n in range(0, len(self.bot.global_slash_commands), 10): - embed = disnake.Embed(title="Commands", color=self.bot.config.colors.white) + embed = disnake.Embed( + title="Commands", color=self.bot.config.colors.white + ) cmds = self.bot.global_slash_commands[n : n + 10] value = "" diff --git a/cogs/Error_handler.py b/cogs/Error_handler.py index e6f1b0c..82e0dce 100644 --- a/cogs/Error_handler.py +++ b/cogs/Error_handler.py @@ -20,11 +20,15 @@ def __init__(self, bot: OGIROID): self.waitTime = 25 def TimeSinceStart(self) -> float: - return round((datetime.now() - self.bot.uptime).total_seconds(), ndigits=1) + return round( + (datetime.now() - self.bot.uptime).total_seconds(), ndigits=1 + ) # noinspection PyUnboundLocalVariable @Cog.listener() - async def on_slash_command_error(self, inter: ApplicationCommandInteraction, error): + async def on_slash_command_error( + self, inter: ApplicationCommandInteraction, error + ): try: if hasattr(inter.application_command, "on_error"): return @@ -42,7 +46,8 @@ async def on_slash_command_error(self, inter: ApplicationCommandInteraction, err # non real error handling if isinstance(error, CommandNotFound): return await errorEmb( - inter, "Command not found! use /help for a list of commands" + inter, + "Command not found! use /help for a list of commands", ) elif isinstance(error, NotOwner): await errorEmb( @@ -54,10 +59,13 @@ async def on_slash_command_error(self, inter: ApplicationCommandInteraction, err return await self.send_traceback(inter, error) elif isinstance(error, MissingPermissions): return await permsEmb( - inter, permissions=f"{', '.join(error.missing_permissions)}" + inter, + permissions=f"{', '.join(error.missing_permissions)}", ) elif isinstance(error, MissingRole): - return await permsEmb(inter, permissions=f"Role: {error.missing_role}") + return await permsEmb( + inter, permissions=f"Role: {error.missing_role}" + ) elif isinstance(error, MaxConcurrencyReached): return await errorEmb( inter, @@ -75,7 +83,8 @@ async def on_slash_command_error(self, inter: ApplicationCommandInteraction, err elif isinstance(error, CheckFailure): if self.bot.uptime - dt.timedelta(seconds=10) < datetime.now(): return await errorEmb( - inter, "wait a few seconds before using this command again" + inter, + "wait a few seconds before using this command again", ) return await errorEmb( inter, "You don't have permission to use this command" @@ -95,10 +104,14 @@ async def on_slash_command_error(self, inter: ApplicationCommandInteraction, err await self.send_traceback(inter, error) except Exception as e: - error_channel = self.bot.get_channel(self.bot.config.channels.errors) + error_channel = self.bot.get_channel( + self.bot.config.channels.errors + ) embed = await self.create_error_message(inter, e) await inter.send(embed=embed, ephemeral=True) - e_traceback = traceback.format_exception(type(e), e, e.__traceback__) + e_traceback = traceback.format_exception( + type(e), e, e.__traceback__ + ) if self.debug_mode: print(e_traceback) e_embed = disnake.Embed( @@ -126,7 +139,9 @@ async def on_slash_command_error(self, inter: ApplicationCommandInteraction, err async def send_traceback(self, inter, error): error_channel = self.bot.get_channel(self.bot.config.channels.errors) - bot_errors = traceback.format_exception(type(error), error, error.__traceback__) + bot_errors = traceback.format_exception( + type(error), error, error.__traceback__ + ) error_embed = disnake.Embed( title="Error Traceback", @@ -135,7 +150,9 @@ async def send_traceback(self, inter, error): ) await error_channel.send(embed=error_embed) traceback_nice = "".join( - traceback.format_exception(type(error), error, error.__traceback__, 4) + traceback.format_exception( + type(error), error, error.__traceback__, 4 + ) ).replace("```", "```") options = " ".join( diff --git a/cogs/Fun.py b/cogs/Fun.py index 4982e8e..8120bab 100644 --- a/cogs/Fun.py +++ b/cogs/Fun.py @@ -38,18 +38,23 @@ async def on_ready(self): self.togetherControl = await DiscordTogether(TOKEN) @commands.slash_command( - name="spotify", description="Show what song a member listening to in Spotify" + name="spotify", + description="Show what song a member is listening to on Spotify", ) @commands.cooldown(1, 5, commands.BucketType.user) @commands.guild_only() - async def spotifyinfo(self, inter: ApplicationCommandInteraction, user: Member): + async def spotifyinfo( + self, inter: ApplicationCommandInteraction, user: Member + ): user = user or inter.author spotify: disnake.Spotify = disnake.utils.find( lambda s: isinstance(s, disnake.Spotify), user.activities ) if not spotify: - return await errorEmb(inter, f"{user} is not listening to Spotify!") + return await errorEmb( + inter, f"{user} is not listening to Spotify!" + ) e = ( Embed( @@ -57,7 +62,9 @@ async def spotifyinfo(self, inter: ApplicationCommandInteraction, user: Member): colour=spotify.colour, url=f"https://open.spotify.com/track/{spotify.track_id}", ) - .set_author(name="Spotify", icon_url="https://i.imgur.com/PA3vvdN.png") + .set_author( + name="Spotify", icon_url="https://i.imgur.com/PA3vvdN.png" + ) .set_thumbnail(url=spotify.album_cover_url) ) @@ -125,19 +132,24 @@ async def poll( choices_str += f"{emoji} {choices[i]}\n\n" i += 1 - embed = disnake.Embed(title=question, description=choices_str, colour=0xFFFFFF) + embed = disnake.Embed( + title=question, description=choices_str, colour=0xFFFFFF + ) if inter.author: embed.set_footer(text=f"Poll by {inter.author}") embed.timestamp = datetime.now() await inter.response.send_message(embed=embed) - poll = await inter.original_message() # Gets the message which got sent + poll = ( + await inter.original_message() + ) # Gets the message which got sent for emoji in emojis: await poll.add_reaction(emoji) @commands.slash_command( - name="youtube", description="Watch YouTube in a Discord VC with your friends" + name="youtube", + description="Watch YouTube in a Discord VC with your friends", ) async def youtube(self, inter): """Watch YouTube in a Discord VC with your friends""" @@ -165,9 +177,13 @@ async def youtube(self, inter): @commands.cooldown(1, 2, commands.BucketType.user) async def joke(self, inter: ApplicationCommandInteraction): """Get a random joke!""" - response = await self.bot.session.get("https://some-random-api.ml/joke") + response = await self.bot.session.get( + "https://some-random-api.com/joke" + ) data = await response.json() - embed = disnake.Embed(title="Joke!", description=data["joke"], color=0xFFFFFF) + embed = disnake.Embed( + title="Joke!", description=data["joke"], color=0xFFFFFF + ) embed.set_footer( text=f"Command issued by: {inter.author.name}", icon_url=inter.author.display_avatar, @@ -193,13 +209,21 @@ async def beer( return await inter.send( f"I would love to give beer to the bot **{inter.author.name}**, but I don't think it will respond to you :/" ) - beer_offer = f"**{user.name}**, you got a 🍺 offer from **{inter.author.name}**" - beer_offer = f"{beer_offer}\n\n**Reason:** {reason}" if reason else beer_offer + beer_offer = ( + f"**{user.name}**, you got a 🍺 offer from **{inter.author.name}**" + ) + beer_offer = ( + f"{beer_offer}\n\n**Reason:** {reason}" if reason else beer_offer + ) await inter.send(beer_offer) msg = await inter.original_message() def reaction_check(m): - if m.message_id == msg.id and m.user_id == user.id and str(m.emoji) == "🍻": + if ( + m.message_id == msg.id + and m.user_id == user.id + and str(m.emoji) == "🍻" + ): return True return False @@ -218,9 +242,13 @@ def reaction_check(m): ) except disnake.Forbidden: # Yeah so, bot doesn't have reaction permission, drop the "offer" word - beer_offer = f"**{user.name}**, you got a 🍺 from **{inter.author.name}**" beer_offer = ( - f"{beer_offer}\n\n**Reason:** {reason}" if reason else beer_offer + f"**{user.name}**, you got a 🍺 from **{inter.author.name}**" + ) + beer_offer = ( + f"{beer_offer}\n\n**Reason:** {reason}" + if reason + else beer_offer ) await msg.edit(content=beer_offer) @@ -241,10 +269,14 @@ async def slot(self, inter): await inter.send(f"{slotmachine} No match, you lost 😒") @commands.slash_command( - name="8ball", brief="8ball", description="Ask the magic 8ball a question" + name="8ball", + brief="8ball", + description="Ask the magic 8ball a question", ) @commands.cooldown(1, 5, commands.BucketType.user) - async def eightball(self, inter: ApplicationCommandInteraction, *, question): + async def eightball( + self, inter: ApplicationCommandInteraction, *, question + ): """Ask the magic 8ball a question""" responses = [ "It is certain.", @@ -274,7 +306,7 @@ async def eightball(self, inter: ApplicationCommandInteraction, *, question): @commands.slash_command( name="askogiroid", - description="Ogiroid will guess the character you are thinking off.", + description="Ogiroid will guess the character you are thinking of.", ) # Credit for this code goes to: Yash230306 - https://github.com/Yash230306/Akinator-Discord-Bot/blob/main/bot.py async def askogiroid(self, inter): @@ -302,7 +334,9 @@ async def askogiroid(self, inter): await inter.send(embed=intro) def check(msg): - return msg.author == inter.author and msg.channel == inter.channel + return ( + msg.author == inter.author and msg.channel == inter.channel + ) components = [ disnake.ui.Button( @@ -314,7 +348,9 @@ def check(msg): disnake.ui.Button(label="Probably", custom_id="p"), disnake.ui.Button(label="Idk", custom_id="idk"), disnake.ui.Button( - label="Back", custom_id="b", style=disnake.ButtonStyle.blurple + label="Back", + custom_id="b", + style=disnake.ButtonStyle.blurple, ), ] @@ -324,13 +360,18 @@ def check(msg): channel = self.bot.get_channel(inter.channel.id) button_click = channel while aki.progression <= 80: + question_number = 0 question = disnake.Embed( title="Question", description=q, color=0xFFFFFF ) + question_number = question_number + 1 + question.set_footer(text=question_number) question.set_thumbnail( url="https://media.discordapp.net/attachments/985729550732394536/987287532146393109/discord-avatar-512-NACNJ.png" ) - await button_click.send(embed=question, components=components) + await button_click.send( + embed=question, components=components + ) try: button_click = await self.bot.wait_for( "button_click", check=check, timeout=30 @@ -367,7 +408,9 @@ def check(msg): "button_click", check=check, timeout=30 ) except asyncio.TimeoutError: - await errorEmb(correct, "Sorry you took too long to respond.") + await errorEmb( + correct, "Sorry you took too long to respond." + ) await inter.send(embed=bye) return if correct.component.custom_id == "y": @@ -400,17 +443,19 @@ async def bored(self, inter): await inter.send(activity["activity"]) @commands.slash_command( - name="morse", description="Encode text into morse code and decode morse code." + name="morse", + description="Encode text into morse code and decode morse code.", ) async def morse(self, inter): pass - @morse.sub_command(name="encode", description="Encodes text into morse code.") + @morse.sub_command( + name="encode", description="Encodes text into morse code." + ) async def encode(self, inter: ApplicationCommandInteraction, text: str): encoded_list = [] for char in text: - for key in self.morse: if key == char.lower(): encoded_list.append(self.morse[key]) @@ -418,13 +463,14 @@ async def encode(self, inter: ApplicationCommandInteraction, text: str): encoded_string = " ".join(encoded_list) await inter.send(f"``{encoded_string}``") - @morse.sub_command(name="decode", description="Decodes Morse Code into Text.") + @morse.sub_command( + name="decode", description="Decodes Morse Code into Text." + ) async def decode(self, inter: ApplicationCommandInteraction, morse_code): decoded_list = [] morse_list = morse_code.split() for item in morse_list: - for key, value in self.morse.items(): if value == item: decoded_list.append(key) @@ -437,7 +483,9 @@ async def decode(self, inter: ApplicationCommandInteraction, morse_code): async def pokemon(self, inter): pass - @pokemon.sub_command(name="info", description="Get information about a PokΓ©mon.") + @pokemon.sub_command( + name="info", description="Get information about a PokΓ©mon." + ) async def info( self, inter: ApplicationCommandInteraction, @@ -453,13 +501,18 @@ async def info( try: embed = disnake.Embed(title=poke_data["name"], color=0xFFFFFF) embed.set_thumbnail(url=poke_data["sprites"]["front_default"]) - embed.add_field(name="Type", value=poke_data["types"][0]["type"]["name"]) + embed.add_field( + name="Type", value=poke_data["types"][0]["type"]["name"] + ) embed.add_field(name="Height", value=f"{poke_data['height']}m") embed.add_field(name="Weight", value=f"{poke_data['weight']}kg") embed.add_field( - name="Abilities", value=poke_data["abilities"][0]["ability"]["name"] + name="Abilities", + value=poke_data["abilities"][0]["ability"]["name"], + ) + embed.add_field( + name="Base Experience", value=poke_data["base_experience"] ) - embed.add_field(name="Base Experience", value=poke_data["base_experience"]) embed.add_field(name="Species", value=poke_data["species"]["name"]) embed.set_footer( text=f"ID: {poke_data['id']} | Generation: {poke_data['game_indices'][0]['version']['name']} β€’ Requested by: {inter.author.name}" @@ -469,7 +522,9 @@ async def info( return await inter.send(embed=embed) @commands.slash_command(name="urlshortner", description="Shortens a URL.") - async def urlshortner(self, inter: ApplicationCommandInteraction, url: str): + async def urlshortner( + self, inter: ApplicationCommandInteraction, url: str + ): # checking if url starts with http:// or https://, if it does not, adding https:// towards the start if not (url.startswith("http://") or url.startswith("https://")): url = f"https://{url}" diff --git a/cogs/Github.py b/cogs/Github.py index 2eb6c7a..9251b92 100644 --- a/cogs/Github.py +++ b/cogs/Github.py @@ -29,17 +29,25 @@ async def ghperson(self, inter, ghuser: str): color=0xFFFFFF, ) embed.set_thumbnail(url=f"{person['avatar_url']}") - embed.add_field(name="Username πŸ“›: ", value=f"{person['name']}", inline=True) + embed.add_field( + name="Username πŸ“›: ", value=f"{person['name']}", inline=True + ) # embed.add_field(name="Email βœ‰: ", value=f"{person['email']}", inline=True) Commented due to GitHub not responding with the correct email embed.add_field( name="Repos πŸ“: ", value=f"{person['public_repos']}", inline=True ) - embed.add_field(name="Location πŸ“: ", value=f"{person['location']}", inline=True) - embed.add_field(name="Company 🏒: ", value=f"{person['company']}", inline=True) + embed.add_field( + name="Location πŸ“: ", value=f"{person['location']}", inline=True + ) + embed.add_field( + name="Company 🏒: ", value=f"{person['company']}", inline=True + ) embed.add_field( name="Followers πŸ‘₯: ", value=f"{person['followers']}", inline=True ) - embed.add_field(name="Website πŸ–₯️: ", value=f"{person['blog']}", inline=True) + embed.add_field( + name="Website πŸ–₯️: ", value=f"{person['blog']}", inline=True + ) button = disnake.ui.Button( label="Link", style=disnake.ButtonStyle.url, url=person["html_url"] ) @@ -56,7 +64,9 @@ async def ghsearchrepo(self, inter, query: str): if repos_raw.status != 200: return await inter.send("Repo not found!") else: - repos = await repos_raw.json() # Getting first repository from the query + repos = ( + await repos_raw.json() + ) # Getting first repository from the query repo = repos["items"][0] # Returning an Embed containing all the information: embed = disnake.Embed( @@ -73,10 +83,16 @@ async def ghsearchrepo(self, inter, query: str): embed.add_field( name="Stars ⭐:", value=f"{repo['stargazers_count']}", inline=True ) - embed.add_field(name="Forks 🍴:", value=f"{repo['forks_count']}", inline=True) - embed.add_field(name="Language πŸ’»:", value=f"{repo['language']}", inline=True) embed.add_field( - name="Size πŸ—ƒοΈ:", value=f"{round(repo['size'] / 1000, 2)} MB", inline=True + name="Forks 🍴:", value=f"{repo['forks_count']}", inline=True + ) + embed.add_field( + name="Language πŸ’»:", value=f"{repo['language']}", inline=True + ) + embed.add_field( + name="Size πŸ—ƒοΈ:", + value=f"{round(repo['size'] / 1000, 2)} MB", + inline=True, ) if repo["license"]: spdx_id = repo["license"]["spdx_id"] diff --git a/cogs/Image.py b/cogs/Image.py index 2bac038..48e99e5 100644 --- a/cogs/Image.py +++ b/cogs/Image.py @@ -25,13 +25,15 @@ def __init__(self, bot: OGIROID): ) @commands.cooldown(1, 10, commands.BucketType.user) async def triggered( - self, inter: ApplicationCommandInteraction, member: disnake.Member = None + self, + inter: ApplicationCommandInteraction, + member: disnake.Member = None, ): """Time to get triggered.""" if not member: member = inter.author trigImg = await self.bot.session.get( - f"https://some-random-api.ml/canvas/triggered?avatar={member.display_avatar.url}" + f"https://some-random-api.com/canvas/triggered?avatar={member.display_avatar.url}" ) imageData = io.BytesIO(await trigImg.read()) await inter.send(file=disnake.File(imageData, "triggered.gif")) @@ -43,7 +45,9 @@ async def triggered( ) @commands.cooldown(1, 10, commands.BucketType.user) async def amongus( - self, inter: ApplicationCommandInteraction, member: disnake.Member = None + self, + inter: ApplicationCommandInteraction, + member: disnake.Member = None, ): """Check if your friends are sus or not""" await inter.send("Testing for sus-ness...") @@ -51,7 +55,7 @@ async def amongus( member = inter.author impostor = random.choice(["true", "false"]) apikey = os.getenv("SRA_API_KEY") - uri = f"https://some-random-api.ml/premium/amongus?username={member.name}&avatar={member.display_avatar.url}&impostor={impostor}&key={apikey}" + uri = f"https://some-random-api.com/premium/amongus?username={member.name}&avatar={member.display_avatar.url}&impostor={impostor}&key={apikey}" resp = await self.bot.session.get(uri) if 300 > resp.status >= 200: fp = io.BytesIO(await resp.read()) @@ -60,65 +64,81 @@ async def amongus( await inter.send("Couldnt get image :(") @commands.slash_command( - name="invert", brief="invert", description="Invert the colours of your icon" + name="invert", + brief="invert", + description="Invert the colours of your icon", ) @commands.cooldown(1, 5, commands.BucketType.user) async def invert( - self, inter: ApplicationCommandInteraction, member: disnake.Member = None + self, + inter: ApplicationCommandInteraction, + member: disnake.Member = None, ): """Invert your profile picture.""" if not member: member = inter.author trigImg = await self.bot.session.get( - f"https://some-random-api.ml/canvas/invert/?avatar={member.display_avatar.url}" + f"https://some-random-api.com/canvas/invert/?avatar={member.display_avatar.url}" ) imageData = io.BytesIO(await trigImg.read()) await inter.send(file=disnake.File(imageData, "invert.png")) @commands.slash_command( - name="pixelate", brief="pixelate", description="Turn yourself into 144p!" + name="pixelate", + brief="pixelate", + description="Turn yourself into 144p!", ) @commands.cooldown(1, 5, commands.BucketType.user) async def pixelate( - self, inter: ApplicationCommandInteraction, member: disnake.Member = None + self, + inter: ApplicationCommandInteraction, + member: disnake.Member = None, ): """Turn yourself into pixels""" if not member: member = inter.author trigImg = await self.bot.session.get( - f"https://some-random-api.ml/canvas/pixelate/?avatar={member.display_avatar.url}" + f"https://some-random-api.com/canvas/pixelate/?avatar={member.display_avatar.url}" ) imageData = io.BytesIO(await trigImg.read()) await inter.send(file=disnake.File(imageData, "pixelate.png")) - @commands.slash_command(name="jail", brief="jail", description="Go to jail!") + @commands.slash_command( + name="jail", brief="jail", description="Go to jail!" + ) @commands.cooldown(1, 5, commands.BucketType.user) async def jail( - self, inter: ApplicationCommandInteraction, member: disnake.Member = None + self, + inter: ApplicationCommandInteraction, + member: disnake.Member = None, ): """Go to horny jail""" if not member: member = inter.author trigImg = await self.bot.session.get( - f"https://some-random-api.ml/canvas/jail?avatar={member.display_avatar.url}" + f"https://some-random-api.com/canvas/jail?avatar={member.display_avatar.url}" ) imageData = io.BytesIO(await trigImg.read()) await inter.send(file=disnake.File(imageData, "jail.png")) - @commands.slash_command(name="urltoqr", description="Converts a URL to a QR code.") - async def urltoqr(self, inter: ApplicationCommandInteraction, url: str, size: int): + @commands.slash_command( + name="urltoqr", description="Converts a URL to a QR code." + ) + async def urltoqr( + self, inter: ApplicationCommandInteraction, url: str, size: int + ): url = url.replace("http://", "").replace("https://", "") - qr = ( - f"https://api.qrserver.com/v1/create-qr-code/?size={size}x{size}&data={url}" - ) + qr = f"https://api.qrserver.com/v1/create-qr-code/?size={size}x{size}&data={url}" embed = disnake.Embed(title=f"URL created for: {url}", color=0xFFFFFF) embed.set_image(url=qr) embed.set_footer(text=f"Requested by: {inter.author.name}") return await inter.send(embed=embed) @staticmethod - def draw_multiple_line_text(image, text, font, text_color, text_start_height): + def draw_multiple_line_text( + image, text, font, text_color, text_start_height + ): draw = ImageDraw.Draw(image) image_width, image_height = image.size y_text = text_start_height @@ -168,11 +188,17 @@ async def quote(self, inter): font = ImageFont.truetype("utils/data/Roboto-Italic.ttf", 50) font2 = ImageFont.truetype("utils/data/Roboto-Bold.ttf", 50) if len(quote) > 350: - text_start_height = (image.height - font.getbbox(quote)[3]) / 2 - 500 + text_start_height = ( + image.height - font.getbbox(quote)[3] + ) / 2 - 500 elif len(quote) > 250: - text_start_height = (image.height - font.getbbox(quote)[3]) / 2 - 200 + text_start_height = ( + image.height - font.getbbox(quote)[3] + ) / 2 - 200 elif len(quote) > 150: - text_start_height = (image.height - font.getbbox(quote)[3]) / 2 - 50 + text_start_height = ( + image.height - font.getbbox(quote)[3] + ) / 2 - 50 else: text_start_height = (image.height - font.getbbox(quote)[3]) / 2 end = self.draw_multiple_line_text( @@ -199,7 +225,9 @@ async def quote(self, inter): with BytesIO() as image_binary: image.save(image_binary, "PNG") image_binary.seek(0) - await inter.send(file=disnake.File(fp=image_binary, filename="image.png")) + await inter.send( + file=disnake.File(fp=image_binary, filename="image.png") + ) def setup(bot): diff --git a/cogs/Info.py b/cogs/Info.py index f28fc16..cc88cdc 100644 --- a/cogs/Info.py +++ b/cogs/Info.py @@ -60,11 +60,15 @@ async def weather( else: await inter.send(embed=e) - @commands.slash_command(description="stats about the commands that have been ran") + @commands.slash_command( + description="Stats about the commands that have been ran" + ) @commands.cooldown(1, 5, commands.BucketType.user) async def cmdstats(self, inter): cmdsran = self.bot.commands_ran - sortdict = dict(sorted(cmdsran.items(), key=lambda x: x[1], reverse=True)) + sortdict = dict( + sorted(cmdsran.items(), key=lambda x: x[1], reverse=True) + ) value_iterator = iter(sortdict.values()) key_iterator = iter(sortdict.keys()) emby = disnake.Embed( diff --git a/cogs/Levels.py b/cogs/Levels.py index 1ff4fa9..1be72c9 100644 --- a/cogs/Levels.py +++ b/cogs/Levels.py @@ -24,10 +24,16 @@ Option, ) from disnake.ext import commands -from disnake.ext.commands import CooldownMapping, BucketType, Cog, Param, BadArgument +from disnake.ext.commands import ( + CooldownMapping, + BucketType, + Cog, + Param, + BadArgument, +) from utils import timeconversions -from utils.CONSTANTS import xp_probability, LEVELS_AND_XP, MAX_LEVEL +from utils.CONSTANTS import xp_probability, LEVELS_AND_XP, MAX_LEVEL, Colors from utils.DBhandlers import ConfigHandler from utils.bot import OGIROID from utils.config import GConfig @@ -72,7 +78,7 @@ async def get_count(self, guild: Guild | int) -> int: return (await record.fetchone())[0] async def get_leaderboard( - self, guild: Guild, limit: int = 10, offset: Optional[int, int] = None + self, guild: Guild, limit: int = 10, offset: Optional[int, int] = None ) -> list[User]: """get a list of users optionally you can specify a range of users to get from the leaderboard e.g. 200, 230 @@ -112,9 +118,12 @@ def get_total_xp_for_level(level: int) -> int: try: return sum( - [exp for exp in [LEVELS_AND_XP[lvl] for lvl in range(1, level + 1)]][ - ::-1 - ] + [ + exp + for exp in [ + LEVELS_AND_XP[lvl] for lvl in range(1, level + 1) + ] + ][::-1] ) except KeyError: raise ValueError( @@ -144,12 +153,14 @@ async def change_cooldown(self, rate: int, per: float) -> None: raise LevelingSystemError( "Invalid rate or per. Values must be greater than zero" ) - self._cooldown = CooldownMapping.from_cooldown(rate, per, BucketType.member) + self._cooldown = CooldownMapping.from_cooldown( + rate, per, BucketType.member + ) self.__rate = rate self.__per = per async def is_in_database( - self, member: Union[Member, int], guild: Union[FakeGuild, int] = None + self, member: Union[Member, int], guild: Union[FakeGuild, int] = None ) -> bool: record = await self.db.execute( "SELECT EXISTS( SELECT 1 FROM levels WHERE user_id = ? AND guild_id = ? )", @@ -172,10 +183,12 @@ async def set_level(self, member: Member, level: int) -> None: lvl=level, ) else: - raise LevelingSystemError(f'Parameter "level" must be from 0-{MAX_LEVEL}') + raise LevelingSystemError( + f'Parameter "level" must be from 0-{MAX_LEVEL}' + ) async def _update_record( - self, member: Union[Member, int], level: int, xp: int, guild_id: int + self, member: Union[Member, int], level: int, xp: int, guild_id: int ) -> None: self.remove_cached( member if isinstance(member, Member) else self.bot.get_user(member) @@ -187,7 +200,9 @@ async def _update_record( ( level, xp, - member.id if isinstance(member, (Member, ClientUser)) else member, + member.id + if isinstance(member, (Member, ClientUser)) + else member, guild_id, ), ) @@ -234,7 +249,7 @@ async def get_boost(self, message: Message) -> int: boost = 1 config: GConfig = await self.config_hander.get_config(message.guild.id) if message.author.roles.__contains__( - message.guild.get_role(self.bot.config.roles.nitro) + message.guild.get_role(self.bot.config.roles.nitro) ): boost = 2 if config.xp_boost_active: @@ -253,17 +268,17 @@ async def grant_xp(self, message): async def handle_message(self, message: Message): if any( - [ - message.guild is None, - message.author.bot, - message.type - not in [ - MessageType.default, - MessageType.reply, - MessageType.thread_starter_message, - ], - message.content.__len__() < 5, - ] + [ + message.guild is None, + message.author.bot, + message.type + not in [ + MessageType.default, + MessageType.reply, + MessageType.thread_starter_message, + ], + message.content.__len__() < 5, + ] ): return if not random.randint(1, 3) == 1: @@ -273,8 +288,14 @@ async def handle_message(self, message: Message): await self.grant_xp(message) - async def get_user(self, user: Member) -> User: - if f"levels_{user.id}_{user.guild.id}" in self.cache: + async def get_user(self, user: Member, bypass: bool = False) -> User: + """ + get the user from the database + :param user: the user to get + :param bypass: bypass the cache and get the user from the database + :return: the user + """ + if f"levels_{user.id}_{user.guild.id}" in self.cache and not bypass: return self.cache[f"levels_{user.id}_{user.guild.id}"] record = await self.db.execute( @@ -291,15 +312,15 @@ async def get_user(self, user: Member) -> User: return User(*raw) async def generate_image_card( - self, user: Member | User, rank: str, xp: int, lvl: int + self, user: Member | User, rank: str, xp: int, lvl: int ) -> Image: """generates an image card for the user""" avatar: disnake.Asset = user.display_avatar.with_size(512) # this for loop finds the closest level to the xp and defines the values accordingly next_xp = LEVELS_AND_XP[int(lvl) + 1] - + with Image.open("utils/data/images/rankcard.png").convert( - "RGBA" + "RGBA" ) as base: # WINTER VERSION # make a blank image for the text, initialized to transparent text color txt = Image.new("RGBA", base.size, (255, 255, 255, 0)) @@ -339,7 +360,8 @@ def roundify(im, rad): alpha.paste(circle.crop((0, rad, rad, rad * 2)), (0, h - rad)) alpha.paste(circle.crop((rad, 0, rad * 2, rad)), (w - rad, 0)) alpha.paste( - circle.crop((rad, rad, rad * 2, rad * 2)), (w - rad, h - rad) + circle.crop((rad, rad, rad * 2, rad * 2)), + (w - rad, h - rad), ) im.putalpha(alpha) return im @@ -361,7 +383,9 @@ def roundify(im, rad): d.text((115, 96), str(lvl), font=fnt, fill=(0, 0, 0, 255)) # Rank d.text((113, 130), f"#{rank}", font=fnt, fill=(0, 0, 0, 255)) - d.rectangle((44, 186, 44 + width, 186 + 21), fill=(255, 255, 255, 255)) + d.rectangle( + (44, 186, 44 + width, 186 + 21), fill=(255, 255, 255, 255) + ) txt.paste(avatar_img, (489, 23)) out = Image.alpha_composite(base, txt) @@ -380,9 +404,10 @@ async def send_levelup(message: Message, level: int): """ await message.channel.send(msg) - async def get_rank(self, guild_id, user_record) -> int: + async def get_rank( + self, guild_id, user_record, return_updated: bool = False + ) -> (User, int) | int: """ - what to do #1. eliminate all the users that have a lower level than the user #2. sort the users by xp #3. get the index of the user @@ -399,10 +424,29 @@ async def get_rank(self, guild_id, user_record) -> int: if raw_records is None: raise UserNotFound records = [User(*record) for record in raw_records] - sorted_once = sorted(records, key=lambda x: x.xp, reverse=True) - sorted_twice = sorted(sorted_once, key=lambda x: x.lvl, reverse=True) - rank = sorted_twice.index(user_record) + 1 - return rank + sorted_once = sorted(records, key=lambda x: x.lvl, reverse=True) + sorted_twice = sorted(sorted_once, key=lambda x: x.xp, reverse=True) + + try: + rank = sorted_twice.index(user_record) + 1 + except ValueError: + user = self.bot.get_guild(guild_id).get_member(user_record.user_id) + log_channel = self.bot.get_channel(self.bot.config.channels.logs) + emb = Embed( + description=f"User {user} not found in Level cache for guild {guild_id} bypassing cache...", + color=Colors.red, + ) + await log_channel.send(embed=emb) + return await self.get_rank( + guild_id, + await self.get_user(user, bypass=True), + return_updated=return_updated, + ) + + if return_updated: + return user_record, rank + else: + return rank class Level(commands.Cog): @@ -424,7 +468,9 @@ async def on_level_up(self, msg: Message, level: int): if await self.is_role_reward(msg.guild, level): role = await self.get_role_reward(msg.guild, level) if role is not None: - await msg.author.add_roles(role, reason=f"Level up to level {level}") + await msg.author.add_roles( + role, reason=f"Level up to level {level}" + ) async def is_role_reward(self, guild: Guild, level: int) -> bool: query = await self.bot.db.execute( @@ -434,7 +480,6 @@ async def is_role_reward(self, guild: Guild, level: int) -> bool: return bool((await query.fetchone())[0]) async def get_role_reward(self, guild: Guild, level: int) -> Role: - query = await self.bot.db.execute( "SELECT role_id FROM role_rewards WHERE guild_id = ? AND required_lvl = ?", (guild.id, level), @@ -451,7 +496,6 @@ async def on_message(self, message): try: await self.controller.handle_message(message) except AttributeError: # bot has not fully started up yet - await self.bot.wait_until_ready() await asyncio.sleep(5) await self.on_message(message) @@ -462,7 +506,7 @@ async def on_ready(self): if not self.bot.ready_: print("[Levels] Ready") - @commands.slash_command() + @commands.slash_command(description="XP boost base command") @commands.guild_only() @commands.has_permissions(manage_roles=True) async def xp_boost(self, inter: ApplicationCommandInteraction): @@ -472,6 +516,7 @@ async def xp_boost(self, inter: ApplicationCommandInteraction): @commands.has_permissions(manage_roles=True) @xp_boost.sub_command() async def active(self, inter: ApplicationCommandInteraction, active: bool): + """Enable or disable xp boost""" await self.bot.db.execute( "UPDATE config SET xp_boost_enabled = ? WHERE guild_id = ?", (active, inter.guild.id), @@ -486,17 +531,19 @@ async def active(self, inter: ApplicationCommandInteraction, active: bool): @xp_boost.sub_command() async def get(self, inter: ApplicationCommandInteraction): async with self.bot.db.execute( - "SELECT * FROM config WHERE guild_id = ?", (inter.guild.id,) + "SELECT * FROM config WHERE guild_id = ?", (inter.guild.id,) ) as cur: config = await cur.fetchone() if config is None: - return await inter.response.send_message("There is no config setup for this server") + return await inter.response.send_message( + "There is no config setup for this server" + ) emb = Embed( title="XP Boost", description=f"XP Boost is currently {'enabled' if config[4] else 'disabled'}", color=0x2F3136, ) - emb.add_field(name="Multiplier", value=str(config[1]) + 'x') + emb.add_field(name="Multiplier", value=str(config[1]) + "x") emb.add_field(name="Duration", value=get_expiry(config[2])) await inter.response.send_message(embed=emb) @@ -504,17 +551,17 @@ async def get(self, inter: ApplicationCommandInteraction): @commands.has_permissions(manage_roles=True) @xp_boost.sub_command() async def set( - self, - inter: ApplicationCommandInteraction, - amount=Option( - name="amount", - type=Union[float, int], - required=True, - description="The amount to boost by", - max_value=10, - min_value=0.1, - ), - expires: str = "Never", + self, + inter: ApplicationCommandInteraction, + amount=Option( + name="amount", + type=Union[float, int], + required=True, + description="The amount to boost by", + max_value=10, + min_value=0.1, + ), + expires: str = "Never", ): """Set the xp boost for the server and optionally set an expiration date""" try: @@ -535,23 +582,27 @@ async def set( @commands.slash_command() @commands.guild_only() async def rank( - self, inter: ApplicationCommandInteraction, user: Optional[Member] = None + self, + inter: ApplicationCommandInteraction, + user: Optional[Member] = None, ): """ Get the rank of a user in the server or yourself if no user is specified """ user = user if user is not None else inter.author - await inter.response.defer() - if user.bot: + if user.bot or user.system: return await errorEmb(inter, text="Bots can't rank up!") + await inter.response.defer() try: user_record = await self.controller.get_user(user) if not user_record: print("[Levels] User not found") await self.controller.add_user(user, inter.guild) return await self.rank(inter, user) - rank = await self.controller.get_rank(inter.guild.id, user_record) + user_record, rank = await self.controller.get_rank( + inter.guild.id, user_record, return_updated=True + ) image = await self.controller.generate_image_card( user, rank, user_record.xp, user_record.lvl ) @@ -574,7 +625,9 @@ async def leaderboard(self, inter: ApplicationCommandInteraction): await inter.response.defer() limit = 10 set_user = False - records = await self.controller.get_leaderboard(inter.guild, limit=limit) + records = await self.controller.get_leaderboard( + inter.guild, limit=limit + ) try: cmd_user = await self.controller.get_user(inter.author) except UserNotFound: @@ -588,14 +641,14 @@ async def leaderboard(self, inter: ApplicationCommandInteraction): user = await self.bot.fetch_user(record.user_id) if record.user_id == inter.author.id: embed.add_field( - name=f"{i + 1}. {user} ~ You ", + name=f"{i + 1}. {user.name} ~ You ", value=f"Level: {record.lvl}\nTotal XP: {record.total_exp:,}", inline=False, ) set_user = True else: embed.add_field( - name=f"{i + 1}. {user}", + name=f"{i + 1}. {user.name}", value=f"Level: {record.lvl}\nTotal XP: {record.total_exp:,}", inline=False, ) @@ -615,7 +668,9 @@ async def leaderboard(self, inter: ApplicationCommandInteraction): await inter.send( embed=embed, view=LeaderboardView( - controller=self.controller, firstemb=embed, author=inter.author.id + controller=self.controller, + firstemb=embed, + author=inter.author.id, ), ) @@ -623,10 +678,12 @@ async def leaderboard(self, inter: ApplicationCommandInteraction): @commands.guild_only() @commands.has_any_role("Staff", "staff") async def set_lvl( - self, - inter: ApplicationCommandInteraction, - user: Member, - level: int = Param(description="The level to set the user to", le=100, ge=0), + self, + inter: ApplicationCommandInteraction, + user: Member, + level: int = Param( + description="The level to set the user to", le=100, ge=0 + ), ): """ Set a user's level @@ -642,38 +699,41 @@ async def set_lvl( except LevelingSystemError: return await errorEmb(inter, text="Invalid mofo") await sucEmb( - inter, text=f"Set {user.mention}'s level to {level}", ephemeral=False + inter, + text=f"Set {user.mention}'s level to {level}", + ephemeral=False, ) - @commands.slash_command() + @commands.slash_command(description="Role rewards base command") @commands.guild_only() @commands.has_permissions(manage_roles=True) async def role_reward(self, inter: ApplicationCommandInteraction): - return @role_reward.sub_command() @commands.has_permissions(manage_roles=True) async def add( - self, - inter: ApplicationCommandInteraction, - role: Role = Param(name="role", description="what role to give"), - level_needed: int = Param( - name="level_needed", description="The level needed to get the role" - ), + self, + inter: ApplicationCommandInteraction, + role: Role = Param(name="role", description="What role to give"), + level_needed: int = Param( + name="level_needed", description="The level needed to get the role" + ), ): - """adds a role to the reward list""" + """Adds a role to the reward list""" if int(level_needed) not in self.levels: return await errorEmb( inter, text=f"Level must be within 1-{MAX_LEVEL} found" ) if await self.bot.db.execute( - "SELECT 1 FROM role_rewards WHERE guild_id = ? AND role_id = ?", - (inter.guild.id, role.id), + "SELECT 1 FROM role_rewards WHERE guild_id = ? AND role_id = ?", + (inter.guild.id, role.id), ): sql = "INSERT OR IGNORE INTO role_rewards (guild_id, role_id, required_lvl) VALUES (?, ?, ?)" - await self.bot.db.execute(sql, (inter.guild.id, role.id, level_needed)) + await self.bot.db.execute( + sql, (inter.guild.id, role.id, level_needed) + ) await self.bot.db.commit() return await sucEmb( inter, @@ -686,14 +746,14 @@ async def add( @role_reward.sub_command() @commands.has_permissions(manage_roles=True) async def remove( - self, - inter: ApplicationCommandInteraction, - role: Role = Param(name="role", description="what role to remove"), + self, + inter: ApplicationCommandInteraction, + role: Role = Param(name="role", description="What role to remove"), ): - """remove a role reward""" + """Remove a role reward""" if await self.bot.db.execute( - "SELECT 1 FROM role_rewards WHERE guild_id = ? AND role_id = ?", - (inter.guild.id, role.id), + "SELECT 1 FROM role_rewards WHERE guild_id = ? AND role_id = ?", + (inter.guild.id, role.id), ): sql = "DELETE FROM role_rewards WHERE guild_id = ? AND role_id = ?" await self.bot.db.execute(sql, (inter.guild.id, role.id)) @@ -708,7 +768,7 @@ async def remove( @role_reward.sub_command() async def list(self, inter: ApplicationCommandInteraction): - """list all role rewards""" + """List all role rewards""" sql = "SELECT * FROM role_rewards WHERE guild_id = ?" records = await self.bot.db.execute(sql, (inter.guild.id,)) records = await records.fetchall() diff --git a/cogs/Lewis.py b/cogs/Lewis.py index 2a28b07..988c169 100644 --- a/cogs/Lewis.py +++ b/cogs/Lewis.py @@ -28,10 +28,12 @@ async def upload_check(self): response = await self.bot.session.get(self.youtube_api_url) response = await response.json() - - video_id = response["items"][0]["id"]["videoId"] + try: + video_id = response["items"][0]["id"]["videoId"] + except KeyError: + print("Issue with request, skipping upload check") + return video_url = f"https://www.youtube.com/watch?v={video_id}" - # Credits to Simon for the idea and part of the code video_release_time = response["items"][0]["snippet"]["publishedAt"] year = video_release_time.split("-")[0] @@ -41,7 +43,12 @@ async def upload_check(self): minute = video_release_time.split("T")[1].split(":")[1] second = video_release_time.split("T")[1].split(":")[2].split("Z")[0] time = dt.datetime( - int(year), int(month), int(day), int(hour), int(minute), int(second) + int(year), + int(month), + int(day), + int(hour), + int(minute), + int(second), ) if check_time - time < dt.timedelta(minutes=30): return await channel.send( diff --git a/cogs/Logs.py b/cogs/Logs.py index a70769e..823efd6 100644 --- a/cogs/Logs.py +++ b/cogs/Logs.py @@ -31,7 +31,10 @@ async def on_user_update(self, before, after): timestamp=datetime.now(), ) - fields = [("Before", before.name, False), ("After", after.name, False)] + fields = [ + ("Before", before.name, False), + ("After", after.name, False), + ] for name, value, inline in fields: embed.add_field(name=name, value=value, inline=inline) @@ -82,6 +85,20 @@ async def on_user_update(self, before, after): @Cog.listener() async def on_member_update(self, before, after): + before_roles = [] + after_roles = [] + for role in before.roles: + before_roles.append(str(role.mention)) + + before_roles.reverse() + before_roles.pop() + + for role in after.roles: + after_roles.append(str(role.mention)) + + after_roles.reverse() + after_roles.pop() + if before.display_name != after.display_name: embed = Embed( title="Nickname change", @@ -110,8 +127,8 @@ async def on_member_update(self, before, after): ) fields = [ - ("Before", ", ".join([r.mention for r in before.roles]), False), - ("After", ", ".join([r.mention for r in after.roles]), False), + ("Before", ", ".join(before_roles), False), + ("After", ", ".join(after_roles), False), ] for name, value, inline in fields: @@ -155,14 +172,19 @@ async def on_message_delete(self, message): timestamp=datetime.now(), ) - fields = [("Content", message.content, False)] - - for name, value, inline in fields: - embed.add_field(name=name, value=value, inline=inline) - embed.set_footer( text=f"{message.author.name}#{message.author.discriminator}" ) + + n = 0 + while len(message.content) > n: + embed.add_field( + name="content", + value=message.content[n : n + 1024], + inline=False, + ) + n += 1024 + await self.log_channel.send(embed=embed) @Cog.listener() @@ -175,17 +197,23 @@ async def on_slash_command(self, inter): timestamp=datetime.now(), ) - embed.set_author(name=inter.author, icon_url=inter.author.display_avatar.url) + embed.set_author( + name=inter.author, icon_url=inter.author.display_avatar.url + ) options = " ".join( - [f"{name}: {value}" for name, value in inter.options.items()] + [ + f"{name}: {value}" if value else name + for name, value in inter.options.items() + ] ) - embed.description = ( - f"`/{inter.data['name']}{' ' + options if options != '' else options}`" + f"`/{inter.data['name']} {options if options != '' else options}`" ) - embed.set_footer(text=f"{inter.author.name}#{inter.author.discriminator}") + embed.set_footer( + text=f"{inter.author.name}#{inter.author.discriminator}" + ) await ogiroid_log_channel.send(embed=embed) @Cog.listener() @@ -216,14 +244,87 @@ async def on_guild_role_delete(self, role: disnake.Role): await self.log_channel.send(embed=embed) @Cog.listener() - async def on_guild_role_update(self, before: disnake.Role, after: disnake.Role): + async def on_guild_role_update( + self, before: disnake.Role, after: disnake.Role + ): """Sends a message in log channel when role edites in the server.""" title = "Role edited" - if before.name != after.name: - content = f"**{before.name}** has been named to **{after.name}**" - else: # needs to check perms aswell + before_, after_ = [], [] + + for before_name in before.permissions: + before_.append(before_name) + + for after_name in after.permissions: + after_.append(after_name) + + added, removed = [], [] + check = set(after_) - set(before_) + + for name in list(check): + names = name[0] + values = name[1] + if values: + value = True + string = str(value) + values = string.replace("True", "added") + else: + value = False + string = str(value) + values = string.replace("False", "removed") + + names_raw = names.replace("_", " ").replace("guild", "server") + + if "added" in values: + added.append(names_raw) + else: + removed.append(names_raw) + + if len(added) == 0 and len(removed) > 0 and after_ != before_: + content = f"**Removed: ** {', '.join(removed)}" + elif len(removed) == 0 and len(added) > 0 and after_ != before_: + content = f"**Added: ** {','.join(added)}\n" + elif ( + len(added) == 0 + and len(removed) > 0 + and after_ != before_ + and after.name != before.name + ): + content = ( + f"**Old name: ** `{before.name}`\n" + f"**New name: ** `{after.name}`\n" + f"**Removed: ** {','.join(removed)}\n" + ) + elif ( + len(removed) == 0 + and len(added) > 0 + and after_ != before_ + and after.name != before.name + ): + content = ( + f"**Old name: ** `{before.name}`\n" + f"**New name: ** `{after.name}`\n" + f"**Added: ** {','.join(added)}\n" + ) + elif after_ != before_ and after.name != before.name: + content = ( + f"**Old name: ** `{before.name}`\n" + f"**New name: ** `{after.name}`\n" + f"**Added: ** {','.join(added)}\n" + f"**Removed: ** {', '.join(removed)}" + ) + elif after_ != before_: + content = ( + f"**Added: ** {','.join(added)}\n" + f"**Removed: ** {', '.join(removed)}" + ) + elif after.name != before.name: + content = ( + f"**Old name: ** `{before.name}`\n" + f"**New name: ** `{after.name}`" + ) + else: return embed = Embed( @@ -236,7 +337,9 @@ async def on_guild_role_update(self, before: disnake.Role, after: disnake.Role): await self.log_channel.send(embed=embed) @Cog.listener() - async def on_guild_update(self, before: disnake.Guild, after: disnake.Guild): + async def on_guild_update( + self, before: disnake.Guild, after: disnake.Guild + ): """Sends a message in log channel when guild updates.""" if before.name != after.name: message = ( @@ -256,13 +359,16 @@ async def on_guild_update(self, before: disnake.Guild, after: disnake.Guild): f"Before: `{before.afk_timeout}`\n" f"After: `{after.afk_timeout}`" ) + else: + message = None embed = Embed( title="Server edited", - description=message, colour=self.bot.config.colors.white, timestamp=datetime.now(), ) + if message is not None: + embed.description = message await self.log_channel.send(embed=embed) @@ -280,7 +386,9 @@ async def on_thread_create(self, thread: disnake.Thread): await self.log_channel.send(embed=embed) @Cog.listener() - async def on_thread_update(self, before: disnake.Thread, after: disnake.Thread): + async def on_thread_update( + self, before: disnake.Thread, after: disnake.Thread + ): """Sends a message in log channel when thread updates.""" embed = Embed( title="Thread name edited", @@ -334,7 +442,9 @@ async def on_member_unban(self, guild: disnake.Guild, user: disnake.User): timestamp=datetime.now(), ) - await self.log_channel.send(embed=embed) + await self.log_channel.send( + embed=embed + ) # todo switch to guild channel. def setup(bot): diff --git a/cogs/Memes.py b/cogs/Memes.py index caa6719..ee09461 100644 --- a/cogs/Memes.py +++ b/cogs/Memes.py @@ -20,7 +20,9 @@ async def onlyfans(self, inter): ) @commands.slash_command( - name="meme", aliases=["dankmeme"], description="Random meme from r/memes" + name="meme", + aliases=["dankmeme"], + description="Random meme from r/memes", ) async def meme(self, inter): """Random meme from r/memes""" diff --git a/cogs/Password.py b/cogs/Password.py index 3cbd2d3..13b7aa4 100644 --- a/cogs/Password.py +++ b/cogs/Password.py @@ -15,23 +15,24 @@ def __init__(self, bot: OGIROID): @commands.slash_command( name="password", aliases=["pass"], - description="Generate a random password & DM's it!", + description="Generates a random password & DM's it!", ) async def password(self, inter, length: int): """Generate a random password & DMs it!""" if length > 100: length = 100 password = "".join( - secrets.choice(string.ascii_letters + string.digits) for _ in range(length) + secrets.choice(string.ascii_letters + string.digits) + for _ in range(length) ) # try to DM if fails send the password to the channel try: - await inter.author.send(f"Your password is: `{password}`") + await inter.author.send(f"Your password is: ||{password}||") await inter.response.send_message("Your password has been sent!") # If DMs are closed, send the password to the channel except: await inter.response.send_message( - f"Your password is: `{password}`", ephemeral=True + f"Your password is: ||{password}||", ephemeral=True ) diff --git a/cogs/Search.py b/cogs/Search.py index eab785a..069f83e 100644 --- a/cogs/Search.py +++ b/cogs/Search.py @@ -1,3 +1,5 @@ +from urllib.parse import quote_plus + from disnake.ext import commands from utils.bot import OGIROID @@ -20,7 +22,7 @@ async def search( query: str = commands.Param(description="The query to search for"), ): """Searches the keyword entered""" - query = query.rstrip().replace(" ", "+") + query = quote_plus(query.rstrip().rstrip()) if engine == "google": await inter.send(f"https://google.com/search?q={query}") elif engine == "duckduckgo": @@ -39,7 +41,9 @@ async def lucky(self, inter, query): query = query.rstrip().replace(" ", "+") await inter.send(f"https://www.google.com/search?q={query}&btnI") - @commands.slash_command(description="Returns a StackOverflow search for your query") + @commands.slash_command( + description="Returns a StackOverflow search for your query" + ) async def stackoverflow(self, inter, query): """Searches StackOverflow for the query""" query = query.rstrip().replace(" ", "+") diff --git a/cogs/Snipe.py b/cogs/Snipe.py index 116bfde..d3cb1dd 100644 --- a/cogs/Snipe.py +++ b/cogs/Snipe.py @@ -24,7 +24,8 @@ async def on_message_edit(self, before, after): self.edit_snipes[after.channel] = (before, after) @commands.slash_command( - name="snipe", description="Get the most recently deleted message in a channel" + name="snipe", + description="Get the most recently deleted message in a channel", ) async def snipe_group(self, inter: ApplicationCommandInteraction): """Get the most recently deleted message in a channel""" @@ -32,11 +33,13 @@ async def snipe_group(self, inter: ApplicationCommandInteraction): try: sniped_message = self.delete_snipes[inter.channel] except KeyError: - await inter.send("There are no deleted messages in this channel to snipe!") + await inter.send( + "There are no deleted messages in this channel to snipe!" + ) else: result = disnake.Embed( color=disnake.Color.red(), - description=sniped_message.content, + description=sniped_message.content[:1024], timestamp=sniped_message.created_at, ) result.set_author( @@ -65,13 +68,22 @@ async def editsnipe(self, inter: ApplicationCommandInteraction): try: before, after = self.edit_snipes[inter.channel] except KeyError: - await inter.send("There are no message edits in this channel to snipe!") + await inter.send( + "There are no message edits in this channel to snipe!" + ) else: - result = disnake.Embed(color=disnake.Color.red(), timestamp=after.edited_at) - result.add_field(name="Before", value=before.content, inline=False) - result.add_field(name="After", value=after.content, inline=False) + result = disnake.Embed( + color=disnake.Color.red(), timestamp=after.edited_at + ) + result.add_field( + name="Before", value=before.content[:1024], inline=False + ) + result.add_field( + name="After", value=after.content[:1024], inline=False + ) result.set_author( - name=after.author.display_name, icon_url=after.author.avatar.url + name=after.author.display_name, + icon_url=after.author.avatar.url, ) is_staff = disnake.utils.find( lambda r: r.name.lower() == "staff", inter.guild.roles diff --git a/cogs/Staff.py b/cogs/Staff.py index 2a97c06..d2d6429 100644 --- a/cogs/Staff.py +++ b/cogs/Staff.py @@ -15,7 +15,13 @@ from utils.DBhandlers import RolesHandler, WarningHandler from utils.bot import OGIROID from utils.exceptions import ReactionAlreadyExists, ReactionNotFound -from utils.shortcuts import sucEmb, errorEmb, warning_embed, QuickEmb, warnings_embed +from utils.shortcuts import ( + sucEmb, + errorEmb, + warning_embed, + QuickEmb, + warnings_embed, +) class StaffVote(disnake.ui.Modal): @@ -75,7 +81,9 @@ async def on_ready(self): self.warning: WarningHandler = WarningHandler(self.bot, self.bot.db) await self.reaction_roles.startup() - @commands.slash_command(name="ban", description="Bans a user from the server.") + @commands.slash_command( + name="ban", description="Bans a user from the server." + ) @commands.has_permissions(ban_members=True) @commands.guild_only() async def ban( @@ -118,7 +126,9 @@ async def softban( await asyncio.sleep(5) await inter.guild.unban(user=user, reason="softban unban") - @commands.slash_command(name="kick", description="Kicks a user from the server.") + @commands.slash_command( + name="kick", description="Kicks a user from the server." + ) @commands.has_permissions(kick_members=True) @commands.guild_only() async def kick( @@ -131,7 +141,9 @@ async def kick( await member.kick(reason=reason) await sucEmb(inter, "User has been kicked successfully!") - @commands.slash_command(name="unban", description="Unbans a user from the server.") + @commands.slash_command( + name="unban", description="Unbans a user from the server." + ) @commands.has_permissions(ban_members=True) @commands.guild_only() async def unban( @@ -139,7 +151,9 @@ async def unban( ): """Unbans a user from the server.""" try: - await inter.guild.unban(disnake.Object(id=int(user_id)), reason=reason) + await inter.guild.unban( + disnake.Object(id=int(user_id)), reason=reason + ) except ValueError: return await errorEmb(inter, "Invalid user ID!") except disnake.errors.NotFound: @@ -148,15 +162,17 @@ async def unban( await sucEmb(inter, "User has been unbanned successfully!") @commands.slash_command( - name="mute", description="Timeout(mute)'s a user from the server." + name="mute", description="'timeout's a user from the server." ) - @commands.has_role("Staff") + @commands.has_permissions(moderate_members=True) @commands.guild_only() async def mute( self, inter: ApplicationCommandInteraction, member: disnake.Member, - duration: str = ParamInfo(description="Format: 1s, 1m, 1h, 1d, max: 28d"), + duration: str = ParamInfo( + description="Format: 1s, 1m, 1h, 1d, max: 28d" + ), reason: str = None, ): """Mutes a user from the server.""" @@ -191,7 +207,7 @@ async def mute( @commands.slash_command( name="unmute", description="Unmutes a user from the server." ) - @commands.has_role("Staff") + @commands.has_permissions(moderate_members=True) @commands.guild_only() async def unmute( self, @@ -209,8 +225,10 @@ async def unmute( await sucEmb(inter, "User has been unmuted successfully!") - @commands.slash_command(name="warn", description="Warns a user from the server.") - @commands.has_role("Staff") + @commands.slash_command( + name="warn", description="Warns a user from the server." + ) + @commands.has_permissions(manage_roles=True) @commands.guild_only() async def warn( self, @@ -220,7 +238,10 @@ async def warn( ): """Warns a user from the server.""" await self.warning.create_warning( - member.id, reason, moderator_id=inter.author.id + member.id, + reason, + moderator_id=inter.author.id, + guild_id=inter.guild.id, ) await warning_embed(inter, user=member, reason=reason) @@ -228,17 +249,19 @@ async def warn( name="removewarning", description="Removes a warning from a user from the server.", ) - @commands.has_role("Staff") + @commands.has_permissions(manage_roles=True) @commands.guild_only() async def remove_warning( self, inter: ApplicationCommandInteraction, member: disnake.Member ): """Removes a warning from a user from the server.""" - warnings = await self.warning.get_warnings(member.id) + warnings = await self.warning.get_warnings(member.id, inter.guild.id) if len(warnings) == 0: return await errorEmb(inter, "User has no warnings!") elif len(warnings) == 1: - status = await self.warning.remove_warning(warnings[0].warning_id) + status = await self.warning.remove_warning( + warnings[0].warning_id, inter.guild.id + ) if status: await sucEmb(inter, "Warning has been removed successfully!") else: @@ -256,7 +279,9 @@ def check(m): return m.author == inter.author and m.channel == inter.channel try: - msg = await self.bot.wait_for("message", check=check, timeout=60) + msg = await self.bot.wait_for( + "message", check=check, timeout=60 + ) except asyncio.TimeoutError: return await errorEmb(inter, "Timed out!") @@ -264,7 +289,9 @@ def check(m): if id > len(warnings) or id < 1: return await errorEmb(inter, "Invalid warning index!") - status = await self.warning.remove_warning(warnings[id - 1].warning_id) + status = await self.warning.remove_warning( + warnings[id - 1].warning_id, inter.guild.id + ) if status: await sucEmb(inter, "Warning has been removed successfully!") else: @@ -277,37 +304,33 @@ def check(m): await msg.delete() @commands.slash_command( - name="warnings", description="Shows the warnings of a user from the server." + name="warnings", + description="Shows the warnings of a user from the server.", ) - @commands.has_role("Staff") + @commands.has_permissions(manage_roles=True) @commands.guild_only() async def warnings( self, inter: ApplicationCommandInteraction, member: disnake.Member ): """Shows the warnings of a user from the server.""" - warnings = await self.warning.get_warnings(member.id) + warnings = await self.warning.get_warnings(member.id, inter.guild.id) if not warnings: return await QuickEmb(inter, "User has no warnings!").send() await warnings_embed(inter, member=member, warnings=warnings) - @commands.slash_command() - @commands.has_role("Staff") - async def punishments(self, inter: ApplicationCommandInteraction): - """Shows the punishments""" - return await inter.send( - "https://media.discordapp.net/attachments/905182869410955356/985264301591916594/unknown-18.png?width=521&height=683", - ephemeral=True, - ) - - @commands.slash_command(description="Steals an emoji from a different server.") + @commands.slash_command( + description="Steals an emoji from a different server." + ) @commands.guild_only() @commands.has_permissions(manage_emojis=True) async def stealemoji( self, inter: ApplicationCommandInteraction, emoji: disnake.PartialEmoji, - name=Option(name="name", required=False, description="Name of the emoji"), + name=Option( + name="name", required=False, description="Name of the emoji" + ), ): """This clones a specified emoji that optionally only specified roles are allowed to use. @@ -324,7 +347,8 @@ async def stealemoji( ) await inter.send( embed=disnake.Embed( - description=f"emoji successfully stolen", color=Color.random() + description=f"emoji successfully stolen", + color=Color.random(), ).set_image(url=emoji.url) ) except Exception as e: @@ -338,7 +362,8 @@ async def addemoji( inter: ApplicationCommandInteraction, name: str, input_type: str = commands.Param( - choices=["file(image)", "url(image)"], description="What you will input." + choices=["file(image)", "url(image)"], + description="What you will input.", ), ): """Adds an image to the server emojis""" @@ -346,11 +371,14 @@ async def addemoji( try: msg = await self.bot.wait_for( "message", - check=lambda m: m.author == inter.author and m.channel == inter.channel, + check=lambda m: m.author == inter.author + and m.channel == inter.channel, ) except asyncio.TimeoutError: return await inter.send("Timed out!") + image = None + if input_type == "url(image)": response = await self.bot.session.get( msg.content.strip().replace("webp", "png") @@ -371,19 +399,9 @@ async def addemoji( except ValueError: pass - @commands.slash_command(name="faq") - @commands.guild_only() - @commands.has_role("Staff") - async def faq(self, inter: ApplicationCommandInteraction, person: disnake.Member): - """FAQ command for the staff team""" - channel = self.bot.get_channel(self.bot.config.channels.reddit_faq) - await channel.send(f"{person.mention}", delete_after=2) - # Sending Done so this Application didn't respond error can be avoided - await inter.send("Done", delete_after=1) - @commands.slash_command(name="prune") @commands.guild_only() - @commands.has_role("Staff") + @commands.has_permissions(manage_messages=True) async def prune(self, inter: ApplicationCommandInteraction, amount: int): """Delete messages, max limit set to 25.""" # Checking if amount > 25: @@ -391,13 +409,17 @@ async def prune(self, inter: ApplicationCommandInteraction, amount: int): await inter.send("Amount is too high, please use a lower amount") return await inter.channel.purge(limit=amount) - await inter.send(f"Deleted {amount} messages successfully!", ephemeral=True) + await inter.send( + f"Deleted {amount} messages successfully!", ephemeral=True + ) @commands.slash_command(name="channellock") @commands.guild_only() - @commands.has_role("Staff") + @commands.has_permissions(manage_channels=True) async def channellock( - self, inter: ApplicationCommandInteraction, channel: disnake.TextChannel = None + self, + inter: ApplicationCommandInteraction, + channel: disnake.TextChannel = None, ): """Lock a channel""" # Lock's a channel by not letting anyone send messages to it @@ -414,9 +436,11 @@ async def channellock( @commands.slash_command(name="channelunlock") @commands.guild_only() - @commands.has_role("Staff") + @commands.has_permissions(manage_channels=True) async def channelunlock( - self, inter: ApplicationCommandInteraction, channel: disnake.TextChannel = None + self, + inter: ApplicationCommandInteraction, + channel: disnake.TextChannel = None, ): """Unlock a channel""" # Unlock's a channel by letting everyone send messages to it @@ -433,10 +457,11 @@ async def channelunlock( # Reaction Roles with buttons: @commands.slash_command( - name="addreactionrole", description="Add a reaction based role to a message" + name="addreactionrole", + description="Add a reaction based role to a message", ) @commands.guild_only() - @commands.has_role("Staff") + @commands.has_permissions(manage_roles=True) async def add_reaction_role( self, inter: ApplicationCommandInteraction, @@ -452,7 +477,9 @@ async def add_reaction_role( emoji = PartialEmoji.from_str(emoji) try: - await self.reaction_roles.create_message(message_id, role.id, str(emoji)) + await self.reaction_roles.create_message( + message_id, role.id, str(emoji) + ) except ReactionAlreadyExists: return await errorEmb(inter, "Reaction already exists!") @@ -465,7 +492,7 @@ async def add_reaction_role( description="Remove a reaction based role from a message", ) @commands.guild_only() - @commands.has_role("Staff") + @commands.has_permissions(manage_roles=True) async def remove_reaction_role( self, inter, message_id, emoji: str, role: disnake.Role ): @@ -479,7 +506,9 @@ async def remove_reaction_role( await message.remove_reaction(emoji, inter.author) try: - await self.reaction_roles.remove_message(message_id, str(emoji), role.id) + await self.reaction_roles.remove_message( + message_id, str(emoji), role.id + ) except ReactionNotFound: return await errorEmb(inter, "Reaction does not exist!") @@ -519,8 +548,8 @@ async def on_raw_reaction_remove(self, payload): description="Create a Message with Buttons where users click them and get roles.", ) @commands.guild_only() - @commands.has_role("Staff") - async def initialise_message( + @commands.has_permissions(manage_roles=True) + async def initialise_message( # todo create better cmd and clean this section up. self, inter: disnake.ApplicationCommandInteraction, emoji: str = ParamInfo( @@ -544,7 +573,9 @@ def check(m): await inter.send("Please send the message", ephemeral=True) try: - msg = await self.bot.wait_for("message", check=check, timeout=300.0) + msg = await self.bot.wait_for( + "message", check=check, timeout=300.0 + ) except asyncio.exceptions.TimeoutError: return await errorEmb( inter, "Due to no response the operation was canceled" @@ -571,7 +602,8 @@ def check(m): await self.reaction_roles.create_message(msg.id, role.id, str(emoji)) await sucEmb( - inter, "Successfully created message. To add more buttons use `/add_button`" + inter, + "Successfully created message. To add more buttons use `/add_button`", ) @commands.slash_command( @@ -580,7 +612,7 @@ def check(m): " Use /initialise-message for that.", ) @commands.guild_only() - @commands.has_role("Staff") + @commands.has_permissions(manage_roles=True) async def add_button( self, inter, @@ -596,7 +628,6 @@ async def add_button( description="The channel where the message is sent.", ), ): - emoji = PartialEmoji.from_str(new_emoji.strip()) message_id = int(message_id) @@ -625,7 +656,9 @@ async def add_button( components = disnake.ui.View.from_message(message) - custom_id = f"{role.id}-{emoji.name if emoji.is_unicode_emoji() else emoji.id}" + custom_id = ( + f"{role.id}-{emoji.name if emoji.is_unicode_emoji() else emoji.id}" + ) new_button = disnake.ui.Button( emoji=emoji, custom_id=custom_id, style=disnake.ButtonStyle[color] @@ -633,7 +666,9 @@ async def add_button( components.add_item(new_button) try: - await self.reaction_roles.create_message(message.id, role.id, str(emoji)) + await self.reaction_roles.create_message( + message.id, role.id, str(emoji) + ) except ReactionAlreadyExists: return await errorEmb(inter, "This Reaction already exists") @@ -646,8 +681,10 @@ async def add_button( description="Edit a message the bot sent(Role messages with buttons only).", ) @commands.guild_only() - @commands.has_role("Staff") - async def edit_message(self, inter, message_id, channel: disnake.TextChannel): + @commands.has_permissions(manage_roles=True) + async def edit_message( + self, inter, message_id, channel: disnake.TextChannel + ): exists = False message_id = int(message_id) for message in self.reaction_roles.messages: @@ -671,7 +708,9 @@ def check(m): await inter.send("Please send the new message", ephemeral=True) try: - msg = await self.bot.wait_for("message", check=check, timeout=300.0) + msg = await self.bot.wait_for( + "message", check=check, timeout=300.0 + ) except asyncio.exceptions.TimeoutError: return await errorEmb( inter, "Due to no response the operation was canceled" @@ -695,8 +734,10 @@ def check(m): description="Delete a message the bot sent(Role messages with buttons only).", ) @commands.guild_only() - @commands.has_role("Staff") - async def delete_message(self, inter, message_id, channel: disnake.TextChannel): + @commands.has_permissions(manage_roles=True) + async def delete_message( + self, inter, message_id, channel: disnake.TextChannel + ): exists = False message_id = int(message_id) for message in self.reaction_roles.messages: @@ -729,7 +770,7 @@ async def delete_message(self, inter, message_id, channel: disnake.TextChannel): name="remove-button", description="Remove a button from a message." ) @commands.guild_only() - @commands.has_role("Staff") + @commands.has_permissions(manage_roles=True) async def remove_button( self, inter, @@ -741,11 +782,15 @@ async def remove_button( message_id = int(message_id.strip()) emoji = PartialEmoji.from_str(emoji.strip()) - button = await self.reaction_roles.exists(message_id, str(emoji), role.id) + button = await self.reaction_roles.exists( + message_id, str(emoji), role.id + ) if not button: return await errorEmb(inter, "The button does not exist.") - await self.reaction_roles.remove_message(message_id, str(emoji), role.id) + await self.reaction_roles.remove_message( + message_id, str(emoji), role.id + ) message = await channel.fetch_message(message_id) @@ -781,7 +826,9 @@ async def button_click(self, inter): except ValueError: return - if not await self.reaction_roles.exists(message.id, str(emoji), role_id): + if not await self.reaction_roles.exists( + message.id, str(emoji), role_id + ): return await errorEmb(inter, "This doesnt exists in the database") await self.reaction_roles.increment_roles_given(message.id, str(emoji)) @@ -792,19 +839,23 @@ async def button_click(self, inter): await member.add_roles( role, reason=f"Clicked button to get role. gave {role.name}" ) - await self.reaction_roles.increment_roles_given(message.id, str(emoji)) - return await sucEmb(inter, "Added Role") + await self.reaction_roles.increment_roles_given( + message.id, str(emoji) + ) + return await sucEmb(inter, f"Added Role {role.mention}") else: await member.remove_roles( role, reason=f"Clicked button while already having the role. removed {role.name}", ) - return await sucEmb(inter, "Removed Role") + return await sucEmb(inter, f"Removed Role {role.mention}") - @commands.slash_command(name="staffvote", description="Propose a Staff Vote.") + @commands.slash_command( + name="staffvote", description="Propose a Staff Vote." + ) @commands.guild_only() - @commands.has_role("Staff") - async def staffvote(self, inter): + @commands.has_permissions(ban_members=True) + async def staffvote(self, inter): # todo remove """Propose a Staff Vote.""" await inter.response.send_modal(modal=StaffVote(self.bot)) @@ -812,12 +863,14 @@ async def staffvote(self, inter): name="staffhelp", description="Get the help for the staff commands." ) @commands.guild_only() - @commands.has_role("Staff") + @commands.has_permissions(manage_roles=True) async def staffhelp(self, inter): """Get the help for the staff commands.""" staff_commands = commands.Cog.get_slash_commands(self) emb = disnake.Embed( - title="Staff Commands", description="All staff commands", color=0xFFFFFF + title="Staff Commands", + description="All staff commands", + color=0xFFFFFF, ) for command in staff_commands: emb.add_field( @@ -828,6 +881,13 @@ async def staffhelp(self, inter): await inter.send(embed=emb) + @commands.slash_command(name="anonymous_dm", description="Send an anonymous dm to a user.") + @commands.guild_only() + @commands.has_permissions(manage_messages=True) + async def anonymous_dm(self, inter, user: disnake.Member, *, message: str): + """Send an anonymous dm to a user.""" + await user.send(message) + await sucEmb(inter, "Sent!") def setup(bot): bot.add_cog(Staff(bot)) diff --git a/cogs/Starboard.py b/cogs/Starboard.py index c574e03..82ae405 100644 --- a/cogs/Starboard.py +++ b/cogs/Starboard.py @@ -22,8 +22,13 @@ async def on_raw_reaction_add(self, payload): ): return message = await channel.fetch_message(payload.message_id) - starboard_channel = message.guild.get_channel(self.starboard_channel_id) - if payload.emoji.name == self.star_emoji and not channel == starboard_channel: + starboard_channel = message.guild.get_channel( + self.starboard_channel_id + ) + if ( + payload.emoji.name == self.star_emoji + and not channel == starboard_channel + ): for reaction in message.reactions: if ( reaction.emoji == self.star_emoji @@ -35,7 +40,8 @@ async def on_raw_reaction_add(self, payload): timestamp=datetime.now(), ) embed.set_author( - name=message.author, icon_url=message.author.display_avatar.url + name=message.author, + icon_url=message.author.display_avatar.url, ) await starboard_channel.send(embed=embed) diff --git a/cogs/Support.py b/cogs/Support.py index 3c25481..c6dd722 100644 --- a/cogs/Support.py +++ b/cogs/Support.py @@ -66,11 +66,15 @@ async def callback(self, inter: disnake.ModalInteraction): ) embed.add_field( - name="Expected Result: ", value=inter.text_values["expected"], inline=False + name="Expected Result: ", + value=inter.text_values["expected"], + inline=False, ) embed.add_field( - name="Actual Result:", value=inter.text_values["actual"], inline=False + name="Actual Result:", + value=inter.text_values["actual"], + inline=False, ) embed.add_field( @@ -86,7 +90,9 @@ async def callback(self, inter: disnake.ModalInteraction): self.bot.config.channels.bug_report_reddit_bot ) else: - channel = self.bot.get_channel(self.bot.config.channels.bug_report_ogiroid) + channel = self.bot.get_channel( + self.bot.config.channels.bug_report_ogiroid + ) await channel.send(embed=embed) await inter.send( @@ -135,10 +141,14 @@ async def callback(self, inter: disnake.ModalInteraction): embed.add_field(name="Type:", value=suggestion_type) - embed.add_field(name="Title:", value=inter.text_values["title"], inline=False) + embed.add_field( + name="Title:", value=inter.text_values["title"], inline=False + ) embed.add_field( - name="Description:", value=inter.text_values["description"], inline=False + name="Description:", + value=inter.text_values["description"], + inline=False, ) if suggestion_type == "Reddit-Bot": @@ -146,7 +156,9 @@ async def callback(self, inter: disnake.ModalInteraction): self.bot.config.channels.suggestion_reddit_bot ) else: - channel = self.bot.get_channel(self.bot.config.channels.suggestion_ogiroid) + channel = self.bot.get_channel( + self.bot.config.channels.suggestion_ogiroid + ) await channel.send(embed=embed) await inter.response.send_message( "Sent suggestion.\nThank you for your suggestion.", ephemeral=True @@ -172,8 +184,12 @@ def __init__(self, bot: OGIROID): ], connectors={"for": "bug_report_for"}, ) - async def bug(self, inter: ApplicationCommandInteraction, bug_report_for: str): - await inter.response.send_modal(modal=BugModal(self.bot, bug_report_for)) + async def bug( + self, inter: ApplicationCommandInteraction, bug_report_for: str + ): + await inter.response.send_modal( + modal=BugModal(self.bot, bug_report_for) + ) @commands.slash_command( name="suggest", @@ -189,7 +205,9 @@ async def bug(self, inter: ApplicationCommandInteraction, bug_report_for: str): connectors={"for": "suggestion_for"}, ) async def suggestion(self, inter, suggestion_for: str): - await inter.response.send_modal(modal=SuggestionModal(self.bot, suggestion_for)) + await inter.response.send_modal( + modal=SuggestionModal(self.bot, suggestion_for) + ) def setup(bot): diff --git a/cogs/Tags.py b/cogs/Tags.py index c366c8c..15e0358 100644 --- a/cogs/Tags.py +++ b/cogs/Tags.py @@ -1,6 +1,7 @@ from __future__ import annotations import re +from typing import Optional import disnake from disnake import Embed, ApplicationCommandInteraction @@ -8,6 +9,7 @@ from utils.CONSTANTS import tag_help from utils.DBhandlers import TagManager +from utils.assorted import getPosition from utils.bot import OGIROID from utils.exceptions import * from utils.models import * @@ -40,34 +42,45 @@ async def valid_name(name) -> bool: def db(self): return self.bot.db - @commands.slash_command() + @commands.slash_command(description="Tags base command") @commands.guild_only() async def tag(self, inter): pass @commands.slash_command( - name="t", aliases=["tg"], description="Get a tag", hidden=True + name="t", description="An alias for `/tag get`", hidden=True ) async def get_tag( - self, inter: ApplicationCommandInteraction, *, name: str, embeded: bool = False + self, + inter: ApplicationCommandInteraction, + *, + name: str, + embedded: Optional[bool] = False, ): - return await self.get(inter, name, embeded) + return await self.get(inter, name, embedded) @tag.sub_command(name="get", description="Gets you the tags value") @commands.guild_only() async def get( - self, inter: ApplicationCommandInteraction, name: str, embeded: bool = False + self, + inter: ApplicationCommandInteraction, + name: str, + embedded: Optional[bool] = False, ): + if not embedded: + embedded = False + if not name: return await errorEmb(inter, "You need to specify a tag name") name = name.casefold() try: tag = await self.tags.get(name) await self.tags.increment_views(name) - if embeded: + if embedded: owner = self.bot.get_user(tag.owner) emb = Embed( - color=disnake.Color.random(seed=hash(tag.name)), title=f"{tag.name}" + color=disnake.Color.random(seed=hash(tag.name)), + title=f"{tag.name}", ) emb.set_footer( text=f'{f"Tag owned by {owner.display_name}" if owner else ""} - Views: {tag.views + 1}' @@ -80,7 +93,7 @@ async def get( content, allowed_mentions=disnake.AllowedMentions.none() ) except TagNotFound: - await errorEmb(inter, f"tag {name} does not exist") + await errorEmb(inter, f"tag **{name}** does not exist") @tag.sub_command(name="random", description="Gets a random tag") async def random(self, inter): @@ -101,10 +114,12 @@ async def create( try: await self.tags.exists(name, TagAlreadyExists, should=False) except TagAlreadyExists: - return await errorEmb(inter, f"tag {name} already exists") + return await errorEmb(inter, f"tag **{name}** already exists") if len(content) >= 1900: - return await errorEmb(inter, "The tag's content must be under 1900 chars") + return await errorEmb( + inter, "The tag's content must be under 1900 chars" + ) elif not await self.valid_name(name): return ( await QuickEmb( @@ -119,13 +134,13 @@ async def create( return ( await QuickEmb( inter, - f"I have successfully made **{name}**. To view it do /tag get {name}", + f"I have successfully made **{name}**. To view it do `/tag get: {name}` or `/t: {name}`", ) .success() .send() ) except TagAlreadyExists: - return await errorEmb(inter, f"tag {name} already exists") + return await errorEmb(inter, f"tag **{name}** already exists") @tag.sub_command(name="edit", description="Edits the tag") @commands.guild_only() @@ -137,7 +152,8 @@ async def edit( *, content: str = commands.Param(le=1900), ): - name = name.casefold() + name = await self.tags.get_name(name.casefold()) + try: if ( inter.author.id != (await self.tags.get(name)).owner @@ -150,22 +166,27 @@ async def edit( await self.tags.update(name, "content", content) await QuickEmb( inter, - f"I have successfully updated **{name}**.\n\nuse /tag get {name} to access it.", + f"I have successfully updated **{name}**.\n\nuse `/tag get: {name}` or `/t: {name}` to access it.", ).success().send() except TagNotFound: - return await errorEmb(inter, f"tag {name} does not exist") + return await errorEmb(inter, f"tag **{name}** does not exist") @tag.sub_command(name="transfer", description="Transfers the tag's owner") @commands.guild_only() @commands.cooldown(1, 5, commands.BucketType.user) async def transfer( - self, inter: ApplicationCommandInteraction, name, new_owner: disnake.Member + self, + inter: ApplicationCommandInteraction, + name, + new_owner: disnake.Member, ): try: name = name.casefold() await self.tags.exists(name, TagNotFound, should=True) if new_owner.bot: - return await errorEmb(inter, "You can't transfer a tag to a bot!") + return await errorEmb( + inter, "You can't transfer a tag to a bot!" + ) elif ( inter.author.id != (await self.tags.get(name)).owner ) and not manage_messages_perms(inter): @@ -178,7 +199,7 @@ async def transfer( f"I have successfully transferred **{name}** to {new_owner.display_name}", ).success().send() except TagNotFound: - return await errorEmb(inter, f"tag {name} does not exist") + return await errorEmb(inter, f"tag **{name}** does not exist") @tag.sub_command( name="claim", @@ -198,7 +219,9 @@ async def claim(self, inter: ApplicationCommandInteraction, name): ): await self.tags.transfer(name, inter.author.id) return ( - await QuickEmb(inter, f"I have transferred **{name}** to you") + await QuickEmb( + inter, f"I have transferred **{name}** to you" + ) .success() .send() ) @@ -218,7 +241,7 @@ async def claim(self, inter: ApplicationCommandInteraction, name): .send() ) except TagNotFound: - return await errorEmb(inter, f"tag {name} does not exist") + return await errorEmb(inter, f"tag **{name}** does not exist") @tag.sub_command(name="delete", description="Deletes the tag") @commands.guild_only() @@ -233,20 +256,20 @@ async def deltag(self, inter: ApplicationCommandInteraction, name): return await errorEmb( inter, "You must be the owner of the tag to delete it!" ) - await self.tags.delete(name) await QuickEmb( inter, f"I have successfully deleted **{name}**." ).success().send() + await self.tags.delete(name) except TagNotFound: - return await errorEmb(inter, f"tag {name} does not exist") + return await errorEmb(inter, f"tag **{name}** does anot exist") @tag.sub_command(name="info", description="Gives you the tags info") @commands.guild_only() @commands.cooldown(1, 3, commands.BucketType.user) async def info(self, inter: ApplicationCommandInteraction, name): name = name.casefold() - await self.tags.exists(name, TagNotFound, should=True) try: + await self.tags.exists(name, TagNotFound, should=True) tag = await self.tags.get(name) await self.tags.increment_views(name) owner = self.bot.get_user(tag.owner) @@ -257,24 +280,56 @@ async def info(self, inter: ApplicationCommandInteraction, name): emb.add_field(name="Owner", value=owner.mention) aliases = await self.tags.get_aliases(name) if aliases: - emb.add_field(name="Aliases", value=", ".join(tag for tag in aliases)) + emb.add_field( + name="Aliases", value=", ".join(tag for tag in aliases) + ) emb.add_field(name="Created At", value=f"") emb.add_field(name="Times Called", value=abs(tag.views)) await inter.send(embed=emb) except TagNotFound: - return await errorEmb(inter, f"tag {name} does not exist") + return await errorEmb(inter, f"tag **{name}** does not exist") + + @tag.sub_command( + name="leaderboard", description="Lists shows top tags (by views)" + ) + @commands.guild_only() + @commands.cooldown(1, 30, commands.BucketType.guild) + async def leaderboard(self, inter: ApplicationCommandInteraction): + try: + tags = await self.tags.all(orderby="views", limit=10) + except AttributeError: + return await errorEmb(inter, "wait for the bot to load") + except TagsNotFound: + return await errorEmb(inter, "There are no tags") + lb_string = "" + lb_header = "Place ***-*** Name ***-*** Total Views ***-*** Owner" + + for i, tag in enumerate(tags): + owner: Optional = self.bot.get_user(tag.owner) + if owner is None: + username = None + else: + username = owner.display_name + lb_string += f"{getPosition(i)} **-** {tag.name} **-** ***{tag.views}*** **-** {username if username is not None else '**Open tag, no owner. use __/tag claim__**'}\n" + embed = disnake.Embed( + title="Flag Quiz All time Leaderboard", + description=f"The top 10 Tags in this server. Sorted by views\n", + color=disnake.Color.random(seed=inter.user.id), + ) + embed.add_field(name=lb_header, value=lb_string) + await inter.send(embed=embed) @tag.sub_command(name="list", description="Lists tags") @commands.guild_only() @commands.cooldown(1, 15, commands.BucketType.channel) @commands.cooldown(1, 15, commands.BucketType.user) - async def list_tags(self, ctx): + async def list_tags(self, inter: ApplicationCommandInteraction): try: tag_count = await self.tags.count() except AttributeError: - return await errorEmb(ctx, "wait for the bot to load") + return await errorEmb(inter, "wait for the bot to load") if tag_count == 0: - return await errorEmb(ctx, "There are no tags") + return await errorEmb(inter, "There are no tags") tags = await self.tags.all(limit=0) tag_embs = [] @@ -315,7 +370,9 @@ async def list_tags(self, ctx): start_emb = Embed(title="Tags", color=self.bot.config.colors.invis) start_emb.description = f"There are currently {tag_count:,d} tag{'s' if tag_count > 1 else ''}, use the arrows below to navigate through them" tag_embs.insert(0, start_emb) - await ctx.send(embed=tag_embs[0], view=CreatePaginator(tag_embs, ctx.author.id)) + await inter.send( + embed=tag_embs[0], view=CreatePaginator(tag_embs, inter.author.id) + ) @tag.sub_command(name="rename", description="Renames a tag") @commands.guild_only() @@ -344,10 +401,11 @@ async def rename(self, inter, name, new_name): ) await self.tags.rename(name, new_name) await QuickEmb( - inter, f"I have successfully renamed **{name}** to **{new_name}**." + inter, + f"I have successfully renamed **{name}** to **{new_name}**.", ).success().send() except TagNotFound: - return await errorEmb(inter, f"tag {name} does not exist") + return await errorEmb(inter, f"tag **{name}** does not exist") except TagAlreadyExists: return await errorEmb( inter, f"A tag with the name {new_name} already exists" @@ -355,7 +413,7 @@ async def rename(self, inter, name, new_name): @tag.sub_command(name="help", description="Help for the tag system") @commands.guild_only() - async def help(self, ctx): + async def help(self, inter): emb = Embed(title="Tag Help", color=self.bot.config.colors.invis) general_cmds = "" owner_cmds = "" @@ -363,19 +421,21 @@ async def help(self, ctx): for cmd, desc in tag_help["public"].items(): general_cmds += f"**/{cmd}** *~~* {desc}\n\t" for cmd, desc in tag_help["owner_only"].items(): - owner_cmds += f"**/{cmd}** *~~* {desc} (only usable by the tag's owner)\n" + owner_cmds += ( + f"**/{cmd}** *~~* {desc} (only usable by the tag's owner)\n" + ) emb.add_field( name=f"General commands", value=general_cmds + "\n\n", inline=False ) - emb.add_field(name=f"Owner only commands", value=owner_cmds, inline=False) - emb.set_footer( - text="Tag system made by @JasonLovesDoggo & @FreebieII", - icon_url=self.bot.user.display_avatar.url, + emb.add_field( + name=f"Tag owner only commands", value=owner_cmds, inline=False ) - await ctx.send(embed=emb) + await inter.send(embed=emb) - @tag.sub_command_group(name="alias", description="Aliases a tag", hidden=True) + @tag.sub_command_group( + name="alias", description="Aliases a tag", hidden=True + ) @commands.guild_only() async def alias(self, inter): pass @@ -409,13 +469,17 @@ async def add_alias(self, inter, name, alias): f"I have successfully added **{alias}** as an alias for **{name}**", ).success().send() except TagNotFound: - return await errorEmb(inter, f"tag {name} does not exist") + return await errorEmb(inter, f"tag **{name}** does not exist") except AliasAlreadyExists: - return await errorEmb(inter, f"tag {alias} already exists") + return await errorEmb(inter, f"alias **{alias}** already exists") except AliasLimitReached: - return await errorEmb(inter, "You can only have 10 aliases per tag") + return await errorEmb( + inter, "You can only have 10 aliases per tag" + ) - @alias.sub_command(name="remove", description="Removes an alias from a tag") + @alias.sub_command( + name="remove", description="Removes an alias from a tag" + ) @commands.guild_only() async def remove_alias(self, inter, name, alias): try: @@ -434,12 +498,13 @@ async def remove_alias(self, inter, name, alias): ) await self.tags.remove_alias(name, alias) await QuickEmb( - inter, f"I have successfully removed **{alias}** from **{name}**" + inter, + f"I have successfully removed **{alias}** from **{name}**", ).success().send() except TagNotFound: - return await errorEmb(inter, f"tag {name} does not exist") + return await errorEmb(inter, f"tag **{name}** does not exist") except AliasNotFound: - return await errorEmb(inter, f"alias {alias} does not exist") + return await errorEmb(inter, f"alias **{alias}** does not exist") def setup(bot): diff --git a/cogs/Tickets.py b/cogs/Tickets.py index dbed242..16305cd 100644 --- a/cogs/Tickets.py +++ b/cogs/Tickets.py @@ -55,7 +55,9 @@ def check(m): return m.author == inter.author and m.channel == inter.channel try: - msg = await self.bot.wait_for("message", check=check, timeout=300.0) + msg = await self.bot.wait_for( + "message", check=check, timeout=300.0 + ) except asyncio.exceptions.TimeoutError: return await errorEmb( inter, "Due to no response the operation was canceled" @@ -90,7 +92,9 @@ async def on_button_click(self, inter): # checks if user has a ticket already open for channel in guild.channels: try: - if int(channel.name.strip().replace("ticket-", "")) == int(user.id): + if int(channel.name.strip().replace("ticket-", "")) == int( + user.id + ): await errorEmb( inter, "You already have a ticket open. Please close it before opening a new one", @@ -148,7 +152,9 @@ async def add_user(self, inter, member: disnake.Member): else: await errorEmb(inter, "This is not a ticket channel.") - @commands.slash_command(name="removeuser", description="Remove user from channel") + @commands.slash_command( + name="removeuser", description="Remove user from channel" + ) @commands.has_role("Staff") async def remove_user(self, inter, member: disnake.Member): if self.check_if_ticket_channel(inter): diff --git a/cogs/Timezone.py b/cogs/Timezone.py index 01fd1de..3f4528b 100644 --- a/cogs/Timezone.py +++ b/cogs/Timezone.py @@ -23,9 +23,13 @@ def __init__(self, bot: OGIROID): @commands.Cog.listener() async def on_ready(self): if not self.bot.ready_: - self.db_timezone: TimezoneHandler = TimezoneHandler(self.bot, self.bot.db) + self.db_timezone: TimezoneHandler = TimezoneHandler( + self.bot, self.bot.db + ) - @commands.slash_command(name="timezone") + @commands.slash_command( + name="timezone", description="Timezone base command" + ) async def timezone(self, inter: disnake.ApplicationCommandInteraction): pass @@ -39,11 +43,18 @@ async def set( autocomplete=autocomplete_timezones, ), ): - if timezone is None: return await errorEmb(inter, "You need to provide a timezone") - if timezone not in timezones: - return await errorEmb(inter, "The timezone you provided is not valid") + elif timezone not in timezones: + return await errorEmb( + inter, "The timezone you provided is not valid" + ) + elif timezone == "Europe/Tbilisi": + timezone = "Asia/Tbilisi" + display_timezone = "Europe/Tbilisi" + else: + display_timezone = timezone + try: await self.db_timezone.create_user(inter.author.id, timezone) except UserAlreadyExists: @@ -51,7 +62,7 @@ async def set( await sucEmb( inter, - f"Your timezone has been set to {timezone}." + f"Your timezone has been set to {display_timezone}." f" Its should be {dt.datetime.now(pytz.timezone(timezone)).strftime('%H:%M')} at your location.", ) @@ -67,17 +78,28 @@ async def edit( ): if timezone is None: return await errorEmb(inter, "You need to provide a timezone") - if timezone not in timezones: - return await errorEmb(inter, "The timezone you provided is not valid") + elif timezone not in timezones: + return await errorEmb( + inter, "The timezone you provided is not valid" + ) + # handles tbilisi cause its annoying me + elif timezone == "Europe/Tbilisi": + timezone = "Asia/Tbilisi" + display_timezone = "Europe/Tbilisi" + else: + display_timezone = timezone + try: await self.db_timezone.update_user(inter.author.id, timezone) await sucEmb( inter, - f"Your timezone has been set to {timezone}." - f" Its should be {dt.datetime.now(pytz.timezone(timezone)).strftime('%H:%M')} at your location.", + f"Your timezone has been set to {display_timezone}." + f" It should be {dt.datetime.now(pytz.timezone(timezone)).strftime('%H:%M')} at your location.", ) except UserNotFound: - return await errorEmb(inter, "The User doesn't have a timezone set") + return await errorEmb( + inter, "The User doesn't have a timezone set" + ) @timezone.sub_command(name="remove", description="Remove your timezone.") async def remove( @@ -87,13 +109,17 @@ async def remove( try: await self.db_timezone.delete_user(inter.author.id) except UserNotFound: - return await errorEmb(inter, "This user doesn't have a timezone set") + return await errorEmb( + inter, "This user doesn't have a timezone set" + ) await sucEmb(inter, "The timezone has been removed") @timezone.sub_command(name="get", description="Get the timezone of a user") async def get( - self, inter, user: disnake.User = commands.Param(name="user", default=None) + self, + inter, + user: disnake.User = commands.Param(name="user", default=None), ): if user is None: user = inter.author @@ -102,10 +128,19 @@ async def get( timezone = await self.db_timezone.get_user(user.id) if timezone is None: - return await errorEmb(inter, "This user doesn't have a timezone set") + return await errorEmb( + inter, "This user doesn't have a timezone set" + ) + + # Handles tbilisi naming cause its annoying me. + if timezone.timezone == "Asia/Tbilisi": + display_timezone = "Europe/Tbilisi" + else: + display_timezone = timezone.timezone + await QuickEmb( inter, - f"{user.mention}'s timezone is {timezone.timezone}." + f"{user.mention}'s timezone is {display_timezone}." f" Its currently {dt.datetime.now(pytz.timezone(timezone.timezone)).strftime('%H:%M')} for them", ).send() diff --git a/cogs/Trivia.py b/cogs/Trivia.py index 33fc94e..b55f3f7 100644 --- a/cogs/Trivia.py +++ b/cogs/Trivia.py @@ -24,7 +24,9 @@ def __init__(self, bot: OGIROID): @commands.Cog.listener() async def on_ready(self): await self.bot.wait_until_ready() - self.flag_quiz: FlagQuizHandler = FlagQuizHandler(self.bot, self.bot.db) + self.flag_quiz: FlagQuizHandler = FlagQuizHandler( + self.bot, self.bot.db + ) @commands.slash_command(name="flagquiz", description="Guess the flags.") async def guess_the_flag(self, inter): @@ -37,10 +39,14 @@ async def guess_the_flag(self, inter): correct = 0 tries = 0 try: - user = await self.flag_quiz.get_user(inter.author.id) + user = await self.flag_quiz.get_user( + inter.author.id, inter.guild.id + ) except UserNotFound: await self.flag_quiz.add_user(inter.author.id) - user = await self.flag_quiz.get_user(inter.author.id) + user = await self.flag_quiz.get_user( + inter.author.id, inter.guild.id + ) def check(m): return ( @@ -54,7 +60,11 @@ def check(m): retry = True while retry: user = await self.flag_quiz.add_data( - user_id=inter.author.id, user=user, correct=correct, tries=tries + user_id=inter.author.id, + user=user, + correct=correct, + tries=tries, + guild_id=inter.guild.id, ) embed = disnake.Embed( title="Guess the Flag.", @@ -73,7 +83,11 @@ def check(m): channel, "Due to no response the quiz ended early." ).error().send() await self.flag_quiz.add_data( - inter.author.id, tries - 1, correct, user=user + inter.author.id, + tries - 1, + correct, + user=user, + guild_id=inter.guild.id, ) return @@ -87,7 +101,8 @@ def check(m): >= 0.7 ): await QuickEmb( - channel, f"Correct. The country indeed was {country[0]}" + channel, + f"Correct. The country indeed was {country[0]}", ).success().send() correct += 1 retry = False @@ -107,7 +122,9 @@ def check(m): if guess.content.lower() == "skip": if type(country) == list: country = country[0] - await QuickEmb(channel, f"The country was {country}").send() + await QuickEmb( + channel, f"The country was {country}" + ).send() retry = False elif guess.content.casefold() == "give up": await guess.reply( @@ -139,20 +156,27 @@ def check(m): f"Your Score: {correct}/{tries - 1}. Thanks for playing.", ).send() await self.flag_quiz.add_data( - guess.author.id, tries - 1, correct, user=user + guess.author.id, + tries - 1, + correct, + user=user, + guild_id=inter.guild.id, ) return # If retry is still True (not changed) then the guess was incorrect elif retry: await errorEmb(inter, "Incorrect") - await self.flag_quiz.add_data(inter.author.id, tries, correct, user=user) + await self.flag_quiz.add_data( + inter.author.id, tries, correct, user=user, guild_id=inter.guild.id + ) await channel.send( f"Great Job on finishing the entire Quiz. Score: {correct}/{tries}" ) @commands.slash_command( - name="flagquiz-leaderboard", description="Leaderboard for the flag quiz." + name="flagquiz-leaderboard", + description="Leaderboard for the flag quiz.", ) async def flag_quiz_leaderboard( self, @@ -166,7 +190,9 @@ async def flag_quiz_leaderboard( ), ): try: - leaderboard = await self.flag_quiz.get_leaderboard(order_by=sortby) + leaderboard = await self.flag_quiz.get_leaderboard( + order_by=sortby, guild_id=inter.guild.id + ) except UsersNotFound: return ( await QuickEmb(inter, "No users have taken the quiz yet.") @@ -181,9 +207,7 @@ async def flag_quiz_leaderboard( leaderboard_string = "" leaderboard_header = "Place ***-*** User ***-*** Correct Guesses/Total Guesses ***-*** Completed " - i = 0 - for user in leaderboard: - i += 1 + for i, user in enumerate(leaderboard): username = self.bot.get_user(user.user_id) leaderboard_string += f"{getPosition(i)} **-** {username} **-** {user.correct}/{user.tries} **-** {user.completed}\n" embed = disnake.Embed( @@ -205,10 +229,11 @@ async def flag_quiz_user(self, inter, user: disnake.User = None): else: user_id = inter.author.id try: - player = await self.flag_quiz.get_user(user_id) + player = await self.flag_quiz.get_user(user_id, inter.guild.id) except UserNotFound: await errorEmb( - inter, "This user never took part in the flag quiz or doesn't exist." + inter, + "This user never took part in the flag quiz or doesn't exist.", ) return @@ -264,7 +289,9 @@ async def flag_quiz_user(self, inter, user: disnake.User = None): "Entertainment: Cartoon & Animations", ], ), - Option(name="amount", description="Amount of Questions", min_value=1), + Option( + name="amount", description="Amount of Questions", min_value=1 + ), Option( name="difficulty", description="Difficulty of the questions", @@ -283,11 +310,18 @@ async def flag_quiz_user(self, inter, user: disnake.User = None): ) @commands.cooldown(1, 5, commands.BucketType.user) async def trivia( - self, inter, category="Any", difficulty=None, amount: int = 5, kind="multiple" + self, + inter, + category="Any", + difficulty=None, + amount: int = 5, + kind="multiple", ): if int(amount) <= 1: return ( - await QuickEmb(inter, "The amount of questions needs to be at least 1") + await QuickEmb( + inter, "The amount of questions needs to be at least 1" + ) .error() .send() ) @@ -316,13 +350,18 @@ def check(m): ) data = await response.json() embed = disnake.Embed(title="Created Quiz", colour=0xFFFFFF) - embed.set_author(name=inter.author, icon_url=inter.author.display_avatar) + embed.set_author( + name=inter.author, icon_url=inter.author.display_avatar + ) embed.set_thumbnail(url=inter.author.display_avatar) embed.add_field(name="Category:", value=category + " ") embed.add_field( - name="Difficulty:", value=difficulty if difficulty else "Any Difficulty" + name="Difficulty:", + value=difficulty if difficulty else "Any Difficulty", + ) + embed.add_field( + name="Amount of Questions:", value=amount, inline=False ) - embed.add_field(name="Amount of Questions:", value=amount, inline=False) embed.add_field(name="Type:", value=kind, inline=False) await inter.send(embed=embed) @@ -339,7 +378,9 @@ def check(m): answers_string = "" for i in range(len(answers)): answers_string += f"{emojis[i]}: {html.unescape(answers[i])}\n" - components.append(disnake.ui.Button(emoji=emojis[i], custom_id=f"{i}")) + components.append( + disnake.ui.Button(emoji=emojis[i], custom_id=f"{i}") + ) embed = disnake.Embed( title=html.unescape(question["question"]), @@ -353,7 +394,9 @@ def check(m): ) except asyncio.exceptions.TimeoutError: return ( - await QuickEmb(channel, "Due to no response the quiz ended early.") + await QuickEmb( + channel, "Due to no response the quiz ended early." + ) .error() .send() ) @@ -385,7 +428,8 @@ def check(m): ).error().send() await QuickEmb( - channel, f"Thanks for playing. Your final Score is {correct} / {questions}." + channel, + f"Thanks for playing. Your final Score is {correct} / {questions}.", ).send() diff --git a/cogs/Welcome.py b/cogs/Welcome.py index d22c0ae..e62a924 100644 --- a/cogs/Welcome.py +++ b/cogs/Welcome.py @@ -17,10 +17,14 @@ async def on_member_join(self, member: disnake.Member): if member.guild.id != self.bot.config.guilds.main_guild: return if member.dm_channel is None: - introduction = self.bot.get_channel(self.bot.config.channels.introduction) + introduction = self.bot.get_channel( + self.bot.config.channels.introduction + ) general = self.bot.get_channel(self.bot.config.channels.general) roles = self.bot.get_channel(self.bot.config.channels.roles) - reddit_bot = self.bot.get_channel(self.bot.config.channels.reddit_bot) + reddit_bot = self.bot.get_channel( + self.bot.config.channels.reddit_bot + ) rules = self.bot.get_channel(self.bot.config.channels.rules) await member.create_dm() @@ -41,7 +45,9 @@ async def on_member_join(self, member: disnake.Member): inline=True, ) embed.add_field( - name="Roles:", value=f"Select some roles, {roles.mention}", inline=True + name="Roles:", + value=f"Select some roles, {roles.mention}", + inline=True, ) embed.add_field( name="Reddit Bot Related:", @@ -49,7 +55,9 @@ async def on_member_join(self, member: disnake.Member): inline=True, ) embed.add_field( - name="Rules:", value=f"Checkout the rules, {rules.mention}", inline=True + name="Rules:", + value=f"Checkout the rules, {rules.mention}", + inline=True, ) embed.set_thumbnail(url=member.display_avatar) try: @@ -59,7 +67,14 @@ async def on_member_join(self, member: disnake.Member): else: pass - greetings = ["Hello", "Hi", "Greetings", "Hola", "Bonjour", "Konnichiwa"] + greetings = [ + "Hello", + "Hi", + "Greetings", + "Hola", + "Bonjour", + "Konnichiwa", + ] secondary_greeting = [ "Welcome to Lewis Menelaws' Official Discord Server! Feel free to look around & introduce yourself.", "Welcome to the server! We wish you have a great time here, make sure you tell us a little bit about yourself.", diff --git a/requirements.txt b/requirements.txt index 3ac0eff..97be370 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,16 +1,16 @@ aiohttp~=3.8.3 -aiosqlite==0.17.0 +aiosqlite==0.18.0 akinator~=1.0.3 -disnake==2.7.0 +disnake==2.8.0 parsedatetime==2.6 -python-dotenv==0.21.0 -requests==2.28.1 +python-dotenv==0.21.1 +requests==2.28.2 textdistance~=4.5.0 -aiocache==0.11.1 -Pillow~=9.3.0 +aiocache==0.12.0 +Pillow==9.4.0 expr.py~=0.3.0 -cachetools~=5.2.0 +cachetools==5.3.0 python-dateutil~=2.8.2 -pytz==2022.6 -asyncpg~=0.27.0 +pytz==2022.7.1 discord_together==1.2.6 +better_profanity==0.7.0 \ No newline at end of file diff --git a/secrets.env.template b/secrets.env.template index 10da747..e6a5d4c 100644 --- a/secrets.env.template +++ b/secrets.env.template @@ -6,9 +6,5 @@ SRA_API_TOKEN= OPEN_WEATHER_MAP_API_KEY= # Used for the upload checker can be ignored in development YT_API_KEY= -POSTGRES_USER= -POSTGRES_PASSWORD= -POSTGRES_HOST= -POSTGRES_PORT= # Needs to be set to true or false -DEVELOPMENT= \ No newline at end of file +DEVELOPMENT= diff --git a/setup.sql b/setup.sql index bbdfa0b..22c9bda 100644 --- a/setup.sql +++ b/setup.sql @@ -32,7 +32,8 @@ IF NOT EXISTS flag_quizz user_id BIGINT, tries INTEGER, correct INTEGER, - completed INTEGER + completed INTEGER, + guild_id INTEGER ); CREATE TABLE @@ -60,7 +61,8 @@ IF NOT EXISTS warnings warning_id SERIAL PRIMARY KEY, user_id BIGINT, moderator_id BIGINT, - reason TEXT + reason TEXT, + guild_id BIGINT ); CREATE TABLE diff --git a/utils/CONSTANTS.py b/utils/CONSTANTS.py index 81fe1ac..870c30a 100644 --- a/utils/CONSTANTS.py +++ b/utils/CONSTANTS.py @@ -2,7 +2,7 @@ from dataclasses import dataclass -__VERSION__ = "2.1" +__VERSION__ = "2.2" from typing import Final @@ -96,6 +96,7 @@ def dev(cls): class Colors: invis: int = 0x2F3136 white: int = 0xFFFFFF + red: int = 0xE23636 @dataclass @@ -192,6 +193,7 @@ def status(stat): "Europe/Sofia", "Europe/Tallinn", "Europe/Vilnius", + "Europe/Tbilisi", "Asia/Baghdad", "Asia/Kuwait", "Africa/Nairobi", @@ -201,7 +203,6 @@ def status(stat): "Asia/Baku", "Europe/Volgograd", "Asia/Muscat", - "Asia/Tbilisi", "Asia/Yerevan", "Asia/Kabul", "Asia/Karachi", diff --git a/utils/DBhandlers.py b/utils/DBhandlers.py index a3dfd30..3391760 100644 --- a/utils/DBhandlers.py +++ b/utils/DBhandlers.py @@ -2,7 +2,7 @@ import random import time -from typing import List, Literal, Optional, TYPE_CHECKING, Dict +from typing import List, Literal, Optional, TYPE_CHECKING, Dict, TypeVar from utils.CONSTANTS import timings from utils.cache import AsyncTTL @@ -21,6 +21,35 @@ if TYPE_CHECKING: from utils.bot import OGIROID +Key = TypeVar("Key", int, str) + + +class BaseModal: + async def save(self): + pass + + async def delete(self): + pass + + +class RocksDBHandler: + def __init__(self, bot: "OGIROID"): + self.bot = bot + + async def update(self, key: Key, values: dict[Key, Key]): + """get a model from the database, update it based on key then update it based off of values then put it back""" + values = list(values.items()) + + modal: BaseModal = await self.get(key) + for key, value in values: + modal.__setattr__(key, value) + + await modal.save() + + async def get(self, key: Key, key2: Optional[Key] = None): + """get a model from the database and if key2 is provided just return that""" + pass + class ConfigHandler: def __init__(self, bot: "OGIROID", db): @@ -62,39 +91,47 @@ def __init__(self, bot: "OGIROID", db): self.db = db self.cache = AsyncTTL(timings.MINUTE * 4) - async def get_user(self, user_id: int) -> FlagQuizUser: - user = await self.cache.get(str(user_id)) + async def get_user(self, user_id: int, guild_id: int) -> FlagQuizUser: + # get cache by combining user_id and guild_id + user = await self.cache.get(str(user_id) + str(guild_id)) if user is not None: return user - elif await self.exists(user_id): + elif await self.exists(user_id, guild_id): + # not prone to injection since it's just id's async with self.db.execute( - f"SELECT * FROM flag_quizz WHERE user_id = {user_id}" + f"SELECT * FROM flag_quizz WHERE user_id = {user_id} AND guild_id = {guild_id}" ) as cur: rawUserData = await cur.fetchone() user = FlagQuizUser(*rawUserData) - await self.cache.set(str(user_id), user) + # set unique index by combining user_id and guild_id + await self.cache.set(str(user_id) + str(guild_id), user) return user else: raise UserNotFound - async def exists(self, user_id: int): + async def exists(self, user_id: int, guild_id: int) -> bool: async with self.db.execute( - f"SELECT EXISTS(SELECT 1 FROM flag_quizz WHERE user_id=?)", [user_id] + f"SELECT EXISTS(SELECT 1 FROM flag_quizz WHERE user_id=? AND guild_id = ?)", + [user_id, guild_id], ) as cur: return bool((await cur.fetchone())[0]) - async def get_leaderboard(self, order_by="correct"): + async def get_leaderboard( + self, order_by="correct", guild_id: int = None + ) -> List[FlagQuizUser]: leaderboard = [] async with self.db.execute( - f"SELECT user_id, tries, correct, completed FROM flag_quizz ORDER BY {order_by} DESC LIMIT 10" + f"SELECT user_id, tries, correct, completed, guild_id FROM flag_quizz WHERE guild_id = {guild_id} ORDER BY {order_by} DESC LIMIT 10" ) as cur: async for row in cur: leaderboard.append(FlagQuizUser(*row)) if len(leaderboard) == 0: raise UsersNotFound leaderboard = sorted( - leaderboard, key=lambda x: x.correct + (x.completed * 199), reverse=True + leaderboard, + key=lambda x: x.correct + (x.completed * 199), + reverse=True, ) # line might fail lol idk return leaderboard @@ -104,12 +141,13 @@ async def add_data( tries: int, correct: int, user: Optional[FlagQuizUser] = None, - ) -> FlagQuizUser: + guild_id: int = None, + ) -> FlagQuizUser or None: if user is not None: try: - user = await self.get_user(user_id) + user = await self.get_user(user_id, guild_id) except UserNotFound: - await self.add_user(user_id, tries, correct) + await self.add_user(user_id, tries, correct, guild_id=guild_id) return if correct == 199: @@ -120,19 +158,26 @@ async def add_data( correct += user.correct async with self.db.execute( - f"UPDATE flag_quizz SET tries = {tries}, correct = {correct}, completed = {completed} WHERE user_id = {user_id}" + # not prone to injection since it's just id's + f"UPDATE flag_quizz SET tries = {tries}, correct = {correct}, completed = {completed} WHERE user_id = {user_id} AND guild_id = {guild_id}" ): await self.db.commit() - return FlagQuizUser(user_id, tries, correct, completed) + return FlagQuizUser(user_id, tries, correct, completed, guild_id) - async def add_user(self, user_id: int, tries: int = 0, correct: int = 0): + async def add_user( + self, + user_id: int, + tries: int = 0, + correct: int = 0, + guild_id: int = None, + ): if correct == 199: completed = 1 else: completed = 0 async with self.db.execute( - f"INSERT INTO flag_quizz (user_id, tries, correct, completed) VALUES ({user_id}, {tries}, {correct}, {completed})" + f"INSERT INTO flag_quizz (user_id, tries, correct, completed, guild_id) VALUES ({user_id}, {tries}, {correct}, {completed}, {guild_id})" ): await self.db.commit() @@ -187,7 +232,9 @@ async def load_blacklist(self): blacklist.append(BlacklistedUser(*row).fix_db_types()) if len(blacklist) == 0: raise BlacklistNotFound - print(f"[BLACKLIST] {len(blacklist)} blacklisted users found and loaded") + print( + f"[BLACKLIST] {len(blacklist)} blacklisted users found and loaded" + ) self.blacklist = blacklist async def get(self, user_id: int) -> BlacklistedUser: @@ -215,7 +262,9 @@ async def add( async def remove(self, user_id: int): """Removes a user from the blacklist""" - await self.db.execute(f"DELETE FROM blacklist WHERE user_id = ?", [user_id]) + await self.db.execute( + f"DELETE FROM blacklist WHERE user_id = ?", [user_id] + ) await self.db.commit() self.blacklist.remove(await self.get_user(user_id)) await self.cache.remove(user_id) @@ -230,7 +279,9 @@ async def blacklisted(self, user_id: int) -> bool: else: return False - async def edit_flags(self, user_id: int, bot: bool, tickets: bool, tags: bool): + async def edit_flags( + self, user_id: int, bot: bool, tickets: bool, tags: bool + ): """Edits the flags (blacklist perms) of a user""" await self.db.execute( f"UPDATE blacklist SET bot = ?, tickets = ?, tags = ? WHERE user_id = ?", @@ -248,7 +299,8 @@ async def edit_flags(self, user_id: int, bot: bool, tickets: bool, tags: bool): async def edit_reason(self, user_id: int, reason: str): """Edits the blacklist reason of a user""" await self.db.execute( - f"UPDATE blacklist SET reason = ? WHERE user_id = ?", [reason, user_id] + f"UPDATE blacklist SET reason = ? WHERE user_id = ?", + [reason, user_id], ) await self.db.commit() indx = await self.get_user_index(user_id) @@ -260,7 +312,8 @@ async def edit_reason(self, user_id: int, reason: str): async def edit_expiry(self, user_id: int, expires: int): """Edits when the blacklist expires of a user""" await self.db.execute( - f"UPDATE blacklist SET expires = ? WHERE user_id = ?", [expires, user_id] + f"UPDATE blacklist SET expires = ? WHERE user_id = ?", + [expires, user_id], ) await self.db.commit() indx = await self.get_user_index(user_id) @@ -281,7 +334,6 @@ def __init__(self, bot: "OGIROID", db): self.session = self.bot.session self.names = {"tags": [], "aliases": []} self.cache = AsyncTTL(timings.DAY / 2) # cache tags for 12 hrs - # self.pool = self.bot.pool todo re-add async def startup(self): await self.bot.wait_until_ready() @@ -301,7 +353,9 @@ async def get_tag_from_cache(self, name): name = await self.get_name(name) return await self.cache.get(name) - async def exists(self, name, exception: TagException, should: bool) -> bool: + async def exists( + self, name, exception: TagException, should: bool + ) -> bool: """Returns True if the tag exists""" name = name.casefold() if await self.cache.exists(name): @@ -327,13 +381,15 @@ async def create(self, name, content, owner): ) await self.db.commit() self.names["tags"].append(name) - await self.cache.add(name, Tag(name, content, owner, int(time.time()), 0)) + await self.cache.add( + name, Tag(name, content, owner, int(time.time()), 0) + ) async def get(self, name, /, force: bool = False) -> Tag: """ Returns the tag object of the tag with the given name or alias args: - name: the name of the tag + name: the name or alias of the tag force: if True, will ignore the cache and get the tag from the database """ name = await self.get_name(name) @@ -344,7 +400,9 @@ async def get(self, name, /, force: bool = False) -> Tag: else: await self.cache.add(name, await self.get(name, force=True)) - _cur = await self.db.execute("SELECT * FROM tags WHERE tag_id= ?", [name]) + _cur = await self.db.execute( + "SELECT * FROM tags WHERE tag_id = ? ", (name,) + ) raw = await _cur.fetchone() if raw is None: return @@ -391,7 +449,8 @@ async def rename(self, name, new_name): name = await self.get_name(name) await self.update(name, "tag_id", new_name) async with self.db.execute( - f"UPDATE tag_relations SET tag_id = ? WHERE tag_id = ?", [new_name, name] + f"UPDATE tag_relations SET tag_id = ? WHERE tag_id = ?", + [new_name, name], ): await self.db.commit() @@ -404,12 +463,13 @@ async def transfer(self, name, new_owner: int): async def increment_views(self, name): name = await self.get_name(name) + tag = await self.cache.get(name, default=False) if tag: tag.views += 1 await self.cache.set(name, tag) await self.db.execute( - "UPDATE tags SET views = views + 1 WHERE tag_id = ?", [name] + f"UPDATE tags SET views = {tag.views} WHERE tag_id = ?", [name] ) await self.db.commit() @@ -425,7 +485,10 @@ async def get_top(self, limit=10): return tags async def get_tags_by_owner( - self, owner: int, limit=10, orderby: Literal["views", "created_at"] = "views" + self, + owner: int, + limit=10, + orderby: Literal["views", "created_at"] = "views", ): tags = [] async with self.db.execute( @@ -441,10 +504,12 @@ async def count(self) -> int: async with self.db.execute("SELECT COUNT(*) FROM tags") as cur: return int(tuple(await cur.fetchone())[0]) - async def get_name(self, name_or_alias): + async def get_name(self, name_or_alias: str | tuple): """gets the true name of a tag (not the alias)""" + if isinstance(name_or_alias, tuple): + return await self.get_name(name_or_alias[0]) - name_or_alias = name_or_alias.casefold() + name_or_alias = name_or_alias.casefold() # todo fix. if name_or_alias in self.names["tags"]: return name_or_alias # it's a tag _cur = await self.db.execute( @@ -453,7 +518,7 @@ async def get_name(self, name_or_alias): value = await _cur.fetchone() if value is None: raise TagNotFound(name_or_alias) - return value + return value[0] async def add_alias(self, name, alias): name = await self.get_name(name) @@ -463,7 +528,8 @@ async def add_alias(self, name, alias): elif len(aliases) > 10: raise AliasLimitReached await self.db.execute( - "INSERT INTO tag_relations (tag_id, alias) VALUES (?, ?)", [name, alias] + "INSERT INTO tag_relations (tag_id, alias) VALUES (?, ?)", + [name, alias], ) await self.db.commit() self.names["aliases"].append(alias) @@ -474,7 +540,8 @@ async def remove_alias(self, name, alias): if alias not in (await self.get_aliases(name)): raise AliasNotFound await self.db.execute( - "DELETE FROM tag_relations WHERE tag_id = ? AND alias = ?", [name, alias] + "DELETE FROM tag_relations WHERE tag_id = ? AND alias = ?", + [name, alias], ) await self.db.commit() self.names["aliases"].remove(alias) @@ -489,10 +556,14 @@ async def remove_aliases(self, name): for alias in aliases: self.names["aliases"].remove(alias) await self.cache.delete(alias) - await self.db.execute("DELETE FROM tag_relations WHERE tag_id = ?", [name]) + await self.db.execute( + "DELETE FROM tag_relations WHERE tag_id = ?", [name] + ) await self.db.commit() - async def get_aliases(self, name: Optional = None) -> List[str] | TagNotFound: + async def get_aliases( + self, name: Optional = None + ) -> List[str] | TagNotFound: if not name: async with self.db.execute("SELECT * FROM tag_relations") as cur: content = await cur.fetchall() @@ -587,7 +658,9 @@ async def remove_messages(self, message_id: int): "DELETE FROM reaction_roles WHERE message_id = ?", [message_id] ) await self.db.commit() - self.messages = [msg for msg in self.messages if msg.message_id != message_id] + self.messages = [ + msg for msg in self.messages if msg.message_id != message_id + ] class WarningHandler: @@ -604,35 +677,46 @@ async def get_warning(self, warning_id: int) -> Optional[WarningModel]: return None return WarningModel(*content) - async def get_warnings(self, user_id: int) -> List[WarningModel]: + async def get_warnings( + self, user_id: int, guild_id: int + ) -> List[WarningModel]: warnings = [] async with self.db.execute( - "SELECT * FROM warnings WHERE user_id = ?", [user_id] + "SELECT * FROM warnings WHERE user_id = ? AND guild_id = ?", + [user_id, guild_id], ) as cur: async for row in cur: warnings.append(WarningModel(*row)) return warnings - async def create_warning(self, user_id: int, reason: str, moderator_id: int): + async def create_warning( + self, user_id: int, reason: str, moderator_id: int, guild_id: int + ): await self.db.execute( - "INSERT INTO warnings (user_id, reason, moderator_id) VALUES (?, ?, ?)", - [user_id, reason, moderator_id], + "INSERT INTO warnings (user_id, reason, moderator_id, guild_id) VALUES (?, ?, ?, ?)", + [user_id, reason, moderator_id, guild_id], ) await self.db.commit() return True - async def remove_all_warnings(self, user_id: int) -> bool: - warnings = await self.get_warnings(user_id) + async def remove_all_warnings(self, user_id: int, guild_id: int) -> bool: + warnings = await self.get_warnings(user_id, guild_id) if len(warnings) == 0: return False - await self.db.execute("DELETE FROM warnings WHERE user_id = ?", [user_id]) + await self.db.execute( + "DELETE FROM warnings WHERE user_id = ? AND guild_id = ?", + [user_id, guild_id], + ) await self.db.commit() - async def remove_warning(self, warning_id: int) -> bool: + async def remove_warning(self, warning_id: int, guild_id: int) -> bool: warning = await self.get_warning(warning_id) if warning is None: return False - await self.db.execute("DELETE FROM warnings WHERE warning_id = ?", [warning_id]) + await self.db.execute( + "DELETE FROM warnings WHERE warning_id = ? AND guild_id = ?", + [warning_id, guild_id], + ) await self.db.commit() return True @@ -662,7 +746,9 @@ async def delete_user(self, user_id: int) -> bool: user = await self.get_user(user_id) if user is None: raise UserNotFound - await self.db.execute("DELETE FROM birthday WHERE user_id = ?", [user_id]) + await self.db.execute( + "DELETE FROM birthday WHERE user_id = ?", [user_id] + ) await self.db.commit() return True @@ -714,7 +800,9 @@ async def delete_user(self, user_id: int) -> bool: user = await self.get_user(user_id) if user is None: raise UserNotFound - await self.db.execute("DELETE FROM timezone WHERE user_id = ?", [user_id]) + await self.db.execute( + "DELETE FROM timezone WHERE user_id = ?", [user_id] + ) await self.db.commit() return True diff --git a/utils/assorted.py b/utils/assorted.py index 865bb05..5803079 100644 --- a/utils/assorted.py +++ b/utils/assorted.py @@ -4,7 +4,9 @@ def traceback_maker(err, advance: bool = True): """A way to debug your code anywhere""" _traceback = "".join(traceback.format_tb(err.__traceback__)) - error = "```py\n{1}{0}: {2}\n```".format(type(err).__name__, _traceback, err) + error = "```py\n{1}{0}: {2}\n```".format( + type(err).__name__, _traceback, err + ) return error if advance else f"{type(err).__name__}: {err}" diff --git a/utils/bot.py b/utils/bot.py index 6b01518..3add0a4 100644 --- a/utils/bot.py +++ b/utils/bot.py @@ -1,10 +1,8 @@ import asyncio -import json from datetime import datetime from os import listdir import aiosqlite -import asyncpg import disnake from disnake import ApplicationCommandInteraction, OptionType from disnake.ext import commands @@ -23,10 +21,11 @@ class OGIROID(commands.InteractionBot): def __init__(self, *args, **kwargs): - super().__init__( intents=disnake.Intents.all(), - command_sync_flags=commands.CommandSyncFlags(sync_commands_debug=True), + command_sync_flags=commands.CommandSyncFlags( + sync_commands_debug=True + ), *args, **kwargs, ) @@ -37,7 +36,6 @@ def __init__(self, *args, **kwargs): self.commands_ran = {} self.total_commands_ran = 0 self.db = None - self.pool: asyncpg.Pool = None self.blacklist: BlacklistHandler = None self.add_app_command_check( self.blacklist_check, slash_commands=True, call_once=True @@ -107,7 +105,9 @@ async def on_ready(self): "--------------------------------------------------------------------------------" ) print("Bot is ready! Logged in as: " + self.user.name) - print("Bot devs: HarryDaDev | FreebieII | JasonLovesDoggo | Levani") + print( + "Bot devs: HarryDaDev | FreebieII | JasonLovesDoggo | Levani" + ) print(f"Bot version: {__VERSION__}") print( "--------------------------------------------------------------------------------" @@ -126,18 +126,11 @@ async def _setup(self): self.blacklist: BlacklistHandler = BlacklistHandler(self, self.db) await self.blacklist.startup() - async def ensure_db_uri_can_run(self) -> bool: - connection: asyncpg.Connection = await asyncpg.connect( - user=self.config.Database.user, - password=self.config.Database.password, - database=self.config.Database.database, - host=self.config.Database.host, - port=self.config.Database.port, - ) - await connection.close() - return True + async def load_db(self): + pass async def start(self, *args, **kwargs): + await self.load_db() async with aiosqlite.connect("data.db") as self.db: await self.db.executescript(SETUP_SQL) # run the db migrations in /migrations @@ -145,37 +138,8 @@ async def start(self, *args, **kwargs): if file.endswith(".sql"): with open(f"migrations/{file}", "r") as migration_sql: await self.db.executescript(migration_sql.read()) - # self.pool: asyncpg.Pool = await self.create_pool() todo re-add await super().start(*args, **kwargs) - async def create_pool(self) -> asyncpg.Pool: - def _encode_jsonb(value): - return json.dumps(value) - - def _decode_jsonb(value): - return json.loads(value) - - async def init(con): - await con.set_type_codec( - "jsonb", - schema="pg_catalog", - encoder=_encode_jsonb, - decoder=_decode_jsonb, - format="text", - ) - - return await asyncpg.create_pool( - user=self.config.Database.user, - password=self.config.Database.password, - database=self.config.Database.database, - host=self.config.Database.host, - port=self.config.Database.port, - init=init, - command_timeout=60, - max_size=20, - min_size=20, - ) - @property def ready_(self): return self._ready_ diff --git a/utils/checks.py b/utils/checks.py index 05865da..d09f534 100644 --- a/utils/checks.py +++ b/utils/checks.py @@ -3,7 +3,12 @@ def is_dev(): - devs = [511724576674414600, 662656158129192961, 963860161976467498] + devs = [ + 511724576674414600, + 662656158129192961, + 963860161976467498, + 627450451297566733, + ] def predicate(inter: ApplicationCommandInteraction): return inter.author.id in devs diff --git a/utils/config.py b/utils/config.py index 80053c6..9cd0f10 100644 --- a/utils/config.py +++ b/utils/config.py @@ -46,7 +46,7 @@ class Tokens: @dataclass -class Database: +class Database: # Todo switch to rockdb info user: str = os.getenv("POSTGRES_USER") password: str = os.getenv("POSTGRES_PASSWORD") host: str = os.getenv("POSTGRES_HOST") @@ -66,7 +66,9 @@ class Config: elif os.getenv("DEVELOPMENT").lower() == "false": Development: bool = False else: - raise ValueError("DEVELOPMENT in secrets.env must be set to true or false") + raise ValueError( + "DEVELOPMENT in secrets.env must be set to true or false" + ) colors = Colors colours = colors tokens = Tokens diff --git a/utils/formats.py b/utils/formats.py index 2d5115d..347ccea 100644 --- a/utils/formats.py +++ b/utils/formats.py @@ -2,7 +2,9 @@ from typing import Sequence, Optional -def human_join(seq: Sequence[str], delim: str = ", ", final: str = "or") -> str: +def human_join( + seq: Sequence[str], delim: str = ", ", final: str = "or" +) -> str: size = len(seq) if size == 0: return "" diff --git a/utils/models.py b/utils/models.py index 8918e66..172032c 100644 --- a/utils/models.py +++ b/utils/models.py @@ -36,7 +36,12 @@ def get_exp(self, level: int): @property def total_exp(self): return sum( - [exp for exp in [self.get_exp(lvl) for lvl in range(1, self.lvl + 1)]][::-1] + [ + exp + for exp in [ + self.get_exp(lvl) for lvl in range(1, self.lvl + 1) + ] + ][::-1] + [self.xp] ) @@ -105,6 +110,7 @@ class FlagQuizUser: tries: int correct: int completed: int + guild_id: int @dataclass @@ -121,6 +127,7 @@ class WarningModel: user_id: int moderator_id: int reason: str + guild_id: int @dataclass diff --git a/utils/pagination.py b/utils/pagination.py index e6e9529..cc8c812 100644 --- a/utils/pagination.py +++ b/utils/pagination.py @@ -81,8 +81,12 @@ async def next(self, button, inter): "You cannot interact with these buttons.", ephemeral=True ) elif self.CurrentEmbed == len(self.embeds) - 1: - return await inter.send("you are already at the end", ephemeral=True) - await inter.response.edit_message(embed=self.embeds[self.CurrentEmbed + 1]) + return await inter.send( + "you are already at the end", ephemeral=True + ) + await inter.response.edit_message( + embed=self.embeds[self.CurrentEmbed + 1] + ) self.CurrentEmbed += 1 except: @@ -96,8 +100,12 @@ async def end(self, button, inter): "You cannot interact with these buttons.", ephemeral=True ) elif self.CurrentEmbed == len(self.embeds) - 1: - return await inter.send("you are already at the end", ephemeral=True) - await inter.response.edit_message(embed=self.embeds[len(self.embeds) - 1]) + return await inter.send( + "you are already at the end", ephemeral=True + ) + await inter.response.edit_message( + embed=self.embeds[len(self.embeds) - 1] + ) self.CurrentEmbed = len(self.embeds) - 1 except: @@ -123,7 +131,6 @@ def __init__( set_user: bool = False, timeout: float = None, ): - self.controller = controller self.author = author self.CurrentEmbed = 0 @@ -184,7 +191,9 @@ async def create_page(self, inter, page_num) -> Embed: inline=False, ) - embed.set_footer(text=f"{inter.author}", icon_url=inter.author.avatar.url) + embed.set_footer( + text=f"{inter.author}", icon_url=inter.author.avatar.url + ) embed.timestamp = dt.datetime.now() return embed @@ -239,7 +248,9 @@ async def next(self, button, inter): "You cannot interact with these buttons.", ephemeral=True ) elif await self.at_last_page(inter): - return await inter.send("you are already at the end", ephemeral=True) + return await inter.send( + "you are already at the end", ephemeral=True + ) await inter.response.edit_message( embed=await self.create_page(inter, self.CurrentEmbed + 1) ) @@ -255,7 +266,9 @@ async def end(self, button, inter): "You cannot interact with these buttons.", ephemeral=True ) elif await self.at_last_page(inter): - return await inter.send("you are already at the end", ephemeral=True) + return await inter.send( + "you are already at the end", ephemeral=True + ) record_count = await self.controller.get_count(inter.guild.id) if ( record_count % 10 == 0 diff --git a/utils/rankcard.py b/utils/rankcard.py index f1c703f..e51399c 100644 --- a/utils/rankcard.py +++ b/utils/rankcard.py @@ -42,7 +42,9 @@ def __init__(self): "STATUS_POS": (290, 330, -40, 20), } - async def getavatar(self, user: Union[disnake.User, disnake.Member]) -> bytes: + async def getavatar( + self, user: Union[disnake.User, disnake.Member] + ) -> bytes: async with session.get(str(user.display_avatar.url)) as response: avatarbytes = await response.read() with Image.open(BytesIO(avatarbytes)) as im: @@ -80,11 +82,12 @@ async def create_img( # status try: - if user.status == disnake.Status.online: draw.ellipse(self.POSITIONS["STATUS_POS"], fill=(67, 181, 129)) elif user.status == disnake.Status.offline: - draw.ellipse(self.POSITIONS["STATUS_POS"], fill=(116, 127, 141)) + draw.ellipse( + self.POSITIONS["STATUS_POS"], fill=(116, 127, 141) + ) elif user.status == disnake.Status.dnd: draw.ellipse(self.POSITIONS["STATUS_POS"], fill=(240, 71, 71)) elif user.status == disnake.Status.idle: diff --git a/utils/shortcuts.py b/utils/shortcuts.py index 86d470c..1a8753e 100644 --- a/utils/shortcuts.py +++ b/utils/shortcuts.py @@ -59,7 +59,9 @@ async def warning_embed(inter: ApplicationCommandInteraction, user, reason): await inter.send(embed=emb) -async def warnings_embed(inter: ApplicationCommandInteraction, member, warnings): +async def warnings_embed( + inter: ApplicationCommandInteraction, member, warnings +): embed = disnake.Embed(title=f"{member.name}'s warnings", color=0xFFFFFF) warning_string = "" i = 0 @@ -90,7 +92,9 @@ async def sucEmb(inter: ApplicationCommandInteraction, text, ephemeral=True): class QuickEmb: - def __init__(self, inter: ApplicationCommandInteraction, msg, color=0xFFFFFF): + def __init__( + self, inter: ApplicationCommandInteraction, msg, color=0xFFFFFF + ): self.inter = inter self.msg = msg self.color = color diff --git a/utils/timeconversions.py b/utils/timeconversions.py index 6c51529..242bac1 100644 --- a/utils/timeconversions.py +++ b/utils/timeconversions.py @@ -35,7 +35,9 @@ class ShortTime: re.VERBOSE, ) - def __init__(self, argument: str, *, now: Optional[datetime.datetime] = None): + def __init__( + self, argument: str, *, now: Optional[datetime.datetime] = None + ): match = self.compiled.fullmatch(argument) if match is None or not match.group(0): raise commands.BadArgument("invalid time provided") @@ -52,7 +54,9 @@ async def convert(cls, ctx: Context, argument: str) -> Self: class HumanTime: calendar = pdt.Calendar(version=pdt.VERSION_CONTEXT_STYLE) - def __init__(self, argument: str, *, now: Optional[datetime.datetime] = None): + def __init__( + self, argument: str, *, now: Optional[datetime.datetime] = None + ): now = now or datetime.datetime.utcnow() dt, status = self.calendar.parseDT(argument, sourceTime=now) if not status.hasDateOrTime: @@ -78,7 +82,9 @@ async def convert(cls, ctx: Context, argument: str) -> Self: class Time(HumanTime): - def __init__(self, argument: str, *, now: Optional[datetime.datetime] = None): + def __init__( + self, argument: str, *, now: Optional[datetime.datetime] = None + ): try: o = ShortTime(argument, now=now) except Exception: @@ -89,7 +95,9 @@ def __init__(self, argument: str, *, now: Optional[datetime.datetime] = None): class FutureTime(Time): - def __init__(self, argument: str, *, now: Optional[datetime.datetime] = None): + def __init__( + self, argument: str, *, now: Optional[datetime.datetime] = None + ): super().__init__(argument, now=now) if self._past: @@ -106,7 +114,9 @@ def __init__(self, dt: datetime.datetime): self.dt = dt self.arg = "" - async def ensure_constraints(self, now: datetime.datetime, remaining: str) -> None: + async def ensure_constraints( + self, now: datetime.datetime, remaining: str + ) -> None: if self.dt.replace(tzinfo=None) < now.replace(tzinfo=None): raise commands.BadArgument("This time is in the past.") @@ -194,7 +204,9 @@ def format_relative(dt: datetime.datetime) -> str: async def convert(argument: str) -> FriendlyTimeResult: try: if argument.casefold() == "never": - return FriendlyTimeResult(datetime.datetime.fromtimestamp(9999999999)) + return FriendlyTimeResult( + datetime.datetime.fromtimestamp(9999999999) + ) calendar = HumanTime.calendar regex = ShortTime.compiled now = datetime.datetime.fromtimestamp(int(time.time())) @@ -251,7 +263,9 @@ async def convert(argument: str) -> FriendlyTimeResult: if begin == 1: # check if it's quoted: if argument[0] != '"': - raise commands.BadArgument("Expected quote before time input...") + raise commands.BadArgument( + "Expected quote before time input..." + ) if not (end < len(argument) and argument[end] == '"'): raise commands.BadArgument( "If the time is quoted, you must unquote it." diff --git a/utils/wrappers/OpenWeatherMap.py b/utils/wrappers/OpenWeatherMap.py index cddac3d..943f6e3 100644 --- a/utils/wrappers/OpenWeatherMap.py +++ b/utils/wrappers/OpenWeatherMap.py @@ -16,7 +16,9 @@ def __str__(self): return "{} K".format(round(self.temperature, 2)) def __repr__(self): - return "Temperature=({}, default_type=K)".format(round(self.temperature, 2)) + return "Temperature=({}, default_type=K)".format( + round(self.temperature, 2) + ) @property def kelvin(self): @@ -68,7 +70,9 @@ def __init__(self, data): self.country = data["sys"]["country"] self.wind = Wind(data["wind"]) self.icon = data["weather"][0]["icon"] - self.iconUrl = "https://openweathermap.org/img/wn/{}@2x.png".format(self.icon) + self.iconUrl = "https://openweathermap.org/img/wn/{}@2x.png".format( + self.icon + ) self.temp = Temperature(data["main"]["temp"]) self.tempMin = Temperature(data["main"]["temp_min"]) self.tempMax = Temperature(data["main"]["temp_max"]) @@ -104,9 +108,7 @@ def __init__(self, key, session=None): """ self.apiKey = key self.session = session or aiohttp.ClientSession() - self.baseUrl = ( - "https://api.openweathermap.org/data/2.5/weather?{type}={query}&appid={key}" - ) + self.baseUrl = "https://api.openweathermap.org/data/2.5/weather?{type}={query}&appid={key}" async def get(self, _type, query): """Get weather report."""