From 11ffe2d5c6b03cca5eb4cd49b8903c32cb268491 Mon Sep 17 00:00:00 2001 From: LBlend Date: Sun, 13 Nov 2022 22:37:43 +0100 Subject: [PATCH 01/54] get bot up and running on dpy 2.0 --- src/cogs/anime.py | 4 +- src/cogs/birthday.py | 8 +- src/cogs/botinfo.py | 8 +- src/cogs/deprecated/camera_channel.py | 4 +- src/cogs/deprecated/social_credit.py | 4 +- src/cogs/deprecated/vær.py | 6 +- src/cogs/deprecated/wiki_search.py | 4 +- src/cogs/dev_tools.py | 4 +- src/cogs/errors.py | 4 +- src/cogs/fun.py | 4 +- src/cogs/funreplies.py | 4 +- src/cogs/info.py | 111 +++++++------------------- src/cogs/mc_whitelist.py | 4 +- src/cogs/misc.py | 10 +-- src/cogs/mod.py | 10 +-- src/cogs/repeated_messages.py | 4 +- src/cogs/utils/embed_templates.py | 2 +- src/run.py | 4 +- 18 files changed, 75 insertions(+), 124 deletions(-) diff --git a/src/cogs/anime.py b/src/cogs/anime.py index a65d9b0..5f09541 100644 --- a/src/cogs/anime.py +++ b/src/cogs/anime.py @@ -1275,5 +1275,5 @@ class Anime(commands.Cog): await ctx.send(embed=embed) -def setup(bot): - bot.add_cog(Anime(bot)) +async def setup(bot): + await bot.add_cog(Anime(bot)) diff --git a/src/cogs/birthday.py b/src/cogs/birthday.py index 905d691..30ade58 100644 --- a/src/cogs/birthday.py +++ b/src/cogs/birthday.py @@ -138,8 +138,8 @@ class Birthday(commands.Cog): next_birthday_days = (next_birthday - datetime.now()).days embed = discord.Embed(description=bruker.mention, color=discord_utils.get_user_color(bruker)) - embed.set_thumbnail(url=bruker.avatar_url) - embed.set_author(name=bruker.name, icon_url=bruker.avatar_url) + embed.set_thumbnail(url=bruker.display_avatar) + embed.set_author(name=bruker.name, icon_url=bruker.display_avatar) embed.add_field(name="Bursdag", value=f"") embed.add_field(name="Hvor gammel?", value=f"{years_old} år\n({days_old} dager)", inline=False) embed.add_field(name="Neste bursdag om", value=f"{next_birthday_days} dager", inline=False) @@ -169,5 +169,5 @@ class Birthday(commands.Cog): await ctx.send(embed=embed) -def setup(bot): - bot.add_cog(Birthday(bot)) +async def setup(bot): + await bot.add_cog(Birthday(bot)) diff --git a/src/cogs/botinfo.py b/src/cogs/botinfo.py index f72d115..f123fd1 100644 --- a/src/cogs/botinfo.py +++ b/src/cogs/botinfo.py @@ -62,8 +62,8 @@ class BotInfo(commands.Cog): # Build embed embed = discord.Embed(color=ctx.me.color, url=self.bot.misc['website']) - embed.set_author(name=dev.name, icon_url=dev.avatar_url) - embed.set_thumbnail(url=self.bot.user.avatar_url) + embed.set_author(name=dev.name, icon_url=dev.display_avatar) + embed.set_thumbnail(url=self.bot.user.display_avatar) embed.add_field(name='Dev', value=f'{dev.mention}\n{dev.name}#{dev.discriminator}') embed.add_field(name='Oppetid', value=self.get_uptime()) embed.add_field(name='Ping', value=f'Ekte ping: {ping} ms\nWebsocket ping: {int(self.bot.latency * 1000)} ms') @@ -139,5 +139,5 @@ class BotInfo(commands.Cog): return f'{days}d, {hours}t, {minutes}m, {seconds}s' -def setup(bot): - bot.add_cog(BotInfo(bot)) +async def setup(bot): + await bot.add_cog(BotInfo(bot)) diff --git a/src/cogs/deprecated/camera_channel.py b/src/cogs/deprecated/camera_channel.py index 5c4a1c4..05ccb15 100644 --- a/src/cogs/deprecated/camera_channel.py +++ b/src/cogs/deprecated/camera_channel.py @@ -20,7 +20,7 @@ class CameraChannel(commands.Cog): await member.move_to(channel=None) -def setup(bot): +async def setup(bot): #bot.add_listener(CameraChannel(bot).on_voice_state_update, 'on_voice_state_update') - #bot.add_cog(CameraChannel(bot)) + #await bot.add_cog(CameraChannel(bot)) pass diff --git a/src/cogs/deprecated/social_credit.py b/src/cogs/deprecated/social_credit.py index 29afed7..464722a 100644 --- a/src/cogs/deprecated/social_credit.py +++ b/src/cogs/deprecated/social_credit.py @@ -269,5 +269,5 @@ class SocialCredit(commands.Cog): await self.social_punishment(user.id, 25) -def setup(bot): - bot.add_cog(SocialCredit(bot)) +async def setup(bot): + await bot.add_cog(SocialCredit(bot)) diff --git a/src/cogs/deprecated/vær.py b/src/cogs/deprecated/vær.py index 03d6eab..1bfdda8 100644 --- a/src/cogs/deprecated/vær.py +++ b/src/cogs/deprecated/vær.py @@ -21,7 +21,7 @@ class Vær(commands.Cog): embed = discord.Embed(title=":white_sun_small_cloud: Værmelding for Oslo :white_sun_small_cloud:") embed.description = "*Værmeldingen levert til deg av Petter*" - embed.set_author(name=dev.name, icon_url=dev.avatar_url) + embed.set_author(name=dev.name, icon_url=dev.display_avatar) half_hour = False klokkeslett = 0 @@ -42,5 +42,5 @@ class Vær(commands.Cog): await channel.send("RISE AND SHINE GAMERS!", embed=embed) -def setup(bot): - bot.add_cog(Vær(bot)) +async def setup(bot): + await bot.add_cog(Vær(bot)) diff --git a/src/cogs/deprecated/wiki_search.py b/src/cogs/deprecated/wiki_search.py index 3d2cdab..cbd1f61 100644 --- a/src/cogs/deprecated/wiki_search.py +++ b/src/cogs/deprecated/wiki_search.py @@ -21,5 +21,5 @@ class Wiki(commands.Cog): embed_templates.default_footer(ctx,embed) await ctx.send(embed=embed) -def setup(bot): - bot.add_cog(Wiki(bot)) \ No newline at end of file +async def setup(bot): + await bot.add_cog(Wiki(bot)) \ No newline at end of file diff --git a/src/cogs/dev_tools.py b/src/cogs/dev_tools.py index 66e95fa..672f8e0 100644 --- a/src/cogs/dev_tools.py +++ b/src/cogs/dev_tools.py @@ -355,5 +355,5 @@ class DevTools(commands.Cog): await ctx.send(embed=embed) -def setup(bot): - bot.add_cog(DevTools(bot)) +async def setup(bot): + await bot.add_cog(DevTools(bot)) diff --git a/src/cogs/errors.py b/src/cogs/errors.py index 1db2701..a3970b4 100644 --- a/src/cogs/errors.py +++ b/src/cogs/errors.py @@ -102,5 +102,5 @@ class Errors(commands.Cog): ) -def setup(bot): - bot.add_cog(Errors(bot)) +async def setup(bot): + await bot.add_cog(Errors(bot)) diff --git a/src/cogs/fun.py b/src/cogs/fun.py index 10f7809..ed2cdf9 100644 --- a/src/cogs/fun.py +++ b/src/cogs/fun.py @@ -41,5 +41,5 @@ class Fun(commands.Cog): await ctx.send('https://i.imgur.com/h84fknj.jpg') -def setup(bot): - bot.add_cog(Fun(bot)) +async def setup(bot): + await bot.add_cog(Fun(bot)) diff --git a/src/cogs/funreplies.py b/src/cogs/funreplies.py index b00d62e..81a460a 100644 --- a/src/cogs/funreplies.py +++ b/src/cogs/funreplies.py @@ -26,6 +26,6 @@ class FunReplies(commands.Cog): '824764933513281596/3xhpwbakz2361.png') -def setup(bot): +async def setup(bot): bot.add_listener(FunReplies(bot).react, 'on_message') - bot.add_cog(FunReplies(bot)) + await bot.add_cog(FunReplies(bot)) diff --git a/src/cogs/info.py b/src/cogs/info.py index f53753b..4798251 100644 --- a/src/cogs/info.py +++ b/src/cogs/info.py @@ -72,54 +72,6 @@ class Info(commands.Cog): categories = len(ctx.guild.categories) total_channels = text_channels + voice_channels - flags = { - 'us': ':flag_us:', - 'eu': ':flag_eu:', - 'singapore': ':flag_sg:', - 'london': ':flag_gb:', - 'sydney': ':flag_au:', - 'amsterdam': ':flag_nl:', - 'frankfurt': ':flag_de:', - 'brazil': ':flag_br:', - 'dubai': ':flag_ae:', - 'japan': ':flag_jp:', - 'russia': ':flag_ru:', - 'southafrica': ':flag_za:', - 'hongkong': ':flag_hk:', - 'india': ':flag_in:' - } - region = str(ctx.guild.region) - if region.startswith('us'): - region = 'us' - elif region.startswith('eu'): - region = 'eu' - elif region.startswith('amsterdam'): - region = 'amsterdam' - try: - flag = flags[region] - except KeyError: - flag = ':question:' - - region_names = { - 'eu-central': 'Sentral-Europa', - 'eu-west': 'Vest-Europa', - 'europe': 'Europa', - 'hongkong': 'Hong Kong', - 'russia': 'Russland', - 'southafrica': 'Sør-Afrika', - 'us-central': 'Midt-USA', - 'us-east': 'New Jersey', - 'us-south': 'Sør-USA', - 'us-west': 'California', - 'vip-amsterdam': 'Amsterdam (VIP)', - 'vip-us-east': 'Øst-USA (VIP)', - 'vip-us-west': 'Vest-USA (VIP)', - } - try: - region_name = region_names[str(ctx.guild.region)] - except KeyError: - region_name = str(ctx.guild.region).title() - features_string = '' if ctx.guild.features is not []: features = { @@ -146,10 +98,10 @@ class Info(commands.Cog): features_string += f'{features[feature]}\n' photos = {} - if ctx.guild.splash_url: - photos['Invitasjonsbilde'] = ctx.guild.splash_url_as(format='png') - if ctx.guild.banner_url: - photos['Banner'] = ctx.guild.banner_url_as(format='png') + if ctx.guild.discovery_splash: + photos['Invitasjonsbilde'] = ctx.guild.discovery_splash + if ctx.guild.banner: + photos['Banner'] = ctx.guild.banner verification_level = { 'none': 'ingen', @@ -171,11 +123,10 @@ class Info(commands.Cog): f'**Innholdsfilter:** {content}\n' + f'**Boost Tier:** {ctx.guild.premium_tier}\n' + f'**Emoji:** {len(ctx.guild.emojis)}') - embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon_url) - embed.set_thumbnail(url=ctx.guild.icon_url_as(static_format='png')) + embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon) + embed.set_thumbnail(url=ctx.guild.icon) embed.add_field(name='ID', value=ctx.guild.id) embed.add_field(name='Eier', value=ctx.guild.owner.mention) - embed.add_field(name='Region', value=f'{flag} {region_name}') embed.add_field(name='Opprettet', value=f'{guild_created_date}\n{since_created_days} ' + f'{since_created_days_string} siden') embed.add_field(name=f'Kanaler ({total_channels})', value=f'💬 Tekst: **{text_channels}**\n' + @@ -240,8 +191,8 @@ class Info(commands.Cog): roles = '**Ingen roller**' embed = discord.Embed(color=ctx.me.color, description=roles) - embed.set_author(name=f'Roller ({len(ctx.guild.roles) - 1})', icon_url=ctx.guild.icon_url) - embed.set_footer(text=ctx.guild.name, icon_url=ctx.guild.icon_url) + embed.set_author(name=f'Roller ({len(ctx.guild.roles) - 1})', icon_url=ctx.guild.icon) + embed.set_footer(text=ctx.guild.name, icon_url=ctx.guild.icon) await ctx.send(embed=embed) @commands.bot_has_permissions(embed_links=True) @@ -265,8 +216,8 @@ class Info(commands.Cog): boosters = '\n'.join(boosters) embed = discord.Embed(color=ctx.me.color, description=boosters) - embed.set_author(name=f'Boosters ({ctx.guild.premium_subscription_count})', icon_url=ctx.guild.icon_url) - embed.set_footer(text=ctx.guild.name, icon_url=ctx.guild.icon_url) + embed.set_author(name=f'Boosters ({ctx.guild.premium_subscription_count})', icon_url=ctx.guild.icon) + embed.set_footer(text=ctx.guild.name, icon_url=ctx.guild.icon) await ctx.send(embed=embed) @commands.bot_has_permissions(embed_links=True) @@ -278,7 +229,7 @@ class Info(commands.Cog): Viser ikonet til serveren du er i """ - url = ctx.guild.icon_url_as(static_format='png') + url = ctx.guild.icon embed = discord.Embed(color=ctx.me.color, description=f'[Lenke]({url})') embed.set_author(name=ctx.guild.name, icon_url=url) @@ -295,14 +246,14 @@ class Info(commands.Cog): Viser invite splash til serveren """ - if not ctx.guild.splash_url: + if not ctx.guild.discovery_splash: embed = embed_templates.error_warning(ctx, text='Serveren har ikke en splash') return await ctx.send(embed=embed) - url = ctx.guild.splash_url_as(format='png') + url = ctx.guild.discovery_splash embed = discord.Embed(color=ctx.me.color, description=f'[Lenke]({url})') - embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon_url) + embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon) embed.set_image(url=url) embed = embed_templates.default_footer(ctx, embed) await ctx.send(embed=embed) @@ -316,14 +267,14 @@ class Info(commands.Cog): Viser invite splash til serveren """ - if not ctx.guild.banner_url: + if not ctx.guild.banner: embed = embed_templates.error_warning(ctx, text='Serveren har ikke et banner :(') return await ctx.send(embed=embed) - url = ctx.guild.banner_url_as(format='png') + url = ctx.guild.banner embed = discord.Embed(color=ctx.me.color, description=f'[Lenke]({url})') - embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon_url) + embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon) embed.set_image(url=url) embed = embed_templates.default_footer(ctx, embed) await ctx.send(embed=embed) @@ -401,11 +352,11 @@ class Info(commands.Cog): embed = discord.Embed(color=color, description=f'{bruker.mention}\nID: {bruker.id}\n{status}\n{app}') if bruker.display_name == bruker.name: - embed.set_author(name=f'{bruker.name}#{bruker.discriminator}', icon_url=bruker.avatar_url) + embed.set_author(name=f'{bruker.name}#{bruker.discriminator}', icon_url=bruker.display_avatar) else: embed.set_author(name=f'{bruker.name}#{bruker.discriminator} | {bruker.display_name}', - icon_url=bruker.avatar_url) - embed.set_thumbnail(url=bruker.avatar_url_as(static_format='png')) + icon_url=bruker.display_avatar) + embed.set_thumbnail(url=bruker.display_avatar) embed.add_field(name='Opprettet', value=f'{bruker_created_date}\n{since_created_days} ' + f'{since_created_days_string} siden') embed.add_field(name='Ble med i serveren', value=f'{bruker_joined_date}\n{since_joined_days} ' + @@ -462,8 +413,8 @@ class Info(commands.Cog): color = discord.Colour(0x99AAB5) embed = discord.Embed(color=color, description=roles) - embed.set_author(name=f'Roller ({len(bruker.roles) - 1})', icon_url=bruker.avatar_url) - embed.set_footer(text=f'{bruker.name}#{bruker.discriminator}', icon_url=bruker.avatar_url) + embed.set_author(name=f'Roller ({len(bruker.roles) - 1})', icon_url=bruker.display_avatar) + embed.set_footer(text=f'{bruker.name}#{bruker.discriminator}', icon_url=bruker.display_avatar) await ctx.send(embed=embed) @commands.bot_has_permissions(embed_links=True) @@ -482,7 +433,7 @@ class Info(commands.Cog): else: color = discord.Colour(0x99AAB5) - url = bruker.avatar_url_as(static_format='png') + url = bruker.display_avatar embed = discord.Embed(color=color, description=f'[Lenke]({url})') embed.set_author(name=f'{bruker.name}#{bruker.discriminator}', icon_url=url) @@ -539,7 +490,7 @@ class Info(commands.Cog): permissions = ', '.join([permission for permission, value in iter(rolle.permissions) if value is True]) embed = discord.Embed(title=rolle.name, description=f'{rolle.mention}\n**ID:** {rolle.id}', color=color) - embed.set_author(name=rolle.guild.name, icon_url=rolle.guild.icon_url) + embed.set_author(name=rolle.guild.name, icon_url=rolle.guild.icon) embed.add_field(name='Fargekode', value=str(rolle.color)) embed.add_field(name='Opprettet', value=f'{rolle_created_date}\n{since_created_days} ' + f'{since_created_days_string} siden') @@ -582,7 +533,7 @@ class Info(commands.Cog): members = 'For mange for å vise her' embed = discord.Embed(color=ctx.me.color, title=kanal.name, description=f'{kanal.mention}\nID: {kanal.id}') - embed.set_author(name=kanal.guild.name, icon_url=kanal.guild.icon_url) + embed.set_author(name=kanal.guild.name, icon_url=kanal.guild.icon) embed.add_field(name='Beskrivelse', value=description, inline=False) embed.add_field(name='Opprettet', value=kanal.created_at.strftime('%d. %b. %Y - %H:%M')) embed.add_field(name='NSFW', value=nsfw) @@ -608,7 +559,7 @@ class Info(commands.Cog): limit = f'{kanal.user_limit} personer' embed = discord.Embed(color=ctx.me.color, title=kanal.name, description=f'ID: {kanal.id}') - embed.set_author(name=kanal.guild.name, icon_url=kanal.guild.icon_url) + embed.set_author(name=kanal.guild.name, icon_url=kanal.guild.icon) embed.add_field(name='Opprettet', value=kanal.created_at.strftime('%d. %b. %Y - %H:%M')) embed.add_field(name='Bitrate', value=f'{int(kanal.bitrate / 1000)}kbps') embed.add_field(name='Maksgrense', value=limit) @@ -638,7 +589,7 @@ class Info(commands.Cog): emoji_creator = 'Jeg trenger `manage_emojis`-tillatelsen på serveren den er fra for å hente dette' embed = discord.Embed(color=ctx.me.color, title=emoji.name, description=f'ID: {emoji.id}') - embed.set_author(name=emoji.guild.name, icon_url=emoji.guild.icon_url) + embed.set_author(name=emoji.guild.name, icon_url=emoji.guild.icon) embed.add_field(name='Opprettet', value=emoji.created_at.strftime('%d. %b. %Y - %H:%M')) embed.add_field(name='Animert', value=animated) embed.add_field(name='Lagt til av', value=emoji_creator) @@ -656,7 +607,7 @@ class Info(commands.Cog): """ embed = discord.Embed(colour=ctx.me.color) - embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon_url) + embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon) emoji_string = '' for emoji in ctx.guild.emojis: @@ -721,11 +672,11 @@ class Info(commands.Cog): members = '\n'.join(page_data['page_content']) embed = discord.Embed(color=ctx.me.color) - embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon_url) + embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon) embed.add_field(name='Første Discordbrukerene på serveren', value=members) embed.set_footer(text=f'Side: {side}/{pagecount}') await ctx.send(embed=embed) -def setup(bot): - bot.add_cog(Info(bot)) +async def setup(bot): + await bot.add_cog(Info(bot)) diff --git a/src/cogs/mc_whitelist.py b/src/cogs/mc_whitelist.py index 4232b6d..e5e57bb 100644 --- a/src/cogs/mc_whitelist.py +++ b/src/cogs/mc_whitelist.py @@ -68,5 +68,5 @@ class MCWhitelist(commands.Cog): ) -def setup(bot): - bot.add_cog(MCWhitelist(bot)) +async def setup(bot): + await bot.add_cog(MCWhitelist(bot)) diff --git a/src/cogs/misc.py b/src/cogs/misc.py index 6891fbc..6b9d8e0 100644 --- a/src/cogs/misc.py +++ b/src/cogs/misc.py @@ -105,7 +105,7 @@ class Misc(commands.Cog): color=0xF5C518, url=f'https://www.imdb.com/title/{data["imdbID"]}/', ) - embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar_url) + embed.set_author(name=ctx.author.name, icon_url=ctx.author.display_avatar) embed.add_field(name="Type", value=data["Type"].title()) embed.add_field(name="Sjanger", value=data["Genre"]) embed.add_field(name="Spilletid", value=data["Runtime"]) @@ -233,8 +233,8 @@ class Misc(commands.Cog): if bruker.id == self.bot.user.id: match_percent = 100 - await ctx.author.avatar_url_as(format="png").save(fp=f"./src/assets/temp/{ctx.author.id}_raw.png") - await bruker.avatar_url_as(format="png").save(fp=f"./src/assets/temp/{bruker.id}_raw.png") + await ctx.author.display_avatar.save(fp=f"./src/assets/temp/{ctx.author.id}_raw.png") + await bruker.display_avatar.save(fp=f"./src/assets/temp/{bruker.id}_raw.png") invoker = Image.open(f"./src/assets/temp/{ctx.author.id}_raw.png").convert("RGBA") invoker = invoker.resize((389, 389), Image.ANTIALIAS) @@ -267,5 +267,5 @@ class Misc(commands.Cog): remove(f"./src/assets/temp/{ctx.author.id}_{bruker.id}_edit.png") -def setup(bot): - bot.add_cog(Misc(bot)) +async def setup(bot): + await bot.add_cog(Misc(bot)) diff --git a/src/cogs/mod.py b/src/cogs/mod.py index 40f2976..a43235c 100644 --- a/src/cogs/mod.py +++ b/src/cogs/mod.py @@ -27,7 +27,7 @@ class Mod(commands.Cog): user = discord.Object(id=brukerid) - for banentry in await ctx.guild.bans(): + async for banentry in ctx.guild.bans(limit=100): if banentry.user.id == user.id: embed = embed_templates.error_warning(ctx, text='Denne brukeren er allerede bannlyst fra serveren!') return await ctx.send(embed=embed) @@ -53,8 +53,8 @@ class Mod(commands.Cog): embed.description = f'`{begrunnelse}`' if user_object: user = user_object - embed.set_thumbnail(url=user.avatar_url) - embed.set_author(name=f'{user.name}#{user.discriminator}', icon_url=user.avatar_url) + embed.set_thumbnail(url=user.display_avatar) + embed.set_author(name=f'{user.name}#{user.discriminator}', icon_url=user.display_avatar) embed.add_field(name='Bruker', value=f'{user.name}#{user.discriminator}\n{user.mention}', inline=False) else: embed.add_field(name='Bruker', value=f'`{user.id}`', inline=False) @@ -63,5 +63,5 @@ class Mod(commands.Cog): await ctx.send(embed=embed) -def setup(bot): - bot.add_cog(Mod(bot)) +async def setup(bot): + await bot.add_cog(Mod(bot)) diff --git a/src/cogs/repeated_messages.py b/src/cogs/repeated_messages.py index ebb8488..5bb68a9 100644 --- a/src/cogs/repeated_messages.py +++ b/src/cogs/repeated_messages.py @@ -34,5 +34,5 @@ class RepeatedMessages(commands.Cog): ) -def setup(bot): - bot.add_cog(RepeatedMessages(bot)) +async def setup(bot): + await bot.add_cog(RepeatedMessages(bot)) diff --git a/src/cogs/utils/embed_templates.py b/src/cogs/utils/embed_templates.py index 64e743d..863d431 100644 --- a/src/cogs/utils/embed_templates.py +++ b/src/cogs/utils/embed_templates.py @@ -13,7 +13,7 @@ def default_footer(ctx: discord.ext.commands.Context, embed: discord.Embed) -> d (discord.Embed): The sent in embed + footer """ - return embed.set_footer(icon_url=ctx.author.avatar_url, text=f"{ctx.author.name}#{ctx.author.discriminator}") + return embed.set_footer(icon_url=ctx.author.avatar, text=f"{ctx.author.name}#{ctx.author.discriminator}") def error_warning(ctx: discord.ext.commands.Context, text: str) -> discord.Embed: diff --git a/src/run.py b/src/run.py index 9385e32..b5438f2 100644 --- a/src/run.py +++ b/src/run.py @@ -70,7 +70,7 @@ async def on_ready(): for file in listdir("./src/cogs"): if file.endswith(".py"): name = file[:-3] - bot.load_extension(f"cogs.{name}") + await bot.load_extension(f"cogs.{name}") print(f"Username: {bot.user.name}") print(f"ID: {bot.user.id}") @@ -81,4 +81,4 @@ async def on_ready(): ) -bot.run(config["bot"]["token"], bot=True, reconnect=True) +bot.run(config["bot"]["token"], reconnect=True) From 32de2b8de66cf17aa40a41afe7e6ce32e5f8c500 Mon Sep 17 00:00:00 2001 From: LBlend Date: Sun, 13 Nov 2022 22:38:16 +0100 Subject: [PATCH 02/54] add database credentials to example config --- src/config/config.yaml.example | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/config/config.yaml.example b/src/config/config.yaml.example index 563b588..e0d92d0 100644 --- a/src/config/config.yaml.example +++ b/src/config/config.yaml.example @@ -7,6 +7,13 @@ bot: activity: listening type: dnd +# Database +database: + host: + dbname: + username: + password: + # Api keys api: clarifai: From 8f2fa2ab2dc50a8f97e8e246a26082d5059ee2ca Mon Sep 17 00:00:00 2001 From: LBlend Date: Mon, 14 Nov 2022 02:39:24 +0100 Subject: [PATCH 03/54] closes #7 --- src/cogs/errors.py | 12 +++--------- src/logger.py | 14 ++++++++++++++ src/run.py | 4 +++- 3 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 src/logger.py diff --git a/src/cogs/errors.py b/src/cogs/errors.py index a3970b4..33883e8 100644 --- a/src/cogs/errors.py +++ b/src/cogs/errors.py @@ -19,7 +19,7 @@ class Errors(commands.Cog): Prints command execution metadata """ - print( + self.bot.logger.info( f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} | ' + f'{"❌ " if ctx.command_failed else "✔ "} {ctx.command} - ' + f'{ctx.author.name}#{ctx.author.discriminator} ({ctx.author.id}) | ' + @@ -92,14 +92,8 @@ class Errors(commands.Cog): embed = embed_templates.error_fatal(ctx, text='En ukjent feil oppstod!') await ctx.send(embed=embed) - # Print full exception to console - print(f'Ignoring exception in command {ctx.command}:', file=sys.stderr) - traceback.print_exception( - type(error), - error, - error.__traceback__, - file=sys.stderr - ) + # Log full exception to file + self.bot.logger.error(''.join(traceback.format_exception(type(error), error, error.__traceback__))) async def setup(bot): diff --git a/src/logger.py b/src/logger.py new file mode 100644 index 0000000..d0eb52a --- /dev/null +++ b/src/logger.py @@ -0,0 +1,14 @@ +import logging +import logging.handlers + + +class BotLogger: + def __init__(self): + logger = logging.getLogger('discord') + logger.setLevel(logging.INFO) + handler = logging.FileHandler(filename='discord.log', encoding='utf-8', mode='w') + formatter = logging.Formatter('[{asctime}] [{levelname:<8}] {name}: {message}', '%Y-%m-%d %H:%M:%S', style='{') + handler.setFormatter(formatter) + logger.addHandler(handler) + + self.logger = logger diff --git a/src/run.py b/src/run.py index b5438f2..57532d6 100644 --- a/src/run.py +++ b/src/run.py @@ -8,6 +8,7 @@ from os import listdir import locale from time import time import psycopg2 +from logger import BotLogger locale.setlocale(locale.LC_ALL, "") @@ -23,6 +24,7 @@ class Bot(commands.Bot): super().__init__( command_prefix=commands.when_mentioned_or(config["bot"]["prefix"]), case_insensitive=True, intents=intents ) + self.logger = BotLogger().logger self.prefix = config["bot"]["prefix"] self.presence = config["bot"].get("presence", {}) @@ -81,4 +83,4 @@ async def on_ready(): ) -bot.run(config["bot"]["token"], reconnect=True) +bot.run(config["bot"]["token"], reconnect=True, log_handler=None) From 38870d1dae6b3365f845c522fe8fa4bb35f0e4cd Mon Sep 17 00:00:00 2001 From: LBlend Date: Mon, 14 Nov 2022 03:51:38 +0100 Subject: [PATCH 04/54] refactor & add slash command support to run.py --- src/run.py | 94 ++++++++++++++++++++++++++---------------------------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/src/run.py b/src/run.py index 57532d6..75a6607 100644 --- a/src/run.py +++ b/src/run.py @@ -1,36 +1,25 @@ -from discord.ext import commands -import discord +from os import listdir +from time import time -from codecs import open +import discord +from discord.ext import commands + +import codecs +from logger import BotLogger +import psycopg2 import yaml -from os import listdir -import locale -from time import time -import psycopg2 -from logger import BotLogger - -locale.setlocale(locale.LC_ALL, "") - -with open("./src/config/config.yaml", "r", encoding="utf8") as f: +# Load config file +with codecs.open("./src/config/config.yaml", "r", encoding="utf8") as f: config = yaml.load(f, Loader=yaml.SafeLoader) -intents = discord.Intents.all() - class Bot(commands.Bot): - def __init__(self): - super().__init__( - command_prefix=commands.when_mentioned_or(config["bot"]["prefix"]), case_insensitive=True, intents=intents - ) - self.logger = BotLogger().logger - - self.prefix = config["bot"]["prefix"] - self.presence = config["bot"].get("presence", {}) - - self.api_keys = config.get("api", {}) + def __init__(self) -> None: + self.logger = BotLogger().logger # Initialize logger + # Connect to database db = config.get("database", {}) self.db_connection = psycopg2.connect( host=db["host"], @@ -39,47 +28,56 @@ class Bot(commands.Bot): password=db["password"], ) + # Fetch misc config values + self.presence = config["bot"].get("presence", {}) + self.api_keys = config.get("api", {}) self.emoji = config.get("emoji", {}) self.misc = config.get("misc", {}) + async def setup_hook(self) -> None: + # Load cogs + for file in listdir("./src/cogs"): + if file.endswith(".py"): + name = file[:-3] + await bot.load_extension(f"cogs.{name}") + # Sync slash commands to Discord + self.tree.copy_global_to(guild=discord.Object(id=412646636771344395)) # Temporary. Development purposes only. + await self.tree.sync() + + +# Create bot instance bot = Bot() -activities = {"playing": 0, "listening": 2, "watching": 3} -if bot.presence["activity"].lower() in activities: - activity_type = activities[bot.presence["activity"].lower()] -else: - activity_type = 0 - -status_types = { - "online": discord.Status.online, - "dnd": discord.Status.dnd, - "idle": discord.Status.idle, - "offline": discord.Status.offline, -} -if bot.presence["type"].lower() in status_types: - status_type = status_types[bot.presence["type"].lower()] -else: - status_type = discord.Status.online - - @bot.event async def on_ready(): if not hasattr(bot, "uptime"): bot.uptime = time() - for file in listdir("./src/cogs"): - if file.endswith(".py"): - name = file[:-3] - await bot.load_extension(f"cogs.{name}") - + # Print bot info print(f"Username: {bot.user.name}") print(f"ID: {bot.user.id}") print(f"Version: {discord.__version__}") print("." * 50 + "\n") + + # Set initial presence + # Presence status + status_types = { + "online": discord.Status.online, + "dnd": discord.Status.dnd, + "idle": discord.Status.idle, + "offline": discord.Status.offline, + } + status_type = status_types.get(bot.presence["status"].lower(), discord.Status.online) + + # Presence actitivity + activities = {"playing": 0, "listening": 2, "watching": 3} + activity_type = activities.get(bot.presence["activity"].lower(), 0) + await bot.change_presence( - activity=discord.Activity(type=activity_type, name=bot.presence["message"]), status=status_type + activity=discord.Activity(type=activity_type, name=bot.presence["message"]), + status=status_type ) From ca8d768f2297eb44f540918e28f6711f2e8745e8 Mon Sep 17 00:00:00 2001 From: LBlend Date: Mon, 14 Nov 2022 03:52:25 +0100 Subject: [PATCH 05/54] make embed templates compatible with discord interactions --- src/cogs/utils/embed_templates.py | 41 ++++++++++++++++++------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/cogs/utils/embed_templates.py b/src/cogs/utils/embed_templates.py index 863d431..21afa99 100644 --- a/src/cogs/utils/embed_templates.py +++ b/src/cogs/utils/embed_templates.py @@ -1,70 +1,77 @@ import discord -def default_footer(ctx: discord.ext.commands.Context, embed: discord.Embed) -> discord.Embed: +def default_footer(interaction: discord.Interaction, embed: discord.Embed) -> discord.Embed: """ Sets a footer containing the command invoker's name, discriminator and avatar + Parameters ----------- - ctx (discord.ext.commands.Context): The current Discord context + interaction (discord.Interaction): Discord interaction context embed (discord.Embed): An embed object to add the footer to + Returns ----------- - (discord.Embed): The sent in embed + footer + (discord.Embed): The passed in embed + footer """ - - return embed.set_footer(icon_url=ctx.author.avatar, text=f"{ctx.author.name}#{ctx.author.discriminator}") + return embed.set_footer( + icon_url=interaction.user.avatar, + text=f"{interaction.user.name}#{interaction.user.discriminator}" + ) -def error_warning(ctx: discord.ext.commands.Context, text: str) -> discord.Embed: +def error_warning(interaction: discord.Interaction, text: str) -> discord.Embed: """ Creates an embed with a specified error message based on a warning template + Parameters ----------- - ctx (discord.ext.commands.Context): The current Discord context + interaction (discord.Interaction): Discord interaction context text (str): The error message + Returns ----------- (discord.Embed): An embed object based on the template with the specified text """ - embed = discord.Embed(color=discord.Color.gold(), description=f"⚠️ {text}") - default_footer(ctx, embed) + default_footer(interaction, embed) return embed -def error_fatal(ctx: discord.ext.commands.Context, text: str) -> discord.Embed: +def error_fatal(interaction: discord.Interaction, text: str) -> discord.Embed: """ Creates an embed with a specified error message based on a warning template + Parameters ----------- - ctx (discord.ext.commands.Context): The current Discord context + interaction (discord.Interaction): Discord interaction context text (str): The error message + Returns ----------- (discord.Embed): An embed object based on the template with the specified text """ - embed = discord.Embed(color=discord.Color.red(), description=f"❌ {text}") - default_footer(ctx, embed) + default_footer(interaction, embed) return embed -def success(ctx: discord.ext.commands.Context, text: str) -> discord.Embed: +def success(interaction: discord.Interaction, text: str) -> discord.Embed: """ Creates an embed with a specified message using a template signifying success. + Parameters ----------- - ctx (discord.ext.commands.Context): The current Discord context + interaction (discord.Interaction): Discord interaction context text (str): The message + Returns ----------- discord.Embed: An embed object based on the template with the specified text """ - embed = discord.Embed(color=discord.Color.green(), description=f"✅ {text}") - default_footer(ctx, embed) + default_footer(interaction, embed) return embed From c0ebacf1edb349b9e6af693fd0883c8ba4215c9c Mon Sep 17 00:00:00 2001 From: LBlend Date: Mon, 14 Nov 2022 04:02:43 +0100 Subject: [PATCH 06/54] reorder imports --- src/run.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/run.py b/src/run.py index 75a6607..3749d3c 100644 --- a/src/run.py +++ b/src/run.py @@ -1,14 +1,14 @@ +import codecs from os import listdir from time import time import discord from discord.ext import commands - -import codecs -from logger import BotLogger import psycopg2 import yaml +from logger import BotLogger + # Load config file with codecs.open("./src/config/config.yaml", "r", encoding="utf8") as f: From 33342c5257ac9a5847fa38a1498e2dcb8ae93d04 Mon Sep 17 00:00:00 2001 From: LBlend Date: Mon, 14 Nov 2022 04:06:29 +0100 Subject: [PATCH 07/54] accept discord.Member type to get_user_color --- src/cogs/utils/discord_utils.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cogs/utils/discord_utils.py b/src/cogs/utils/discord_utils.py index e76ccde..934bd4d 100644 --- a/src/cogs/utils/discord_utils.py +++ b/src/cogs/utils/discord_utils.py @@ -1,17 +1,18 @@ import discord -def get_user_color(user: discord.User) -> discord.Color: +def get_user_color(user: discord.User | discord.Member) -> discord.Color: """ Returns the user's top role color + Parameters ----------- - user (discord.User): A discord user or member object + user (discord.User|discord.Member): A discord user or member object + Returns ----------- (discord.Color): The user's displayed color """ - if str(user.color) != '#000000': return user.color From f5cbca1a5b76ed7390c1a3eaa93f7bd850da6511 Mon Sep 17 00:00:00 2001 From: LBlend Date: Mon, 14 Nov 2022 04:14:55 +0100 Subject: [PATCH 08/54] readd super Bot class call --- src/run.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/run.py b/src/run.py index 3749d3c..33b1187 100644 --- a/src/run.py +++ b/src/run.py @@ -17,6 +17,12 @@ with codecs.open("./src/config/config.yaml", "r", encoding="utf8") as f: class Bot(commands.Bot): def __init__(self) -> None: + super().__init__( + command_prefix=commands.when_mentioned_or(config["bot"]["prefix"]), + case_insensitive=True, + intents=discord.Intents.all() + ) + self.logger = BotLogger().logger # Initialize logger # Connect to database From 0604912016bffa340c3de73bf5590872a2ffce9f Mon Sep 17 00:00:00 2001 From: LBlend Date: Mon, 14 Nov 2022 04:15:19 +0100 Subject: [PATCH 09/54] sync only to dev guild, for now --- src/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/run.py b/src/run.py index 33b1187..333c532 100644 --- a/src/run.py +++ b/src/run.py @@ -49,7 +49,7 @@ class Bot(commands.Bot): # Sync slash commands to Discord self.tree.copy_global_to(guild=discord.Object(id=412646636771344395)) # Temporary. Development purposes only. - await self.tree.sync() + await self.tree.sync(guild=discord.Object(id=412646636771344395)) # Temporary. Development purposes only. # Create bot instance From 31e4397778309910b12a731fa21fccb6de9c6f54 Mon Sep 17 00:00:00 2001 From: LBlend Date: Tue, 15 Nov 2022 00:38:29 +0100 Subject: [PATCH 10/54] convert birthday cog to slash commands & refactor --- src/cogs/birthday.py | 172 +++++++++++++++++++++++++++---------------- 1 file changed, 107 insertions(+), 65 deletions(-) diff --git a/src/cogs/birthday.py b/src/cogs/birthday.py index 30ade58..8cd17a5 100644 --- a/src/cogs/birthday.py +++ b/src/cogs/birthday.py @@ -1,13 +1,12 @@ -from discord.ext import commands -import discord - -import psycopg2 -import aiocron -import asyncio from datetime import datetime -from dateutil.relativedelta import relativedelta -from cogs.utils import embed_templates, discord_utils +from dateutil.relativedelta import relativedelta +import discord +from discord import app_commands +from discord.ext import commands +import psycopg2 + +from cogs.utils import discord_utils, embed_templates class Birthday(commands.Cog): @@ -16,6 +15,7 @@ class Birthday(commands.Cog): self.cursor = self.bot.db_connection.cursor() def init_db(self): + """Create the necessary tables for the birthday cog to work.""" self.cursor.execute( """ CREATE TABLE IF NOT EXISTS birthdays ( @@ -26,11 +26,16 @@ class Birthday(commands.Cog): ) self.bot.db_connection.commit() - def _fetch_user_birthday(self, user_id): + def __fetch_user_birthday(self, user_id: int) -> datetime | None: + """Fetch the birthday of a user from the database""" self.cursor.execute("SELECT birthday FROM birthdays WHERE discord_id = (%s)", (user_id,)) - return self.cursor.fetchone() + result = self.cursor.fetchone() + if result: + birthday = result[0] + return datetime(birthday.year, birthday.month, birthday.day) - def _fetch_user_next_birthday(self, user_id): + def __fetch_user_next_birthday(self, user_id: int) -> datetime: + """Get date of next birthday of a user""" self.cursor.execute( """ SELECT *, CAST(birthday + ((EXTRACT(YEAR FROM AGE(birthday)) + 1) * interval '1' YEAR) AS DATE) AS next_birthday @@ -39,9 +44,15 @@ class Birthday(commands.Cog): """, (user_id,), ) - return self.cursor.fetchone() + results = self.cursor.fetchone() + if results: + birthday = results[2] + return datetime(birthday.year, birthday.month, birthday.day) + else: + return datetime.now() # This will never happen unless a database error occurs. Keeping the type checker happy - def _fetch_next_birthdays(self): + def __fetch_next_birthdays(self) -> list[tuple[int, datetime, datetime]]: + """Fetch the 5 first future birthdays of all users in the database""" self.cursor.execute( """ SELECT *, CAST(birthday + ((EXTRACT(YEAR FROM AGE(birthday)) + 1) * interval '1' YEAR) AS DATE) AS next_birthday @@ -50,20 +61,36 @@ class Birthday(commands.Cog): LIMIT 5 """ ) - return self.cursor.fetchall() + results = self.cursor.fetchall() - def _set_user_birthday(self, user_id, birthday): + # Convert date objects to datetime objects + for result in results: + _, birthday, next_birthday = result + birthday = datetime(birthday.year, birthday.month, birthday.day) + next_birthday = datetime(next_birthday.year, next_birthday.month, next_birthday.day) + + return results + + def __set_user_birthday(self, user_id: int, birthday: datetime) -> None: + """Iset the birthday of a user in the database. If it already exists, update it.""" try: - self.cursor.execute("INSERT INTO birthdays (discord_id, birthday) VALUES (%s, %s)", (user_id, birthday)) + self.cursor.execute( + """ + INSERT INTO birthdays (discord_id, birthday) + VALUES (%s, %s) + """, (user_id, birthday)) self.bot.db_connection.commit() except psycopg2.errors.UniqueViolation: self.bot.db_connection.rollback() self.cursor.execute("UPDATE birthdays SET birthday = %s WHERE discord_id = %s", (birthday, user_id)) self.bot.db_connection.commit() + ''' @aiocron.crontab("0 0 * * *") - async def _check_birthdays(self): - await asyncio.sleep(1) + async def __check_birthdays(self): + """Check if it's someone's birthday every day at midgnight and send a greeting if it is.""" + await asyncio.sleep(1) # Prevent sending message before date has really changed + self.cursor.execute( """ SELECT * @@ -81,92 +108,107 @@ class Birthday(commands.Cog): user = self.bot.get_user(birthday[0]) if user: await channel.send(f"Gratulrer med dagen {user.mention}!") + ''' - @commands.cooldown(1, 5, commands.BucketType.user) - @commands.command() - async def settbursdag(self, ctx, date): - + @app_commands.checks.cooldown(1, 5) + @app_commands.command() + async def settbursdag(self, interaction: discord.Interaction, dag: int, måned: int, år: int): + """Lagrer din bursdag i databasen""" try: - date = datetime.strptime(date, "%d.%m.%Y") + date = datetime(år, måned, dag) except ValueError: - return await ctx.send(embed=embed_templates.error_fatal(ctx, text="Feil format, gjøk!")) + return await interaction.response.send_message( + embed=embed_templates.error_fatal(interaction, text="ikke en gyldig dato, gjøk!"), + ephemeral=True + ) if date.year < 1970: - return await ctx.send( - embed=embed_templates.error_fatal(ctx, text="Ok boomer, men nei. Hvorfor? Unix timestamps. Google it") + return await interaction.response.send_message( + embed=embed_templates.error_fatal(interaction, text="Ok boomer, men nei. Hvorfor? Unix timestamps. Google it"), + ephemeral=True ) - self._set_user_birthday(ctx.author.id, date) + if date > datetime.now(): + return await interaction.response.send_message( + embed=embed_templates.error_fatal(interaction, text="Du kan ikke sette en fremtidig dato!"), + ephemeral=True + ) - embed = embed_templates.success(ctx, text=f"Bursdag satt til ") - await ctx.send(embed=embed) + self.__set_user_birthday(interaction.user.id, date) - @commands.cooldown(1, 5, commands.BucketType.user) - @commands.command() - async def fjernbursdag(self, ctx): + embed = embed_templates.success(interaction, text=f"Bursdag satt til {discord.utils.format_dt(date, style='D')}") + await interaction.response.send_message(embed=embed) - self.cursor.execute("DELETE FROM birthdays WHERE discord_id = (%s)", (ctx.author.id,)) + @app_commands.checks.cooldown(1, 5) + @app_commands.command() + async def fjernbursdag(self, interaction: discord.Interaction): + """Fjerner bursdagen din fra databasen""" + self.cursor.execute("DELETE FROM birthdays WHERE discord_id = (%s)", (interaction.user.id,)) self.bot.db_connection.commit() - embed = embed_templates.success(ctx, text="Bursdag fjernet") - await ctx.send(embed=embed) - - @commands.cooldown(1, 5, commands.BucketType.user) - @commands.command() - async def bursdag(self, ctx, *, bruker: discord.Member = None): + embed = embed_templates.success(interaction, text="Bursdag fjernet") + await interaction.response.send_message(embed=embed) + @app_commands.checks.cooldown(1, 5) + @app_commands.command() + async def bursdag(self, interaction: discord.Interaction, bruker: discord.Member | discord.User | None = None): + """Viser bursdagen til en bruker. Om ingen er gitt, vises din egen bursdag.""" if not bruker: - bruker = ctx.author + bruker = interaction.user - user = self._fetch_user_birthday(bruker.id) + user = self.__fetch_user_birthday(bruker.id) if not user: - return await ctx.send( - embed=embed_templates.error_fatal(ctx, text="Brukeren har ikke registrert bursdagen sin") + return await interaction.response.send_message( + embed=embed_templates.error_fatal(interaction, text="Brukeren har ikke registrert bursdagen sin") ) - birthday = datetime(user[0].year, user[0].month, user[0].day) + # Current age information + birthday = datetime(user.year, user.month, user.day) days_old = (datetime.now() - birthday).days years_old = relativedelta(datetime.now(), birthday).years - # Next birthday - birthday_current_year = birthday.replace(year=datetime.now().year) - if (datetime.now() - birthday_current_year).days < 0: - next_birthday = birthday_current_year - else: - next_birthday = birthday_current_year.replace(year=datetime.now().year + 1) + # Next birthday information + next_birthday = self.__fetch_user_next_birthday(bruker.id) next_birthday_days = (next_birthday - datetime.now()).days embed = discord.Embed(description=bruker.mention, color=discord_utils.get_user_color(bruker)) embed.set_thumbnail(url=bruker.display_avatar) embed.set_author(name=bruker.name, icon_url=bruker.display_avatar) - embed.add_field(name="Bursdag", value=f"") + embed.add_field(name="Bursdag", value=discord.utils.format_dt(birthday, style="D")) embed.add_field(name="Hvor gammel?", value=f"{years_old} år\n({days_old} dager)", inline=False) embed.add_field(name="Neste bursdag om", value=f"{next_birthday_days} dager", inline=False) - embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) + embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) - @commands.command() - async def kommendebursdager(self, ctx): - - next_birthdays = self._fetch_next_birthdays() + @app_commands.guild_only() + @app_commands.checks.cooldown(1, 5) + @app_commands.command() + async def kommendebursdager(self, interaction: discord.Interaction): + """Se opptil de 5 neste bursdagene i UiO Gaming""" + next_birthdays = self.__fetch_next_birthdays() if not next_birthdays: - return await ctx.send(embed=embed_templates.error_fatal(ctx, text="Ingen bursdager")) + return await interaction.response.send_message(embed=embed_templates.error_fatal(interaction, text="Ingen bursdager")) birthday_string = "" for user in next_birthdays: - discord_user = ctx.guild.get_member(user[0]) - if not discord_user: + user_id, _, next_birthday = user + try: + discord_user = await interaction.guild.fetch_member(user_id) # Guild should always be available because of guild_only + except (discord.NotFound, discord.HTTPException): continue - next_birthday = datetime(user[2].year, user[2].month, user[2].day) - next_birthday_days = (next_birthday - datetime.now()).days - birthday_string += f"{discord_user.name}#{discord_user.discriminator} - ({next_birthday_days} dager)\n" + timestamp = discord.utils.format_dt(next_birthday, style="D") + relative_timestamp = discord.utils.format_dt(next_birthday, style="R") + birthday_string += f"{discord_user.name}#{discord_user.discriminator} - {timestamp} ({relative_timestamp})\n" + + if not birthday_string: + return await interaction.response.send_message(embed=embed_templates.error_fatal(interaction, text="Ingen bursdager")) embed = discord.Embed(title="Kommende bursdager", description=birthday_string) - embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) + embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) async def setup(bot): From ae9853d522a28262bff33fe75d75bd41f43aeef3 Mon Sep 17 00:00:00 2001 From: LBlend Date: Tue, 15 Nov 2022 00:42:57 +0100 Subject: [PATCH 11/54] refactor repeated messages cog --- src/cogs/repeated_messages.py | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/cogs/repeated_messages.py b/src/cogs/repeated_messages.py index 5bb68a9..397d743 100644 --- a/src/cogs/repeated_messages.py +++ b/src/cogs/repeated_messages.py @@ -1,36 +1,32 @@ -from discord.ext import commands - import aiocron +from discord.ext import commands class RepeatedMessages(commands.Cog): def __init__(self, bot): self.bot = bot + uio_gaming = self.bot.get_guild(747542543750660178) + self.uiog_general = uio_gaming.get_channel(747542544291987597) + @aiocron.crontab("0 0 * * 5") async def fredag(): - guild = self.bot.get_guild(747542543750660178) - channel = guild.get_channel(747542544291987597) - await channel.send( - "NU ÄR DET FREDAG!\n" - + "https://cdn.discordapp.com/attachments/750052141346979850/851216786259181578/video0.mp4" + await self.uiog_general.send( + "NU ÄR DET FREDAG!\n" + \ + "https://cdn.discordapp.com/attachments/750052141346979850/851216786259181578/video0.mp4" ) @aiocron.crontab("0 0 * * 1") async def mandag(): - guild = self.bot.get_guild(747542543750660178) - channel = guild.get_channel(747542544291987597) - await channel.send( - "ENDELIG MANDAG!\n\n" - + "https://cdn.discordapp.com/attachments/678396498089738250/862853827278929940/hvorfor_de_rike_br_spises.mp4" + await self.uiog_general.send( + "ENDELIG MANDAG!\n\n" + \ + "https://cdn.discordapp.com/attachments/678396498089738250/862853827278929940/hvorfor_de_rike_br_spises.mp4" ) @aiocron.crontab("0 0 1 1 *") async def new_year(): - guild = self.bot.get_guild(747542543750660178) - channel = guild.get_channel(747542544291987597) - await channel.send( - "GODT NYTTÅR!\n\nNå, meld dere inn i UiO Gaming igjen <:ThisIsAThreat:874998234072903710>\nhttps://youtu.be/5qXkEvHrZ-0" + await self.uiog_general.send( + "GODT NYTTÅR!\n\nNå, meld dere inn i UiO Gaming igjen <:ThisIsAThreat:874998234072903710>" ) From c4be795048a8d8bf371c7ed5be1b91654aba1478 Mon Sep 17 00:00:00 2001 From: LBlend Date: Tue, 15 Nov 2022 01:07:13 +0100 Subject: [PATCH 12/54] always fetch non-cached version of channel --- src/cogs/repeated_messages.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/cogs/repeated_messages.py b/src/cogs/repeated_messages.py index 397d743..84a5dbb 100644 --- a/src/cogs/repeated_messages.py +++ b/src/cogs/repeated_messages.py @@ -6,26 +6,28 @@ class RepeatedMessages(commands.Cog): def __init__(self, bot): self.bot = bot - uio_gaming = self.bot.get_guild(747542543750660178) - self.uiog_general = uio_gaming.get_channel(747542544291987597) + self.uiog_server = self.bot.get_guild(747542543750660178) @aiocron.crontab("0 0 * * 5") async def fredag(): - await self.uiog_general.send( + channel = await self.uiog_server.fetch_channel(747542544291987597) + await channel.send( "NU ÄR DET FREDAG!\n" + \ "https://cdn.discordapp.com/attachments/750052141346979850/851216786259181578/video0.mp4" ) @aiocron.crontab("0 0 * * 1") async def mandag(): - await self.uiog_general.send( + channel = await self.uiog_server.fetch_channel(747542544291987597) + await channel.send( "ENDELIG MANDAG!\n\n" + \ "https://cdn.discordapp.com/attachments/678396498089738250/862853827278929940/hvorfor_de_rike_br_spises.mp4" ) @aiocron.crontab("0 0 1 1 *") async def new_year(): - await self.uiog_general.send( + channel = await self.uiog_server.fetch_channel(747542544291987597) + await channel.send( "GODT NYTTÅR!\n\nNå, meld dere inn i UiO Gaming igjen <:ThisIsAThreat:874998234072903710>" ) From 10ed39edd42e52e488548eebd61fc352b4c341e5 Mon Sep 17 00:00:00 2001 From: LBlend Date: Tue, 15 Nov 2022 01:18:08 +0100 Subject: [PATCH 13/54] remove mod cog apparently the built in ban command on discord allows you to ban users that are not in the server --- src/cogs/mod.py | 67 ------------------------------------------------- 1 file changed, 67 deletions(-) delete mode 100644 src/cogs/mod.py diff --git a/src/cogs/mod.py b/src/cogs/mod.py deleted file mode 100644 index a43235c..0000000 --- a/src/cogs/mod.py +++ /dev/null @@ -1,67 +0,0 @@ -from discord.ext import commands -import discord - -from datetime import datetime - -from cogs.utils import embed_templates - - -class Mod(commands.Cog): - def __init__(self, bot) -> None: - self.bot = bot - - @commands.bot_has_permissions(ban_members=True) - @commands.has_permissions(ban_members=True) - @commands.guild_only() - @commands.command() - async def hackban(self, ctx, brukerid, *, begrunnelse=None): - """ - Bannlys en bruker før de i det hele tatt har blitt med i serveren - """ - - try: - brukerid = int(brukerid) - except ValueError: - embed = embed_templates.error_warning(ctx, text='Du må gi meg en bruker-id') - return await ctx.send(embed=embed) - - user = discord.Object(id=brukerid) - - async for banentry in ctx.guild.bans(limit=100): - if banentry.user.id == user.id: - embed = embed_templates.error_warning(ctx, text='Denne brukeren er allerede bannlyst fra serveren!') - return await ctx.send(embed=embed) - - if begrunnelse is None: - begrunnelse = 'Ingen grunn angitt' - - date = datetime.now().strftime('%d. %b. %Y - %H:%M') - - try: - await ctx.guild.ban( - user, - reason=f'{date} | {ctx.author.name}#{ctx.author.discriminator} ({ctx.author.id}): {begrunnelse}' - ) - except discord.errors.NotFound: - embed = embed_templates.error_warning(ctx, text='Bruker finnes ikke!') - return await ctx.send(embed=embed) - - user_object = self.bot.get_user(brukerid) - - embed = discord.Embed(color=discord.Color.red(), title='🔨 Bruker utestengt!') - if begrunnelse != 'Ingen grunn angitt': - embed.description = f'`{begrunnelse}`' - if user_object: - user = user_object - embed.set_thumbnail(url=user.display_avatar) - embed.set_author(name=f'{user.name}#{user.discriminator}', icon_url=user.display_avatar) - embed.add_field(name='Bruker', value=f'{user.name}#{user.discriminator}\n{user.mention}', inline=False) - else: - embed.add_field(name='Bruker', value=f'`{user.id}`', inline=False) - embed.add_field(name='Av', value=f'{ctx.author.name}#{ctx.author.discriminator}\n{ctx.author.mention}') - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) - - -async def setup(bot): - await bot.add_cog(Mod(bot)) From daba2269a6afb28dd3d4291c39e43d97f233dea5 Mon Sep 17 00:00:00 2001 From: LBlend Date: Tue, 15 Nov 2022 01:30:08 +0100 Subject: [PATCH 14/54] convert mc whitelist to slash commands & refactor --- src/cogs/mc_whitelist.py | 43 +++++++++++++++++++++------------- src/config/config.yaml.example | 4 ++++ src/run.py | 1 + 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/cogs/mc_whitelist.py b/src/cogs/mc_whitelist.py index e5e57bb..702db34 100644 --- a/src/cogs/mc_whitelist.py +++ b/src/cogs/mc_whitelist.py @@ -1,8 +1,8 @@ +import discord +from discord import app_commands from discord.ext import commands - -import requests -import json from mcrcon import MCRcon +import requests from cogs.utils import embed_templates @@ -14,6 +14,7 @@ class MCWhitelist(commands.Cog): self.init_db() def init_db(self): + """Create the necessary tables for the mc_whitelist cog to work.""" self.cursor.execute( """ CREATE TABLE IF NOT EXISTS mc_whitelist ( @@ -24,46 +25,56 @@ class MCWhitelist(commands.Cog): ) self.bot.db_connection.commit() - @commands.cooldown(1, 10, commands.BucketType.user) - @commands.command() - async def whitelist(self, ctx, minecraftbrukernavn): + @app_commands.checks.cooldown(1, 5) + @app_commands.command() + async def whitelist(self, interaction: discord.Interaction, minecraftbrukernavn: str): + """Whitelist Minecraftbrukeren din på serveren vår""" - # fetch minecraft uuid from api + # Fetch minecraft uuid from api data = requests.get(f"https://api.mojang.com/users/profiles/minecraft/{minecraftbrukernavn}") if data.status_code != 200: - return await ctx.send( - embed=embed_templates.error_fatal(ctx, text=f"Brukeren `{minecraftbrukernavn}` finnes ikk på minecraft") + return await interaction.response.send_message( + embed=embed_templates.error_fatal(interaction, text=f"Brukeren `{minecraftbrukernavn}` finnes ikke på minecraft"), + ephemeral=True ) data = data.json() # check if the discord user or minecraft user is in the db self.cursor.execute( - "SELECT * FROM mc_whitelist WHERE minecraft_id = %s OR discord_id = %s", (data["id"], ctx.author.id) + """ + SELECT * + FROM mc_whitelist + WHERE minecraft_id = %s OR discord_id = %s + """, (data["id"], interaction.user.id) ) if self.cursor.fetchone(): - return await ctx.send( + return await interaction.response.send_message( embed=embed_templates.error_fatal( - ctx, text="Du har allerede whitelisted en bruker eller så er brukeren du oppga whitelisted" + interaction, + text="Du har allerede whitelisted en bruker eller så er brukeren du oppga whitelisted" ) ) - with MCRcon(host="192.168.0.24", password="hallaballayeet", port=25575) as mcr: + # Whitelist user on minecraft server + # Unfortunately, this requires an active connection to the server, with correct credentials + with MCRcon(host="127.0.0.1", password=self.bot.mc_rcon_password, port=25575) as mcr: mcr.command(f"whitelist add {data['name']}") mcr.command("whitelist reload") + # Add user to db self.cursor.execute( """ INSERT INTO mc_whitelist (discord_id, minecraft_id) VALUES (%s, %s) """, - (ctx.author.id, data["id"]), + (interaction.user.id, data["id"]), ) self.bot.db_connection.commit() - await ctx.send( + await interaction.response.send_message( embed=embed_templates.success( - ctx, text=f"`{data['name']}` er nå tilknyttet din discordbruker og whitelisted!" + interaction, text=f"`{data['name']}` er nå tilknyttet din discordbruker og whitelisted!" ) ) diff --git a/src/config/config.yaml.example b/src/config/config.yaml.example index e0d92d0..3c6870f 100644 --- a/src/config/config.yaml.example +++ b/src/config/config.yaml.example @@ -14,6 +14,10 @@ database: username: password: +# Minecraft server +minecraft: + rcon_password: + # Api keys api: clarifai: diff --git a/src/run.py b/src/run.py index 333c532..b71ecae 100644 --- a/src/run.py +++ b/src/run.py @@ -35,6 +35,7 @@ class Bot(commands.Bot): ) # Fetch misc config values + self.mc_rcon_password = config["minecraft"]["rcon_password"] self.presence = config["bot"].get("presence", {}) self.api_keys = config.get("api", {}) self.emoji = config.get("emoji", {}) From 3fec1c0974fda1bb1c1d4a7cd8331e826af6fbfe Mon Sep 17 00:00:00 2001 From: LBlend Date: Tue, 15 Nov 2022 01:31:23 +0100 Subject: [PATCH 15/54] add request timeout --- src/cogs/mc_whitelist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cogs/mc_whitelist.py b/src/cogs/mc_whitelist.py index 702db34..f79f0b8 100644 --- a/src/cogs/mc_whitelist.py +++ b/src/cogs/mc_whitelist.py @@ -31,7 +31,7 @@ class MCWhitelist(commands.Cog): """Whitelist Minecraftbrukeren din på serveren vår""" # Fetch minecraft uuid from api - data = requests.get(f"https://api.mojang.com/users/profiles/minecraft/{minecraftbrukernavn}") + data = requests.get(f"https://api.mojang.com/users/profiles/minecraft/{minecraftbrukernavn}", timeout=10).json() if data.status_code != 200: return await interaction.response.send_message( embed=embed_templates.error_fatal(interaction, text=f"Brukeren `{minecraftbrukernavn}` finnes ikke på minecraft"), From 7a8e8818228f2590a675db695be2a00b19c242b8 Mon Sep 17 00:00:00 2001 From: LBlend Date: Tue, 15 Nov 2022 02:18:40 +0100 Subject: [PATCH 16/54] refactor & fix funreplies cog --- src/cogs/funreplies.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/cogs/funreplies.py b/src/cogs/funreplies.py index 81a460a..f3531f0 100644 --- a/src/cogs/funreplies.py +++ b/src/cogs/funreplies.py @@ -1,31 +1,30 @@ -from discord.ext import commands - import re +import discord +from discord.ext import commands + class FunReplies(commands.Cog): def __init__(self, bot): self.bot = bot - async def react(self, message): + async def reply_to_triggers(self, message: discord.Message): if message.author.bot: return - if re.findall(r'(^|\W)borgerlønn(\W|$)', message.clean_content.lower()) != []: + message_content = message.content.lower() + + if re.search(r'(^|\W)borgerlønn(\W|$)', message_content, flags=re.IGNORECASE): await message.channel.send('<@267415183931080715> DE SNAKKER OM BORGERLØNN') - elif re.findall(r'(^|\W)olof palme(\W|$)', message.clean_content.lower()) != []: + elif re.search(r'(^|\W)olof palme(\W|$)', message_content, flags=re.IGNORECASE): await message.channel.send('Jeg vet hvem som drepte Olof Palme 👀') - # The Ultimate spaghetti - elif ( - re.match(r'(\W|\s)*lesgo{1}(\s|\W)*', message.clean_content.lower()) or - message.clean_content.lower() == 'lesgo' - ): + elif re.search(r'(\W|\s)*le+s+go+(\s|\W)*', message_content): await message.channel.send('https://cdn.discordapp.com/attachments/750052141346979850/' + '824764933513281596/3xhpwbakz2361.png') async def setup(bot): - bot.add_listener(FunReplies(bot).react, 'on_message') + bot.add_listener(FunReplies(bot).reply_to_triggers, 'on_message') await bot.add_cog(FunReplies(bot)) From fcef57bbe5e4f1702834e460f9850f1b029d559d Mon Sep 17 00:00:00 2001 From: LBlend Date: Tue, 15 Nov 2022 02:31:44 +0100 Subject: [PATCH 17/54] =?UTF-8?q?add=20'folk=20p=C3=A5=20neuf'=20trigger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/cogs/funreplies.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cogs/funreplies.py b/src/cogs/funreplies.py index f3531f0..9475689 100644 --- a/src/cogs/funreplies.py +++ b/src/cogs/funreplies.py @@ -24,6 +24,9 @@ class FunReplies(commands.Cog): await message.channel.send('https://cdn.discordapp.com/attachments/750052141346979850/' + '824764933513281596/3xhpwbakz2361.png') + elif re.search(r'^((er det|hvor)\s+)*(noen|folk|mange)\s+på\s+(rommet|neuf)\?*$', message_content, flags=re.IGNORECASE): + await message.channel.send('Ja, <@554977854971183125> bor der') + async def setup(bot): bot.add_listener(FunReplies(bot).reply_to_triggers, 'on_message') From 367695815b87ae173d076c6cc57d53e5f7f3f15f Mon Sep 17 00:00:00 2001 From: LBlend Date: Tue, 15 Nov 2022 02:32:03 +0100 Subject: [PATCH 18/54] add yeet trigger --- src/cogs/funreplies.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cogs/funreplies.py b/src/cogs/funreplies.py index 9475689..e0820c9 100644 --- a/src/cogs/funreplies.py +++ b/src/cogs/funreplies.py @@ -27,6 +27,9 @@ class FunReplies(commands.Cog): elif re.search(r'^((er det|hvor)\s+)*(noen|folk|mange)\s+på\s+(rommet|neuf)\?*$', message_content, flags=re.IGNORECASE): await message.channel.send('Ja, <@554977854971183125> bor der') + elif re.search(r'(^|\W)+ye+et($|\W)+', message_content, flags=re.IGNORECASE): + await message.channel.send('<:Nei:826593267642662912>') + async def setup(bot): bot.add_listener(FunReplies(bot).reply_to_triggers, 'on_message') From d724b673d02c10f23103de0676ab6a8d3b591f6c Mon Sep 17 00:00:00 2001 From: LBlend Date: Tue, 15 Nov 2022 02:33:37 +0100 Subject: [PATCH 19/54] rename fun commands --- src/cogs/fun.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cogs/fun.py b/src/cogs/fun.py index ed2cdf9..023a0b5 100644 --- a/src/cogs/fun.py +++ b/src/cogs/fun.py @@ -12,17 +12,17 @@ class Fun(commands.Cog): @commands.cooldown(1, 2, commands.BucketType.guild) @commands.command() - async def informatikkuio(self, ctx): + async def ifi(self, ctx): await ctx.send('https://i.imgur.com/ypyK1mi.jpg') @commands.cooldown(1, 2, commands.BucketType.guild) @commands.command() - async def informatikkuio2(self, ctx): + async def ifi2(self, ctx): await ctx.send('https://i.imgur.com/ZqgZEEA.jpg') @commands.cooldown(1, 2, commands.BucketType.guild) @commands.command() - async def informatikkuio3(self, ctx): + async def ifi3(self, ctx): await ctx.send('https://i.imgur.com/Gx9DQE5.jpg') @commands.cooldown(1, 2, commands.BucketType.guild) From ec99d7362a6a6f14d72388a0c55e66db91307d2e Mon Sep 17 00:00:00 2001 From: LBlend Date: Tue, 15 Nov 2022 02:43:17 +0100 Subject: [PATCH 20/54] add docstring --- src/cogs/funreplies.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cogs/funreplies.py b/src/cogs/funreplies.py index e0820c9..ac35261 100644 --- a/src/cogs/funreplies.py +++ b/src/cogs/funreplies.py @@ -9,6 +9,7 @@ class FunReplies(commands.Cog): self.bot = bot async def reply_to_triggers(self, message: discord.Message): + """Replies to messages that trigger certain key words/phrases""" if message.author.bot: return From 8e69bad1c86398fc82f6fc19d9be494ecc347a57 Mon Sep 17 00:00:00 2001 From: LBlend Date: Tue, 15 Nov 2022 03:57:50 +0100 Subject: [PATCH 21/54] allow get_color to take objects as arguments --- src/cogs/utils/discord_utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cogs/utils/discord_utils.py b/src/cogs/utils/discord_utils.py index 934bd4d..62efec0 100644 --- a/src/cogs/utils/discord_utils.py +++ b/src/cogs/utils/discord_utils.py @@ -1,19 +1,19 @@ import discord -def get_user_color(user: discord.User | discord.Member) -> discord.Color: +def get_color(discord_object: discord.User | discord.Member | discord.Role) -> discord.Color: """ Returns the user's top role color Parameters ----------- - user (discord.User|discord.Member): A discord user or member object + discord_object (discord.User|discord.Member|discord.Role): A discord object that has a color attribute Returns ----------- (discord.Color): The user's displayed color """ - if str(user.color) != '#000000': - return user.color + if str(discord_object.color) != '#000000': + return discord_object.color return discord.Colour(0x99AAB5) From ff9f8f7e31afdb85e744567b2d3a3b045f5f32e1 Mon Sep 17 00:00:00 2001 From: LBlend Date: Tue, 15 Nov 2022 03:59:15 +0100 Subject: [PATCH 22/54] make sure object has color attribute --- src/cogs/utils/discord_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cogs/utils/discord_utils.py b/src/cogs/utils/discord_utils.py index 62efec0..c843767 100644 --- a/src/cogs/utils/discord_utils.py +++ b/src/cogs/utils/discord_utils.py @@ -13,7 +13,7 @@ def get_color(discord_object: discord.User | discord.Member | discord.Role) -> d ----------- (discord.Color): The user's displayed color """ - if str(discord_object.color) != '#000000': + if hasattr(discord_object, "color") and str(discord_object.color) != '#000000': return discord_object.color return discord.Colour(0x99AAB5) From 18d5bd4d391593bcdea02c4ebbc0dadc9b612856 Mon Sep 17 00:00:00 2001 From: LBlend Date: Tue, 15 Nov 2022 03:59:53 +0100 Subject: [PATCH 23/54] update name of color fetching function --- src/cogs/birthday.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cogs/birthday.py b/src/cogs/birthday.py index 8cd17a5..e0b66bc 100644 --- a/src/cogs/birthday.py +++ b/src/cogs/birthday.py @@ -172,7 +172,7 @@ class Birthday(commands.Cog): next_birthday = self.__fetch_user_next_birthday(bruker.id) next_birthday_days = (next_birthday - datetime.now()).days - embed = discord.Embed(description=bruker.mention, color=discord_utils.get_user_color(bruker)) + embed = discord.Embed(description=bruker.mention, color=discord_utils.get_color(bruker)) embed.set_thumbnail(url=bruker.display_avatar) embed.set_author(name=bruker.name, icon_url=bruker.display_avatar) embed.add_field(name="Bursdag", value=discord.utils.format_dt(birthday, style="D")) From 5270f2073030ef938b56b32797297102a45efaf9 Mon Sep 17 00:00:00 2001 From: LBlend Date: Tue, 15 Nov 2022 04:13:13 +0100 Subject: [PATCH 24/54] use correct presence key for type --- src/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/run.py b/src/run.py index b71ecae..665af81 100644 --- a/src/run.py +++ b/src/run.py @@ -76,7 +76,7 @@ async def on_ready(): "idle": discord.Status.idle, "offline": discord.Status.offline, } - status_type = status_types.get(bot.presence["status"].lower(), discord.Status.online) + status_type = status_types.get(bot.presence["type"].lower(), discord.Status.online) # Presence actitivity activities = {"playing": 0, "listening": 2, "watching": 3} From bf99a9d228e9fa517acfebe8a2f010f547ef9cc3 Mon Sep 17 00:00:00 2001 From: LBlend Date: Tue, 15 Nov 2022 04:25:15 +0100 Subject: [PATCH 25/54] migrate to slash commands and refactor botinfo --- src/cogs/botinfo.py | 112 +++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 59 deletions(-) diff --git a/src/cogs/botinfo.py b/src/cogs/botinfo.py index f123fd1..404f5d3 100644 --- a/src/cogs/botinfo.py +++ b/src/cogs/botinfo.py @@ -1,9 +1,10 @@ -from discord.ext import commands -import discord - -from time import time, perf_counter +from os import environ, getpid import platform -from os import getpid, environ +from time import time + +import discord +from discord import app_commands +from discord.ext import commands from psutil import Process from cogs.utils import embed_templates @@ -13,36 +14,29 @@ class BotInfo(commands.Cog): def __init__(self, bot): self.bot = bot - @commands.bot_has_permissions(embed_links=True, external_emojis=True) - @commands.cooldown(1, 2, commands.BucketType.guild) - @commands.command(aliases=['info', 'about', 'om']) - async def botinfo(self, ctx): + @app_commands.checks.bot_has_permissions(embed_links=True, external_emojis=True) + @app_commands.checks.cooldown(1, 2) + @app_commands.command() + async def botinfo(self, interaction: discord.Interaction): """ Viser informasjon om bot """ - + # Dev user info dev = await self.bot.fetch_user(170506717140877312) - # Ping - start = perf_counter() - status_msg = await ctx.send('Beregner ping...') - end = perf_counter() - ping = int((end - start) * 1000) - # Memory usage process = Process(getpid()) memory_usage = round(process.memory_info().rss / 1000000, 1) # Member stats - """ - This is a pretty stupid way of doing things but it's the best I can come up with. - Using curly brackets for both dicts and sets is retarded becuase - I have to do this shit in order to create a fucking empty set. - - Apparently you can't access user presences, only member presences - Because of that I have to use sets instead of ints in order to not count duplicate users. - Fucking hell discord... - """ + # + # This is a pretty stupid way of doing things but it's the best I can come up with. + # Using curly brackets for both dicts and sets is retarded becuase + # I have to do this shit in order to create a fucking empty set. + # + # Apparently you can't access user presences, only member presences + # Because of that I have to use sets instead of ints in order to not count duplicate users. + # Fucking hell discord... total_members = set([]) online_members = set([]) idle_members = set([]) @@ -61,12 +55,12 @@ class BotInfo(commands.Cog): offline_members.add(member.id) # Build embed - embed = discord.Embed(color=ctx.me.color, url=self.bot.misc['website']) + embed = discord.Embed(color=interaction.client.user.color, url=self.bot.misc['website']) embed.set_author(name=dev.name, icon_url=dev.display_avatar) embed.set_thumbnail(url=self.bot.user.display_avatar) embed.add_field(name='Dev', value=f'{dev.mention}\n{dev.name}#{dev.discriminator}') - embed.add_field(name='Oppetid', value=self.get_uptime()) - embed.add_field(name='Ping', value=f'Ekte ping: {ping} ms\nWebsocket ping: {int(self.bot.latency * 1000)} ms') + embed.add_field(name='Oppetid', value=self.__get_uptime()) + embed.add_field(name='Ping', value=f'Websocket ping: {self.__get_ping()} ms') embed.add_field(name='Servere', value=len(self.bot.guilds)) embed.add_field(name='Discord.py', value=discord.__version__) embed.add_field(name='Python', value=platform.python_version()) @@ -81,44 +75,44 @@ class BotInfo(commands.Cog): f'{self.bot.emoji["offline"]}{len(offline_members)}') embed.add_field(name='Lenker', value=f'[Nettside]({self.bot.misc["website"]}) | ' + f'[Kildekode]({self.bot.misc["source_code"]})') - embed_templates.default_footer(ctx, embed) - await status_msg.edit(embed=embed, content=None) + embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) - @commands.bot_has_permissions(embed_links=True) - @commands.cooldown(1, 2, commands.BucketType.guild) - @commands.command(aliases=['uptime']) - async def oppetid(self, ctx): - """ - Viser hvor lenge bot har vært kjørende - """ + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.checks.cooldown(1, 2) + @app_commands.command() + async def oppetid(self, interaction: discord.Interaction): + """Viser hvor lenge bot har vært kjørende""" - embed = discord.Embed(color=ctx.me.color) - embed.add_field(name='🔌 Oppetid', value=self.get_uptime()) - embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) + embed = discord.Embed(color=interaction.client.user.color) + embed.add_field(name='🔌 Oppetid', value=self.__get_uptime()) + embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) - @commands.bot_has_permissions(embed_links=True) - @commands.cooldown(1, 2, commands.BucketType.guild) - @commands.command() - async def ping(self, ctx): - """ - Viser bot sin ping - """ - - start = perf_counter() - status_msg = await ctx.send('Pinging...') - end = perf_counter() - ping = int((end - start) * 1000) - - embed = discord.Embed(color=ctx.me.color) + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.checks.cooldown(1, 2) + @app_commands.command() + async def ping(self, interaction: discord.Interaction): + """Viser bot sin ping""" + embed = discord.Embed(color=interaction.client.user.color) embed.add_field( name='📶 Ping', - value=f'**Ekte ping:** {ping} ms\n**Websocket ping:** {int(self.bot.latency * 1000)} ms' + value=f'**Websocket ping:** {self.__get_ping()} ms' ) - embed_templates.default_footer(ctx, embed) - await status_msg.edit(embed=embed, content=None) + embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed, content=None) - def get_uptime(self) -> str: + def __get_ping(self) -> int: + """ + Get the bot's ping in milliseconds + + Returns + ---------- + (int) The bot's ping in milliseconds + """ + return int(self.bot.latency * 1000) + + def __get_uptime(self) -> str: """ Returns the current uptime of the bot in string format From 56dec0c058ab3ad95a5d67d6e960fd7a87490ba1 Mon Sep 17 00:00:00 2001 From: LBlend Date: Tue, 15 Nov 2022 23:44:42 +0100 Subject: [PATCH 26/54] add kontoret as triggerword --- src/cogs/funreplies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cogs/funreplies.py b/src/cogs/funreplies.py index ac35261..e012705 100644 --- a/src/cogs/funreplies.py +++ b/src/cogs/funreplies.py @@ -25,7 +25,7 @@ class FunReplies(commands.Cog): await message.channel.send('https://cdn.discordapp.com/attachments/750052141346979850/' + '824764933513281596/3xhpwbakz2361.png') - elif re.search(r'^((er det|hvor)\s+)*(noen|folk|mange)\s+på\s+(rommet|neuf)\?*$', message_content, flags=re.IGNORECASE): + elif re.search(r'^((er det|hvor)\s+)*(noen|folk|mange)\s+på\s+(rommet|neuf|kontoret)\?*$', message_content, flags=re.IGNORECASE): await message.channel.send('Ja, <@554977854971183125> bor der') elif re.search(r'(^|\W)+ye+et($|\W)+', message_content, flags=re.IGNORECASE): From ddfd241ff0e63df1be412bb53aab2fdfb5579c59 Mon Sep 17 00:00:00 2001 From: LBlend Date: Tue, 15 Nov 2022 23:46:10 +0100 Subject: [PATCH 27/54] new paginator + refactor --- src/cogs/utils/misc_utils.py | 101 +++++++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 28 deletions(-) diff --git a/src/cogs/utils/misc_utils.py b/src/cogs/utils/misc_utils.py index 4d5fa4a..4eec3b0 100644 --- a/src/cogs/utils/misc_utils.py +++ b/src/cogs/utils/misc_utils.py @@ -1,6 +1,5 @@ -from typing import Union, Dict -from math import ceil from contextlib import contextmanager +from math import ceil @contextmanager @@ -16,41 +15,87 @@ def ignore_exception(*exceptions) -> None: ---------- None """ - try: yield - except (exceptions): + except exceptions: pass +class Paginator: + def __init__(self, content: list): + self.content = content + self.total_page_count = ceil(len(content) / 10) + self.current_page = 1 -def paginator(content: list, page: int) -> Dict[str, Union[int, str]]: - """ - Divides content into 10 element pages + def get_page(self, page: int) -> list | None: + """ + Returns the page of the paginator - Parameters - ----------- - content: A list of strings - page: The page + Parameters + ---------- + page (int): The page you want to get - Returns - ----------- - (dict): A dictionay containing content and metadata - keys: pagecount, page, page_content - """ + Returns + ---------- + (list|None): The page you requested + """ + if page < 1 or page > self.total_page_count: + return None - pagecount = ceil(len(content) / 10) + start_index = (page - 1) * 10 + end_index = page * 10 - if not page or page <= 0 or page > pagecount: - page = 1 + return self.content[start_index:end_index] - start_index = (page - 1) * 10 - end_index = page * 10 + def get_current_page(self) -> list: + """ + Returns the current page of the paginator - page_content = content[start_index:end_index] + Returns + ---------- + (list): The current page of the paginator + """ + return self.get_page(self.current_page) - page_data = { - 'pagecount': pagecount, - 'page': page, - 'page_content': page_content - } - return page_data + def next_page(self) -> list | None: + """ + Returns the next page of the paginator + + Returns + ---------- + (list): The next page of the paginator + """ + self.current_page += 1 + return self.get_page(self.current_page) + + def previous_page(self) -> list | None: + """ + Returns the previous page of the paginator + + Returns + ---------- + (list): The previous page of the paginator + """ + self.current_page -= 1 + return self.get_page(self.current_page) + + def first_page(self) -> list | None: + """ + Returns the first page of the paginator + + Returns + ---------- + (list): The first page of the paginator + """ + self.current_page = 1 + return self.get_page(self.current_page) + + def last_page(self) -> list | None: + """ + Returns the last page of the paginator + + Returns + ---------- + (list): The last page of the paginator + """ + self.current_page = self.total_page_count + return self.get_page(self.current_page) From c272154cc7449add002c3256a06a2c8ca34262b5 Mon Sep 17 00:00:00 2001 From: LBlend Date: Tue, 15 Nov 2022 23:53:00 +0100 Subject: [PATCH 28/54] add scroller view --- src/cogs/utils/discord_utils.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/cogs/utils/discord_utils.py b/src/cogs/utils/discord_utils.py index c843767..953a6b9 100644 --- a/src/cogs/utils/discord_utils.py +++ b/src/cogs/utils/discord_utils.py @@ -1,5 +1,7 @@ import discord +from .misc_utils import Paginator + def get_color(discord_object: discord.User | discord.Member | discord.Role) -> discord.Color: """ @@ -17,3 +19,25 @@ def get_color(discord_object: discord.User | discord.Member | discord.Role) -> d return discord_object.color return discord.Colour(0x99AAB5) + +class ScrollerButton(discord.ui.Button): + def __init__(self, paginator: Paginator, button_action: callable, content_constructor: callable, label: str, disabled: bool = False): + super().__init__(label=label, disabled=disabled) + self.paginator = paginator + self.button_action = button_action + self.content_constructor = content_constructor + + async def callback(self, interaction: discord.Interaction): + await interaction.response.defer() + content = self.content_constructor(self.paginator, self.button_action(), interaction.message.embeds[0]) + await interaction.message.edit(embed=content, view=Scroller(self.paginator, self.content_constructor)) + +class Scroller(discord.ui.View): + def __init__(self, paginatior: Paginator, content_constructor: callable): + super().__init__() + self.paginator = paginatior + + self.add_item(ScrollerButton(self.paginator, self.paginator.first_page, content_constructor, label='<<', disabled=True if self.paginator.current_page == 1 else False)) + self.add_item(ScrollerButton(self.paginator, self.paginator.previous_page, content_constructor, label='<', disabled=True if self.paginator.current_page == 1 else False)) + self.add_item(ScrollerButton(self.paginator, self.paginator.next_page, content_constructor, label='>', disabled=True if self.paginator.current_page == self.paginator.total_page_count else False)) + self.add_item(ScrollerButton(self.paginator, self.paginator.last_page, content_constructor, label='>>', disabled=True if self.paginator.current_page == self.paginator.total_page_count else False)) From bfd62ccd7f9e76ed07f31b7a873e3c2e6284931f Mon Sep 17 00:00:00 2001 From: LBlend Date: Wed, 16 Nov 2022 00:45:21 +0100 Subject: [PATCH 29/54] migrate info cog & refactor --- src/cogs/info.py | 906 +++++++++++++++++++---------------------------- 1 file changed, 374 insertions(+), 532 deletions(-) diff --git a/src/cogs/info.py b/src/cogs/info.py index 4798251..38a1145 100644 --- a/src/cogs/info.py +++ b/src/cogs/info.py @@ -1,39 +1,84 @@ -from discord.ext import commands -import discord - from os import remove -from cogs.utils import embed_templates, misc_utils +import discord +from discord import app_commands +from discord.ext import commands + +from cogs.utils import discord_utils, embed_templates, misc_utils class Info(commands.Cog): def __init__(self, bot): self.bot = bot - @commands.bot_has_permissions(embed_links=True, external_emojis=True) - @commands.guild_only() - @commands.cooldown(1, 2, commands.BucketType.guild) - @commands.command(aliases=['guildinfo', 'server', 'serverinfo', 'si', 'gi']) - async def guild(self, ctx): - """ - Viser info om guilden - """ + guild_group = app_commands.Group(name="guild", description="Se ting om serveren") + user_group = app_commands.Group(name="bruker", description="Se ting om brukeren") - guild_created_date = ctx.guild.created_at.strftime('%d. %b. %Y - %H:%M') - since_created_days = (ctx.message.created_at - ctx.guild.created_at).days + def __construct_role_string(self, roles: list[discord.Role]) -> str: + """Puts all roles except @everyone into a list and joins them to a string""" + if not roles: + return "**Ingen roller**" - if since_created_days == 1: - since_created_days_string = 'dag' - else: - since_created_days_string = 'dager' + roles = [role.name for role in roles if role.name != "@everyone"] + roles.reverse() - total_members = ctx.guild.member_count + return ', '.join(roles) + + def __construct_booster_string(self, interaction: discord.Interaction, join_method: callable = ', '.join) -> str: + """Joins all boosters into a string""" + if not interaction.guild.premium_subscribers: + return '**Ingen boostere**' + + # Sort boosters by boost date and put name#discrim as well as date of boost into a list of stirngs + # Use map instead of list comprehension because I can divide the code into 2 lines + boosters = list(map(lambda booster: f'{booster.name}#{booster.discriminator} - {discord.utils.format_dt(booster.premium_since)}', + sorted(interaction.guild.premium_subscribers, key=lambda m: m.premium_since))) + boosters = [f'{booster.name}#{booster.discriminator}' for booster in boosters] + boosters.reverse() + + return join_method(boosters) + + def __construct_member_string(self, members: list[discord.Member]) -> str: + """Joins all name#discrim into a string""" + if not members: + return '**Ingen**' + + members = [f'{member.name}#{member.discriminator}' for member in members] + + return ', '.join(members) + + async def __send_as_txt_file(self, interaction: discord.Interaction, content: str, file_path: str): + """Sends a string as a txt file and deletes the file afterwards""" + # Create file + with open(file_path, 'w', encoding='utf-8') as file: + file.write(content) + + # Send file + txt_file = discord.File(file_path) + await interaction.response.send_message(file=txt_file) + + # Delete file + with misc_utils.ignore_exception(OSError): + remove(file_path) + + @app_commands.checks.bot_has_permissions(embed_links=True, external_emojis=True) + @app_commands.guild_only() + @app_commands.checks.cooldown(1, 2) + @guild_group.command(name="info", description="Hent informasjon om en server") + async def guild_info(self, interaction: discord.Interaction): + # Days since guild was created + creation_date_timestamp = discord.utils.format_dt(interaction.guild.created_at, style="F") + since_created_days = (interaction.created_at - interaction.guild.created_at).days + days_ago = f'{since_created_days} dager' if since_created_days != 1 else f'{since_created_days} dag' + + # Member count and status counts + total_members = interaction.guild.member_count bot_members = 0 online_members = 0 idle_members = 0 dnd_members = 0 offline_members = 0 - for member in ctx.guild.members: + for member in interaction.guild.members: if member.bot: bot_members += 1 if str(member.status) == 'online': @@ -45,63 +90,57 @@ class Info(commands.Cog): elif str(member.status) == 'offline': offline_members += 1 - roles = [] - for role in ctx.guild.roles: - if role.name != '@everyone': - roles.append(role.name) - roles.reverse() - roles = ', '.join(roles) - if len(roles) > 1024: - roles = f'Skriv `{self.bot.prefix}guildroller` for å se rollene' - if roles == '': - roles = '**Ingen roller**' + # Roles + roles = self.__construct_role_string(interaction.guild.roles) + roles = roles if len(roles) < 1024 else f'Bruk kommandoen for å se rollene' - boosters = [] - premium_subscribers = sorted( - ctx.guild.premium_subscribers, key=lambda m: m.premium_since) - for booster in premium_subscribers: - boosters.append(f'{booster.name}#{booster.discriminator}') - boosters = ', '.join(boosters) - if len(boosters) > 1024: - boosters = f'Skriv `{self.bot.prefix}boosters` for å se boosters' - if boosters == '': - boosters = '**Ingen boosters**' + # Boosts + boosters = self.__construct_booster_string(interaction) + boosters = boosters if len(boosters) < 1024 else f'Bruk kommandoen for å se boostere' - text_channels = len(ctx.guild.text_channels) - voice_channels = len(ctx.guild.voice_channels) - categories = len(ctx.guild.categories) + # Channels counts + text_channels = len(interaction.guild.text_channels) + voice_channels = len(interaction.guild.voice_channels) + categories = len(interaction.guild.categories) total_channels = text_channels + voice_channels + # Features features_string = '' - if ctx.guild.features is not []: + if interaction.guild.features is not []: features = { - 'VIP_REGIONS': 'VIP', - 'VANITY_URL': 'Egen URL', + 'ANIMATED_BANNER': 'Animer serverbanner', + 'ANIMATED_ICON': 'Animert serverikon', + 'BANNER': 'Serverbanner', + 'COMMERCE': 'E-handel', + 'COMMUNITY': 'Samfunnsserver', + 'DISCOVERABLE': 'På utforsksiden', + 'FEATURABLE': '', 'INVITE_SPLASH': 'Invitasjonsbilde', - 'VERIFIED': 'Verifisert', - 'PARTNERED': 'Discord Partner', - 'MORE_EMOJI': 'Ekstra emoji', - 'DISCOVERABLE': 'Fremhevet', - 'FEATURABLE': 'Kan fremheves', - 'COMMUNITY': 'Sammfunsguild', - 'COMMERCE': 'Butikkanaler', - 'PUBLIC': 'Offentlig guild', + 'MEMBER_VERIFICATION_GATE_ENABLED': 'Medlemsverifisering', + 'MONETIZATION_ENABLED': 'Betalingskanal', + 'MORE_EMOJI': 'Flere emoji', + 'MORE_STICKERS': 'Flere stickers', 'NEWS': 'Nyhetskanaler', - 'BANNER': 'Banner', - 'ANIMATED_ICON': 'Animert ikon', - 'PUBLIC_DISABLED': 'Ikke offentlig', - 'WELCOME_SCREEN_ENABLED': 'Velkomstvindu', + 'PARTNERED': 'Partner', 'PREVIEW_ENABLED': 'Forhåndsvisning', - 'MEMBER_VERIFICATION_GATE_ENABLED': 'Medlemsverifisering' + 'PRIVATE_THREADS': 'Private tråder', + 'ROLE_ICONS': 'Rolleikon', + 'TICKETED_EVENTS_ENABLED': 'Arrangmenter med billettsalg', + 'VANITY_URL': 'Egendefinert URL', + 'VERIFIED': 'Verifisert', + 'VIP_REGIONS': 'Høyere bitrate i stemmekanaler', + 'WELCOME_SCREEN_ENABLED': 'Velkomstskjerm', + 'INVITES_DISABLED': 'Invitasjoner deaktivert' } - for feature in ctx.guild.features: - features_string += f'{features[feature]}\n' + for feature in interaction.guild.features: + if translation := features.get(feature): + features_string += f'{translation}\n' photos = {} - if ctx.guild.discovery_splash: - photos['Invitasjonsbilde'] = ctx.guild.discovery_splash - if ctx.guild.banner: - photos['Banner'] = ctx.guild.banner + if interaction.guild.discovery_splash: + photos['Invitasjonsbilde'] = interaction.guild.discovery_splash + if interaction.guild.banner: + photos['Banner'] = interaction.guild.banner verification_level = { 'none': 'ingen', @@ -110,25 +149,25 @@ class Info(commands.Cog): 'high': 'e-post, registrert i 5 min, medlem i 10 min', 'extreme': 'telefon' } - verification = verification_level[str(ctx.guild.verification_level)] + verification = verification_level[str(interaction.guild.verification_level)] content_filter = { 'disabled': 'nei', 'no_role': 'for alle uten rolle', 'all_members': 'ja' } - content = content_filter[str(ctx.guild.explicit_content_filter)] + content = content_filter[str(interaction.guild.explicit_content_filter)] - embed = discord.Embed(color=ctx.me.color, description=f'**Verifiseringskrav:** {verification}\n' + + embed = discord.Embed(color=interaction.guild.me.color, description=f'**Verifiseringskrav:** {verification}\n' + f'**Innholdsfilter:** {content}\n' + - f'**Boost Tier:** {ctx.guild.premium_tier}\n' + - f'**Emoji:** {len(ctx.guild.emojis)}') - embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon) - embed.set_thumbnail(url=ctx.guild.icon) - embed.add_field(name='ID', value=ctx.guild.id) - embed.add_field(name='Eier', value=ctx.guild.owner.mention) - embed.add_field(name='Opprettet', value=f'{guild_created_date}\n{since_created_days} ' + - f'{since_created_days_string} siden') + f'**Boost Tier:** {interaction.guild.premium_tier}\n' + + f'**Emoji:** {len(interaction.guild.emojis)}') + embed.set_author(name=interaction.guild.name, icon_url=interaction.guild.icon) + embed.set_thumbnail(url=interaction.guild.icon) + embed.add_field(name='ID', value=interaction.guild.id) + embed.add_field(name='Eier', value=interaction.guild.owner.mention) + embed.add_field(name='Opprettet', value=f'{creation_date_timestamp}\n{since_created_days} ' + + f'{days_ago} siden') embed.add_field(name=f'Kanaler ({total_channels})', value=f'💬 Tekst: **{text_channels}**\n' + f'🔊 Tale: **{voice_channels}**\n' + f'🗃️ Kategorier: **{categories}**') @@ -139,158 +178,248 @@ class Info(commands.Cog): f'{self.bot.emoji["idle"]}{idle_members} ' + f'{self.bot.emoji["dnd"]}{dnd_members} ' + f'{self.bot.emoji["offline"]}{offline_members}') - embed.add_field(name=f'Roller ({len(ctx.guild.roles) - 1})', value=roles, inline=False) - if ctx.guild.premium_tier != 0: - embed.add_field(name=f'Boosters ({ctx.guild.premium_subscription_count})', value=boosters, inline=False) + embed.add_field(name=f'Roller ({len(interaction.guild.roles) - 1})', value=roles, inline=False) + if interaction.guild.premium_tier != 0: + embed.add_field(name=f'Boosters ({interaction.guild.premium_subscription_count})', value=boosters, inline=False) - if features_string != '': + if not features_string: embed.add_field(name='Tillegsfunksjoner', value=features_string) - if photos != {}: + if photos: photos_string = '' for key, value in photos.items(): photos_string += f'[{key}]({value})\n' embed.add_field(name='Bilder', value=photos_string) - await ctx.send(embed=embed) + await interaction.response.send_message(embed=embed) - @commands.bot_has_permissions(embed_links=True) - @commands.guild_only() - @commands.cooldown(1, 2, commands.BucketType.guild) - @commands.command(aliases=['serverroller', 'guildroles', 'serverroles']) - async def guildroller(self, ctx): - """ - Viser rollene i en guild - """ - - roles = [] - for role in ctx.guild.roles: - if role.name != '@everyone': - roles.append(role.name) - if roles is []: - roles = ['**Ingen Roller**'] - roles.reverse() - roles = ', '.join(roles) - - roles = roles.replace(', --', '\n--') - roles = roles.replace('--, ', '--\n') + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.guild_only() + @app_commands.checks.cooldown(1, 2) + @guild_group.command(name="roller", description="Se rollene på serveren") + async def guild_roles(self, interaction: discord.Interaction): + roles = self.__construct_role_string(interaction.guild.roles) + # IF roles list is longer than 2048, create text file and send it if len(roles) > 2048: - with open(f'./assets/temp/{ctx.guild.id}_roles.txt', 'w') as f: - f.seek(0) - f.write(roles) + self.__send_as_txt_file(interaction, f'./assets/temp/{interaction.guild.id}_roles.txt', roles) + else: + embed = discord.Embed(color=interaction.guild.me.color, description=roles) + embed.set_author(name=f'Roller ({len(interaction.guild.roles)})', icon_url=interaction.guild.icon) + embed.set_footer(text=interaction.guild.name, icon_url=interaction.guild.icon) + await interaction.response.send_message(embed=embed) - txt_file = discord.File(f'./assets/temp/{ctx.guild.id}_roles.txt') - await ctx.send(file=txt_file) + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.guild_only() + @app_commands.checks.cooldown(1, 2) + @guild_group.command(name="boosters", description="Se boostere på serveren") + async def guild_boosters(self, interaction: discord.Interaction): + if len(interaction.guild.premium_subscribers) == 0: + embed = embed_templates.error_warning(interaction, text='Serveren har ikke noen boosts :(') + return await interaction.response.send_message(embed=embed) - with misc_utils.ignore_exception(OSError): - remove(f'./assets/temp/{ctx.guild.id}_roles.txt') + boosters = self.__construct_booster_string(interaction, '\n'.join) - return + embed = discord.Embed(color=interaction.guild.me.color, description=boosters) + embed.set_author(name=f'Boosters ({interaction.guild.premium_subscription_count})', icon_url=interaction.guild.icon) + embed.set_footer(text=interaction.guild.name, icon_url=interaction.guild.icon) + await interaction.response.send_message(embed=embed) - if roles == '': - roles = '**Ingen roller**' + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.guild_only() + @app_commands.checks.cooldown(1, 2) + @guild_group.command(name="ikon", description="Hent serverens ikon") + async def guild_icon(self, interaction: discord.Interaction): + embed = discord.Embed(color=interaction.guild.me.color, description=f'[Lenke]({interaction.guild.icon})') + embed.set_author(name=interaction.guild.name, icon_url=interaction.guild.icon) + embed.set_image(url=interaction.guild.icon) + embed = embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) - embed = discord.Embed(color=ctx.me.color, description=roles) - embed.set_author(name=f'Roller ({len(ctx.guild.roles) - 1})', icon_url=ctx.guild.icon) - embed.set_footer(text=ctx.guild.name, icon_url=ctx.guild.icon) - await ctx.send(embed=embed) + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.guild_only() + @app_commands.checks.cooldown(1, 2) + @guild_group.command(name="splash", description="Hent serverens splash (bakgrunnsbilde på invitasjonsside)") + async def guild_splash(self, interaction: discord.Interaction): + if not interaction.guild.discovery_splash: + embed = embed_templates.error_warning(interaction, text='Serveren har ikke en splash') + return await interaction.response.send_message(embed=embed) + + embed = discord.Embed(color=interaction.guild.me.color, description=f'[Lenke]({interaction.guild.discovery_splash})') + embed.set_author(name=interaction.guild.name, icon_url=interaction.guild.icon) + embed.set_image(url=interaction.guild.discovery_splash) + embed = embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) + + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.guild_only() + @app_commands.checks.cooldown(1, 2) + @guild_group.command(name="banner", description="Hen serverens banner") + async def guild_banner(self, interaction: discord.Interaction): + if not interaction.guild.banner: + embed = embed_templates.error_warning(interaction, text='Serveren har ikke et banner :(') + return await interaction.response.send_message(embed=embed) + + embed = discord.Embed(color=interaction.me.guild.color, description=f'[Lenke]({interaction.guild.banner})') + embed.set_author(name=interaction.guild.name, icon_url=interaction.guild.icon) + embed.set_image(url=interaction.guild.banner) + embed = embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) + + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.guild_only() + @app_commands.checks.cooldown(1, 2) + @guild_group.command(name="rolle", description="Hent informasjon om en rolle på serveren") + async def guild_role(self, interaction: discord.Interaction, rolle: discord.Role): + if rolle.name == '@everyone': + embed = embed_templates.error_fatal(interaction, text='Skriv inn en annen rolle enn @everyone') + return await interaction.response.send_message(embed=embed) + + # Timestamp and days since creation + created_at_timestamp = discord.utils.format_dt(rolle.created_at, style="F") + since_created_days = (interaction.created_at - rolle.created_at).days + + # List of members with the role + members = self.__construct_member_string(rolle.members) + members = members if len(members) < 1024 else 'For mange medlemmer for å vise her' + + # List of permissions + permissions = ', '.join([permission for permission, value in iter(rolle.permissions) if value is True]) + + embed = discord.Embed(title=rolle.name, description=f'{rolle.mention}\n**ID:** {rolle.id}', color=discord_utils.get_color(rolle)) + embed.set_author(name=rolle.guild.name, icon_url=rolle.guild.icon) + embed.add_field(name='Fargekode', value=str(rolle.color)) + embed.add_field(name='Opprettet', value=f'{created_at_timestamp}\n{since_created_days} ' + + f'{"dager" if since_created_days > 1 else "dag"} siden') + embed.add_field(name='Posisjon', value=rolle.position) + embed.add_field(name='Nevnbar', value='Ja' if rolle.mentionable else 'Nei') + embed.add_field(name='Vises separat i medlemsliste', value='Ja' if rolle.hoist else 'Nei') + if permissions: + embed.add_field(name='Tillatelser', value=permissions, inline=False) + embed.add_field(name=f'Brukere med rollen ({len(rolle.members)})', value=members, inline=False) + embed = embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) + + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.guild_only() + @app_commands.checks.cooldown(1, 2) + @guild_group.command(name="tekstkanal", description="Hent informasjon om en tekstkanal") + async def guild_text_channel(self, interaction: discord.Interaction, kanal: discord.TextChannel): + members = self.__construct_member_string(kanal.members) + if len(members) > 1024: + members = 'For mange for å vise her' + + embed = discord.Embed(color=interaction.guild.me.color, title=kanal.name, description=f'{kanal.mention}\nID: {kanal.id}') + embed.set_author(name=kanal.guild.name, icon_url=kanal.guild.icon) + embed.add_field(name='Beskrivelse', value=kanal.topic if kanal.topic else '**Ingen**', inline=False) + embed.add_field(name='Opprettet', value=discord.utils.format_dt(kanal.created_at, style='F')) + embed.add_field(name='NSFW', value='Ja' if kanal.is_nsfw() else 'Nei') + embed.add_field(name='Saktemodus', value=f'Ja ({kanal.slowmode_delay} sekunder)' if kanal.slowmode_delay else 'Nei') + if kanal.category: + embed.add_field(name='Kategori', value=kanal.category.name) + embed.add_field(name=f'Antall med tilgang ({len(kanal.members)})', value=members) + embed = embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) + + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.guild_only() + @app_commands.checks.cooldown(1, 2) + @guild_group.command(name="talekanal", description="Hent informasjon om en talekanal") + async def guild_voice_channel(self, interaction: discord.Interaction, kanal: discord.VoiceChannel): + embed = discord.Embed(color=interaction.guild.me.color, title=kanal.name, description=f'ID: {kanal.id}') + embed.set_author(name=kanal.guild.name, icon_url=kanal.guild.icon) + embed.add_field(name='Opprettet', value=discord.utils.format_dt(kanal.created_at, style='F')) + embed.add_field(name='Bitrate', value=f'{int(kanal.bitrate / 1000)}kbps') + embed.add_field(name='Maksgrense', value=f'{kanal.user_limit} personer' if kanal.user_limit != 0 else '∞ personer') + if kanal.category: + embed.add_field(name='Kategori', value=kanal.category.name) + embed.add_field(name='Antall koblet til', value=len(kanal.members)) + embed = embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) + + # @app_commands.checks.bot_has_permissions(embed_links=True) + # @app_commands.guild_only() + # @app_commands.checks.cooldown(1, 2) + # @app_commands.command() + # async def emoji(self, interaction: discord.Interaction, emoji: discord.Emoji): + # """ + # Viser info om en CUSTOM emoji som tilhører serveren + # """ + # emoji = await emoji.guild.fetch_emoji(emoji.id) + # try: + # emoji_creator = f'{emoji.user.mention}\n{emoji.user.name}#{emoji.user.discriminator}' + # except AttributeError: + # emoji_creator = 'Jeg trenger `manage_emojis`-tillatelsen på serveren den er fra for å hente dette' + + # embed = discord.Embed(color=interaction.guild.me.color, title=emoji.name, description=f'ID: {emoji.id}') + # embed.set_author(name=emoji.guild.name, icon_url=emoji.guild.icon) + # embed.add_field(name='Opprettet', value=discord.utils.format_dt(emoji.created_at, style="F")) + # embed.add_field(name='Animert', value='Ja' if emoji.animated else 'Nei') + # embed.add_field(name='Lagt til av', value=emoji_creator) + # embed.set_image(url=emoji.url) + # embed_templates.default_footer(interaction, embed) + # await interaction.response.send_message(embed=embed) + + guild_oldest_group = app_commands.Group(name="eldst", description="Viser de eldste medlemmene på serveren", parent=guild_group) + + def __construct_oldest_embed(self, paginator: misc_utils.Paginator, page: list, embed: discord.Embed) -> discord.Embed: + """Constructs the embed for the oldest commands""" + embed.description = '\n'.join(page) + embed.set_footer(text=f'Side {paginator.current_page}/{paginator.total_page_count}') + return embed @commands.bot_has_permissions(embed_links=True) @commands.guild_only() - @commands.cooldown(1, 2, commands.BucketType.guild) - @commands.command(aliases=['guildboosters', 'serverboosters', 'boosts']) - async def boosters(self, ctx): - """ - Viser boosters i en guild - """ + @app_commands.checks.cooldown(1, 5) + @guild_oldest_group.command(name="lagd", description="Liste over de eldste brukerene på serveren basert på når de ble lagd") + async def guild_user_created_oldest(self, interaction: discord.Interaction): + # Sort members by creation date + members = sorted(interaction.guild.members, key=lambda m: m.created_at) - if len(ctx.guild.premium_subscribers) == 0: - embed = embed_templates.error_warning(ctx, text='Serveren har ikke noen boosts :(') - return await ctx.send(embed=embed) + # Create list of members with index, name#discriminator and creation date + members_formatted = list(map( + lambda m: f'**#{(m[0] + 1)}** {m[1].name}#{m[1].discriminator} - {discord.utils.format_dt(m[1].created_at, style="F")}', + enumerate(members) + )) - boosters = [] - premium_subscribers = sorted(ctx.guild.premium_subscribers, key=lambda m: m.premium_since) - for booster in premium_subscribers: - date = booster.premium_since.strftime('%d.%m.%Y %H:%M') - boosters.append(f'{booster.name}#{booster.discriminator} - {date}') - boosters = '\n'.join(boosters) + paginator = misc_utils.Paginator(members_formatted) + view = discord_utils.Scroller(paginator, self.__construct_oldest_embed) - embed = discord.Embed(color=ctx.me.color, description=boosters) - embed.set_author(name=f'Boosters ({ctx.guild.premium_subscription_count})', icon_url=ctx.guild.icon) - embed.set_footer(text=ctx.guild.name, icon_url=ctx.guild.icon) - await ctx.send(embed=embed) + # Send first page + embed = discord.Embed(color=interaction.guild.me.color, title='Eldste brukere på serveren basert på når de ble lagd') + embed = self.__construct_oldest_embed(paginator, paginator.get_current_page(), embed) + await interaction.response.send_message(embed=embed, view=view) @commands.bot_has_permissions(embed_links=True) @commands.guild_only() - @commands.cooldown(1, 2, commands.BucketType.guild) - @commands.command(aliases=['serverikon', 'servericon', 'guildicon', 'icon', 'guildikon']) - async def ikon(self, ctx): - """ - Viser ikonet til serveren du er i - """ + @app_commands.checks.cooldown(1, 5) + @guild_oldest_group.command(name="joined", description="Liste over de eldste brukerene på serveren basert på når de ble med") + async def guild_user_joined_oldest(self, interaction: discord.Interaction): + # Sort members by creation date + members = sorted(interaction.guild.members, key=lambda m: m.joined_at) - url = ctx.guild.icon + # Create list of members with index, name#discriminator and creation date + members_formatted = list(map( + lambda m: f'**#{(m[0] + 1)}** {m[1].name}#{m[1].discriminator} - {discord.utils.format_dt(m[1].joined_at, style="F")}', + enumerate(members) + )) - embed = discord.Embed(color=ctx.me.color, description=f'[Lenke]({url})') - embed.set_author(name=ctx.guild.name, icon_url=url) - embed.set_image(url=url) - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) + paginator = misc_utils.Paginator(members_formatted) + view = discord_utils.Scroller(paginator, self.__construct_oldest_embed) - @commands.bot_has_permissions(embed_links=True) - @commands.guild_only() - @commands.cooldown(1, 2, commands.BucketType.guild) - @commands.command(aliases=['guildsplash', 'serversplash']) - async def splash(self, ctx): - """ - Viser invite splash til serveren - """ - - if not ctx.guild.discovery_splash: - embed = embed_templates.error_warning(ctx, text='Serveren har ikke en splash') - return await ctx.send(embed=embed) - - url = ctx.guild.discovery_splash - - embed = discord.Embed(color=ctx.me.color, description=f'[Lenke]({url})') - embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon) - embed.set_image(url=url) - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.guild_only() - @commands.cooldown(1, 2, commands.BucketType.guild) - @commands.command(aliases=['guildbanner', 'serverbanner']) - async def banner(self, ctx): - """ - Viser invite splash til serveren - """ - - if not ctx.guild.banner: - embed = embed_templates.error_warning(ctx, text='Serveren har ikke et banner :(') - return await ctx.send(embed=embed) - - url = ctx.guild.banner - - embed = discord.Embed(color=ctx.me.color, description=f'[Lenke]({url})') - embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon) - embed.set_image(url=url) - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True, external_emojis=True) - @commands.guild_only() - @commands.cooldown(1, 2, commands.BucketType.guild) - @commands.command(aliases=['userinfo', 'ui', 'brukerinfo', 'user']) - async def bruker(self, ctx, *, bruker: discord.Member = None): - """ - Viser info om en bruker - """ + # Send first page + embed = discord.Embed(color=interaction.guild.me.color, title='Eldste brukere på serveren basert på når de ble med') + embed = self.__construct_oldest_embed(paginator, paginator.get_current_page(), embed) + await interaction.response.send_message(embed=embed, view=view) + @app_commands.checks.bot_has_permissions(embed_links=True, external_emojis=True) + @app_commands.guild_only() + @app_commands.checks.cooldown(1, 2) + @user_group.command(name="info", description="Hent informasjon om en bruker") + async def user_info(self, interaction: discord.Interaction, *, bruker: discord.Member | None = None): if not bruker: - bruker = ctx.author + bruker = interaction.user + # Get device(s) that is currently used by the user app = '' if str(bruker.mobile_status) != 'offline': app += '📱 ' @@ -299,48 +428,28 @@ class Info(commands.Cog): if str(bruker.desktop_status) != 'offline': app += '💻' - join_index = sorted(ctx.guild.members, key=lambda m: m.joined_at).index(bruker) + 1 - creation_index = sorted(ctx.guild.members, key=lambda m: m.created_at).index(bruker) + 1 + # Get index of join, creation and/or boost date in comparison to other users in the guild + join_index = sorted(interaction.guild.members, key=lambda m: m.joined_at).index(bruker) + 1 + creation_index = sorted(interaction.guild.members, key=lambda m: m.created_at).index(bruker) + 1 if bruker.premium_since: - premium_index = sorted(ctx.guild.premium_subscribers, key=lambda m: m.premium_since).index(bruker) + 1 + premium_index = sorted(interaction.guild.premium_subscribers, key=lambda m: m.premium_since).index(bruker) + 1 - bruker_joined_date = bruker.joined_at.strftime('%d. %b. %Y - %H:%M') - bruker_created_date = bruker.created_at.strftime('%d. %b. %Y - %H:%M') - since_joined_days = (ctx.message.created_at - bruker.joined_at).days - since_created_days = (ctx.message.created_at - bruker.created_at).days - if since_created_days == 1: - since_created_days_string = 'dag' - else: - since_created_days_string = 'dager' - if since_joined_days == 1: - since_joined_days_string = 'dag' - else: - since_joined_days_string = 'dager' + # Creation/join date & days ago + created_timestamp = discord.utils.format_dt(bruker.created_at, style='F') + joined_timestamp = discord.utils.format_dt(bruker.joined_at, style='F') + joined_days = (interaction.created_at - bruker.joined_at).days + created_days = (interaction.created_at - bruker.created_at).days + # Boost date & days ago if bruker.premium_since: - premium_since = bruker.premium_since.strftime('%d. %b. %Y - %H:%M') - premium_since_days = (ctx.message.created_at - bruker.premium_since).days - if since_joined_days == 1: - premium_since_days_string = 'dag' - else: - premium_since_days_string = 'dager' + premium_since = discord.utils.format_dt(bruker.premium_since, style='F') + premium_since_days = (interaction.message.created_at - bruker.premium_since).days - roles = [] - for role in bruker.roles: - if role.name != '@everyone': - roles.append(role.name) - roles.reverse() - roles = ', '.join(roles) + # Get user roles + roles = self.__construct_role_string(bruker.roles) if len(roles) > 1024: - roles = f'Skriv `{self.bot.prefix}{ctx.command}` for å se rollene' - if roles == '': - roles = '**Ingen roller**' - - if str(bruker.color) != '#000000': - color = bruker.color - else: - color = discord.Colour(0x99AAB5) + roles = f'Skriv `{self.bot.prefix}{interaction.command}` for å se rollene' statuses = { 'online': f'{self.bot.emoji["online"]} Pålogget', @@ -350,332 +459,65 @@ class Info(commands.Cog): } status = statuses[str(bruker.status)] - embed = discord.Embed(color=color, description=f'{bruker.mention}\nID: {bruker.id}\n{status}\n{app}') + embed = discord.Embed(color=discord_utils.get_color(bruker), description=f'{bruker.mention}\nID: {bruker.id}\n{status}\n{app}') if bruker.display_name == bruker.name: embed.set_author(name=f'{bruker.name}#{bruker.discriminator}', icon_url=bruker.display_avatar) else: embed.set_author(name=f'{bruker.name}#{bruker.discriminator} | {bruker.display_name}', icon_url=bruker.display_avatar) embed.set_thumbnail(url=bruker.display_avatar) - embed.add_field(name='Opprettet', value=f'{bruker_created_date}\n{since_created_days} ' + - f'{since_created_days_string} siden') - embed.add_field(name='Ble med i serveren', value=f'{bruker_joined_date}\n{since_joined_days} ' + - f'{since_joined_days_string} siden') + embed.add_field(name='Opprettet', value=f'{created_timestamp}\n{created_days} ' + + f'{"dag" if created_days == 1 else "dager"} siden') + embed.add_field(name='Ble med i serveren', value=f'{joined_timestamp}\n{joined_days} ' + + f'{"dag" if joined_days == 1 else "dager"} siden') if bruker.premium_since: embed.add_field(name='Boost', value=f'{premium_since}\n{premium_since_days} ' + - f'{premium_since_days_string} siden\n' + + f'{"dag" if premium_since_days == 1 else "dager"} siden\n' + f'Booster #{premium_index} av serveren', inline=False) - embed.add_field(name=f'Roller ({len(bruker.roles) - 1})', value=roles, inline=False) + embed.add_field(name=f'Roller ({len(bruker.roles)})', value=roles, inline=False) embed.set_footer(text=f'#{join_index} Medlem av serveren | #{creation_index} Eldste brukeren på serveren') + # Playing activity if bruker.activities: games = '' for activity in bruker.activities: games += f'{activity.name}\n' if games: embed.add_field(name='Spiller', value=games, inline=False) - await ctx.send(embed=embed) - @commands.bot_has_permissions(embed_links=True) - @commands.guild_only() - @commands.cooldown(1, 2, commands.BucketType.guild) - @commands.command(aliases=['userroles']) - async def brukerroller(self, ctx, bruker: discord.Member = None): - """ - Viser rollene til en bruker - """ + await interaction.response.send_message(embed=embed) + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.guild_only() + @app_commands.checks.cooldown(1, 2) + @user_group.command(name="roller", description="Hent roller til en bruker") + async def user_roles(self, interaction: discord.Interaction, bruker: discord.Member | None = None): if not bruker: - bruker = ctx.author + bruker = interaction.user - roles = [] - for role in bruker.roles: - if role.name != '@everyone': - roles.append(role.name) - roles.reverse() - roles = ', '.join(roles) + roles = self.__construct_role_string(bruker.roles) + # If the list of roles is too long, send it as a file if len(roles) > 2048: - txt_file = discord.File(f'./assets/temp/{ctx.guild.id}_{ctx.author.id}_roles.txt') - await ctx.send(file=txt_file) - - with misc_utils.ignore_exception(OSError): - remove(f'./assets/temp/{ctx.guild.id}_{ctx.author.id}_roles.txt') - - return - - if roles == '': - roles = '**Ingen roller**' - - if str(bruker.color) != '#000000': - color = bruker.color + self.__send_as_txt_file(interaction, f'./assets/temp/{interaction.guild.id}_{interaction.user.id}_roles.txt', roles) else: - color = discord.Colour(0x99AAB5) - - embed = discord.Embed(color=color, description=roles) - embed.set_author(name=f'Roller ({len(bruker.roles) - 1})', icon_url=bruker.display_avatar) - embed.set_footer(text=f'{bruker.name}#{bruker.discriminator}', icon_url=bruker.display_avatar) - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.cooldown(1, 2, commands.BucketType.guild) - @commands.command(aliases=['profilbilde', 'brukeravatar', 'useravatar']) - async def avatar(self, ctx, bruker: discord.Member = None): - """ - Viser avataren til en bruker - """ + embed = discord.Embed(color=discord_utils.get_color(bruker), description=roles) + embed.set_author(name=f'Roller ({len(bruker.roles)})', icon_url=bruker.display_avatar) + embed.set_footer(text=f'{bruker.name}#{bruker.discriminator}', icon_url=bruker.display_avatar) + await interaction.response.send_message(embed=embed) + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.checks.cooldown(1, 2) + @user_group.command(name="avatar", description="Hent avatar til en bruker") + async def user_avatar(self, interaction: discord.Interaction, bruker: discord.User | discord.Member | None = None): if not bruker: - bruker = ctx.author + bruker = interaction.user - if str(bruker.color) != '#000000': - color = bruker.color - else: - color = discord.Colour(0x99AAB5) - - url = bruker.display_avatar - - embed = discord.Embed(color=color, description=f'[Lenke]({url})') - embed.set_author(name=f'{bruker.name}#{bruker.discriminator}', icon_url=url) - embed.set_image(url=url) - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.guild_only() - @commands.cooldown(1, 2, commands.BucketType.guild) - @commands.command(aliases=['roleinfo', 'rolleinfo']) - async def rolle(self, ctx, *, rolle: discord.Role): - """ - Viser info om en rolle - """ - - if rolle.name == '@everyone': - embed = embed_templates.fatal(ctx, text='Skriv inn en annen rolle enn @everyone') - return await ctx.send(embed=embed) - - if str(rolle.color) != '#000000': - color = rolle.color - else: - color = discord.Colour(0x99AAB5) - - if rolle.mentionable: - mentionable = 'Ja' - else: - mentionable = 'Nei' - - if rolle.hoist: - hoisted = 'Ja' - else: - hoisted = 'Nei' - - rolle_created_date = rolle.created_at.strftime('%d. %b. %Y - %H:%M') - since_created_days = (ctx.message.created_at - rolle.created_at).days - - if since_created_days == 1: - since_created_days_string = 'dag' - else: - since_created_days_string = 'dager' - - members = [] - for member in rolle.members: - members.append(f'{member.name}#{member.discriminator}') - members = ', '.join(members) - - if len(members) > 1024: - members = 'For mange medlemmer for å vise her' - if len(members) == 0: - members = '**Ingen**' - - permissions = ', '.join([permission for permission, value in iter(rolle.permissions) if value is True]) - - embed = discord.Embed(title=rolle.name, description=f'{rolle.mention}\n**ID:** {rolle.id}', color=color) - embed.set_author(name=rolle.guild.name, icon_url=rolle.guild.icon) - embed.add_field(name='Fargekode', value=str(rolle.color)) - embed.add_field(name='Opprettet', value=f'{rolle_created_date}\n{since_created_days} ' + - f'{since_created_days_string} siden') - embed.add_field(name='Posisjon', value=rolle.position) - embed.add_field(name='Nevnbar', value=mentionable) - embed.add_field(name='Vises separat i medlemsliste', value=hoisted) - if permissions: - embed.add_field(name='Tillatelser', value=permissions, inline=False) - embed.add_field(name=f'Brukere med rollen ({len(rolle.members)})', value=members, inline=False) - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.guild_only() - @commands.cooldown(1, 5, commands.BucketType.guild) - @commands.command(aliases=['kanal', 'channel', 'channelinfo', 'textchannel']) - async def tekstkanal(self, ctx, *, kanal: discord.TextChannel): - """ - Viser info om en tekstkanal - """ - - nsfw = 'Nei' - if kanal.is_nsfw(): - nsfw = 'Ja' - - if kanal.slowmode_delay == 0: - saktemodus = 'Nei' - else: - saktemodus = f'Ja ({kanal.slowmode_delay} sekunder)' - - description = '**Ingen**' - if kanal.topic: - description = kanal.topic - - members = [] - for member in kanal.members: - members.append(f'{member.name}#{member.discriminator}') - members = ', '.join(members) - if len(members) > 1024: - members = 'For mange for å vise her' - - embed = discord.Embed(color=ctx.me.color, title=kanal.name, description=f'{kanal.mention}\nID: {kanal.id}') - embed.set_author(name=kanal.guild.name, icon_url=kanal.guild.icon) - embed.add_field(name='Beskrivelse', value=description, inline=False) - embed.add_field(name='Opprettet', value=kanal.created_at.strftime('%d. %b. %Y - %H:%M')) - embed.add_field(name='NSFW', value=nsfw) - embed.add_field(name='Saktemodus', value=saktemodus) - if kanal.category: - embed.add_field(name='Kategori', value=kanal.category.name) - embed.add_field(name=f'Antall med tilgang ({len(kanal.members)})', value=members) - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.guild_only() - @commands.cooldown(1, 5, commands.BucketType.guild) - @commands.command(aliases=['talekanalinfo', 'voicechannel']) - async def talekanal(self, ctx, *, kanal: discord.VoiceChannel): - """ - Viser info om en talekanal - """ - - if kanal.user_limit == 0: - limit = '∞ personer' - else: - limit = f'{kanal.user_limit} personer' - - embed = discord.Embed(color=ctx.me.color, title=kanal.name, description=f'ID: {kanal.id}') - embed.set_author(name=kanal.guild.name, icon_url=kanal.guild.icon) - embed.add_field(name='Opprettet', value=kanal.created_at.strftime('%d. %b. %Y - %H:%M')) - embed.add_field(name='Bitrate', value=f'{int(kanal.bitrate / 1000)}kbps') - embed.add_field(name='Maksgrense', value=limit) - if kanal.category: - embed.add_field(name='Kategori', value=kanal.category.name) - embed.add_field(name='Antall koblet til', value=len(kanal.members)) - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.guild_only() - @commands.cooldown(1, 5, commands.BucketType.guild) - @commands.command(aliases=['emote']) - async def emoji(self, ctx, emoji: discord.Emoji): - """ - Viser info om en CUSTOM emoji som tilhører serveren - """ - - animated = 'Nei' - if emoji.animated: - animated = 'Ja' - - emoji = await emoji.guild.fetch_emoji(emoji.id) - try: - emoji_creator = f'{emoji.user.mention}\n{emoji.user.name}#{emoji.user.discriminator}' - except AttributeError: - emoji_creator = 'Jeg trenger `manage_emojis`-tillatelsen på serveren den er fra for å hente dette' - - embed = discord.Embed(color=ctx.me.color, title=emoji.name, description=f'ID: {emoji.id}') - embed.set_author(name=emoji.guild.name, icon_url=emoji.guild.icon) - embed.add_field(name='Opprettet', value=emoji.created_at.strftime('%d. %b. %Y - %H:%M')) - embed.add_field(name='Animert', value=animated) - embed.add_field(name='Lagt til av', value=emoji_creator) - embed.set_image(url=emoji.url) - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.guild_only() - @commands.cooldown(1, 5, commands.BucketType.guild) - @commands.command(aliases=['serveremoji', 'serveremotes']) - async def guildemoji(self, ctx): - """ - Viser alle emoji som serveren har - """ - - embed = discord.Embed(colour=ctx.me.color) - embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon) - - emoji_string = '' - for emoji in ctx.guild.emojis: - if len(emoji_string) > 2000: - embed.description = emoji_string - await ctx.send(embed=embed) - emoji_string = '' - emoji_string += f'{emoji} ' - - embed.description = emoji_string - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.guild_only() - @commands.cooldown(1, 5, commands.BucketType.guild) - @commands.command(aliases=['oldest']) - async def eldst(self, ctx, side=0): - """ - Liste over de eldste brukerene på serveren - """ - - members = sorted(ctx.guild.members, key=lambda m: m.created_at) - - formatted_members = [] - for member in members: - member_created_date = member.created_at.strftime('%d. %b. %Y - %H:%M') - member_index = (members.index(member) + 1) - formatted_members.append( - f'**#{member_index}** {member.name}#{member.discriminator} - {member_created_date}' - ) - - page_data = misc_utils.paginator(formatted_members, int(side)) - side = page_data['page'] - pagecount = page_data['pagecount'] - members = '\n'.join(page_data['page_content']) - - embed = discord.Embed(color=ctx.me.color) - embed.add_field(name='Eldste Discordbrukerene på serveren', value=members) - embed.set_footer(text=f'Side: {side}/{pagecount}') - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.guild_only() - @commands.cooldown(1, 5, commands.BucketType.guild) - @commands.command() - async def joinorder(self, ctx, side=0): - """ - Liste over de første medlemmene av serveren - """ - - members = sorted(ctx.guild.members, key=lambda m: m.joined_at) - - formatted_members = [] - for member in members: - member_joined_date = member.joined_at.strftime('%d. %b. %Y - %H:%M') - member_index = (members.index(member) + 1) - formatted_members.append(f'**#{member_index}** {member.name}#{member.discriminator} - {member_joined_date}') - - page_data = misc_utils.paginator(formatted_members, int(side)) - side = page_data['page'] - pagecount = page_data['pagecount'] - members = '\n'.join(page_data['page_content']) - - embed = discord.Embed(color=ctx.me.color) - embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon) - embed.add_field(name='Første Discordbrukerene på serveren', value=members) - embed.set_footer(text=f'Side: {side}/{pagecount}') - await ctx.send(embed=embed) + embed = discord.Embed(color=discord_utils.get_color(bruker), description=f'[Lenke]({bruker.display_avatar})') + embed.set_author(name=f'{bruker.name}#{bruker.discriminator}', icon_url=bruker.display_avatar) + embed.set_image(url=bruker.display_avatar) + embed = embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) async def setup(bot): From 385b030a0a4d275c86bbd50f91c18a722688bf7e Mon Sep 17 00:00:00 2001 From: LBlend Date: Wed, 16 Nov 2022 02:52:21 +0100 Subject: [PATCH 30/54] initial migration and refactor of misc cog --- src/cogs/misc.py | 305 +++++++++++++++++++---------------------------- 1 file changed, 123 insertions(+), 182 deletions(-) diff --git a/src/cogs/misc.py b/src/cogs/misc.py index 6b9d8e0..87a4ac7 100644 --- a/src/cogs/misc.py +++ b/src/cogs/misc.py @@ -1,15 +1,14 @@ -from discord.ext import commands -import discord - -from re import sub from datetime import datetime -import requests -import json -from asyncio import sleep -from PIL import Image, ImageDraw, ImageFont +import re from os import remove import urllib +import discord +from discord import app_commands +from discord.ext import commands +from PIL import Image, ImageDraw, ImageFont +import requests + from cogs.utils import embed_templates from cogs.utils.misc_utils import ignore_exception @@ -19,98 +18,101 @@ class Misc(commands.Cog): self.bot = bot @commands.bot_has_permissions(embed_links=True) - @commands.cooldown(1, 2, commands.BucketType.guild) - @commands.command() - async def weeb(self, ctx): - """ - Kjeft på weebs - """ - - await ctx.send("<:sven:762725919604473866> Weebs 👉 <#803596668129509417>") + @app_commands.checks.cooldown(1, 2) + @app_commands.command() + async def weeb(self, interaction: discord.Interaction): + """Kjeft på weebs""" + await interaction.response.send_message("<:sven:762725919604473866> Weebs 👉 <#803596668129509417>") @commands.bot_has_permissions(embed_links=True) - @commands.cooldown(1, 2, commands.BucketType.guild) - @commands.command(aliases=["owoify", "uwu"]) - async def owo(self, ctx, *, tekst: str): - """ - Oversetter teksten din til owo - """ + @app_commands.checks.cooldown(1, 2) + @app_commands.command() + async def kweeb(self, interaction: discord.Interaction): + """Kjeft på weebs""" + await interaction.response.send_message( + """ + TBD + """ + ) + + @commands.bot_has_permissions(embed_links=True) + @app_commands.checks.cooldown(1, 2) + @app_commands.command() + async def owo(self, interaction: discord.Interaction, tekst: str): + """Oversetter teksten din til owo (we_eb)""" owo_rules = {"r": "w", "l": "w", "R": "W", "L": "W", "n": "ny", "N": "Ny", "ove": "uv"} for key, value in owo_rules.items(): - tekst = sub(key, value, tekst) + tekst = re.sub(key, value, tekst) # https://kaomoji.moe/ - if not tekst or len(tekst) >= 1000: - embed = embed_templates.error_warning(ctx, text="Teksten er for lang") - return await ctx.send(embed=embed) + if len(tekst) >= 1000: + embed = embed_templates.error_warning(interaction, text="Teksten er for lang") + return await interaction.response.send_message(embed=embed) - embed = discord.Embed(color=ctx.me.color, description=tekst) - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) + embed = discord.Embed(color=interaction.client.user.color, description=tekst) + embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) - @commands.command() - async def emne(self, ctx, emnekode: str): - - data = requests.get(f"https://data.uio.no/studies/v1/course/{emnekode}") + @app_commands.checks.cooldown(1, 5) + @app_commands.command() + async def emne(self, interaction: discord.Interaction, emnekode: str): + """For NTNU'ere som ikke kan emnekoder på UiO""" + data = requests.get(f"https://data.uio.no/studies/v1/course/{emnekode}", timeout=10) if data.status_code != 200: - return await ctx.send(embed=embed_templates.error_fatal(ctx, text="Fant ikke emnekode")) + return await interaction.response.send_message(embed=embed_templates.error_fatal(interaction, text="Fant ikke emnekode")) - emne = data.json()["info"]["name"] - await ctx.send(emne) + await interaction.response.send_message(data.json()["info"]["name"]) @commands.bot_has_permissions(embed_links=True) - @commands.cooldown(1, 2, commands.BucketType.guild) - @commands.command(aliases=["clapify"]) - async def klappifiser(self, ctx, *, tekst): - """ - Klapppifiserer teksten din - """ + @app_commands.checks.cooldown(1, 2) + @app_commands.command() + async def klappifiser(self, interation: discord.Interaction, tekst: str): + """Klapppifiserer teksten din""" + if len(tekst) >= 1000: + embed = embed_templates.error_warning(interation, text="Teksten er for lang") + return await interation.response.send_message(embed=embed) - if not tekst or len(tekst) >= 1000: - embed = embed_templates.error_warning(ctx, text="Teksten er for lang") - return await ctx.send(embed=embed) + tekst = re.sub(" ", "👏", tekst) - tekst = sub(" ", "👏", tekst) + embed = discord.Embed(color=interation.client.user.color, description=f"**{tekst.upper()}**") + embed_templates.default_footer(interation, embed) + await interation.response.send_message(embed=embed) - embed = discord.Embed(color=ctx.me.color, description=f"**{tekst.upper()}**") - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) - - @commands.cooldown(1, 5, commands.BucketType.user) - @commands.command(aliases=["filmsøk"]) - async def imdb(self, ctx, *, film): - """ - Se informasjon om en film eller serie - """ - - url = "http://www.omdbapi.com/?" + urllib.parse.urlencode({"s": film, "apikey": self.bot.api_keys["omdb"]}) - search = requests.get(url).json() + @app_commands.checks.cooldown(1, 5) + @app_commands.command() + async def imdb(self, interaction: discord.Interaction, tittel: str): + """Se informasjon om en film eller serie""" + # Search for movie/show + url = "http://www.omdbapi.com/?" + urllib.parse.urlencode({"s": tittel, "apikey": self.bot.api_keys["omdb"]}) + search = requests.get(url, timeout=10).json() try: best_result_id = search["Search"][0]["imdbID"] except KeyError: - embed = embed_templates.error_fatal(ctx, text="Fant ikke filmen!") - return await ctx.send(embed=embed) - - data = requests.get(f'http://www.omdbapi.com/?i={best_result_id}&apikey={self.bot.api_keys["omdb"]}').json() + embed = embed_templates.error_fatal(interaction, text="Fant ikke filmen!") + return await interaction.response.send_message(embed=embed) + # Get movie/show info + data = requests.get(f'http://www.omdbapi.com/?i={best_result_id}&apikey={self.bot.api_keys["omdb"]}', timeout=10).json() acceptable_media_types = ["movie", "series", "episode"] if data["Type"] not in acceptable_media_types: - embed = embed_templates.error_fatal(ctx, text="Fant ikke filmen!") - return await ctx.send(embed=embed) + embed = embed_templates.error_fatal(interaction, text="Fant ikke filmen!") + return await interaction.response.send_message(embed=embed) embed = discord.Embed( title=f'{data["Title"]} ({data["Year"]})', color=0xF5C518, url=f'https://www.imdb.com/title/{data["imdbID"]}/', ) - embed.set_author(name=ctx.author.name, icon_url=ctx.author.display_avatar) + embed.set_author(name=interaction.user.name, icon_url=interaction.user.display_avatar) embed.add_field(name="Type", value=data["Type"].title()) embed.add_field(name="Sjanger", value=data["Genre"]) embed.add_field(name="Spilletid", value=data["Runtime"]) embed.add_field(name="Vurdering på IMDb", value=f'{data["imdbRating"]}/10') - embed.set_footer(text=f'Utgitt: {data["Released"]}') + release_date = datetime.strptime(data["Released"], "%d %b %Y") + embed.add_field(name="Utgitt", value=discord.utils.format_dt(release_date, style="D")) + embed_templates.default_footer(interaction, embed) if data["Poster"] != "N/A": embed.set_thumbnail(url=data["Poster"]) @@ -119,152 +121,91 @@ class Misc(commands.Cog): if data["Plot"] != "N/A" and len(data["Plot"]) < 1024: embed.add_field(name="Sammendrag", value=data["Plot"], inline=False) - await ctx.send(embed=embed) + await interaction.response.send_message(embed=embed) @commands.bot_has_permissions(embed_links=True) - @commands.cooldown(1, 5, commands.BucketType.guild) - @commands.command(aliases=["identify"]) - async def identifiser(self, ctx, bilde=None): - """ - Beskriver hva som er på bildet - """ + @app_commands.checks.cooldown(1, 5) + @app_commands.command() + async def helligdager(self, interaction: discord.Interaction, *, land: str = "NO", år: int = None): + """Se hellidagene i et land""" + land = land.upper() + aar = datetime.now().year if not år else int(år) - if ctx.message.attachments != []: - bilde = ctx.message.attachments[0].url - - if bilde is None: - embed = embed_templates.error_fatal(ctx, text="Du må gi meg et bilde!") - - async with ctx.channel.typing(): - - payload = {"inputs": [{"data": {"image": {"url": bilde}}}]} - header = {"Authorization": f'Key {self.bot.api_keys["clarifai"]}'} - url = ( - "https://api.clarifai.com/v2/models/aaa03c23b3724a16a56b629203edc62c" - + "/versions/aa7f35c01e0642fda5cf400f543e7c40/outputs" - ) - data = requests.post(url, data=json.dumps(payload), headers=header) - if data.status_code != 200: - embed = embed_templates.default_footer("API request feilet") - return await ctx.send(embed=embed) - - data = data.json() - - words = [] - for i, concepts in enumerate(data["outputs"][0]["data"]["concepts"]): - words.append(concepts["name"]) - if i >= 5: - break - - words = ", ".join(words) - - await sleep(2) - - embed = discord.Embed(color=ctx.me.color) - embed.set_author(name="Clarifai AI", icon_url="https://github.com/Clarifai.png") - embed.description = f"**Ord som beskriver dette bildet:**\n\n{words}" - embed.set_image(url=bilde) - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.cooldown(1, 5, commands.BucketType.guild) - @commands.command(aliases=["holidays", "fridager", "ferie"]) - async def helligdager(self, ctx, land=None, aar=None): - """ - Se hellidagene i et land - """ - - if not land: - land = "NO" - else: - land = land.upper() - - if not aar: - aar = datetime.now().year - else: - int(aar) - - data = requests.get(f"https://date.nager.at/api/v2/publicholidays/{aar}/{land}") + data = requests.get(f"https://date.nager.at/api/v2/publicholidays/{aar}/{land}", timeout=10) if data.status_code != 200: embed = embed_templates.error_fatal( - ctx, text="Ugyldig land\nHusk å bruke landskoder\n" + "For eksempel: `NO`" + interaction, text="Ugyldig land\nHusk å bruke landskoder\n" + "For eksempel: `NO`" ) - return await ctx.send(embed=embed) + return await interaction.response.send_message(embed=embed) data = data.json() country = data[0]["countryCode"].lower() + + # Construct output holiday_str = "" for day in data: - date = day["date"] - date = datetime.strptime(date, "%Y-%m-%d").strftime("%d. %B") + date = discord.utils.format_dt(datetime.strptime(day["date"], "%Y-%m-%d"), style="D") holiday_str += f'**{date}**: {day["localName"]}\n' - embed = discord.Embed(color=ctx.me.color, title=f":flag_{country}: Helligdager {aar} :flag_{country}:") + embed = discord.Embed(color=interaction.client.user.color, title=f":flag_{country}: Helligdager {aar} :flag_{country}:") embed.description = holiday_str - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) + embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) @commands.bot_has_permissions(embed_links=True) - @commands.cooldown(1, 5, commands.BucketType.guild) - @commands.command() - async def match(self, ctx, *, bruker: discord.Member = None): + @app_commands.checks.cooldown(1, 5) + @app_commands.command() + async def match(self, interaction: discord.Interaction, bruker: discord.Member): """ Se hvor mye du matcher med en annen """ - - if not bruker: - embed = embed_templates.error_warning(ctx, text="Du må gi meg en bruker") - return await ctx.send(embed=embed) - if bruker == ctx.author: + if bruker == interaction.user: embed = embed_templates.error_warning( - ctx, text="Jeg vet du er ensom, men du kan " "ikke matche med deg selv" + interaction, text="Jeg vet du er ensom, men du kan " "ikke matche med deg selv" ) - return await ctx.send(embed=embed) + return await interaction.response.send_message(embed=embed) - async with ctx.channel.typing(): + invoker_id = int(str(interaction.user.id)[11:14]) + user_id = int(str(bruker.id)[11:14]) - invoker_id = int(str(ctx.author.id)[11:14]) - user_id = int(str(bruker.id)[11:14]) + match_percent = int((invoker_id + user_id) % 100) - match_percent = int((invoker_id + user_id) % 100) + if bruker.id == self.bot.user.id: + match_percent = 100 - if bruker.id == self.bot.user.id: - match_percent = 100 + await interaction.user.display_avatar.save(fp=f"./src/assets/temp/{interaction.user.id}_raw.png") + await bruker.display_avatar.save(fp=f"./src/assets/temp/{bruker.id}_raw.png") - await ctx.author.display_avatar.save(fp=f"./src/assets/temp/{ctx.author.id}_raw.png") - await bruker.display_avatar.save(fp=f"./src/assets/temp/{bruker.id}_raw.png") + invoker = Image.open(f"./src/assets/temp/{interaction.user.id}_raw.png").convert("RGBA") + invoker = invoker.resize((389, 389), Image.ANTIALIAS) + user = Image.open(f"./src/assets/temp/{bruker.id}_raw.png").convert("RGBA") + user = user.resize((389, 389), Image.ANTIALIAS) + heart = Image.open("./src/assets/misc/heart.png") + mask = Image.open("./src/assets/misc/heart.png", "r") - invoker = Image.open(f"./src/assets/temp/{ctx.author.id}_raw.png").convert("RGBA") - invoker = invoker.resize((389, 389), Image.ANTIALIAS) - user = Image.open(f"./src/assets/temp/{bruker.id}_raw.png").convert("RGBA") - user = user.resize((389, 389), Image.ANTIALIAS) - heart = Image.open("./src/assets/misc/heart.png") - mask = Image.open("./src/assets/misc/heart.png", "r") + image = Image.new("RGBA", (1024, 576)) + image.paste(invoker, (0, 94)) + image.paste(user, (635, 94)) + image.paste(heart, (311, 94), mask=mask) + draw = ImageDraw.Draw(image) + font = ImageFont.truetype("./src/assets/fonts/RobotoMono-Medium.ttf", 86) + font_size = font.getsize(f"{match_percent}%") + font_size = ((image.size[0] - font_size[0]) / 2, (image.size[1] - font_size[1]) / 2) + draw.text(font_size, f"{match_percent}%", font=font, fill=(255, 255, 255, 255)) - image = Image.new("RGBA", (1024, 576)) - image.paste(invoker, (0, 94)) - image.paste(user, (635, 94)) - image.paste(heart, (311, 94), mask=mask) - draw = ImageDraw.Draw(image) - font = ImageFont.truetype("./src/assets/fonts/RobotoMono-Medium.ttf", 86) - font_size = font.getsize(f"{match_percent}%") - font_size = ((image.size[0] - font_size[0]) / 2, (image.size[1] - font_size[1]) / 2) - draw.text(font_size, f"{match_percent}%", font=font, fill=(255, 255, 255, 255)) + image.save(f"./src/assets/temp/{interaction.user.id}_{bruker.id}_edit.png") - image.save(f"./src/assets/temp/{ctx.author.id}_{bruker.id}_edit.png") + f = discord.File(f"./src/assets/temp/{interaction.user.id}_{bruker.id}_edit.png") + embed = discord.Embed() + embed.set_image(url=f"attachment://{interaction.user.id}_{bruker.id}_edit.png") + embed = embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed, file=f) - f = discord.File(f"./src/assets/temp/{ctx.author.id}_{bruker.id}_edit.png") - embed = discord.Embed() - embed.set_image(url=f"attachment://{ctx.author.id}_{bruker.id}_edit.png") - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed, file=f) - - with ignore_exception(OSError): - remove(f"./src/assets/temp/{bruker.id}_raw.png") - remove(f"./src/assets/temp/{ctx.author.id}_raw.png") - remove(f"./src/assets/temp/{ctx.author.id}_{bruker.id}_edit.png") + with ignore_exception(OSError): + remove(f"./src/assets/temp/{bruker.id}_raw.png") + remove(f"./src/assets/temp/{interaction.user.id}_raw.png") + remove(f"./src/assets/temp/{interaction.user.id}_{bruker.id}_edit.png") async def setup(bot): From 6404a71c13ff9b8d454d0f8ff45c25dfeb72d19f Mon Sep 17 00:00:00 2001 From: LBlend Date: Wed, 16 Nov 2022 03:11:26 +0100 Subject: [PATCH 31/54] remove redundant assignment --- src/cogs/info.py | 14 +++++++------- src/cogs/misc.py | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cogs/info.py b/src/cogs/info.py index 38a1145..9e53e9d 100644 --- a/src/cogs/info.py +++ b/src/cogs/info.py @@ -232,7 +232,7 @@ class Info(commands.Cog): embed = discord.Embed(color=interaction.guild.me.color, description=f'[Lenke]({interaction.guild.icon})') embed.set_author(name=interaction.guild.name, icon_url=interaction.guild.icon) embed.set_image(url=interaction.guild.icon) - embed = embed_templates.default_footer(interaction, embed) + embed_templates.default_footer(interaction, embed) await interaction.response.send_message(embed=embed) @app_commands.checks.bot_has_permissions(embed_links=True) @@ -247,7 +247,7 @@ class Info(commands.Cog): embed = discord.Embed(color=interaction.guild.me.color, description=f'[Lenke]({interaction.guild.discovery_splash})') embed.set_author(name=interaction.guild.name, icon_url=interaction.guild.icon) embed.set_image(url=interaction.guild.discovery_splash) - embed = embed_templates.default_footer(interaction, embed) + embed_templates.default_footer(interaction, embed) await interaction.response.send_message(embed=embed) @app_commands.checks.bot_has_permissions(embed_links=True) @@ -262,7 +262,7 @@ class Info(commands.Cog): embed = discord.Embed(color=interaction.me.guild.color, description=f'[Lenke]({interaction.guild.banner})') embed.set_author(name=interaction.guild.name, icon_url=interaction.guild.icon) embed.set_image(url=interaction.guild.banner) - embed = embed_templates.default_footer(interaction, embed) + embed_templates.default_footer(interaction, embed) await interaction.response.send_message(embed=embed) @app_commands.checks.bot_has_permissions(embed_links=True) @@ -296,7 +296,7 @@ class Info(commands.Cog): if permissions: embed.add_field(name='Tillatelser', value=permissions, inline=False) embed.add_field(name=f'Brukere med rollen ({len(rolle.members)})', value=members, inline=False) - embed = embed_templates.default_footer(interaction, embed) + embed_templates.default_footer(interaction, embed) await interaction.response.send_message(embed=embed) @app_commands.checks.bot_has_permissions(embed_links=True) @@ -317,7 +317,7 @@ class Info(commands.Cog): if kanal.category: embed.add_field(name='Kategori', value=kanal.category.name) embed.add_field(name=f'Antall med tilgang ({len(kanal.members)})', value=members) - embed = embed_templates.default_footer(interaction, embed) + embed_templates.default_footer(interaction, embed) await interaction.response.send_message(embed=embed) @app_commands.checks.bot_has_permissions(embed_links=True) @@ -333,7 +333,7 @@ class Info(commands.Cog): if kanal.category: embed.add_field(name='Kategori', value=kanal.category.name) embed.add_field(name='Antall koblet til', value=len(kanal.members)) - embed = embed_templates.default_footer(interaction, embed) + embed_templates.default_footer(interaction, embed) await interaction.response.send_message(embed=embed) # @app_commands.checks.bot_has_permissions(embed_links=True) @@ -516,7 +516,7 @@ class Info(commands.Cog): embed = discord.Embed(color=discord_utils.get_color(bruker), description=f'[Lenke]({bruker.display_avatar})') embed.set_author(name=f'{bruker.name}#{bruker.discriminator}', icon_url=bruker.display_avatar) embed.set_image(url=bruker.display_avatar) - embed = embed_templates.default_footer(interaction, embed) + embed_templates.default_footer(interaction, embed) await interaction.response.send_message(embed=embed) diff --git a/src/cogs/misc.py b/src/cogs/misc.py index 87a4ac7..da07a30 100644 --- a/src/cogs/misc.py +++ b/src/cogs/misc.py @@ -199,7 +199,7 @@ class Misc(commands.Cog): f = discord.File(f"./src/assets/temp/{interaction.user.id}_{bruker.id}_edit.png") embed = discord.Embed() embed.set_image(url=f"attachment://{interaction.user.id}_{bruker.id}_edit.png") - embed = embed_templates.default_footer(interaction, embed) + embed_templates.default_footer(interaction, embed) await interaction.response.send_message(embed=embed, file=f) with ignore_exception(OSError): From 374c6780b4f77f5c21e66e8c7589732351e74439 Mon Sep 17 00:00:00 2001 From: LBlend Date: Wed, 16 Nov 2022 16:39:30 +0100 Subject: [PATCH 32/54] migrate to slash commands & (small) refactor --- src/cogs/anime.py | 2160 +++++++++++++++++++++------------------------ 1 file changed, 1015 insertions(+), 1145 deletions(-) diff --git a/src/cogs/anime.py b/src/cogs/anime.py index 5f09541..547cc91 100644 --- a/src/cogs/anime.py +++ b/src/cogs/anime.py @@ -1,651 +1,761 @@ -from discord.ext import commands -import discord +from datetime import datetime +import re -from requests import post -from re import compile, sub +import discord +from discord import app_commands +from discord.ext import commands +import requests from cogs.utils import embed_templates -async def set_color(color: str) -> int: - """ - Converts anilist colors to hext - - Parameters - ---------- - color: The color string returned from the Anilist API - - Returns - ---------- - (int): The color in hex for use in discord embeds - """ - - colors = { - 'blue': 0x3db4f2, - 'purple': 0xc063ff, - 'pink': 0xFC9DD6, - 'orange': 0xEF881A, - 'red': 0xE13333, - 'green': 0x4CCA51, - 'gray': 0x677B94 - } - if color in colors: - color = colors[color] - else: - color = color.replace('#', '0x') - color = int(color, 0) - return color - - -async def convert_media_format(media_format: str): - """ - Translates media format names into norwegian - - Parameters - ---------- - media_format: The english media format name returned from the Anilist API - - Returns - ---------- - (str): The media format name in norwegian. If a translation doesn't exist it returns the english name - """ - - media_formats = { - 'TV': 'TV-Serie', - 'TV_SHORT': 'Kort TV-Serie', - 'MOVIE': 'Film', - 'SPECIAL': 'Ekstramateriale', - 'MUSIC': 'Musikk', - 'MANGA': 'Manga', - 'NOVEL': 'Novelle', - 'ONE_SHOT': 'Kort Manga' - } - try: - return media_formats[media_format] - except KeyError: - return media_format - - -async def convert_language_names(language_name: str): - """ - Translates language names into norwegian - - Parameters - ---------- - language_name: The english language name returned from the Anilist API - - Returns - ---------- - (str): The language name in norwegian. If a translation doesn't exist it returns the english name - """ - - languages = { - 'JAPANESE': 'Japansk', - 'ENGLISH': 'Engelsk', - 'KOREAN': 'Koreansk', - 'ITALIAN': 'Italiensk', - 'SPANISH': 'Spansk', - 'PORTUGUESE': 'Portugisisk', - 'FRENCH': 'Fransk', - 'GERMAN': 'Tysk', - 'HEBREW': 'Hebraisk', - 'HUNGARIAN': 'Ungarsk' - } - try: - return languages[language_name] - except KeyError: - return language_name - - -async def convert_role_names(role_name: str): - """ - Translates role names into norwegian - - Parameters - ---------- - role_name: The english role name returned from the Anilist API - - Returns - ---------- - (str): The role name in norwegian. If a translation doesn't exist it returns the english name - """ - - roles = { - 'MAIN': 'Hovedkarakter', - 'SUPPORTING': 'Bikarakter/Biperson', - 'BACKGROUND': 'Statist' - } - try: - return roles[role_name] - except KeyError: - return role_name - - -async def convert_status(status: str): - """ - Translates role names into norwegian - - Parameters - ---------- - status: The english status name returned from the Anilist API - - Returns - ---------- - (str): The status name in norwegian. If a translation doesn't exist it returns the english name - """ - - statuses = { - 'FINISHED': 'Fullt utgitt', - 'RELEASING': 'Pågående', - 'NOT_YET_RELEASED': 'Ikke utgitt enda', - 'CANCELLED': 'Kansellert' - } - try: - return statuses[status] - except KeyError: - return status - - -async def remove_html(text): - """ - Removes HTML tag elements - - Parameters - ---------- - text: The raw HTML - - Returns - ---------- - (str): The input string without HTML tags - """ - - compiled = compile(r'<.*?>') - clean_text = sub(compiled, '', text) - return clean_text - - class Anime(commands.Cog): def __init__(self, bot): self.bot = bot - @commands.bot_has_permissions(embed_links=True) - @commands.cooldown(1, 5, commands.BucketType.guild) - @commands.group() - async def anilist(self, ctx): + anilist = app_commands.Group(name='anilist', description='Hent informasjon fra Anilist') + anilist_profile = app_commands.Group(parent=anilist, name='profil', description='Hent informasjon om en bruker på Anilist') + + def __convert_color(self, color: str) -> int: """ - Se informasjon om noe fra Anilist + Converts anilist colors to hext + + Parameters + ---------- + color: The color string returned from the Anilist API + + Returns + ---------- + (int): The color in hex for use in discord embeds + """ + colors = { + 'blue': 0x3db4f2, + 'purple': 0xc063ff, + 'pink': 0xFC9DD6, + 'orange': 0xEF881A, + 'red': 0xE13333, + 'green': 0x4CCA51, + 'gray': 0x677B94 + } + return colors.get(color, 0x677B94) + + def __convert_media_format(self, media_format: str) -> str: + """ + Translates media format names into norwegian + + Parameters + ---------- + media_format: The english media format name returned from the Anilist API + + Returns + ---------- + (str): The media format name in norwegian. If a translation doesn't exist it returns the english name + """ + media_formats = { + 'TV': 'TV-Serie', + 'TV_SHORT': 'Kort TV-Serie', + 'MOVIE': 'Film', + 'SPECIAL': 'Ekstramateriale', + 'MUSIC': 'Musikk', + 'MANGA': 'Manga', + 'NOVEL': 'Novelle', + 'ONE_SHOT': 'Kort Manga' + } + return media_formats.get(media_format, media_format) + + def __convert_language_names(self, language_name: str) -> str: + """ + Translates language names into norwegian + + Parameters + ---------- + language_name: The english language name returned from the Anilist API + + Returns + ---------- + (str): The language name in norwegian. If a translation doesn't exist it returns the english name + """ + languages = { + 'JAPANESE': 'Japansk', + 'ENGLISH': 'Engelsk', + 'KOREAN': 'Koreansk', + 'ITALIAN': 'Italiensk', + 'SPANISH': 'Spansk', + 'PORTUGUESE': 'Portugisisk', + 'FRENCH': 'Fransk', + 'GERMAN': 'Tysk', + 'HEBREW': 'Hebraisk', + 'HUNGARIAN': 'Ungarsk' + } + return languages.get(language_name, language_name) + + def __convert_role_names(self, role_name: str) -> str: + """ + Translates role names into norwegian + + Parameters + ---------- + role_name: The english role name returned from the Anilist API + + Returns + ---------- + (str): The role name in norwegian. If a translation doesn't exist it returns the english name """ - if ctx.invoked_subcommand is None: - await ctx.send_help(ctx.command) + roles = { + 'MAIN': 'Hovedkarakter', + 'SUPPORTING': 'Bikarakter/Biperson', + 'BACKGROUND': 'Statist' + } + try: + return roles[role_name] + except KeyError: + return role_name - @anilist.command(aliases=['profile']) - async def profil(self, ctx, *, bruker: str): + def __convert_status(self, status: str) -> str: """ - Viser informasjon om en Anilist-profil + Translates role names into norwegian + + Parameters + ---------- + status: The english status name returned from the Anilist API + + Returns + ---------- + (str): The status name in norwegian. If a translation doesn't exist it returns the english name """ + statuses = { + 'FINISHED': 'Fullt utgitt', + 'RELEASING': 'Pågående', + 'NOT_YET_RELEASED': 'Ikke utgitt enda', + 'CANCELLED': 'Kansellert' + } + return statuses.get(status, status) - async with ctx.channel.typing(): + def __remove_html(self, text: str) -> str: + """ + Removes HTML tag elements - # GraphQL - query = """ - query ($name: String) { - User (name: $name) { - name - siteUrl - avatar { - large + Parameters + ---------- + text: The raw HTML + + Returns + ---------- + (str): The input string without HTML tags + """ + compiled = re.compile(r'<.*?>') + clean_text = re.sub(compiled, '', text) + return clean_text + + async def __request_anilist(self, interaction: discord.Interaction, query: str, variables: dict, key: str) -> tuple[dict, str] | tuple[None, None]: + """ + Makes a request to the Anilist API + + Parameters + ---------- + query: The GraphQL query + variables: The variables for the query + key: The key to return from the response + + Returns + ---------- + (dict): The response from the Anilist API + """ + try: + url = 'https://graphql.anilist.co' + response = requests.post(url, json={'query': query, 'variables': variables}, timeout=10) + response = response.json() + data = response['data'][key] + url = data['siteUrl'] + except TypeError: + embed = embed_templates.error_fatal(interaction, text='Kunne ikke finne det du søkte etter!') + await interaction.response.send_message(embed=embed) + return None, None + + return data, url + + def __construct_favorite_media_string(self, media: list) -> str: + """ + Constructs a string with the user's favorite media + + Parameters + ---------- + media: The list of media returned from the Anilist API + + Returns + ---------- + (str): The constructed string + """ + favorite_media = [] + for node in media: + try: + nsfwtag = '🔞' if node['isAdult'] else '' + anime_name = node['title']['romaji'] + anime_url = node['siteUrl'] + favorite_media.append(f'[{anime_name}]({anime_url}){nsfwtag}') + except KeyError: + pass + return ' | '.join(favorite_media) + + def __construct_favorite_entity_string(self, entities: list, studio=False) -> str: + """ + Constructs a string with the user's favorite entities + + Parameters + ---------- + entity: The list of entities returned from the Anilist API + + Returns + ---------- + (str): The constructed string + """ + favortie_entities = [] + for entity in entities: + try: + if studio: + entity_name = entity['name'] + else: + entity_name = entity['name']["full"] + entity_url = entity['siteUrl'] + favortie_entities.append(f'[{entity_name}]({entity_url})') + except KeyError: + pass + return ' | '.join(favortie_entities) + + def __construct_release_schedule_string(self, numbers: dict) -> str: + """ + Constructs a string with the media's release schedule + + Parameters + ---------- + numbers (dict): dict containing release date and end date + + Returns + ---------- + (str): The constructed string + """ + # Release and end date formatting + if numbers['start_day'] != '?' and numbers['start_month'] != '?' and numbers['start_year'] != '?': + release_date = datetime(numbers['start_year'], numbers['start_month'], numbers['start_day']) + release_date = discord.utils.format_dt(release_date, style="d") + else: + release_date = f'{numbers["start_day"]}.{numbers["start_month"]}.{numbers["start_year"]}' + + if numbers['end_day'] != '?' and numbers['end_month'] != '?' and numbers['end_year'] != '?': + end_date = datetime(numbers['end_year'], numbers['end_month'], numbers['end_day']) + end_date = discord.utils.format_dt(end_date, style="d") + else: + end_date = f'{numbers["end_day"]}.{numbers["end_month"]}.{numbers["end_year"]}' + + if release_date == end_date: + date = release_date + else: + date = f'{release_date} - {end_date}' + + return date + + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.checks.cooldown(1, 5) + @anilist_profile.command(name='generelt', description='Viser informasjon om en Anilistprofil') + async def anilist_profile_general(self, interaction: discord.Interaction, bruker: str): + # GraphQL + query = """ + query ($name: String) { + User (name: $name) { + name + siteUrl + avatar { + large + } + options { + profileColor + } + statistics { + anime { + minutesWatched + statuses { + count + status + } } - options { - profileColor + manga { + chaptersRead + statuses { + count + status + } } - statistics { - anime { + } + favourites { + anime (perPage: 3) { + nodes { + isAdult + title { + romaji + } + siteUrl + } + } + manga (perPage: 3) { + nodes { + isAdult + title { + romaji + } + siteUrl + } + } + studios (perPage: 3) { + nodes { + name + siteUrl + } + } + characters (perPage: 3) { + nodes { + name { + full + } + siteUrl + } + } + staff (perPage: 3) { + nodes { + name { + full + } + siteUrl + } + } + } + } + } + """ + variables = { + 'name': bruker + } + data, url = await self.__request_anilist(interaction, query, variables, 'User') + + if not data: + return + + user_name = data['name'] + profile_pic = data['avatar']['large'] + days_watched = round(data['statistics']['anime']['minutesWatched'] / 1440, 1) + chapters_read = data['statistics']['manga']['chaptersRead'] + color = self.__convert_color(data['options']['profileColor']) + + anime_statuses = {status['status']: { 'count': status['count'] } for status in data['statistics']['anime']['statuses']} + manga_statuses = {status['status']: { 'count': status['count'] } for status in data['statistics']['manga']['statuses']} + + try: + anime_completed = anime_statuses['COMPLETED']['count'] + except KeyError: + anime_completed = 0 + try: + manga_completed = manga_statuses['COMPLETED']['count'] + except KeyError: + manga_completed = 0 + + favourite_anime = self.__construct_favorite_media_string(data['favourites']['anime']['nodes']) + favourite_manga = self.__construct_favorite_media_string(data['favourites']['manga']['nodes']) + favourite_character = self.__construct_favorite_entity_string(data['favourites']['characters']['nodes']) + favourite_staff = self.__construct_favorite_entity_string(data['favourites']['staff']['nodes']) + favourite_studio = self.__construct_favorite_entity_string(data['favourites']['studios']['nodes'], studio=True) + + embed = discord.Embed(title=user_name, color=color, url=url) + embed.set_author(name='Anilist', icon_url='https://anilist.co/img/logo_al.png') + embed.set_thumbnail(url=profile_pic) + embed.add_field(name='Antall dager sett', value=days_watched) + embed.add_field(name='Antall anime sett', value=anime_completed) + embed.add_field(name='Antall kapitler lest', value=chapters_read) + embed.add_field(name='Antall manga lest', value=manga_completed) + if favourite_anime: + embed.add_field(name='Noen favorittanime', value=favourite_anime, inline=False) + if favourite_manga: + embed.add_field(name='Noen favorittmanga', value=favourite_manga, inline=False) + if favourite_character: + embed.add_field(name='Noen favorittkarakterer', value=favourite_character, inline=False) + if favourite_studio: + embed.add_field(name='Noen favorittstudioer', value=favourite_studio, inline=False) + if favourite_studio: + embed.add_field(name='Noen favorittskapere', value=favourite_staff, inline=False) + embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) + + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.checks.cooldown(1, 5) + @anilist_profile.command(name='anime', description='Se statistikk om animetittingen til en bruker') + async def anilist_profile_anime_stats(self, interaction: discord.Interaction, bruker: str): + # GraphQL + query = """ + query ($name: String) { + User (name: $name) { + name + siteUrl + avatar { + large + } + options { + profileColor + } + statistics { + anime { + minutesWatched + episodesWatched + meanScore + statuses { + count + status minutesWatched - statuses { - count - status - } } - manga { - chaptersRead - statuses { - count - status - } - } - } - favourites { - anime (perPage: 3) { - nodes { - isAdult - title { - romaji - } - siteUrl - } - } - manga (perPage: 3) { - nodes { - isAdult - title { - romaji - } - siteUrl - } - } - studios (perPage: 3) { - nodes { + studios (limit: 3, sort: COUNT_DESC) { + studio { name siteUrl } } - characters (perPage: 3) { - nodes { - name { - full - } - siteUrl - } - } - staff (perPage: 3) { - nodes { - name { - full - } - siteUrl - } + genres (limit: 3, sort: COUNT_DESC) { + genre } } } } - """ - variables = { - 'name': bruker - } - try: - data = post( - 'https://graphql.anilist.co', - json={'query': query, 'variables': variables}).json() - data = data['data']['User'] - url = data['siteUrl'] - except TypeError: - embed = embed_templates.error_fatal(ctx, text='Kunne ikke finne brukeren!') - return await ctx.send(embed=embed) - - user_name = data['name'] - profile_pic = data['avatar']['large'] - days_watched = round(data['statistics']['anime']['minutesWatched'] / 1440, 1) - chapters_read = data['statistics']['manga']['chaptersRead'] - color = await set_color(data['options']['profileColor']) - - anime_statuses = {} - for status in data['statistics']['anime']['statuses']: - status_name = status['status'] - status_count = status['count'] - anime_statuses.update({f'{status_name}': {'count': status_count}}) - - manga_statuses = {} - for status in data['statistics']['manga']['statuses']: - status_name = status['status'] - status_count = status['count'] - manga_statuses.update({f'{status_name}': {'count': status_count}}) - - try: - anime_completed = anime_statuses['COMPLETED']['count'] - except KeyError: - anime_completed = 0 - try: - manga_completed = manga_statuses['COMPLETED']['count'] - except KeyError: - manga_completed = 0 - - favourite_anime = [] - for anime in data['favourites']['anime']['nodes']: - try: - if anime['isAdult'] is True: - nsfwtag = ' (NSFW)' - else: - nsfwtag = '' - anime_name = anime['title']['romaji'] - anime_url = anime['siteUrl'] - favourite_anime.append(f'[{anime_name}]({anime_url}){nsfwtag}') - except KeyError: - pass - favourite_anime = ' | '.join(favourite_anime) - - favourite_manga = [] - for manga in data['favourites']['manga']['nodes']: - try: - if manga['isAdult'] is True: - nsfwtag = ' (NSFW)' - else: - nsfwtag = '' - manga_name = manga['title']['romaji'] - manga_url = manga['siteUrl'] - favourite_manga.append(f'[{manga_name}]({manga_url}){nsfwtag}') - except KeyError: - pass - favourite_manga = ' | '.join(favourite_manga) - - favourite_character = [] - for character in data['favourites']['characters']['nodes']: - try: - character_name = character['name']['full'] - character_url = character['siteUrl'] - favourite_character.append(f'[{character_name}]({character_url})') - except KeyError: - pass - favourite_character = ' | '.join(favourite_character) - - favourite_staff = [] - for staff in data['favourites']['staff']['nodes']: - try: - staff_name = staff['name']['full'] - staff_url = staff['siteUrl'] - favourite_staff.append(f'[{staff_name}]({staff_url})') - except KeyError: - pass - favourite_staff = ' | '.join(favourite_staff) - - favourite_studio = [] - for studio in data['favourites']['studios']['nodes']: - try: - studio_name = studio['name'] - studio_url = studio['siteUrl'] - favourite_studio.append(f'[{studio_name}]({studio_url})') - except KeyError: - pass - favourite_studio = ' | '.join(favourite_studio) - - embed = discord.Embed(title=user_name, color=color, url=url) - embed.set_author(name='Anilist', icon_url='https://anilist.co/img/logo_al.png') - embed.set_thumbnail(url=profile_pic) - embed.add_field(name='Antall dager sett', value=days_watched) - embed.add_field(name='Antall anime sett', value=anime_completed) - embed.add_field(name='Antall kapitler lest', value=chapters_read) - embed.add_field(name='Antall manga lest', value=manga_completed) - if favourite_anime != '': - embed.add_field(name='Noen favorittanime', value=favourite_anime, inline=False) - if favourite_manga != '': - embed.add_field(name='Noen favorittmanga', value=favourite_manga, inline=False) - if favourite_character != '': - embed.add_field(name='Noen favorittkarakterer', value=favourite_character, inline=False) - if favourite_studio != '': - embed.add_field(name='Noen favorittstudioer', value=favourite_studio, inline=False) - if favourite_studio != '': - embed.add_field(name='Noen favorittskapere', value=favourite_staff, inline=False) - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) - - - @anilist.command(aliases=['anime']) - async def animestats(self, ctx, *, bruker: str): - """ - Viser animeinformasjon til en Anilist-profil + } """ + variables = { + 'name': bruker + } + data, url = await self.__request_anilist(interaction, query, variables, 'User') - async with ctx.channel.typing(): + if not data: + return - # GraphQL - query = """ - query ($name: String) { - User (name: $name) { - name - siteUrl - avatar { - large - } - options { - profileColor - } - statistics { - anime { - minutesWatched - episodesWatched - meanScore - statuses { - count - status - minutesWatched - } - studios (limit: 3, sort: COUNT_DESC) { - studio { - name - siteUrl - } - } - genres (limit: 3, sort: COUNT_DESC) { - genre - } - } - } + user_name = data['name'] + profile_pic = data['avatar']['large'] + days_watched = round(data['statistics']['anime']['minutesWatched'] / 1440, 1) + episodes_watched = data['statistics']['anime']['episodesWatched'] + color = self.__convert_color(data['options']['profileColor']) + + statuses = {status['status']: { 'count': status['count'], 'minutes': status['minutesWatched'] } for status in data['statistics']['anime']['statuses']} + + try: + completed = statuses['COMPLETED']['count'] + except KeyError: + completed = 0 + try: + watching = statuses['CURRENT']['count'] + except KeyError: + watching = 0 + try: + planning = statuses['PLANNING']['count'] + planning_days = round(statuses['PLANNING']['minutes'] / 1440, 1) + except KeyError: + planning = 0 + planning_days = 0 + try: + dropped = statuses['DROPPED']['count'] + except KeyError: + dropped = 0 + + anime_mean_score = data['statistics']['anime']['meanScore'] + if not anime_mean_score: + anime_mean_score = '**Ingen**' + + most_watched_genres = ', '.join([genre['genre'] for genre in data['statistics']['anime']['genres']]) + most_watched_studios = self.__construct_favorite_entity_string(data['statistics']['anime']['studios']) + + embed = discord.Embed(title=user_name, color=color, url=url) + embed.set_author(name='Anilist', icon_url='https://anilist.co/img/logo_al.png') + embed.set_thumbnail(url=profile_pic) + embed.add_field(name='Gj.snittsvurdering gitt', value=anime_mean_score) + embed.add_field(name='Antall dager sett', value=days_watched) + embed.add_field(name='Antall episoder sett', value=episodes_watched) + embed.add_field(name='Antall anime sett', value=completed) + embed.add_field(name='Ser på nå', value=watching) + embed.add_field(name='Planlegger å se', value=f'{planning}\n({planning_days} dager)') + embed.add_field(name='Droppet', value=dropped) + if most_watched_genres: + embed.add_field(name='Mest sette sjangere', value=most_watched_genres, inline=False) + if most_watched_studios: + embed.add_field(name='Mest sette studioer', value=most_watched_studios, inline=False) + embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) + + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.checks.cooldown(1, 5) + @anilist_profile.command(name='manga', description='Se statistikk om mangalesingen til en bruker') + async def anilist_profile_manga_stats(self, interaction: discord.Interaction, bruker: str): + # GraphQL + query = """ + query ($name: String) { + User(name: $name) { + name + siteUrl + avatar { + large } - } - """ - variables = { - 'name': bruker - } - try: - data = post( - 'https://graphql.anilist.co', - json={'query': query, 'variables': variables}).json() - data = data['data']['User'] - url = data['siteUrl'] - except TypeError: - embed = embed_templates.error_fatal(ctx, text='Kunne ikke finne brukeren!') - return await ctx.send(embed=embed) - - user_name = data['name'] - profile_pic = data['avatar']['large'] - days_watched = round(data['statistics']['anime']['minutesWatched'] / 1440, 1) - episodes_watched = data['statistics']['anime']['episodesWatched'] - color = await set_color(data['options']['profileColor']) - - statuses = {} - for status in data['statistics']['anime']['statuses']: - status_name = status['status'] - status_count = status['count'] - status_minutes = status['minutesWatched'] - statuses.update({f'{status_name}': {'count': status_count, 'minutes': status_minutes}}) - - try: - completed = statuses['COMPLETED']['count'] - except KeyError: - completed = 0 - try: - watching = statuses['CURRENT']['count'] - except KeyError: - watching = 0 - try: - planning = statuses['PLANNING']['count'] - planning_days = round(statuses['PLANNING']['minutes'] / 1440, 1) - except KeyError: - planning = 0 - planning_days = 0 - try: - dropped = statuses['DROPPED']['count'] - except KeyError: - dropped = 0 - - anime_mean_score = data['statistics']['anime']['meanScore'] - if anime_mean_score == 0: - anime_mean_score = '**Ingen**' - - genres = [] - for genre in data['statistics']['anime']['genres']: - genres.append(genre['genre']) - most_watched_genres = ', '.join(genres) - - studios = [] - for studio in data['statistics']['anime']['studios']: - studio_name = studio['studio']['name'] - studio_url = studio['studio']['siteUrl'] - studios.append(f'[{studio_name}]({studio_url})') - most_watched_studios = ' | '.join(studios) - - embed = discord.Embed(title=user_name, color=color, url=url) - embed.set_author(name='Anilist', icon_url='https://anilist.co/img/logo_al.png') - embed.set_thumbnail(url=profile_pic) - embed.add_field(name='Gj.snittsvurdering gitt', value=anime_mean_score) - embed.add_field(name='Antall dager sett', value=days_watched) - embed.add_field(name='Antall episoder sett', value=episodes_watched) - embed.add_field(name='Antall anime sett', value=completed) - embed.add_field(name='Ser på nå', value=watching) - embed.add_field(name='Planlegger å se', value=f'{planning}\n({planning_days} dager)') - embed.add_field(name='Droppet', value=dropped) - if most_watched_genres != '': - embed.add_field(name='Mest sette sjangere', value=most_watched_genres, inline=False) - if most_watched_studios != '': - embed.add_field(name='Mest sette studioer', value=most_watched_studios, inline=False) - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) - - @anilist.command(aliases=['manga']) - async def mangastats(self, ctx, *, bruker: str): - """ - Viser mnagainformasjonen til en Anilist-profil - """ - - async with ctx.channel.typing(): - - # GraphQL - query = """ - query ($name: String) { - User(name: $name) { - name - siteUrl - avatar { - large - } - options { - profileColor - } - statistics { - manga { + options { + profileColor + } + statistics { + manga { + chaptersRead + volumesRead + meanScore + statuses { + count + status chaptersRead - volumesRead - meanScore - statuses { - count - status - chaptersRead - } - staff (limit: 3, sort: COUNT_DESC) { - staff { - name { - full - } - siteUrl + } + staff (limit: 3, sort: COUNT_DESC) { + staff { + name { + full } + siteUrl } - genres (limit: 3, sort: COUNT_DESC) { - genre + } + genres (limit: 3, sort: COUNT_DESC) { + genre + } + } + } + } + } + """ + variables = { + 'name': bruker + } + data, url = await self.__request_anilist(interaction, query, variables, 'User') + + if not data: + return + + user_name = data['name'] + profile_pic = data['avatar']['large'] + chapters_read = round(data['statistics']['manga']['chaptersRead'] / 1440, 1) + volumes_read = data['statistics']['manga']['volumesRead'] + color = self.__convert_color(data['options']['profileColor']) + + statuses = {status['status']: { 'count': status['count'], 'minutes': status['chaptersRead'] } for status in data['statistics']['manga']['statuses']} + + try: + completed = statuses['COMPLETED']['count'] + except KeyError: + completed = 0 + try: + reading = statuses['CURRENT']['count'] + except KeyError: + reading = 0 + try: + planning = statuses['PLANNING']['count'] + planning_days = statuses['PLANNING']['chaptersRead'] + except KeyError: + planning = 0 + planning_days = 0 + try: + dropped = statuses['DROPPED']['count'] + except KeyError: + dropped = 0 + + manga_mean_score = data['statistics']['manga']['meanScore'] + if manga_mean_score == 0: + manga_mean_score = '**Ingen**' + + most_read_genres = ', '.join([genre['genre'] for genre in data['statistics']['manga']['genres']]) + most_read_staff = self.__construct_favorite_entity_string(data['statistics']['manga']['staff']) + + embed = discord.Embed(title=user_name, color=color, url=url) + embed.set_author(name='Anilist', icon_url='https://anilist.co/img/logo_al.png') + embed.set_thumbnail(url=profile_pic) + embed.add_field(name='Gj.snittsvurdering gitt', value=manga_mean_score) + embed.add_field(name='Antall kapitler lest', value=chapters_read) + embed.add_field(name='Antall volum lest', value=volumes_read) + embed.add_field(name='Antall manga lest', value=completed) + embed.add_field(name='Leser nå', value=reading) + embed.add_field(name='Planlegger å lese', value=f'{planning}\n({planning_days} kapitler)') + embed.add_field(name='Droppet', value=dropped) + if most_read_genres: + embed.add_field(name='Mest leste sjangere', value=most_read_genres, inline=False) + if most_read_staff: + embed.add_field(name='Mest leste skapere', value=most_read_staff, inline=False) + embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) + + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.checks.cooldown(1, 5) + @anilist.command(name='anime', description='Hent informasjon om en anime') + async def anilist_anime(self, interaction: discord.Interaction, navn: str): + # GraphQL + query = ''' + query ($search: String, $isMain: Boolean) { + Media (search: $search, type: ANIME) { + siteUrl + format + status + description (asHtml: false) + episodes + duration + genres + isAdult + bannerImage + meanScore + coverImage { + large + color + } + startDate { + year + month + day + } + endDate { + year + month + day + } + title { + romaji + english + native + } + studios (isMain: $isMain) { + nodes { + name + siteUrl + } + } + staff (sort: ROLE) { + edges { + role + node { + siteUrl + name { + first + last + native } } } } } - """ - variables = { - 'name': bruker - } + } + ''' + variables = { + 'search': navn, + 'isMain': True + } + data, url = await self.__request_anilist(interaction, query, variables, 'Media') - try: - data = post( - 'https://graphql.anilist.co', - json={'query': query, 'variables': variables}).json() - data = data['data']['User'] - url = data['siteUrl'] - except TypeError: - embed = embed_templates.error_fatal(ctx, text='Kunne ikke finne brukeren!') - return await ctx.send(embed=embed) + if not data: + return - user_name = data['name'] - profile_pic = data['avatar']['large'] - chapters_read = round(data['statistics']['manga']['chaptersRead'] / 1440, 1) - volumes_read = data['statistics']['manga']['volumesRead'] - color = await set_color(data['options']['profileColor']) + nsfw = data['isAdult'] + if nsfw: + embed = embed_templates.error_fatal(interaction, text='Animen du søkte på er NSFW. ' + + 'Gjør kommandoen i en NSFW-kanal i stedet') + return await interaction.response.send_message(embed=embed) - statuses = {} - for status in data['statistics']['manga']['statuses']: - status_name = status['status'] - status_count = status['count'] - status_minutes = status['chaptersRead'] - statuses.update({f'{status_name}': {'count': status_count, 'minutes': status_minutes}}) + cover_image = data['coverImage']['large'] + banner_image = data['bannerImage'] + mean_score = data['meanScore'] - try: - completed = statuses['COMPLETED']['count'] - except KeyError: - completed = 0 - try: - reading = statuses['CURRENT']['count'] - except KeyError: - reading = 0 - try: - planning = statuses['PLANNING']['count'] - planning_days = statuses['PLANNING']['chaptersRead'] - except KeyError: - planning = 0 - planning_days = 0 - try: - dropped = statuses['DROPPED']['count'] - except KeyError: - dropped = 0 + color = self.__convert_color(data['coverImage']['color']) - manga_mean_score = data['statistics']['manga']['meanScore'] - if manga_mean_score == 0: - manga_mean_score = '**Ingen**' + title_romaji = data['title']['romaji'] + title_native = data['title']['native'] + title_english = data['title']['english'] + titles = [title_romaji, title_native, title_english] + title_romaji, title_native, title_english = [title if title else '' for title in titles] - genres = [] - for genre in data['statistics']['manga']['genres']: - genres.append(genre['genre']) - most_read_genres = ', '.join(genres) + description = self.__remove_html(data['description']) if data['description'] else '' + genres = ', '.join(data['genres']) + studios_string = self.__construct_favorite_entity_string(data['studios']['nodes'], studio=True) - staff = [] - for staffmember in data['statistics']['manga']['staff']: - staff_name = staffmember['staff']['name']['full'] - staff_url = staffmember['staff']['siteUrl'] - staff.append(f'[{staff_name}]({staff_url})') - most_read_staff = ' | '.join(staff) + staff = data['staff']['edges'] + director_string = '' + for staff_member in staff: + if staff_member['role'] == 'Director': + director_first_name = staff_member['node']['name']['first'] + director_last_name = staff_member['node']['name']['last'] + director_native_name = staff_member['node']['name']['native'] + director_url = staff_member['node']['siteUrl'] + director_string += f'[{director_first_name} {director_last_name} ' +\ + f'({director_native_name})]({director_url})' - embed = discord.Embed(title=user_name, color=color, url=url) - embed.set_author(name='Anilist', icon_url='https://anilist.co/img/logo_al.png') - embed.set_thumbnail(url=profile_pic) - embed.add_field(name='Gj.snittsvurdering gitt', value=manga_mean_score) - embed.add_field(name='Antall kapitler lest', value=chapters_read) - embed.add_field(name='Antall volum lest', value=volumes_read) - embed.add_field(name='Antall manga lest', value=completed) - embed.add_field(name='Leser nå', value=reading) - embed.add_field(name='Planlegger å lese', value=f'{planning}\n({planning_days} kapitler)') - embed.add_field(name='Droppet', value=dropped) - if most_read_genres != '': - embed.add_field(name='Mest leste sjangere', value=most_read_genres, inline=False) - if most_read_staff != '': - embed.add_field(name='Mest leste skapere', value=most_read_staff, inline=False) - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) + episodes = data['episodes'] + duration = data['duration'] - @commands.bot_has_permissions(embed_links=True) - @commands.cooldown(1, 5, commands.BucketType.guild) - @commands.command() - async def anime(self, ctx, *, animenavn: str): - """ - Viser informasjon om en anime - """ + numbers = { + 'episodes': episodes, + 'duration': duration, + 'start_day': data['startDate'].get('day') if data['startDate'].get('day') else '?', + 'start_month': data['startDate'].get('month') if data['startDate'].get('month') else '?', + 'start_year': data['startDate'].get('year') if data['startDate'].get('year') else '?', + 'end_day': data['endDate'].get('day') if data['endDate'].get('day') else '?', + 'end_month': data['endDate'].get('month') if data['endDate'].get('month') else '?', + 'end_year': data['endDate'].get('year') if data['endDate'].get('year') else '?' + } - async with ctx.channel.typing(): + date = self.__construct_release_schedule_string(numbers) - # GraphQL - query = ''' - query ($search: String, $isMain: Boolean) { - Media (search: $search, type: ANIME) { + length_string = '' + if numbers['duration'] == 1: + length_string += 'minutt ' + else: + length_string += 'minutter ' + if numbers['episodes'] == 1: + length_string += 'lang' + else: + length_string += 'lange' + + # Status + status = data['status'] + status = self.__convert_status(status) + + # Format + media_format = data['format'] + media_format = self.__convert_media_format(media_format) + + embed = discord.Embed(color=color, title=title_romaji, url=url, + description=f'{title_native}\n{title_english}') + embed.set_thumbnail(url=cover_image) + embed.add_field(name='Format', value=media_format) + embed.add_field(name='Status', value=status) + if studios_string: + embed.add_field(name='Studio', value=studios_string) + if director_string: + embed.add_field(name='Regissør', value=director_string) + embed.add_field(name='Utgivelsesdato', value=date) + if media_format == 'Film': + embed.add_field(name='Lengde', value=f'{duration} minutter') + else: + embed.add_field(name='Episoder', value=f'{episodes} ({duration} {length_string})') + if mean_score: + embed.add_field(name='Gj.snittsvurdering', value=f'{mean_score}/100') + embed.add_field(name='Sjangere', value=genres) + if len(description) < 1024 and description: + embed.add_field(name='Sammendrag', value=description, inline=False) + if banner_image: + embed.set_image(url=banner_image) + embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) + + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.checks.cooldown(1, 5) + @anilist.command(name='manga', description='Hent informasjon om en manga') + async def anilist_manga(self, interaction: discord.Interaction, navn: str): + query = ''' + query ($search: String) { + Media (search: $search, type: MANGA) { siteUrl format status description (asHtml: false) - episodes - duration + volumes + chapters genres isAdult bannerImage @@ -669,13 +779,7 @@ class Anime(commands.Cog): english native } - studios (isMain: $isMain) { - nodes { - name - siteUrl - } - } - staff (sort: ROLE) { + staff(sort: ROLE) { edges { role node { @@ -691,588 +795,354 @@ class Anime(commands.Cog): } } ''' - variables = { - 'search': animenavn, - 'isMain': True + variables = { + 'search': navn + } + data, url = await self.__request_anilist(interaction, query, variables, 'Media') + + if not data: + return + + nsfw = data['isAdult'] + if nsfw: + embed = embed_templates.error_fatal(interaction, text='Mangaen du søkte på er NSFW. ' + + 'Gjør kommandoen i en NSFW-kanal i stedet') + return await interaction.response.send_message(embed=embed) + + cover_image = data['coverImage']['large'] + banner_image = data['bannerImage'] + mean_score = data['meanScore'] + + color = self.__convert_color(data['coverImage']['color']) + + title_romaji = data['title']['romaji'] + title_native = data['title']['native'] + title_english = data['title']['english'] + titles = [title_romaji, title_native, title_english] + title_romaji, title_native, title_english = [title if title else '' for title in titles] + + description = self.__remove_html(data['description']) if data.get('description') else '' + genres = ', '.join(data['genres']) + + staff = data['staff']['edges'] + staff_string = '' + for staff_member in staff: + staff_first_name = staff_member['node']['name']['first'] + staff_last_name = staff_member['node']['name']['last'] + staff_native_name = staff_member['node']['name']['native'] + staff_url = staff_member['node']['siteUrl'] + staff_string += f'[{staff_first_name} {staff_last_name} ({staff_native_name})]({staff_url})\n' + + numbers = { + 'chapters': data['chapters'], + 'volumes': data['volumes'], + 'start_day': data['startDate'].get('day') if data['startDate'].get('day') else '?', + 'start_month': data['startDate'].get('month') if data['startDate'].get('month') else '?', + 'start_year': data['startDate'].get('year') if data['startDate'].get('year') else '?', + 'end_day': data['endDate'].get('day') if data['endDate'].get('day') else '?', + 'end_month': data['endDate'].get('month') if data['endDate'].get('month') else '?', + 'end_year': data['endDate'].get('year') if data['endDate'].get('year') else '?' + } + date = self.__construct_release_schedule_string(numbers) + + chapters_string = '' + if numbers['chapters'] == 1: + chapters_string += 'kapittel' + else: + chapters_string += 'kapitler' + + status = data['status'] + status = self.__convert_status(status) + + media_format = data['format'] + media_format = self.__convert_media_format(media_format) + + embed = discord.Embed(color=color, title=title_romaji, url=url, + description=f'{title_native}\n{title_english}') + embed.set_thumbnail(url=cover_image) + embed.add_field(name='Format', value=media_format) + embed.add_field(name='Status', value=status) + if staff_string: + embed.add_field(name='Laget av', value=staff_string) + embed.add_field(name='Utgivelsesdato', value=date) + embed.add_field(name='Lengde', value=f'{numbers["chapters"]} {chapters_string}\n{numbers["volumes"]} volum') + if mean_score: + embed.add_field(name='Gj.snittsvurdering', value=f'{mean_score}/100') + embed.add_field(name='Sjangere', value=genres) + if len(description) < 1024 and description: + embed.add_field(name='Sammendrag', value=description, inline=False) + if banner_image: + embed.set_image(url=banner_image) + embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) + + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.checks.cooldown(1, 5) + @anilist.command(name='karakter', description='Hent informasjon om en karakter') + async def anilist_character(self, interaction: discord.Interaction, navn: str): + query = ''' + query ($search: String) { + Character (search: $search) { + name { + full + native + } + siteUrl + favourites + image { + large + } + description (asHtml: false) + media (sort: POPULARITY_DESC, perPage: 1) { + edges { + node { + siteUrl + isAdult + title { + romaji + } + } + characterRole + voiceActors { + siteUrl + language + name { + full + native + } + } + } + } + } } - try: - data = post('https://graphql.anilist.co', json={'query': query, 'variables': variables}).json() - data = data['data']['Media'] - url = data['siteUrl'] - except TypeError: - embed = embed_templates.error_fatal('Kunne ikke finne animen!') - return await ctx.send(embed=embed) + ''' + variables = { + 'search': navn + } + data, url = await self.__request_anilist(interaction, query, variables, 'Character') - nsfw = data['isAdult'] - if nsfw: - embed = embed_templates.error_fatal(ctx, text='Animen du søkte på er NSFW. ' + - 'Gjør kommandoen i en NSFW-kanal i stedet') - return await ctx.send(embed=embed) + if not data: + return - cover_image = data['coverImage']['large'] - banner_image = data['bannerImage'] - mean_score = data['meanScore'] + name_romaji = data['name']['full'] + name_native = data['name']['native'] + image = data['image']['large'] + favourites = data['favourites'] - color = data['coverImage']['color'] - if not color: - color = 0x02A9FF - else: - color = color.replace('#', '0x') - color = int(color, 0) + if data['description']: + description = self.__remove_html(data['description']) + if len(description) > 1024: + description = description[0:1020] + '...' + else: + description = '*Ingen biografi funnet* 😔' - title_romaji = data['title']['romaji'] - title_native = data['title']['native'] - title_english = data['title']['english'] - titles = [title_romaji, title_native, title_english] - for i, title in enumerate(titles): - if title is None: - titles[i] = '' - title_romaji, title_native, title_english = titles + featured_in = [] + voice_actors = [] + for media in data['media']['edges']: + nsfwtag = '🔞' if media['node']['isAdult'] else '' + media_name = media['node']['title']['romaji'] + media_url = media['node']['siteUrl'] + media_role = self.__convert_role_names(media['characterRole']) + for voice_actor in media['voiceActors']: + voice_actor_url = voice_actor['siteUrl'] + voice_actor_language = self.__convert_language_names(voice_actor['language']) + voice_actor_name = voice_actor['name']['full'] + if voice_actor['name']['native'] != ' ' and voice_actor['name']['native'] is not None: + voice_actor_name += f' ({voice_actor["name"]["native"]}' + voice_actors.append(f'{voice_actor_language} stemme: [{voice_actor_name}]({voice_actor_url})') - description = data['description'] - if description: - description = await remove_html(data['description']) - else: - description = '' - genres = ', '.join(data['genres']) + voice_actors_string = '\n'.join(voice_actors) + featured_in.append(f'[{media_name}]({media_url}){nsfwtag} som {media_role}\n{voice_actors_string}') - studios = data['studios']['nodes'] - studios_string = '' - for studio in studios: - studio_name = studio['name'] - studio_url = studio['siteUrl'] - studios_string += f'[{studio_name}]({studio_url})\n' + featured_in = '\n\n'.join(featured_in) - staff = data['staff']['edges'] - director_string = '' - for staff_member in staff: - if staff_member['role'] == 'Director': - director_first_name = staff_member['node']['name']['first'] - director_last_name = staff_member['node']['name']['last'] - director_native_name = staff_member['node']['name']['native'] - director_url = staff_member['node']['siteUrl'] - director_string += f'[{director_first_name} {director_last_name} ' +\ - f'({director_native_name})]({director_url})' + embed = discord.Embed(color=0x02A9FF, title=name_romaji, url=url, description=name_native) + embed.set_thumbnail(url=image) + embed.add_field(name='Antall favoritter på Anilist', value=favourites, inline=False) + embed.add_field(name='Biografi', value=description, inline=False) + embed.add_field(name='Er med i bl.a.', value=featured_in, inline=False) + embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) - episodes = data['episodes'] - duration = data['duration'] + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.checks.cooldown(1, 5) + @anilist.command(name='skaper', description='Hent informasjon om en skaper') + async def anilist_creator(self, interaction: discord.Interaction, navn: str): + query = ''' + query ($search: String) { + Staff(search: $search) { + name { + full + native + } + siteUrl + image { + large + } + language + favourites + description(asHtml: false) + staffMedia(sort: POPULARITY_DESC, perPage: 2) { + edges { + staffRole + node { + siteUrl + isAdult + title { + romaji + } + } + } + } + characters(sort: FAVOURITES_DESC, perPage: 2) { + edges { + node { + name { + full + native + } + siteUrl + } + } + } + } + } + ''' + variables = { + 'search': navn + } + data, url = await self.__request_anilist(interaction, query, variables, 'Staff') - release_day = data['startDate']['day'] - release_month = data['startDate']['month'] - release_year = data['startDate']['year'] - end_day = data['endDate']['day'] - end_month = data['endDate']['month'] - end_year = data['endDate']['year'] + if not data: + return - numbers = [episodes, duration, release_day, release_month, release_year, end_day, end_month, end_year] - for i, number in enumerate(numbers): - if number is None: - numbers[i] = '?' - episodes, duration, release_day, release_month, release_year, end_day, end_month, end_year = numbers - length_string = '' - if duration == 1: - length_string += 'minutt ' - else: - length_string += 'minutter ' - if episodes == 1: - length_string += 'lang' - else: - length_string += 'lange' + name_romaji = data['name']['full'] + name_native = data['name']['native'] + image = data['image']['large'] + favourites = data['favourites'] - release_date = f'{release_day}.{release_month}.{release_year}' - end_date = f'{end_day}.{end_month}.{end_year}' - if release_date == end_date: - date = release_date - else: - date = f'{release_date} - {end_date}' + if data['description']: + description = self.__remove_html(data['description']) + if len(description) > 1024: + description = description[0:1020] + '...' + else: + description = '*Ingen biografi funnet* 😔' - status = data['status'] - status = await convert_status(status) + featured_in = [] + for media in data['staffMedia']['edges']: + nsfwtag = '🔞' if media['node']['isAdult'] else '' + media_name = media['node']['title']['romaji'] + media_url = media['node']['siteUrl'] + media_role = media['staffRole'] + featured_in.append(f'[{media_name}]({media_url}){nsfwtag}\n{media_role}') + featured_in = '\n\n'.join(featured_in) - media_format = data['format'] - media_format = await convert_media_format(media_format) + characters = [] + for character in data['characters']['edges']: + character_url = character['node']['siteUrl'] + character_name = character['node']['name']['full'] + if character['node']['name']['native'] != ' ' and character['node']['name']['native'] is not None: + character_name += f' ({character["node"]["name"]["native"]}' + characters.append(f'[{character_name}]({character_url})') + characters = '\n\n'.join(characters) - embed = discord.Embed(color=color, title=title_romaji, url=url, - description=f'{title_native}\n{title_english}') - embed.set_thumbnail(url=cover_image) - embed.add_field(name='Format', value=media_format) - embed.add_field(name='Status', value=status) - if studios_string: - embed.add_field(name='Studio', value=studios_string) - if director_string: - embed.add_field(name='Regissør', value=director_string) - embed.add_field(name='Utgivelsesdato', value=date) - if media_format == 'Film': - embed.add_field(name='Lengde', value=f'{duration} minutter') - else: - embed.add_field(name='Episoder', value=f'{episodes} ({duration} {length_string})') - if mean_score: - embed.add_field(name='Gj.snittsvurdering', value=f'{mean_score}/100') - embed.add_field(name='Sjangere', value=genres) - if len(description) < 1024 and description != '': - embed.add_field(name='Sammendrag', value=description, inline=False) - if banner_image: - embed.set_image(url=banner_image) - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) + embed = discord.Embed(color=0x02A9FF, title=name_romaji, url=url, description=name_native) + embed.set_thumbnail(url=image) + embed.add_field(name='Antall favoritter på Anilist', value=favourites, inline=False) + embed.add_field(name='Biografi', value=description, inline=False) + if characters: + embed.add_field(name='Har vært stemmen til bl.a.', value=characters, inline=False) + if featured_in: + embed.add_field(name='Har deltatt i produksjonen av bl.a.', value=featured_in, inline=False) + embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) - @commands.bot_has_permissions(embed_links=True) - @commands.cooldown(1, 5, commands.BucketType.guild) - @commands.command() - async def manga(self, ctx, *, manganavn: str): - """ - Viser informasjon om en manga - """ - - async with ctx.channel.typing(): - - query = ''' - query ($search: String) { - Media (search: $search, type: MANGA) { + @app_commands.checks.bot_has_permissions(embed_links=True) + @app_commands.checks.cooldown(1, 5) + @anilist.command(name='studio', description='Hent informasjon om et studio') + async def anilist_studio(self, interaction: discord.Interaction, navn: str): + query = ''' + query ($search: String) { + Studio(search: $search) { + name + siteUrl + favourites + media(sort: POPULARITY_DESC, perPage: 3) { + nodes { siteUrl - format - status - description (asHtml: false) - volumes - chapters - genres - isAdult - bannerImage - meanScore coverImage { large - color - } - startDate { - year - month - day - } - endDate { - year - month - day } + isAdult title { - romaji - english - native - } - staff(sort: ROLE) { - edges { - role - node { - siteUrl - name { - first - last - native - } - } - } + romaji } } + } } - ''' - variables = { - 'search': manganavn - } - try: - data = post('https://graphql.anilist.co', json={'query': query, 'variables': variables}).json() - data = data['data']['Media'] - url = data['siteUrl'] - except TypeError: - embed = embed_templates.error_fatal('Kunne ikke finne Mangaen!') - return await ctx.send(embed=embed) + } + ''' + variables = { + 'search': navn + } + data, url = await self.__request_anilist(interaction, query, variables, 'Studio') - nsfw = data['isAdult'] - if nsfw: - embed = embed_templates.error_fatal(ctx, text='Mangaen du søkte på er NSFW. ' + - 'Gjør kommandoen i en NSFW-kanal i stedet') - return await ctx.send(embed=embed) + if not data: + return - cover_image = data['coverImage']['large'] - banner_image = data['bannerImage'] - mean_score = data['meanScore'] + name = data['name'] + favourites = data['favourites'] - color = data['coverImage']['color'] - if not color: - color = 0x02A9FF - else: - color = color.replace('#', '0x') - color = int(color, 0) + popular_media = [] + for media in data['media']['nodes']: + nsfwtag = '🔞' if media['isAdult'] else '' + media_name = media['title']['romaji'] + media_url = media['siteUrl'] + popular_media.append(f'- [{media_name}]({media_url}){nsfwtag}') + popular_media = '\n'.join(popular_media) - title_romaji = data['title']['romaji'] - title_native = data['title']['native'] - title_english = data['title']['english'] - titles = [title_romaji, title_native, title_english] - for i, title in enumerate(titles): - if title is None: - titles[i] = '' - title_romaji, title_native, title_english = titles - - description = data['description'] - if description: - description = await remove_html(data['description']) - else: - description = '' - genres = ', '.join(data['genres']) - - staff = data['staff']['edges'] - staff_string = '' - for staff_member in staff: - staff_first_name = staff_member['node']['name']['first'] - staff_last_name = staff_member['node']['name']['last'] - staff_native_name = staff_member['node']['name']['native'] - staff_url = staff_member['node']['siteUrl'] - staff_string += f'[{staff_first_name} {staff_last_name} ({staff_native_name})]({staff_url})\n' - - chapters = data['chapters'] - volumes = data['volumes'] - - release_day = data['startDate']['day'] - release_month = data['startDate']['month'] - release_year = data['startDate']['year'] - - numbers = [chapters, volumes, release_day, release_month, release_year] - for i, number in enumerate(numbers): - if number is None: - numbers[i] = '?' - chapters, volumes, release_day, release_month, release_year = numbers - release_date = f'{release_day}.{release_month}.{release_year}' - - chapters_string = '' - if chapters == 1: - chapters_string += 'kapittel' - else: - chapters_string += 'kapitler' - - status = data['status'] - status = await convert_status(status) - - media_format = data['format'] - media_format = await convert_media_format(media_format) - - embed = discord.Embed(color=color, title=title_romaji, url=url, - description=f'{title_native}\n{title_english}') - embed.set_thumbnail(url=cover_image) - embed.add_field(name='Format', value=media_format) - embed.add_field(name='Status', value=status) - if staff_string: - embed.add_field(name='Laget av', value=staff_string) - embed.add_field(name='Utgivelsesdato', value=release_date) - embed.add_field(name='Lengde', value=f'{chapters} {chapters_string}\n{volumes} volum') - if mean_score: - embed.add_field(name='Gj.snittsvurdering', value=f'{mean_score}/100') - embed.add_field(name='Sjangere', value=genres) - if len(description) < 1024 and description != '': - embed.add_field(name='Sammendrag', value=description, inline=False) - if banner_image: - embed.set_image(url=banner_image) - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.cooldown(1, 5, commands.BucketType.guild) - @commands.command(aliases=['character']) - async def karakter(self, ctx, *, karakternavn: str): - """Viser informasjon om en karakter""" - - async with ctx.channel.typing(): - - query = ''' - query ($search: String) { - Character (search: $search) { - name { - full - native - } + query2 = ''' + query ($search: String) { + Studio(search: $search) { + media(sort: START_DATE_DESC) { + nodes { siteUrl - favourites - image { - large - } - description (asHtml: false) - media (sort: POPULARITY_DESC, perPage: 1) { - edges { - node { - siteUrl - isAdult - title { - romaji - } - } - characterRole - voiceActors { - siteUrl - language - name { - full - native - } - } - } + status + isAdult + title { + romaji } } + } } - ''' - variables = { - 'search': karakternavn - } - try: - data = post('https://graphql.anilist.co', json={'query': query, 'variables': variables}).json() - data = data['data']['Character'] - url = data['siteUrl'] - except TypeError: - embed = embed_templates.error_fatal('Kunne ikke finne karakteren!') - return await ctx.send(embed=embed) - - name_romaji = data['name']['full'] - name_native = data['name']['native'] - image = data['image']['large'] - favourites = data['favourites'] - - if data['description']: - description = await remove_html(data['description']) - if len(description) > 1024: - description = description[0:1020] + '...' - else: - description = '*Ingen biografi funnet* 😔' - - featured_in = [] - voice_actors = [] - for media in data['media']['edges']: - if media['node']['isAdult'] is True: - nsfwtag = ' (NSFW)' - else: - nsfwtag = '' - media_name = media['node']['title']['romaji'] - media_url = media['node']['siteUrl'] - media_role = await convert_role_names(media['characterRole']) - for voice_actor in media['voiceActors']: - voice_actor_url = voice_actor['siteUrl'] - voice_actor_language = await convert_language_names(voice_actor['language']) - voice_actor_name = voice_actor['name']['full'] - if voice_actor['name']['native'] != ' ' and voice_actor['name']['native'] is not None: - voice_actor_name += ' (' + voice_actor['name']['native'] + ')' - voice_actors.append(f'{voice_actor_language} stemme: [{voice_actor_name}]({voice_actor_url})') - - voice_actors_string = '\n'.join(voice_actors) - featured_in.append(f'[{media_name}]({media_url}){nsfwtag} som {media_role}\n{voice_actors_string}') - - featured_in = '\n\n'.join(featured_in) - - embed = discord.Embed(color=0x02A9FF, title=name_romaji, url=url, description=name_native) - embed.set_thumbnail(url=image) - embed.add_field(name='Antall favoritter på Anilist', value=favourites, inline=False) - embed.add_field(name='Biografi', value=description, inline=False) - embed.add_field(name='Er med i bl.a.', value=featured_in, inline=False) - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.cooldown(1, 5, commands.BucketType.guild) - @commands.command(aliases=['staff']) - async def skaper(self, ctx, *, skapernavn: str): - """Viser informasjon om en skaper""" - - async with ctx.channel.typing(): - - query = ''' - query ($search: String) { - Staff(search: $search) { - name { - full - native - } - siteUrl - image { - large - } - language - favourites - description(asHtml: false) - staffMedia(sort: POPULARITY_DESC, perPage: 2) { - edges { - staffRole - node { - siteUrl - isAdult - title { - romaji - } - } - } - } - characters(sort: FAVOURITES_DESC, perPage: 2) { - edges { - node { - name { - full - native - } - siteUrl - } - } - } - } } - ''' - variables = { - 'search': skapernavn - } - try: - data = post('https://graphql.anilist.co', json={'query': query, 'variables': variables}).json() - data = data['data']['Staff'] - url = data['siteUrl'] - except TypeError: - embed = embed_templates.error_fatal('Kunne ikke finne skaperen!') - return await ctx.send(embed=embed) + ''' + data = requests.post('https://graphql.anilist.co', json={'query': query2, 'variables': variables}, timeout=10).json() + data = data['data']['Studio'] - name_romaji = data['name']['full'] - name_native = data['name']['native'] - image = data['image']['large'] - language = data['language'] - favourites = data['favourites'] - - if data['description']: - description = await remove_html(data['description']) - if len(description) > 1024: - description = description[0:1020] + '...' + recent_media = [] + upcoming_media = [] + for media in data['media']['nodes']: + nsfwtag = '🔞' if media['isAdult'] else '' + media_name = media['title']['romaji'] + media_url = media['siteUrl'] + if media['status'] == 'NOT_YET_RELEASED': + upcoming_media.append(f'- [{media_name}]({media_url}){nsfwtag}') else: - description = '*Ingen biografi funnet* 😔' + recent_media.append(f'- [{media_name}]({media_url}){nsfwtag}') + recent_media = '\n'.join(recent_media[0:3]) + upcoming_media = '\n'.join(upcoming_media[0:3]) - featured_in = [] - for media in data['staffMedia']['edges']: - if media['node']['isAdult'] is True: - nsfwtag = ' (NSFW)' - else: - nsfwtag = '' - media_name = media['node']['title']['romaji'] - media_url = media['node']['siteUrl'] - media_role = media['staffRole'] - featured_in.append(f'[{media_name}]({media_url}){nsfwtag}\n{media_role}') - featured_in = '\n\n'.join(featured_in) - - characters = [] - for character in data['characters']['edges']: - character_url = character['node']['siteUrl'] - character_name = character['node']['name']['full'] - if character['node']['name']['native'] != ' ' and character['node']['name']['native'] is not None: - character_name += ' (' + character['node']['name']['native'] + ')' - characters.append(f'[{character_name}]({character_url})') - characters = '\n\n'.join(characters) - - embed = discord.Embed(color=0x02A9FF, title=name_romaji, url=url, description=name_native) - embed.set_thumbnail(url=image) - embed.add_field(name='Antall favoritter på Anilist', value=favourites, inline=False) - embed.add_field(name='Biografi', value=description, inline=False) - if characters != '': - embed.add_field(name='Har vært stemmen til bl.a.', value=characters, inline=False) - if featured_in != '': - embed.add_field(name='Har deltatt i produksjonen av bl.a.', value=featured_in, inline=False) - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.cooldown(1, 5, commands.BucketType.guild) - @commands.command() - async def studio(self, ctx, *, studionavn: str): - """Viser informasjon om et animestudio""" - - async with ctx.channel.typing(): - - query = ''' - query ($search: String) { - Studio(search: $search) { - name - siteUrl - favourites - media(sort: POPULARITY_DESC, perPage: 3) { - nodes { - siteUrl - coverImage { - large - } - isAdult - title { - romaji - } - } - } - } - } - ''' - variables = { - 'search': studionavn - } - try: - data = post('https://graphql.anilist.co', json={'query': query, 'variables': variables}).json() - data = data['data']['Studio'] - url = data['siteUrl'] - except TypeError: - embed = embed_templates.error_fatal('Kunne ikke finne studioet!') - return await ctx.send(embed=embed) - - name = data['name'] - favourites = data['favourites'] - - popular_media = [] - for media in data['media']['nodes']: - if media['isAdult'] is True: - nsfwtag = ' (NSFW)' - else: - nsfwtag = '' - media_name = media['title']['romaji'] - media_url = media['siteUrl'] - popular_media.append(f'- [{media_name}]({media_url}){nsfwtag}') - popular_media = '\n'.join(popular_media) - - query2 = ''' - query ($search: String) { - Studio(search: $search) { - media(sort: START_DATE_DESC) { - nodes { - siteUrl - status - isAdult - title { - romaji - } - } - } - } - } - ''' - data = post('https://graphql.anilist.co', json={'query': query2, 'variables': variables}).json() - data = data['data']['Studio'] - - recent_media = [] - upcoming_media = [] - for media in data['media']['nodes']: - if media['isAdult'] is True: - nsfwtag = ' (NSFW)' - else: - nsfwtag = '' - media_name = media['title']['romaji'] - media_url = media['siteUrl'] - if media['status'] == 'NOT_YET_RELEASED': - upcoming_media.append(f'- [{media_name}]({media_url}){nsfwtag}') - else: - recent_media.append(f'- [{media_name}]({media_url}){nsfwtag}') - recent_media = '\n'.join(recent_media[0:3]) - upcoming_media = '\n'.join(upcoming_media[0:3]) - - embed = discord.Embed(color=0x02A9FF, title=name, url=url) - embed.add_field(name='Antall favoritter på Anilist', value=favourites, inline=False) - if popular_media != '': - embed.add_field(name='Mest Populære Anime', value=popular_media, inline=False) - if recent_media != '': - embed.add_field(name='Nyeste Utgitte Anime', value=recent_media, inline=False) - if upcoming_media != '': - embed.add_field(name='Kommende Anime', value=upcoming_media, inline=False) - embed = embed_templates.default_footer(ctx, embed) - await ctx.send(embed=embed) + embed = discord.Embed(color=0x02A9FF, title=name, url=url) + embed.add_field(name='Antall favoritter på Anilist', value=favourites, inline=False) + if popular_media: + embed.add_field(name='Mest Populære Anime', value=popular_media, inline=False) + if recent_media: + embed.add_field(name='Nyeste Utgitte Anime', value=recent_media, inline=False) + if upcoming_media: + embed.add_field(name='Kommende Anime', value=upcoming_media, inline=False) + embed_templates.default_footer(interaction, embed) + await interaction.response.send_message(embed=embed) async def setup(bot): From 6a7d8bc07f4a48fae88d1ea2772d265ca0aa0d11 Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 03:18:36 +0100 Subject: [PATCH 33/54] refactor dev tools cog removes a substantial number of commands that I find unecessary to keep --- src/cogs/dev_tools.py | 374 ++++++++++++------------------------------ 1 file changed, 102 insertions(+), 272 deletions(-) diff --git a/src/cogs/dev_tools.py b/src/cogs/dev_tools.py index 672f8e0..637adb6 100644 --- a/src/cogs/dev_tools.py +++ b/src/cogs/dev_tools.py @@ -1,262 +1,44 @@ -from discord.ext import commands -import discord - import asyncio -from sys import exit -from os import listdir, system -import socket -from requests import get +from os import listdir +import requests -from cogs.utils import misc_utils, embed_templates +import discord +from discord.ext import commands + +from cogs.utils import embed_templates class DevTools(commands.Cog): def __init__(self, bot): self.bot = bot - @commands.bot_has_permissions(embed_links=True) - @commands.is_owner() - @commands.command(aliases=['shutdown', 'stoppbått']) - async def stoppbot(self, ctx): - """ - Log out and shut down the bot - """ - - embed = discord.Embed(color=ctx.me.color, description='Logger av...') - await ctx.send(embed=embed) - await self.bot.logout() - exit('Bot stopped through command') - @commands.bot_has_permissions(embed_links=True) @commands.is_owner() @commands.command() - async def custommsg(self, ctx, channel: int, *text): - """ - Send a message to a specified channel - """ - + async def custommsg(self, ctx: commands.Context, channel: int, *text: tuple[str]): + """Send a message to a specified channel""" + # Send message to the requested channel channel = self.bot.get_channel(channel) custommessage = ' '.join(text) await channel.send(custommessage) + # Send confirmation message to the invoker embed = discord.Embed(color=ctx.me.color) embed.add_field(name='Sent', value=custommessage) await ctx.send(embed=embed) - @commands.bot_has_permissions(embed_links=True) - @commands.is_owner() - @commands.command(aliases=['listservers']) - async def listguilds(self, ctx, page=0): - """ - Lists all guilds the bot is a member of - """ - - guild_list = [f'{guild.name} - {guild.id}' for guild in self.bot.guilds] - - page_data = misc_utils.paginator(guild_list, int(page)) - page = page_data['page'] - pagecount = page_data['pagecount'] - guilds = '\n'.join(page_data['page_content']) - - embed = discord.Embed(color=ctx.me.color) - embed.add_field(name='Guilds', value=guilds) - embed.set_footer(text=f'page: {page}/{pagecount}') - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.is_owner() - @commands.command() - async def listusers(self, ctx, page=0): - """ - Lists all users the bot has access to - """ - - user_list = [f'{user.name}#{user.discriminator} - {user.id}' for user in self.bot.users] - page_data = misc_utils.paginator(user_list, int(page)) - - pagecount = page_data['pagecount'] - page = page_data['page'] - users = '\n'.join(page_data['page_content']) - - embed = discord.Embed(color=ctx.me.color) - embed.add_field(name='Users', value=users) - embed.set_footer(text=f'page: {page}/{pagecount}') - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.is_owner() - @commands.command(alises=['unloadcog', 'disablecog']) - async def unload(self, ctx, cog: str): - """ - Disables a specified cog - """ - - for file in listdir('./src/cogs'): - if file.endswith('.py'): - name = file[:-3] - if name == cog: - self.bot.unload_extension(f'cogs.{name}') - embed = discord.Embed(color=ctx.me.color, description=f'{cog} has been disabled') - return await ctx.send(embed=embed) - - embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist') - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.is_owner() - @commands.command(aliases=['loadcog', 'enablecog']) - async def load(self, ctx, cog): - """ - Enables a speicifed cog - """ - - for file in listdir('./src/cogs'): - if file.endswith('.py'): - name = file[:-3] - if name == cog: - self.bot.load_extension(f'cogs.{name}') - embed = discord.Embed(color=ctx.me.color, description=f'{cog} loaded') - return await ctx.send(embed=embed) - - embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist') - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.is_owner() - @commands.command(aliases=['reloadcog']) - async def reload(self, ctx, cog): - """ - Reloads a specified cog - """ - - for file in listdir('./src/cogs'): - if file.endswith('.py'): - name = file[:-3] - if name == cog: - self.bot.reload_extension(f'cogs.{name}') - embed = discord.Embed(color=ctx.me.color, description=f'{cog} has been reloaded') - return await ctx.send(embed=embed) - - embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist') - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.is_owner() - @commands.command() - async def reloadcommand(self, ctx, cog): - """ - Reloads a specified cog - """ - - for file in listdir('./src/cogs'): - if file.endswith('.py'): - name = file[:-3] - if name == cog: - self.bot.reload_extension(f'cogs.{name}') - embed = discord.Embed(color=ctx.me.color, description=f'{cog} has been reloaded') - return await ctx.send(embed=embed) - - embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist') - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.is_owner() - @commands.command(aliases=['reloadunloadedcogs']) - async def reloadunloaded(self, ctx): - """ - Reloads all cogs, including previously disabled ones - """ - - for file in listdir('./src/cogs'): - if file.endswith('.py'): - name = file[:-3] - self.bot.unload_extension(f'cogs.{name}') - - for file in listdir('./src/cogs'): - if file.endswith('.py'): - name = file[:-3] - self.bot.load_extension(f'cogs.{name}') - - embed = discord.Embed(color=ctx.me.color, description='Reloaded all cogs') - await ctx.send(embed=embed) - - @commands.bot_has_permissions(embed_links=True) - @commands.is_owner() - @commands.command(aliases=['reloadallcogs']) - async def reloadall(self, ctx): - """ - Reloads all previously enabled cogs - """ - - for file in listdir('./src/cogs'): - if file.endswith('.py'): - name = file[:-3] - self.bot.reload_extension(f'cogs.{name}') - - embed = discord.Embed(color=ctx.me.color, description='Reloaded all previously enabled cogs') - await ctx.send(embed=embed) - - @commands.is_owner() - @commands.command() - async def cmd(self, ctx, *, command: str): - """ - Execute terminal commands - """ - - system(command) - await ctx.send('Done!') - - @commands.bot_has_permissions(embed_links=True) - @commands.is_owner() - @commands.command() - async def localip(self, ctx): - """ - Sends LAN IP-address - """ - - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - s.connect(('8.8.8.8', 80)) - embed = discord.Embed(color=ctx.me.color) - embed.add_field(name='LAN IP-address', value=s.getsockname()[0]) - await ctx.send(embed=embed) - s.close() - - @commands.bot_has_permissions(embed_links=True) - @commands.is_owner() - @commands.command() - async def publicip(self, ctx): - """ - Sends WAN IP-address - inb4 I leak my IP-address - """ - - data = get('https://wtfismyip.com/json').json() - ip = data['YourFuckingIPAddress'] - location = data['YourFuckingLocation'] - isp = data['YourFuckingISP'] - - embed = discord.Embed(color=ctx.me.color) - embed.add_field(name='WAN IP-address', value=f'{ip}\n{location}\n{isp}') - await ctx.send(embed=embed) - @commands.bot_has_permissions(embed_links=True) @commands.is_owner() @commands.cooldown(1, 10, commands.BucketType.guild) @commands.command() - async def changepresence(self, ctx, activity_type, message, status_type): - """ - Changes presence status - """ - + async def changepresence(self, ctx: commands.Context, activity_type: str, message: str, status_type: str): + """Changes the bot's presence status""" activities = { 'playing': 0, 'listening': 2, 'watching': 3 } - if activity_type in activities: - activity_type = activities[activity_type] - else: - activity_type = 0 + activity_type = activities.get(activity_type, 0) status_types = { 'online': discord.Status.online, @@ -264,10 +46,7 @@ class DevTools(commands.Cog): 'idle': discord.Status.idle, 'offline': discord.Status.offline } - if status_type in status_types: - status_type = status_types[status_type] - else: - status_type = discord.Status.online + status_type = status_types.get(status_type, discord.Status.online) await self.bot.change_presence( status=status_type, @@ -279,30 +58,28 @@ class DevTools(commands.Cog): @commands.bot_has_permissions(embed_links=True, add_reactions=True) @commands.is_owner() @commands.command() - async def leave(self, ctx, *guild_id: int): - """ - Leaves a specified guild - """ - - if guild_id == (): - guild_id = ctx.guild.id - else: - guild_id = guild_id + async def leave(self, ctx: commands.Context, *guild_id: int): + """Leaves a specified guild""" + # If no guild id is specified, leave the current guild + guild_id = guild_id if guild_id else ctx.guild.id + # Get guild try: guild = await self.bot.fetch_guild(guild_id) except discord.errors.Forbidden: embed = embed_templates.error_fatal(ctx, text='Bot is not a member of this guild') return await ctx.send(embed=embed) + # Send confirmation message for leaving confirmation_msg = await ctx.send(f'Do you want to leave {guild.name} (`{guild.id}`)?') await confirmation_msg.add_reaction('✅') + # Check confirmation def comfirm(reaction, user): return user == ctx.author and str(reaction.emoji) == '✅' try: - reaction, user = await self.bot.wait_for('reaction_add', timeout=15.0, check=comfirm) + await self.bot.wait_for('reaction_add', timeout=15.0, check=comfirm) except asyncio.TimeoutError: await ctx.message.delete() await confirmation_msg.delete() @@ -317,41 +94,94 @@ class DevTools(commands.Cog): @commands.bot_has_permissions(embed_links=True) @commands.is_owner() @commands.command() - async def resetcooldown(self, ctx, command: str): - """ - Resets the cooldown period for a specified command - """ + async def publicip(self, ctx: commands.Context): + """Sends WAN IP-address. inb4 I leak my IP-address""" + data = requests.get('https://wtfismyip.com/json').json() + ip = data['YourFuckingIPAddress'] + location = data['YourFuckingLocation'] + isp = data['YourFuckingISP'] - try: - self.bot.get_command(command).reset_cooldown(ctx) - except AttributeError: - embed = embed_templates.error_fatal(ctx, text=f'{command} is not a command') - return await ctx.send(embed=embed) - - embed = discord.Embed(color=ctx.me.color, description='Cooldown reset!') + embed = discord.Embed(color=ctx.me.color) + embed.add_field(name='WAN IP-address', value=f'{ip}\n{location}\n{isp}') await ctx.send(embed=embed) - @commands.bot_has_permissions(embed_links=True, external_emojis=True) + @commands.bot_has_permissions(embed_links=True) @commands.is_owner() + @commands.group() + async def cogs(self, ctx: commands.Context): + """Cog management commands""" + if not ctx.invoked_subcommand: + await ctx.send_help(ctx.command) + + @cogs.command() + async def unload(self, ctx: commands.Context, cog: str): + """Disables a specified cog""" + for file in listdir('./src/cogs'): + if file.endswith('.py'): + name = file[:-3] + if name == cog: + await self.bot.unload_extension(f'cogs.{name}') + embed = discord.Embed(color=ctx.me.color, description=f'{cog} has been disabled') + return await ctx.send(embed=embed) + + embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist') + await ctx.send(embed=embed) + @commands.command() - async def allemoji(self, ctx): - """ - Sends all emoji the bot has access to - """ + async def load(self, ctx: commands.Context, cog: str): + """Enables a speicifed cog""" + for file in listdir('./src/cogs'): + if file.endswith('.py'): + name = file[:-3] + if name == cog: + await self.bot.load_extension(f'cogs.{name}') + embed = discord.Embed(color=ctx.me.color, description=f'{cog} loaded') + return await ctx.send(embed=embed) - embed = discord.Embed(colour=ctx.me.color) + embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist') + await ctx.send(embed=embed) - emoji_string = '' - for guild in self.bot.guilds: - emoji_string += f'\n**{guild.name}**\n' - for emoji in guild.emojis: - if len(emoji_string) > 2000: - embed.description = emoji_string - await ctx.send(embed=embed) - emoji_string = f'\n**{guild.name}**\n' - emoji_string += f'{emoji} ' + @commands.command() + async def reload(self, ctx: commands.Context, cog: str): + """Reloads a specified cog""" + for file in listdir('./src/cogs'): + if file.endswith('.py'): + name = file[:-3] + if name == cog: + await self.bot.reload_extension(f'cogs.{name}') + embed = discord.Embed(color=ctx.me.color, description=f'{cog} has been reloaded') + return await ctx.send(embed=embed) - embed.description = emoji_string + embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist') + await ctx.send(embed=embed) + + @commands.command() + async def reloadunloaded(self, ctx: commands.Context): + """Reloads all cogs, including previously disabled ones""" + # Unload all cogs + for file in listdir('./src/cogs'): + if file.endswith('.py'): + name = file[:-3] + await self.bot.unload_extension(f'cogs.{name}') + + # Load all cogs + for file in listdir('./src/cogs'): + if file.endswith('.py'): + name = file[:-3] + await self.bot.load_extension(f'cogs.{name}') + + embed = discord.Embed(color=ctx.me.color, description='Reloaded all cogs') + await ctx.send(embed=embed) + + @commands.command() + async def reloadall(self, ctx: commands.Context): + """Reloads all previously enabled cogs""" + for file in listdir('./src/cogs'): + if file.endswith('.py'): + name = file[:-3] + await self.bot.reload_extension(f'cogs.{name}') + + embed = discord.Embed(color=ctx.me.color, description='Reloaded all previously enabled cogs') await ctx.send(embed=embed) From 46b5b15fcf512b99ab81a05fb1946d8345d0ad1e Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 03:22:24 +0100 Subject: [PATCH 34/54] add randomized ping for neuf funreply --- src/cogs/funreplies.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/cogs/funreplies.py b/src/cogs/funreplies.py index e012705..b0877ec 100644 --- a/src/cogs/funreplies.py +++ b/src/cogs/funreplies.py @@ -1,4 +1,5 @@ import re +import random import discord from discord.ext import commands @@ -26,7 +27,11 @@ class FunReplies(commands.Cog): '824764933513281596/3xhpwbakz2361.png') elif re.search(r'^((er det|hvor)\s+)*(noen|folk|mange)\s+på\s+(rommet|neuf|kontoret)\?*$', message_content, flags=re.IGNORECASE): - await message.channel.send('Ja, <@554977854971183125> bor der') + # Choose a random user to ping + pingable = ["<@554977854971183125>", "<@276061121776058370>"] + to_be_pinged = random.choice(pingable) + + await message.channel.send(f'Ja, {to_be_pinged} bor der') elif re.search(r'(^|\W)+ye+et($|\W)+', message_content, flags=re.IGNORECASE): await message.channel.send('<:Nei:826593267642662912>') From beef71a1d757006ddbef20e0219d91cb53559990 Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 03:35:11 +0100 Subject: [PATCH 35/54] closes #8 --- src/cogs/funreplies.py | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/cogs/funreplies.py b/src/cogs/funreplies.py index b0877ec..a08c799 100644 --- a/src/cogs/funreplies.py +++ b/src/cogs/funreplies.py @@ -1,5 +1,6 @@ -import re import random +import re +from datetime import datetime import discord from discord.ext import commands @@ -9,6 +10,19 @@ class FunReplies(commands.Cog): def __init__(self, bot): self.bot = bot + # Cooldowns for trigger words + # + # This is kind of a shitty way to do it but I'm too lazy to implement anything good right now + self.cooldown_seconds = 120 + initial_datetime = datetime(2000, 9, 11) # Set initial datetime far in the past to allow triggering right after boot + self.previous_invokations = { + 'borgerlønn': initial_datetime, + 'olof palme': initial_datetime, + 'lesgo': initial_datetime, + 'neuf': initial_datetime, + 'yeet': initial_datetime + } + async def reply_to_triggers(self, message: discord.Message): """Replies to messages that trigger certain key words/phrases""" if message.author.bot: @@ -17,24 +31,34 @@ class FunReplies(commands.Cog): message_content = message.content.lower() if re.search(r'(^|\W)borgerlønn(\W|$)', message_content, flags=re.IGNORECASE): - await message.channel.send('<@267415183931080715> DE SNAKKER OM BORGERLØNN') + if (datetime.now() - self.previous_invokations['borgerlønn']).seconds > self.cooldown_seconds: + await message.channel.send('<@267415183931080715> DE SNAKKER OM BORGERLØNN') + self.previous_invokations['borgerlønn'] = datetime.now() elif re.search(r'(^|\W)olof palme(\W|$)', message_content, flags=re.IGNORECASE): - await message.channel.send('Jeg vet hvem som drepte Olof Palme 👀') + if (datetime.now() - self.previous_invokations['olof palme']).seconds > self.cooldown_seconds: + await message.channel.send('Jeg vet hvem som drepte Olof Palme 👀') + self.previous_invokations['olof palme'] = datetime.now() elif re.search(r'(\W|\s)*le+s+go+(\s|\W)*', message_content): - await message.channel.send('https://cdn.discordapp.com/attachments/750052141346979850/' + + if (datetime.now() - self.previous_invokations['lesgo']).seconds > self.cooldown_seconds: + await message.channel.send('https://cdn.discordapp.com/attachments/750052141346979850/' + '824764933513281596/3xhpwbakz2361.png') + self.previous_invokations['lesgo'] = datetime.now() elif re.search(r'^((er det|hvor)\s+)*(noen|folk|mange)\s+på\s+(rommet|neuf|kontoret)\?*$', message_content, flags=re.IGNORECASE): - # Choose a random user to ping - pingable = ["<@554977854971183125>", "<@276061121776058370>"] - to_be_pinged = random.choice(pingable) + if (datetime.now() - self.previous_invokations['neuf']).seconds > self.cooldown_seconds: + # Choose a random user to ping + pingable = ["<@554977854971183125>", "<@276061121776058370>"] + to_be_pinged = random.choice(pingable) + await message.channel.send(f'Ja, {to_be_pinged} bor der') - await message.channel.send(f'Ja, {to_be_pinged} bor der') + self.previous_invokations['neuf'] = datetime.now() elif re.search(r'(^|\W)+ye+et($|\W)+', message_content, flags=re.IGNORECASE): - await message.channel.send('<:Nei:826593267642662912>') + if (datetime.now() - self.previous_invokations['yeet']).seconds > self.cooldown_seconds: + await message.channel.send('<:Nei:826593267642662912>') + self.previous_invokations['yeet'] = datetime.now() async def setup(bot): From 0795c4d4f85d2f075a3febbaa1087c7e59b5f543 Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 03:38:24 +0100 Subject: [PATCH 36/54] use the correct bot permission decorator --- src/cogs/info.py | 4 ++-- src/cogs/misc.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cogs/info.py b/src/cogs/info.py index 9e53e9d..9502ab6 100644 --- a/src/cogs/info.py +++ b/src/cogs/info.py @@ -367,7 +367,7 @@ class Info(commands.Cog): embed.set_footer(text=f'Side {paginator.current_page}/{paginator.total_page_count}') return embed - @commands.bot_has_permissions(embed_links=True) + @app_commands.checks.bot_has_permissions(embed_links=True) @commands.guild_only() @app_commands.checks.cooldown(1, 5) @guild_oldest_group.command(name="lagd", description="Liste over de eldste brukerene på serveren basert på når de ble lagd") @@ -389,7 +389,7 @@ class Info(commands.Cog): embed = self.__construct_oldest_embed(paginator, paginator.get_current_page(), embed) await interaction.response.send_message(embed=embed, view=view) - @commands.bot_has_permissions(embed_links=True) + @app_commands.checks.bot_has_permissions(embed_links=True) @commands.guild_only() @app_commands.checks.cooldown(1, 5) @guild_oldest_group.command(name="joined", description="Liste over de eldste brukerene på serveren basert på når de ble med") diff --git a/src/cogs/misc.py b/src/cogs/misc.py index da07a30..400bce5 100644 --- a/src/cogs/misc.py +++ b/src/cogs/misc.py @@ -17,14 +17,14 @@ class Misc(commands.Cog): def __init__(self, bot): self.bot = bot - @commands.bot_has_permissions(embed_links=True) + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) @app_commands.command() async def weeb(self, interaction: discord.Interaction): """Kjeft på weebs""" await interaction.response.send_message("<:sven:762725919604473866> Weebs 👉 <#803596668129509417>") - @commands.bot_has_permissions(embed_links=True) + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) @app_commands.command() async def kweeb(self, interaction: discord.Interaction): @@ -35,7 +35,7 @@ class Misc(commands.Cog): """ ) - @commands.bot_has_permissions(embed_links=True) + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) @app_commands.command() async def owo(self, interaction: discord.Interaction, tekst: str): @@ -64,7 +64,7 @@ class Misc(commands.Cog): await interaction.response.send_message(data.json()["info"]["name"]) - @commands.bot_has_permissions(embed_links=True) + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) @app_commands.command() async def klappifiser(self, interation: discord.Interaction, tekst: str): @@ -123,7 +123,7 @@ class Misc(commands.Cog): await interaction.response.send_message(embed=embed) - @commands.bot_has_permissions(embed_links=True) + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) @app_commands.command() async def helligdager(self, interaction: discord.Interaction, *, land: str = "NO", år: int = None): @@ -153,7 +153,7 @@ class Misc(commands.Cog): embed_templates.default_footer(interaction, embed) await interaction.response.send_message(embed=embed) - @commands.bot_has_permissions(embed_links=True) + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) @app_commands.command() async def match(self, interaction: discord.Interaction, bruker: discord.Member): From 1c7649faed726ccec5d189061abb0580d318ea06 Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 03:42:05 +0100 Subject: [PATCH 37/54] consistent doc string styling --- src/cogs/botinfo.py | 5 +---- src/cogs/misc.py | 4 +--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/cogs/botinfo.py b/src/cogs/botinfo.py index 404f5d3..52e44af 100644 --- a/src/cogs/botinfo.py +++ b/src/cogs/botinfo.py @@ -18,9 +18,7 @@ class BotInfo(commands.Cog): @app_commands.checks.cooldown(1, 2) @app_commands.command() async def botinfo(self, interaction: discord.Interaction): - """ - Viser informasjon om bot - """ + """Viser informasjon om bot""" # Dev user info dev = await self.bot.fetch_user(170506717140877312) @@ -123,7 +121,6 @@ class BotInfo(commands.Cog): ---------- (str) The bot's uptime in human readable format """ - now = time() diff = int(now - self.bot.uptime) days, remainder = divmod(diff, 24 * 60 * 60) diff --git a/src/cogs/misc.py b/src/cogs/misc.py index 400bce5..e295443 100644 --- a/src/cogs/misc.py +++ b/src/cogs/misc.py @@ -157,9 +157,7 @@ class Misc(commands.Cog): @app_commands.checks.cooldown(1, 5) @app_commands.command() async def match(self, interaction: discord.Interaction, bruker: discord.Member): - """ - Se hvor mye du matcher med en annen - """ + """Se hvor mye du matcher med en annen""" if bruker == interaction.user: embed = embed_templates.error_warning( interaction, text="Jeg vet du er ensom, men du kan " "ikke matche med deg selv" From f393ffae3e86d40b679c6bda5095455acf00c2ea Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 04:04:42 +0100 Subject: [PATCH 38/54] kweeb command --- src/cogs/misc.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/cogs/misc.py b/src/cogs/misc.py index e295443..6207e1a 100644 --- a/src/cogs/misc.py +++ b/src/cogs/misc.py @@ -28,10 +28,25 @@ class Misc(commands.Cog): @app_commands.checks.cooldown(1, 2) @app_commands.command() async def kweeb(self, interaction: discord.Interaction): - """Kjeft på weebs""" + """김정운, 최고도""" await interaction.response.send_message( """ - TBD +Melding du nettopp sendte ga muligens følgende vibber for brukeren som brukte denne kommandoen: + +> KOREA NUMBER ONE! +> OMG I LUUUUV JUNGKOK!!! 🥰😍🥰 STAN JUNGKOOK! 🤞🤞🤞 사랑해!!! ♥♥♥ +> JEG EEEEELSKER 김치 +> OP OP OP OPPAN GANGNAM STYLE! + +Så da kan man spørre seg selv? Hva faen skjedde med god gammeldags weebdom? +Til vår skuffelse finnes den fortsatt, men ble det for mainstream å være weeb. +Så da beveger massen seg til det neste hyperkapitalistiske øst-asiatiske staten med økonomisk boom. +Så hvor ble da av weebsa? Denne videoen svarer ganske godt på det +https://cdn.discordapp.com/attachments/448502155184439297/1027355652059832351/THEY_WENT_TO_KOREA.mp4 + +Du har nå blitt kalt en kweeb. Det er over for deg. +Våkne! +https://cdn.discordapp.com/attachments/674726496878723082/1042631569396990004/kweeb_er_den_nye_weeb.png """ ) From 9680a362c2792793ffde857fb0aa8ceaef45703a Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 04:23:22 +0100 Subject: [PATCH 39/54] add cog commands to cog group --- src/cogs/dev_tools.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cogs/dev_tools.py b/src/cogs/dev_tools.py index 637adb6..ec6b32f 100644 --- a/src/cogs/dev_tools.py +++ b/src/cogs/dev_tools.py @@ -127,7 +127,7 @@ class DevTools(commands.Cog): embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist') await ctx.send(embed=embed) - @commands.command() + @cogs.command() async def load(self, ctx: commands.Context, cog: str): """Enables a speicifed cog""" for file in listdir('./src/cogs'): @@ -141,7 +141,7 @@ class DevTools(commands.Cog): embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist') await ctx.send(embed=embed) - @commands.command() + @cogs.command() async def reload(self, ctx: commands.Context, cog: str): """Reloads a specified cog""" for file in listdir('./src/cogs'): @@ -155,7 +155,7 @@ class DevTools(commands.Cog): embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist') await ctx.send(embed=embed) - @commands.command() + @cogs.command() async def reloadunloaded(self, ctx: commands.Context): """Reloads all cogs, including previously disabled ones""" # Unload all cogs @@ -173,7 +173,7 @@ class DevTools(commands.Cog): embed = discord.Embed(color=ctx.me.color, description='Reloaded all cogs') await ctx.send(embed=embed) - @commands.command() + @cogs.command() async def reloadall(self, ctx: commands.Context): """Reloads all previously enabled cogs""" for file in listdir('./src/cogs'): From cbb479558c30fa87401845c1e5b82dd4f60d8052 Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 04:48:57 +0100 Subject: [PATCH 40/54] add support for slash commands & (small) refactor --- src/cogs/errors.py | 95 +++++++++++++++++++++++++++++++--------------- 1 file changed, 65 insertions(+), 30 deletions(-) diff --git a/src/cogs/errors.py b/src/cogs/errors.py index 33883e8..6648c4e 100644 --- a/src/cogs/errors.py +++ b/src/cogs/errors.py @@ -1,9 +1,8 @@ -from discord.ext import commands -import discord - import traceback -import sys -from datetime import datetime + +import discord +from discord import app_commands +from discord.ext import commands from cogs.utils import embed_templates from cogs.utils.misc_utils import ignore_exception @@ -12,27 +11,30 @@ from cogs.utils.misc_utils import ignore_exception class Errors(commands.Cog): def __init__(self, bot): self.bot = bot + bot.tree.on_error = self.on_app_command_error @commands.Cog.listener() - async def on_command(self, ctx): - """ - Prints command execution metadata - """ - + async def on_command(self, ctx: commands.Context): + """Logs prefix command execution metadata""" self.bot.logger.info( - f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} | ' + - f'{"❌ " if ctx.command_failed else "✔ "} {ctx.command} - ' + + f'{"❌ " if ctx.command_failed else "✔ "} {ctx.command} | ' + f'{ctx.author.name}#{ctx.author.discriminator} ({ctx.author.id}) | ' + f'{ctx.guild.id}-{ctx.channel.id}-{ctx.message.id}' ) @commands.Cog.listener() - async def on_command_error(self, ctx, error): - """ - Returns something based on the type of command error - """ + async def on_app_command_completion(self, interaction: discord.Interaction, command: app_commands.Command | app_commands.ContextMenu): + """Logs slash command execution metadata""" + self.bot.logger.info( + f'{"❌ " if interaction.command_failed else "✔ "} {command.name} | ' + + f'{interaction.user.name}#{interaction.user.discriminator} ({interaction.user.id}) | ' + + f'{interaction.guild_id}-{interaction.channel_id}-{interaction.id}' + ) - # Reset cooldown if command throws an error + @commands.Cog.listener() + async def on_command_error(self, ctx: commands.Context, error: commands.CommandError): + """Handle prefix command errors""" + # Reset cooldown if command throws AttributeError with ignore_exception(AttributeError): self.bot.get_command(f'{ctx.command}').reset_cooldown(ctx) @@ -57,29 +59,29 @@ class Errors(commands.Cog): elif isinstance(error, commands.BotMissingPermissions): permissions = ', '.join(error.missing_perms) - embed = embed_templates.error_warning(ctx, text='Jeg mangler følgende tillatelser:\n\n' + - f'```\n{permissions}\n```') - return await ctx.send(embed=embed) + text = 'Jeg mangler følgende tillatelser:\n\n' + \ + f'```\n{permissions}\n```' + return await ctx.send(text) elif isinstance(error, commands.MissingPermissions): permissions = ', '.join(error.missing_perms) - embed = embed_templates.error_warning(ctx, text='Du mangler følgende tillatelser\n\n' + - f'```\n{permissions}\n```') - return await ctx.send(embed=embed) + text = 'Du mangler følgende tillatelser\n\n' + \ + f'```\n{permissions}\n```' + return await ctx.send(text) elif isinstance(error, commands.NotOwner): - embed = embed_templates.error_fatal(ctx, text='Bare boteieren kan gjøre dette') - return await ctx.send(embed=embed) + text = 'Bare boteieren kan gjøre dette' + return await ctx.send(text) elif isinstance(error, commands.CommandOnCooldown): - embed = embed_templates.error_warning(ctx, text='Kommandoen har nettopp blitt brukt' + - f'Prøv igjen om `{error.retry_after:.1f}` sekunder.') - return await ctx.send(embed=embed) + text = 'Kommandoen har nettopp blitt brukt' + \ + f'Prøv igjen om `{error.retry_after:.1f}` sekunder.' + return await ctx.send(text) elif isinstance(error, commands.NoPrivateMessage): try: - embed = embed_templates.error_fatal(ctx, text='Denne kommandoen kan bare utføres i servere') - return await ctx.send(embed=embed) + text = 'Denne kommandoen kan bare utføres i servere' + return await ctx.send(text) except discord.errors.Forbidden: # Thrown if bot is blocked by the user or if the user has closed their DMs print("DM Blocked!") @@ -96,5 +98,38 @@ class Errors(commands.Cog): self.bot.logger.error(''.join(traceback.format_exception(type(error), error, error.__traceback__))) + async def on_app_command_error(self, interaction: discord.Interaction, error: app_commands.AppCommandError): + """Handle slash command errors""" + # Log command usage, just in case + await self.on_app_command_completion(interaction, interaction.command) + + if isinstance(error, commands.BotMissingPermissions): + permissions = ', '.join(error.missing_perms) + embed = embed_templates.error_warning(interaction, text='Jeg mangler følgende tillatelser:\n\n' + + f'```\n{permissions}\n```') + return await interaction.response.send_message(embed=embed) + + elif isinstance(error, commands.MissingPermissions): + permissions = ', '.join(error.missing_perms) + embed = embed_templates.error_warning(interaction, text='Du mangler følgende tillatelser\n\n' + + f'```\n{permissions}\n```') + return await interaction.response.send_message(embed=embed) + + elif isinstance(error, commands.NotOwner): + embed = embed_templates.error_fatal(interaction, text='Bare boteieren kan gjøre dette') + return await interaction.response.send_message(embed=embed) + + elif isinstance(error, commands.CommandOnCooldown): + embed = embed_templates.error_warning(interaction, text='Kommandoen har nettopp blitt brukt' + + f'Prøv igjen om `{error.retry_after:.1f}` sekunder.') + return await interaction.response.send_message(embed=embed) + + embed = embed_templates.error_fatal(interaction, text='En ukjent feil oppstod!') + await interaction.response.send_message(embed=embed) + + # Log full exception to file + self.bot.logger.error(''.join(traceback.format_exception(type(error), error, error.__traceback__))) + + async def setup(bot): await bot.add_cog(Errors(bot)) From 0a12661e7aecf30f897f0e6f7baddcbefbd29112 Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 04:58:26 +0100 Subject: [PATCH 41/54] command mentions for long outputs --- src/cogs/info.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/cogs/info.py b/src/cogs/info.py index 9502ab6..2a30f37 100644 --- a/src/cogs/info.py +++ b/src/cogs/info.py @@ -92,11 +92,11 @@ class Info(commands.Cog): # Roles roles = self.__construct_role_string(interaction.guild.roles) - roles = roles if len(roles) < 1024 else f'Bruk kommandoen for å se rollene' + roles = roles if len(roles) < 1024 else f'Bruk /{self.bot.tree.get_command("guild").get_command("roller").qualified_name} for å se rollene' # Boosts boosters = self.__construct_booster_string(interaction) - boosters = boosters if len(boosters) < 1024 else f'Bruk kommandoen for å se boostere' + boosters = boosters if len(boosters) < 1024 else f'Bruk /{self.bot.tree.get_command("guild").get_command("boosters").qualified_name} for å se boostere' # Channels counts text_channels = len(interaction.guild.text_channels) @@ -448,8 +448,7 @@ class Info(commands.Cog): # Get user roles roles = self.__construct_role_string(bruker.roles) - if len(roles) > 1024: - roles = f'Skriv `{self.bot.prefix}{interaction.command}` for å se rollene' + roles = roles if len(roles) < 1024 else f'Bruk /{self.bot.tree.get_command("bruker").get_command("roller").qualified_name} for å se rollene' statuses = { 'online': f'{self.bot.emoji["online"]} Pålogget', From 381e383006576c55b6f280bf0b98f154f75bba5e Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 05:08:52 +0100 Subject: [PATCH 42/54] resolve flake8 issues (except line lengths) --- src/cogs/anime.py | 22 +++++++++++----------- src/cogs/errors.py | 7 +++---- src/cogs/funreplies.py | 4 ++-- src/cogs/info.py | 6 +++--- src/cogs/repeated_messages.py | 4 ++-- src/cogs/utils/discord_utils.py | 2 ++ src/cogs/utils/misc_utils.py | 1 + src/run.py | 2 +- 8 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/cogs/anime.py b/src/cogs/anime.py index 547cc91..ed78e57 100644 --- a/src/cogs/anime.py +++ b/src/cogs/anime.py @@ -247,7 +247,7 @@ class Anime(commands.Cog): end_date = discord.utils.format_dt(end_date, style="d") else: end_date = f'{numbers["end_day"]}.{numbers["end_month"]}.{numbers["end_year"]}' - + if release_date == end_date: date = release_date else: @@ -346,8 +346,8 @@ class Anime(commands.Cog): chapters_read = data['statistics']['manga']['chaptersRead'] color = self.__convert_color(data['options']['profileColor']) - anime_statuses = {status['status']: { 'count': status['count'] } for status in data['statistics']['anime']['statuses']} - manga_statuses = {status['status']: { 'count': status['count'] } for status in data['statistics']['manga']['statuses']} + anime_statuses = {status['status']: {'count': status['count']} for status in data['statistics']['anime']['statuses']} + manga_statuses = {status['status']: {'count': status['count']} for status in data['statistics']['manga']['statuses']} try: anime_completed = anime_statuses['COMPLETED']['count'] @@ -438,7 +438,7 @@ class Anime(commands.Cog): episodes_watched = data['statistics']['anime']['episodesWatched'] color = self.__convert_color(data['options']['profileColor']) - statuses = {status['status']: { 'count': status['count'], 'minutes': status['minutesWatched'] } for status in data['statistics']['anime']['statuses']} + statuses = {status['status']: {'count': status['count'], 'minutes': status['minutesWatched']} for status in data['statistics']['anime']['statuses']} try: completed = statuses['COMPLETED']['count'] @@ -539,7 +539,7 @@ class Anime(commands.Cog): volumes_read = data['statistics']['manga']['volumesRead'] color = self.__convert_color(data['options']['profileColor']) - statuses = {status['status']: { 'count': status['count'], 'minutes': status['chaptersRead'] } for status in data['statistics']['manga']['statuses']} + statuses = {status['status']: {'count': status['count'], 'minutes': status['chaptersRead']} for status in data['statistics']['manga']['statuses']} try: completed = statuses['COMPLETED']['count'] @@ -655,7 +655,7 @@ class Anime(commands.Cog): nsfw = data['isAdult'] if nsfw: embed = embed_templates.error_fatal(interaction, text='Animen du søkte på er NSFW. ' + - 'Gjør kommandoen i en NSFW-kanal i stedet') + 'Gjør kommandoen i en NSFW-kanal i stedet') return await interaction.response.send_message(embed=embed) cover_image = data['coverImage']['large'] @@ -683,7 +683,7 @@ class Anime(commands.Cog): director_native_name = staff_member['node']['name']['native'] director_url = staff_member['node']['siteUrl'] director_string += f'[{director_first_name} {director_last_name} ' +\ - f'({director_native_name})]({director_url})' + f'({director_native_name})]({director_url})' episodes = data['episodes'] duration = data['duration'] @@ -720,7 +720,7 @@ class Anime(commands.Cog): media_format = self.__convert_media_format(media_format) embed = discord.Embed(color=color, title=title_romaji, url=url, - description=f'{title_native}\n{title_english}') + description=f'{title_native}\n{title_english}') embed.set_thumbnail(url=cover_image) embed.add_field(name='Format', value=media_format) embed.add_field(name='Status', value=status) @@ -806,7 +806,7 @@ class Anime(commands.Cog): nsfw = data['isAdult'] if nsfw: embed = embed_templates.error_fatal(interaction, text='Mangaen du søkte på er NSFW. ' + - 'Gjør kommandoen i en NSFW-kanal i stedet') + 'Gjør kommandoen i en NSFW-kanal i stedet') return await interaction.response.send_message(embed=embed) cover_image = data['coverImage']['large'] @@ -832,7 +832,7 @@ class Anime(commands.Cog): staff_native_name = staff_member['node']['name']['native'] staff_url = staff_member['node']['siteUrl'] staff_string += f'[{staff_first_name} {staff_last_name} ({staff_native_name})]({staff_url})\n' - + numbers = { 'chapters': data['chapters'], 'volumes': data['volumes'], @@ -858,7 +858,7 @@ class Anime(commands.Cog): media_format = self.__convert_media_format(media_format) embed = discord.Embed(color=color, title=title_romaji, url=url, - description=f'{title_native}\n{title_english}') + description=f'{title_native}\n{title_english}') embed.set_thumbnail(url=cover_image) embed.add_field(name='Format', value=media_format) embed.add_field(name='Status', value=status) diff --git a/src/cogs/errors.py b/src/cogs/errors.py index 6648c4e..0accd4d 100644 --- a/src/cogs/errors.py +++ b/src/cogs/errors.py @@ -97,7 +97,6 @@ class Errors(commands.Cog): # Log full exception to file self.bot.logger.error(''.join(traceback.format_exception(type(error), error, error.__traceback__))) - async def on_app_command_error(self, interaction: discord.Interaction, error: app_commands.AppCommandError): """Handle slash command errors""" # Log command usage, just in case @@ -106,13 +105,13 @@ class Errors(commands.Cog): if isinstance(error, commands.BotMissingPermissions): permissions = ', '.join(error.missing_perms) embed = embed_templates.error_warning(interaction, text='Jeg mangler følgende tillatelser:\n\n' + - f'```\n{permissions}\n```') + f'```\n{permissions}\n```') return await interaction.response.send_message(embed=embed) elif isinstance(error, commands.MissingPermissions): permissions = ', '.join(error.missing_perms) embed = embed_templates.error_warning(interaction, text='Du mangler følgende tillatelser\n\n' + - f'```\n{permissions}\n```') + f'```\n{permissions}\n```') return await interaction.response.send_message(embed=embed) elif isinstance(error, commands.NotOwner): @@ -121,7 +120,7 @@ class Errors(commands.Cog): elif isinstance(error, commands.CommandOnCooldown): embed = embed_templates.error_warning(interaction, text='Kommandoen har nettopp blitt brukt' + - f'Prøv igjen om `{error.retry_after:.1f}` sekunder.') + f'Prøv igjen om `{error.retry_after:.1f}` sekunder.') return await interaction.response.send_message(embed=embed) embed = embed_templates.error_fatal(interaction, text='En ukjent feil oppstod!') diff --git a/src/cogs/funreplies.py b/src/cogs/funreplies.py index a08c799..fe187c0 100644 --- a/src/cogs/funreplies.py +++ b/src/cogs/funreplies.py @@ -43,7 +43,7 @@ class FunReplies(commands.Cog): elif re.search(r'(\W|\s)*le+s+go+(\s|\W)*', message_content): if (datetime.now() - self.previous_invokations['lesgo']).seconds > self.cooldown_seconds: await message.channel.send('https://cdn.discordapp.com/attachments/750052141346979850/' + - '824764933513281596/3xhpwbakz2361.png') + '824764933513281596/3xhpwbakz2361.png') self.previous_invokations['lesgo'] = datetime.now() elif re.search(r'^((er det|hvor)\s+)*(noen|folk|mange)\s+på\s+(rommet|neuf|kontoret)\?*$', message_content, flags=re.IGNORECASE): @@ -58,7 +58,7 @@ class FunReplies(commands.Cog): elif re.search(r'(^|\W)+ye+et($|\W)+', message_content, flags=re.IGNORECASE): if (datetime.now() - self.previous_invokations['yeet']).seconds > self.cooldown_seconds: await message.channel.send('<:Nei:826593267642662912>') - self.previous_invokations['yeet'] = datetime.now() + self.previous_invokations['yeet'] = datetime.now() async def setup(bot): diff --git a/src/cogs/info.py b/src/cogs/info.py index 2a30f37..edfb6f7 100644 --- a/src/cogs/info.py +++ b/src/cogs/info.py @@ -159,9 +159,9 @@ class Info(commands.Cog): content = content_filter[str(interaction.guild.explicit_content_filter)] embed = discord.Embed(color=interaction.guild.me.color, description=f'**Verifiseringskrav:** {verification}\n' + - f'**Innholdsfilter:** {content}\n' + - f'**Boost Tier:** {interaction.guild.premium_tier}\n' + - f'**Emoji:** {len(interaction.guild.emojis)}') + f'**Innholdsfilter:** {content}\n' + + f'**Boost Tier:** {interaction.guild.premium_tier}\n' + + f'**Emoji:** {len(interaction.guild.emojis)}') embed.set_author(name=interaction.guild.name, icon_url=interaction.guild.icon) embed.set_thumbnail(url=interaction.guild.icon) embed.add_field(name='ID', value=interaction.guild.id) diff --git a/src/cogs/repeated_messages.py b/src/cogs/repeated_messages.py index 84a5dbb..f02528e 100644 --- a/src/cogs/repeated_messages.py +++ b/src/cogs/repeated_messages.py @@ -12,7 +12,7 @@ class RepeatedMessages(commands.Cog): async def fredag(): channel = await self.uiog_server.fetch_channel(747542544291987597) await channel.send( - "NU ÄR DET FREDAG!\n" + \ + "NU ÄR DET FREDAG!\n" + "https://cdn.discordapp.com/attachments/750052141346979850/851216786259181578/video0.mp4" ) @@ -20,7 +20,7 @@ class RepeatedMessages(commands.Cog): async def mandag(): channel = await self.uiog_server.fetch_channel(747542544291987597) await channel.send( - "ENDELIG MANDAG!\n\n" + \ + "ENDELIG MANDAG!\n\n" + "https://cdn.discordapp.com/attachments/678396498089738250/862853827278929940/hvorfor_de_rike_br_spises.mp4" ) diff --git a/src/cogs/utils/discord_utils.py b/src/cogs/utils/discord_utils.py index 953a6b9..ae107ae 100644 --- a/src/cogs/utils/discord_utils.py +++ b/src/cogs/utils/discord_utils.py @@ -20,6 +20,7 @@ def get_color(discord_object: discord.User | discord.Member | discord.Role) -> d return discord.Colour(0x99AAB5) + class ScrollerButton(discord.ui.Button): def __init__(self, paginator: Paginator, button_action: callable, content_constructor: callable, label: str, disabled: bool = False): super().__init__(label=label, disabled=disabled) @@ -32,6 +33,7 @@ class ScrollerButton(discord.ui.Button): content = self.content_constructor(self.paginator, self.button_action(), interaction.message.embeds[0]) await interaction.message.edit(embed=content, view=Scroller(self.paginator, self.content_constructor)) + class Scroller(discord.ui.View): def __init__(self, paginatior: Paginator, content_constructor: callable): super().__init__() diff --git a/src/cogs/utils/misc_utils.py b/src/cogs/utils/misc_utils.py index 4eec3b0..bb97244 100644 --- a/src/cogs/utils/misc_utils.py +++ b/src/cogs/utils/misc_utils.py @@ -20,6 +20,7 @@ def ignore_exception(*exceptions) -> None: except exceptions: pass + class Paginator: def __init__(self, content: list): self.content = content diff --git a/src/run.py b/src/run.py index 665af81..5fbb194 100644 --- a/src/run.py +++ b/src/run.py @@ -22,7 +22,7 @@ class Bot(commands.Bot): case_insensitive=True, intents=discord.Intents.all() ) - + self.logger = BotLogger().logger # Initialize logger # Connect to database From f20ea366da294a0d9a77ae0b7c18415487cb5e5f Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 05:15:00 +0100 Subject: [PATCH 43/54] sort imports alphabetically and specify major/minor number --- requirements.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/requirements.txt b/requirements.txt index 0c1b8e4..e768269 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,9 @@ -discord.py -pyyaml -requests -asyncio -psutil -pillow -aiocron -psycopg2 -mcrcon +aiocron==1.8.* +asyncio==3.4.* +discord.py==2.1.* +mcrcon==0.7.* +pillow==9.3.* +psutil==5.9.* +psycopg2==2.9.* +pyyaml==6.0.* +requests==2.28.* From 2c94de4c9385ff3075e65681c9597176d6406969 Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 05:17:33 +0100 Subject: [PATCH 44/54] fill name and year sections of license --- LICENSE | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/LICENSE b/LICENSE index e72bfdd..2a15844 100644 --- a/LICENSE +++ b/LICENSE @@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - - Copyright (C) + UiO Gaming's multi-purpose Discord bot + Copyright (C) 2022 Leander West Furumo This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - Copyright (C) + uiogaming-bot Copyright (C) 2022 Leander West Furumo This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. From a987ff6d3f12421c1f547264b0dc5e57d2cfef09 Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 05:24:13 +0100 Subject: [PATCH 45/54] add dev/prod mode specification --- src/config/config.yaml.example | 2 ++ src/run.py | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/config/config.yaml.example b/src/config/config.yaml.example index 3c6870f..d2343c9 100644 --- a/src/config/config.yaml.example +++ b/src/config/config.yaml.example @@ -1,3 +1,5 @@ +config_mode: dev + # Bot settings bot: token: diff --git a/src/run.py b/src/run.py index 5fbb194..25f3bf9 100644 --- a/src/run.py +++ b/src/run.py @@ -40,6 +40,7 @@ class Bot(commands.Bot): self.api_keys = config.get("api", {}) self.emoji = config.get("emoji", {}) self.misc = config.get("misc", {}) + self.config_mode = config.get("mode") async def setup_hook(self) -> None: # Load cogs @@ -49,8 +50,11 @@ class Bot(commands.Bot): await bot.load_extension(f"cogs.{name}") # Sync slash commands to Discord - self.tree.copy_global_to(guild=discord.Object(id=412646636771344395)) # Temporary. Development purposes only. - await self.tree.sync(guild=discord.Object(id=412646636771344395)) # Temporary. Development purposes only. + if self.config_mode == 'prod': + await self.tree.sync() + else: + self.tree.copy_global_to(guild=discord.Object(id=412646636771344395)) + await self.tree.sync(guild=discord.Object(id=412646636771344395)) # Create bot instance From f11238a1505a67a25f30600ee98844b3a632f061 Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 18:20:53 +0100 Subject: [PATCH 46/54] add missing command checks + consistent order of checks --- src/cogs/birthday.py | 4 ++++ src/cogs/dev_tools.py | 10 +++++----- src/cogs/info.py | 26 +++++++++++++------------- src/cogs/mc_whitelist.py | 1 + src/cogs/misc.py | 1 + 5 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/cogs/birthday.py b/src/cogs/birthday.py index e0b66bc..8662ab0 100644 --- a/src/cogs/birthday.py +++ b/src/cogs/birthday.py @@ -110,6 +110,7 @@ class Birthday(commands.Cog): await channel.send(f"Gratulrer med dagen {user.mention}!") ''' + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) @app_commands.command() async def settbursdag(self, interaction: discord.Interaction, dag: int, måned: int, år: int): @@ -139,6 +140,7 @@ class Birthday(commands.Cog): embed = embed_templates.success(interaction, text=f"Bursdag satt til {discord.utils.format_dt(date, style='D')}") await interaction.response.send_message(embed=embed) + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) @app_commands.command() async def fjernbursdag(self, interaction: discord.Interaction): @@ -149,6 +151,7 @@ class Birthday(commands.Cog): embed = embed_templates.success(interaction, text="Bursdag fjernet") await interaction.response.send_message(embed=embed) + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) @app_commands.command() async def bursdag(self, interaction: discord.Interaction, bruker: discord.Member | discord.User | None = None): @@ -182,6 +185,7 @@ class Birthday(commands.Cog): await interaction.response.send_message(embed=embed) @app_commands.guild_only() + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) @app_commands.command() async def kommendebursdager(self, interaction: discord.Interaction): diff --git a/src/cogs/dev_tools.py b/src/cogs/dev_tools.py index ec6b32f..eb1ff7f 100644 --- a/src/cogs/dev_tools.py +++ b/src/cogs/dev_tools.py @@ -12,8 +12,8 @@ class DevTools(commands.Cog): def __init__(self, bot): self.bot = bot - @commands.bot_has_permissions(embed_links=True) @commands.is_owner() + @commands.bot_has_permissions(embed_links=True) @commands.command() async def custommsg(self, ctx: commands.Context, channel: int, *text: tuple[str]): """Send a message to a specified channel""" @@ -27,8 +27,8 @@ class DevTools(commands.Cog): embed.add_field(name='Sent', value=custommessage) await ctx.send(embed=embed) - @commands.bot_has_permissions(embed_links=True) @commands.is_owner() + @commands.bot_has_permissions(embed_links=True) @commands.cooldown(1, 10, commands.BucketType.guild) @commands.command() async def changepresence(self, ctx: commands.Context, activity_type: str, message: str, status_type: str): @@ -55,8 +55,8 @@ class DevTools(commands.Cog): embed = discord.Embed(color=ctx.me.color, description='Endret Presence!') await ctx.send(embed=embed) - @commands.bot_has_permissions(embed_links=True, add_reactions=True) @commands.is_owner() + @commands.bot_has_permissions(embed_links=True, add_reactions=True) @commands.command() async def leave(self, ctx: commands.Context, *guild_id: int): """Leaves a specified guild""" @@ -91,8 +91,8 @@ class DevTools(commands.Cog): except discord.errors.Forbidden: pass - @commands.bot_has_permissions(embed_links=True) @commands.is_owner() + @commands.bot_has_permissions(embed_links=True) @commands.command() async def publicip(self, ctx: commands.Context): """Sends WAN IP-address. inb4 I leak my IP-address""" @@ -105,8 +105,8 @@ class DevTools(commands.Cog): embed.add_field(name='WAN IP-address', value=f'{ip}\n{location}\n{isp}') await ctx.send(embed=embed) - @commands.bot_has_permissions(embed_links=True) @commands.is_owner() + @commands.bot_has_permissions(embed_links=True) @commands.group() async def cogs(self, ctx: commands.Context): """Cog management commands""" diff --git a/src/cogs/info.py b/src/cogs/info.py index edfb6f7..0e65983 100644 --- a/src/cogs/info.py +++ b/src/cogs/info.py @@ -61,8 +61,8 @@ class Info(commands.Cog): with misc_utils.ignore_exception(OSError): remove(file_path) - @app_commands.checks.bot_has_permissions(embed_links=True, external_emojis=True) @app_commands.guild_only() + @app_commands.checks.bot_has_permissions(embed_links=True, external_emojis=True) @app_commands.checks.cooldown(1, 2) @guild_group.command(name="info", description="Hent informasjon om en server") async def guild_info(self, interaction: discord.Interaction): @@ -192,8 +192,8 @@ class Info(commands.Cog): embed.add_field(name='Bilder', value=photos_string) await interaction.response.send_message(embed=embed) - @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.guild_only() + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) @guild_group.command(name="roller", description="Se rollene på serveren") async def guild_roles(self, interaction: discord.Interaction): @@ -208,8 +208,8 @@ class Info(commands.Cog): embed.set_footer(text=interaction.guild.name, icon_url=interaction.guild.icon) await interaction.response.send_message(embed=embed) - @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.guild_only() + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) @guild_group.command(name="boosters", description="Se boostere på serveren") async def guild_boosters(self, interaction: discord.Interaction): @@ -224,8 +224,8 @@ class Info(commands.Cog): embed.set_footer(text=interaction.guild.name, icon_url=interaction.guild.icon) await interaction.response.send_message(embed=embed) - @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.guild_only() + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) @guild_group.command(name="ikon", description="Hent serverens ikon") async def guild_icon(self, interaction: discord.Interaction): @@ -235,8 +235,8 @@ class Info(commands.Cog): embed_templates.default_footer(interaction, embed) await interaction.response.send_message(embed=embed) - @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.guild_only() + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) @guild_group.command(name="splash", description="Hent serverens splash (bakgrunnsbilde på invitasjonsside)") async def guild_splash(self, interaction: discord.Interaction): @@ -250,8 +250,8 @@ class Info(commands.Cog): embed_templates.default_footer(interaction, embed) await interaction.response.send_message(embed=embed) - @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.guild_only() + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) @guild_group.command(name="banner", description="Hen serverens banner") async def guild_banner(self, interaction: discord.Interaction): @@ -265,8 +265,8 @@ class Info(commands.Cog): embed_templates.default_footer(interaction, embed) await interaction.response.send_message(embed=embed) - @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.guild_only() + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) @guild_group.command(name="rolle", description="Hent informasjon om en rolle på serveren") async def guild_role(self, interaction: discord.Interaction, rolle: discord.Role): @@ -299,8 +299,8 @@ class Info(commands.Cog): embed_templates.default_footer(interaction, embed) await interaction.response.send_message(embed=embed) - @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.guild_only() + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) @guild_group.command(name="tekstkanal", description="Hent informasjon om en tekstkanal") async def guild_text_channel(self, interaction: discord.Interaction, kanal: discord.TextChannel): @@ -320,8 +320,8 @@ class Info(commands.Cog): embed_templates.default_footer(interaction, embed) await interaction.response.send_message(embed=embed) - @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.guild_only() + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) @guild_group.command(name="talekanal", description="Hent informasjon om en talekanal") async def guild_voice_channel(self, interaction: discord.Interaction, kanal: discord.VoiceChannel): @@ -367,8 +367,8 @@ class Info(commands.Cog): embed.set_footer(text=f'Side {paginator.current_page}/{paginator.total_page_count}') return embed - @app_commands.checks.bot_has_permissions(embed_links=True) @commands.guild_only() + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) @guild_oldest_group.command(name="lagd", description="Liste over de eldste brukerene på serveren basert på når de ble lagd") async def guild_user_created_oldest(self, interaction: discord.Interaction): @@ -389,8 +389,8 @@ class Info(commands.Cog): embed = self.__construct_oldest_embed(paginator, paginator.get_current_page(), embed) await interaction.response.send_message(embed=embed, view=view) - @app_commands.checks.bot_has_permissions(embed_links=True) @commands.guild_only() + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) @guild_oldest_group.command(name="joined", description="Liste over de eldste brukerene på serveren basert på når de ble med") async def guild_user_joined_oldest(self, interaction: discord.Interaction): @@ -411,8 +411,8 @@ class Info(commands.Cog): embed = self.__construct_oldest_embed(paginator, paginator.get_current_page(), embed) await interaction.response.send_message(embed=embed, view=view) - @app_commands.checks.bot_has_permissions(embed_links=True, external_emojis=True) @app_commands.guild_only() + @app_commands.checks.bot_has_permissions(embed_links=True, external_emojis=True) @app_commands.checks.cooldown(1, 2) @user_group.command(name="info", description="Hent informasjon om en bruker") async def user_info(self, interaction: discord.Interaction, *, bruker: discord.Member | None = None): @@ -486,8 +486,8 @@ class Info(commands.Cog): await interaction.response.send_message(embed=embed) - @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.guild_only() + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) @user_group.command(name="roller", description="Hent roller til en bruker") async def user_roles(self, interaction: discord.Interaction, bruker: discord.Member | None = None): diff --git a/src/cogs/mc_whitelist.py b/src/cogs/mc_whitelist.py index f79f0b8..ff0be78 100644 --- a/src/cogs/mc_whitelist.py +++ b/src/cogs/mc_whitelist.py @@ -25,6 +25,7 @@ class MCWhitelist(commands.Cog): ) self.bot.db_connection.commit() + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) @app_commands.command() async def whitelist(self, interaction: discord.Interaction, minecraftbrukernavn: str): diff --git a/src/cogs/misc.py b/src/cogs/misc.py index 6207e1a..3f85bd3 100644 --- a/src/cogs/misc.py +++ b/src/cogs/misc.py @@ -94,6 +94,7 @@ https://cdn.discordapp.com/attachments/674726496878723082/1042631569396990004/kw embed_templates.default_footer(interation, embed) await interation.response.send_message(embed=embed) + @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) @app_commands.command() async def imdb(self, interaction: discord.Interaction, tittel: str): From 8a143260ee3e6f25fe012db088e7f77445861a66 Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 18:22:28 +0100 Subject: [PATCH 47/54] replace multi-line commented code with line commented code --- src/cogs/birthday.py | 46 +++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/cogs/birthday.py b/src/cogs/birthday.py index 8662ab0..ddd7ea3 100644 --- a/src/cogs/birthday.py +++ b/src/cogs/birthday.py @@ -85,30 +85,28 @@ class Birthday(commands.Cog): self.cursor.execute("UPDATE birthdays SET birthday = %s WHERE discord_id = %s", (birthday, user_id)) self.bot.db_connection.commit() - ''' - @aiocron.crontab("0 0 * * *") - async def __check_birthdays(self): - """Check if it's someone's birthday every day at midgnight and send a greeting if it is.""" - await asyncio.sleep(1) # Prevent sending message before date has really changed - - self.cursor.execute( - """ - SELECT * - FROM birthdays - WHERE EXTRACT(MONTH FROM birthday) = EXTRACT(MONTH FROM current_date) - AND EXTRACT(DAY FROM birthday) = EXTRACT(DAY FROM current_date); - """ - ) - - birthdays = self.cursor.fetchall() - if birthdays: - guild = self.bot.get_guild(747542543750660178) - channel = guild.get_channel(747542544291987597) - for birthday in birthdays: - user = self.bot.get_user(birthday[0]) - if user: - await channel.send(f"Gratulrer med dagen {user.mention}!") - ''' + # @aiocron.crontab("0 0 * * *") + # async def __check_birthdays(self): + # """Check if it's someone's birthday every day at midgnight and send a greeting if it is.""" + # await asyncio.sleep(1) # Prevent sending message before date has really changed + # + # self.cursor.execute( + # """ + # SELECT * + # FROM birthdays + # WHERE EXTRACT(MONTH FROM birthday) = EXTRACT(MONTH FROM current_date) + # AND EXTRACT(DAY FROM birthday) = EXTRACT(DAY FROM current_date); + # """ + # ) + # + # birthdays = self.cursor.fetchall() + # if birthdays: + # guild = self.bot.get_guild(747542543750660178) + # channel = guild.get_channel(747542544291987597) + # for birthday in birthdays: + # user = self.bot.get_user(birthday[0]) + # if user: + # await channel.send(f"Gratulrer med dagen {user.mention}!") @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) From 911b695924a3eee17a30d91bfb9a07f6dcc41ce3 Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 18:23:00 +0100 Subject: [PATCH 48/54] add request timeout publicip --- src/cogs/dev_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cogs/dev_tools.py b/src/cogs/dev_tools.py index eb1ff7f..861974b 100644 --- a/src/cogs/dev_tools.py +++ b/src/cogs/dev_tools.py @@ -96,7 +96,7 @@ class DevTools(commands.Cog): @commands.command() async def publicip(self, ctx: commands.Context): """Sends WAN IP-address. inb4 I leak my IP-address""" - data = requests.get('https://wtfismyip.com/json').json() + data = requests.get('https://wtfismyip.com/json', timeout=10).json() ip = data['YourFuckingIPAddress'] location = data['YourFuckingLocation'] isp = data['YourFuckingISP'] From 1860f1fc4484a212a085236ef5b58eb3cee78183 Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 18:55:20 +0100 Subject: [PATCH 49/54] add missing type annotations --- src/cogs/anime.py | 6 +++--- src/cogs/birthday.py | 4 ++-- src/cogs/botinfo.py | 4 ++-- src/cogs/deprecated/camera_channel.py | 4 ++-- src/cogs/deprecated/social_credit.py | 4 ++-- src/cogs/deprecated/vær.py | 4 ++-- src/cogs/deprecated/wiki_search.py | 2 +- src/cogs/dev_tools.py | 4 ++-- src/cogs/errors.py | 4 ++-- src/cogs/fun.py | 18 +++++++++--------- src/cogs/funreplies.py | 4 ++-- src/cogs/info.py | 4 ++-- src/cogs/mc_whitelist.py | 4 ++-- src/cogs/misc.py | 4 ++-- src/cogs/repeated_messages.py | 4 ++-- 15 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/cogs/anime.py b/src/cogs/anime.py index ed78e57..b8dab21 100644 --- a/src/cogs/anime.py +++ b/src/cogs/anime.py @@ -10,7 +10,7 @@ from cogs.utils import embed_templates class Anime(commands.Cog): - def __init__(self, bot): + def __init__(self, bot: commands.Bot): self.bot = bot anilist = app_commands.Group(name='anilist', description='Hent informasjon fra Anilist') @@ -198,7 +198,7 @@ class Anime(commands.Cog): pass return ' | '.join(favorite_media) - def __construct_favorite_entity_string(self, entities: list, studio=False) -> str: + def __construct_favorite_entity_string(self, entities: list, studio: bool = False) -> str: """ Constructs a string with the user's favorite entities @@ -1145,5 +1145,5 @@ class Anime(commands.Cog): await interaction.response.send_message(embed=embed) -async def setup(bot): +async def setup(bot: commands.Bot): await bot.add_cog(Anime(bot)) diff --git a/src/cogs/birthday.py b/src/cogs/birthday.py index ddd7ea3..9ee3450 100644 --- a/src/cogs/birthday.py +++ b/src/cogs/birthday.py @@ -10,7 +10,7 @@ from cogs.utils import discord_utils, embed_templates class Birthday(commands.Cog): - def __init__(self, bot): + def __init__(self, bot: commands.Bot): self.bot = bot self.cursor = self.bot.db_connection.cursor() @@ -213,5 +213,5 @@ class Birthday(commands.Cog): await interaction.response.send_message(embed=embed) -async def setup(bot): +async def setup(bot: commands.Bot): await bot.add_cog(Birthday(bot)) diff --git a/src/cogs/botinfo.py b/src/cogs/botinfo.py index 52e44af..7e238df 100644 --- a/src/cogs/botinfo.py +++ b/src/cogs/botinfo.py @@ -11,7 +11,7 @@ from cogs.utils import embed_templates class BotInfo(commands.Cog): - def __init__(self, bot): + def __init__(self, bot: commands.Bot): self.bot = bot @app_commands.checks.bot_has_permissions(embed_links=True, external_emojis=True) @@ -130,5 +130,5 @@ class BotInfo(commands.Cog): return f'{days}d, {hours}t, {minutes}m, {seconds}s' -async def setup(bot): +async def setup(bot: commands.Bot): await bot.add_cog(BotInfo(bot)) diff --git a/src/cogs/deprecated/camera_channel.py b/src/cogs/deprecated/camera_channel.py index 05ccb15..11e9889 100644 --- a/src/cogs/deprecated/camera_channel.py +++ b/src/cogs/deprecated/camera_channel.py @@ -4,7 +4,7 @@ import asyncio class CameraChannel(commands.Cog): - def __init__(self, bot): + def __init__(self, bot: commands.Bot): self.bot = bot async def on_voice_state_update(self, member, before, after): @@ -20,7 +20,7 @@ class CameraChannel(commands.Cog): await member.move_to(channel=None) -async def setup(bot): +async def setup(bot: commands.Bot): #bot.add_listener(CameraChannel(bot).on_voice_state_update, 'on_voice_state_update') #await bot.add_cog(CameraChannel(bot)) pass diff --git a/src/cogs/deprecated/social_credit.py b/src/cogs/deprecated/social_credit.py index 464722a..048d619 100644 --- a/src/cogs/deprecated/social_credit.py +++ b/src/cogs/deprecated/social_credit.py @@ -47,7 +47,7 @@ class CreditUser: class SocialCredit(commands.Cog): - def __init__(self, bot): + def __init__(self, bot: commands.Bot): self.bot = bot self.fuck_uwu.start() @@ -269,5 +269,5 @@ class SocialCredit(commands.Cog): await self.social_punishment(user.id, 25) -async def setup(bot): +async def setup(bot: commands.Bot): await bot.add_cog(SocialCredit(bot)) diff --git a/src/cogs/deprecated/vær.py b/src/cogs/deprecated/vær.py index 1bfdda8..84bc9ce 100644 --- a/src/cogs/deprecated/vær.py +++ b/src/cogs/deprecated/vær.py @@ -7,7 +7,7 @@ import json class Vær(commands.Cog): - def __init__(self, bot): + def __init__(self, bot: commands.Bot): self.bot = bot @aiocron.crontab("0 6 * * *") @@ -42,5 +42,5 @@ class Vær(commands.Cog): await channel.send("RISE AND SHINE GAMERS!", embed=embed) -async def setup(bot): +async def setup(bot: commands.Bot): await bot.add_cog(Vær(bot)) diff --git a/src/cogs/deprecated/wiki_search.py b/src/cogs/deprecated/wiki_search.py index cbd1f61..5ce9bdc 100644 --- a/src/cogs/deprecated/wiki_search.py +++ b/src/cogs/deprecated/wiki_search.py @@ -21,5 +21,5 @@ class Wiki(commands.Cog): embed_templates.default_footer(ctx,embed) await ctx.send(embed=embed) -async def setup(bot): +async def setup(bot: commands.Bot): await bot.add_cog(Wiki(bot)) \ No newline at end of file diff --git a/src/cogs/dev_tools.py b/src/cogs/dev_tools.py index 861974b..11137da 100644 --- a/src/cogs/dev_tools.py +++ b/src/cogs/dev_tools.py @@ -9,7 +9,7 @@ from cogs.utils import embed_templates class DevTools(commands.Cog): - def __init__(self, bot): + def __init__(self, bot: commands.Bot): self.bot = bot @commands.is_owner() @@ -185,5 +185,5 @@ class DevTools(commands.Cog): await ctx.send(embed=embed) -async def setup(bot): +async def setup(bot: commands.Bot): await bot.add_cog(DevTools(bot)) diff --git a/src/cogs/errors.py b/src/cogs/errors.py index 0accd4d..d11dd93 100644 --- a/src/cogs/errors.py +++ b/src/cogs/errors.py @@ -9,7 +9,7 @@ from cogs.utils.misc_utils import ignore_exception class Errors(commands.Cog): - def __init__(self, bot): + def __init__(self, bot: commands.Bot): self.bot = bot bot.tree.on_error = self.on_app_command_error @@ -130,5 +130,5 @@ class Errors(commands.Cog): self.bot.logger.error(''.join(traceback.format_exception(type(error), error, error.__traceback__))) -async def setup(bot): +async def setup(bot: commands.Bot): await bot.add_cog(Errors(bot)) diff --git a/src/cogs/fun.py b/src/cogs/fun.py index 023a0b5..b59d3c9 100644 --- a/src/cogs/fun.py +++ b/src/cogs/fun.py @@ -2,44 +2,44 @@ from discord.ext import commands class Fun(commands.Cog): - def __init__(self, bot): + def __init__(self, bot: commands.Bot): self.bot = bot @commands.cooldown(1, 2, commands.BucketType.guild) @commands.command() - async def norskeuniversiteter(self, ctx): + async def norskeuniversiteter(self, ctx: commands.Context): await ctx.send('') @commands.cooldown(1, 2, commands.BucketType.guild) @commands.command() - async def ifi(self, ctx): + async def ifi(self, ctx: commands.Context): await ctx.send('https://i.imgur.com/ypyK1mi.jpg') @commands.cooldown(1, 2, commands.BucketType.guild) @commands.command() - async def ifi2(self, ctx): + async def ifi2(self, ctx: commands.Context): await ctx.send('https://i.imgur.com/ZqgZEEA.jpg') @commands.cooldown(1, 2, commands.BucketType.guild) @commands.command() - async def ifi3(self, ctx): + async def ifi3(self, ctx: commands.Context): await ctx.send('https://i.imgur.com/Gx9DQE5.jpg') @commands.cooldown(1, 2, commands.BucketType.guild) @commands.command() - async def uio(self, ctx): + async def uio(self, ctx: commands.Context): await ctx.send('https://i.imgur.com/188MoIV.jpg') @commands.cooldown(1, 2, commands.BucketType.guild) @commands.command() - async def ntnu(self, ctx): + async def ntnu(self, ctx: commands.Context): await ctx.send('https://twitter.com/NTNU/status/970667413564993536') @commands.cooldown(1, 2, commands.BucketType.guild) @commands.command() - async def ntnu2(self, ctx): + async def ntnu2(self, ctx: commands.Context): await ctx.send('https://i.imgur.com/h84fknj.jpg') -async def setup(bot): +async def setup(bot: commands.Bot): await bot.add_cog(Fun(bot)) diff --git a/src/cogs/funreplies.py b/src/cogs/funreplies.py index fe187c0..fcc42ce 100644 --- a/src/cogs/funreplies.py +++ b/src/cogs/funreplies.py @@ -7,7 +7,7 @@ from discord.ext import commands class FunReplies(commands.Cog): - def __init__(self, bot): + def __init__(self, bot: commands.Bot): self.bot = bot # Cooldowns for trigger words @@ -61,6 +61,6 @@ class FunReplies(commands.Cog): self.previous_invokations['yeet'] = datetime.now() -async def setup(bot): +async def setup(bot: commands.Bot): bot.add_listener(FunReplies(bot).reply_to_triggers, 'on_message') await bot.add_cog(FunReplies(bot)) diff --git a/src/cogs/info.py b/src/cogs/info.py index 0e65983..c989d78 100644 --- a/src/cogs/info.py +++ b/src/cogs/info.py @@ -8,7 +8,7 @@ from cogs.utils import discord_utils, embed_templates, misc_utils class Info(commands.Cog): - def __init__(self, bot): + def __init__(self, bot: commands.Bot): self.bot = bot guild_group = app_commands.Group(name="guild", description="Se ting om serveren") @@ -519,5 +519,5 @@ class Info(commands.Cog): await interaction.response.send_message(embed=embed) -async def setup(bot): +async def setup(bot: commands.Bot): await bot.add_cog(Info(bot)) diff --git a/src/cogs/mc_whitelist.py b/src/cogs/mc_whitelist.py index ff0be78..ab6fbcc 100644 --- a/src/cogs/mc_whitelist.py +++ b/src/cogs/mc_whitelist.py @@ -8,7 +8,7 @@ from cogs.utils import embed_templates class MCWhitelist(commands.Cog): - def __init__(self, bot): + def __init__(self, bot: commands.Bot): self.bot = bot self.cursor = self.bot.db_connection.cursor() self.init_db() @@ -80,5 +80,5 @@ class MCWhitelist(commands.Cog): ) -async def setup(bot): +async def setup(bot: commands.Bot): await bot.add_cog(MCWhitelist(bot)) diff --git a/src/cogs/misc.py b/src/cogs/misc.py index 3f85bd3..73cbc24 100644 --- a/src/cogs/misc.py +++ b/src/cogs/misc.py @@ -14,7 +14,7 @@ from cogs.utils.misc_utils import ignore_exception class Misc(commands.Cog): - def __init__(self, bot): + def __init__(self, bot: commands.Bot): self.bot = bot @app_commands.checks.bot_has_permissions(embed_links=True) @@ -222,5 +222,5 @@ https://cdn.discordapp.com/attachments/674726496878723082/1042631569396990004/kw remove(f"./src/assets/temp/{interaction.user.id}_{bruker.id}_edit.png") -async def setup(bot): +async def setup(bot: commands.Bot): await bot.add_cog(Misc(bot)) diff --git a/src/cogs/repeated_messages.py b/src/cogs/repeated_messages.py index f02528e..7fa6bc9 100644 --- a/src/cogs/repeated_messages.py +++ b/src/cogs/repeated_messages.py @@ -3,7 +3,7 @@ from discord.ext import commands class RepeatedMessages(commands.Cog): - def __init__(self, bot): + def __init__(self, bot: commands.Bot): self.bot = bot self.uiog_server = self.bot.get_guild(747542543750660178) @@ -32,5 +32,5 @@ class RepeatedMessages(commands.Cog): ) -async def setup(bot): +async def setup(bot: commands.Bot): await bot.add_cog(RepeatedMessages(bot)) From 4ea61bcfadd85d03b61e52f7d18acdea00246df5 Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 22:02:32 +0100 Subject: [PATCH 50/54] initial contribution guideline --- CONTRIBUTING.md | 102 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..18bee40 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,102 @@ +# Bidragsguide + +Heisann sveisann! Er du interessert i å bidra? Kult! + +Det er derimot noen kodestilsregler jeg ønsker at skal følges. Dette dokumentet fungerer som en defacto regelbok for både meg og/eller andre. + +## Sjekk koden din + +Viktigst av alt er at koden din består flake8 sin PEP8-test. Dette kan du teste på egen maskin ved å installere flake8: + +``` +pip install flake8 +``` + +for å så kjøre følgende kommando: + +``` +flake8 --max-line-length=120 --show-source +``` + +## Regler + +### Typing + +All kode skal være typet til beste evne. Dette betyr at man for eksempel også gjerne bør spesifisere typene innad i en dict eller lignende. + +### Docstrings + +Alle funksjoner skal ha docsstrings som følger denne stilen: + +```python +def greet(self, navn: str) -> str: + """ + Greets the invoking user using the inputted name + + Parameters + ---------- + navn (str): The user's inputted name + + Returns + ---------- + (str): A greeting + """ + + return f'Hello, {name}' +``` + +Sløyf alt som ikke brukes. + +### Sortering av imports + +Alle imports skal være gruppert på følgende måte: + +1. Standardbibliotek +2. Tredjepartsbibliotek +3. Lokal kode + +Innad i hver gruppe skal importeringer sortert alfabetisk med vekt på forelderpakken. Det vil si at hvis du importerer bare en del av en pakke skal den fortsatt være sortert i henhold til forelderpakkens navn. + +### Rekkefølge på kommandosjekker (decorators) + +Hver kommando er en funksjon som har et eller flere decorators over seg. Dette er sjekker som utføres før kommandoen kjøres. En av disse konverterer også funksjonen til en faktisk kommando som vi kan kjøre gjennom Discord. + +Decorators skal sorteres som følgende: + +1. is owner +2. Guild only +3. Bot permissions +4. User permissions +5. Cooldown +6. Kommando (denne må alltid være med) + +Merk deg at ikke alle disse trenger å være med, og ikke alle sjekker som finnes er nevnt her. Bruk hodet og tenk hva som er logisk for deg om du møter på en slik situasjon. + +### Quotation marks + +Ta i bruk enkle quotation marks `'` der det er mulig. Multiline comments skal skrives med dobble quotation marks `"`. + +### Newline mellom docstring og kode samt mellom siste kodelinje returstatement + +Ja, det tittelen sier. Det er et unntak for returstatements hvis det er tatt i bruk early returning. + +### Norske parameternavn i kommandoer + +Selv om Discord.py 2.0 støtter oversettelser, er ikke dette noe vi implementerer. Med tanke på at botten er norsk og for norske brukere må dette reflekteres i parameternavn siden dette er hva Discord bruker som grensesnitt for kommandoer. + +### Engelsk for alt annet + +All annen kode holdes på engelsk. Dette inkluderer kommentarer. + +Et tips er å ha et engelsk funksjonsnavn, men spesifisere i decoratoren over med navnparameteren. + +### Avhengigheter + +Hvis en ny avhengighet trengs, sørg for å spesifisere major og minor versjon i requirementsfila. + + +## Ønsket utforming av ny funksjonalitet + +- Bruk embeds til feilmeldinger og som respons om mulig. +- Sett cooldown til 5 sekunder for kommandoer som krever eksterne API kall eller særlig mye prosessering. + - Ellers, settes denne til 2 sekunder. From 1ee4b92ad7a5acbc63479ed929dee4bdd53bbd0b Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 22:03:23 +0100 Subject: [PATCH 51/54] follow contribution guidelines --- src/cogs/anime.py | 156 +++++++++++++---- src/cogs/birthday.py | 167 ++++++++++++++----- src/cogs/botinfo.py | 55 ++++-- src/cogs/deprecated/social_credit.py | 44 ++--- src/cogs/deprecated/vær.py | 10 +- src/cogs/deprecated/wiki_search.py | 4 +- src/cogs/dev_tools.py | 147 +++++++++++++--- src/cogs/errors.py | 59 ++++++- src/cogs/fun.py | 73 +++++++- src/cogs/funreplies.py | 25 ++- src/cogs/info.py | 241 +++++++++++++++++++++++---- src/cogs/mc_whitelist.py | 46 +++-- src/cogs/misc.py | 218 ++++++++++++++++-------- src/cogs/repeated_messages.py | 33 +++- src/cogs/utils/discord_utils.py | 30 +++- src/cogs/utils/embed_templates.py | 20 ++- src/cogs/utils/misc_utils.py | 9 +- src/logger.py | 2 + src/run.py | 66 ++++---- 19 files changed, 1092 insertions(+), 313 deletions(-) diff --git a/src/cogs/anime.py b/src/cogs/anime.py index b8dab21..3269fa1 100644 --- a/src/cogs/anime.py +++ b/src/cogs/anime.py @@ -10,8 +10,7 @@ from cogs.utils import embed_templates class Anime(commands.Cog): - def __init__(self, bot: commands.Bot): - self.bot = bot + """View information about different media on Anilist""" anilist = app_commands.Group(name='anilist', description='Hent informasjon fra Anilist') anilist_profile = app_commands.Group(parent=anilist, name='profil', description='Hent informasjon om en bruker på Anilist') @@ -22,12 +21,13 @@ class Anime(commands.Cog): Parameters ---------- - color: The color string returned from the Anilist API + color (str): The color string returned from the Anilist API Returns ---------- (int): The color in hex for use in discord embeds """ + colors = { 'blue': 0x3db4f2, 'purple': 0xc063ff, @@ -37,6 +37,7 @@ class Anime(commands.Cog): 'green': 0x4CCA51, 'gray': 0x677B94 } + return colors.get(color, 0x677B94) def __convert_media_format(self, media_format: str) -> str: @@ -45,12 +46,13 @@ class Anime(commands.Cog): Parameters ---------- - media_format: The english media format name returned from the Anilist API + media_format (str): The english media format name returned from the Anilist API Returns ---------- (str): The media format name in norwegian. If a translation doesn't exist it returns the english name """ + media_formats = { 'TV': 'TV-Serie', 'TV_SHORT': 'Kort TV-Serie', @@ -69,12 +71,13 @@ class Anime(commands.Cog): Parameters ---------- - language_name: The english language name returned from the Anilist API + language_name (str): The english language name returned from the Anilist API Returns ---------- (str): The language name in norwegian. If a translation doesn't exist it returns the english name """ + languages = { 'JAPANESE': 'Japansk', 'ENGLISH': 'Engelsk', @@ -87,6 +90,7 @@ class Anime(commands.Cog): 'HEBREW': 'Hebraisk', 'HUNGARIAN': 'Ungarsk' } + return languages.get(language_name, language_name) def __convert_role_names(self, role_name: str) -> str: @@ -95,7 +99,7 @@ class Anime(commands.Cog): Parameters ---------- - role_name: The english role name returned from the Anilist API + role_name (str): The english role name returned from the Anilist API Returns ---------- @@ -118,18 +122,20 @@ class Anime(commands.Cog): Parameters ---------- - status: The english status name returned from the Anilist API + status (str): The english status name returned from the Anilist API Returns ---------- (str): The status name in norwegian. If a translation doesn't exist it returns the english name """ + statuses = { 'FINISHED': 'Fullt utgitt', 'RELEASING': 'Pågående', 'NOT_YET_RELEASED': 'Ikke utgitt enda', 'CANCELLED': 'Kansellert' } + return statuses.get(status, status) def __remove_html(self, text: str) -> str: @@ -144,8 +150,10 @@ class Anime(commands.Cog): ---------- (str): The input string without HTML tags """ + compiled = re.compile(r'<.*?>') clean_text = re.sub(compiled, '', text) + return clean_text async def __request_anilist(self, interaction: discord.Interaction, query: str, variables: dict, key: str) -> tuple[dict, str] | tuple[None, None]: @@ -154,14 +162,16 @@ class Anime(commands.Cog): Parameters ---------- - query: The GraphQL query - variables: The variables for the query - key: The key to return from the response + interaction (discord.Interaction): Slash command context object + query (str): The GraphQL query + variables (str): The variables for the query + key (str): The key to return from the response Returns ---------- - (dict): The response from the Anilist API + (tuple[dict, str] | tuple[None, None]): The response from the Anilist API """ + try: url = 'https://graphql.anilist.co' response = requests.post(url, json={'query': query, 'variables': variables}, timeout=10) @@ -181,12 +191,13 @@ class Anime(commands.Cog): Parameters ---------- - media: The list of media returned from the Anilist API + media (list): The list of media returned from the Anilist API Returns ---------- (str): The constructed string """ + favorite_media = [] for node in media: try: @@ -196,6 +207,7 @@ class Anime(commands.Cog): favorite_media.append(f'[{anime_name}]({anime_url}){nsfwtag}') except KeyError: pass + return ' | '.join(favorite_media) def __construct_favorite_entity_string(self, entities: list, studio: bool = False) -> str: @@ -204,23 +216,26 @@ class Anime(commands.Cog): Parameters ---------- - entity: The list of entities returned from the Anilist API + entities (list): The list of entities returned from the Anilist API + studio (bool): Whether the entities are studios or not Returns ---------- (str): The constructed string """ + favortie_entities = [] for entity in entities: try: if studio: entity_name = entity['name'] else: - entity_name = entity['name']["full"] + entity_name = entity['name']['full'] entity_url = entity['siteUrl'] favortie_entities.append(f'[{entity_name}]({entity_url})') except KeyError: pass + return ' | '.join(favortie_entities) def __construct_release_schedule_string(self, numbers: dict) -> str: @@ -235,16 +250,17 @@ class Anime(commands.Cog): ---------- (str): The constructed string """ + # Release and end date formatting if numbers['start_day'] != '?' and numbers['start_month'] != '?' and numbers['start_year'] != '?': release_date = datetime(numbers['start_year'], numbers['start_month'], numbers['start_day']) - release_date = discord.utils.format_dt(release_date, style="d") + release_date = discord.utils.format_dt(release_date, style='d') else: release_date = f'{numbers["start_day"]}.{numbers["start_month"]}.{numbers["start_year"]}' - if numbers['end_day'] != '?' and numbers['end_month'] != '?' and numbers['end_year'] != '?': - end_date = datetime(numbers['end_year'], numbers['end_month'], numbers['end_day']) - end_date = discord.utils.format_dt(end_date, style="d") + if numbers["end_day"] != '?' and numbers["end_month"] != '?' and numbers["end_year"] != '?': + end_date = datetime(numbers["end_year"], numbers["end_month"], numbers["end_day"]) + end_date = discord.utils.format_dt(end_date, style='d') else: end_date = f'{numbers["end_day"]}.{numbers["end_month"]}.{numbers["end_year"]}' @@ -259,6 +275,15 @@ class Anime(commands.Cog): @app_commands.checks.cooldown(1, 5) @anilist_profile.command(name='generelt', description='Viser informasjon om en Anilistprofil') async def anilist_profile_general(self, interaction: discord.Interaction, bruker: str): + """ + Shows general information about a user's Anilist profile + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + bruker (str): The user to get information about + """ + # GraphQL query = """ query ($name: String) { @@ -388,6 +413,15 @@ class Anime(commands.Cog): @app_commands.checks.cooldown(1, 5) @anilist_profile.command(name='anime', description='Se statistikk om animetittingen til en bruker') async def anilist_profile_anime_stats(self, interaction: discord.Interaction, bruker: str): + """ + Shows anime viewing statistics from a user's Anilist profile + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + bruker (str): The user to get information about + """ + # GraphQL query = """ query ($name: String) { @@ -487,6 +521,15 @@ class Anime(commands.Cog): @app_commands.checks.cooldown(1, 5) @anilist_profile.command(name='manga', description='Se statistikk om mangalesingen til en bruker') async def anilist_profile_manga_stats(self, interaction: discord.Interaction, bruker: str): + """ + Shows manga reading statistics from a user's Anilist profile + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + bruker (str): The user to get information about + """ + # GraphQL query = """ query ($name: String) { @@ -588,8 +631,17 @@ class Anime(commands.Cog): @app_commands.checks.cooldown(1, 5) @anilist.command(name='anime', description='Hent informasjon om en anime') async def anilist_anime(self, interaction: discord.Interaction, navn: str): + """ + Shows information about an anime + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + navn (str): The name of the anime to fetch information about + """ + # GraphQL - query = ''' + query = """ query ($search: String, $isMain: Boolean) { Media (search: $search, type: ANIME) { siteUrl @@ -642,7 +694,7 @@ class Anime(commands.Cog): } } } - ''' + """ variables = { 'search': navn, 'isMain': True @@ -747,7 +799,16 @@ class Anime(commands.Cog): @app_commands.checks.cooldown(1, 5) @anilist.command(name='manga', description='Hent informasjon om en manga') async def anilist_manga(self, interaction: discord.Interaction, navn: str): - query = ''' + """ + Shows information about a manga + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + navn (str): The name of the manga to fetch information about + """ + + query = """ query ($search: String) { Media (search: $search, type: MANGA) { siteUrl @@ -794,7 +855,7 @@ class Anime(commands.Cog): } } } - ''' + """ variables = { 'search': navn } @@ -880,7 +941,16 @@ class Anime(commands.Cog): @app_commands.checks.cooldown(1, 5) @anilist.command(name='karakter', description='Hent informasjon om en karakter') async def anilist_character(self, interaction: discord.Interaction, navn: str): - query = ''' + """ + Shows information about a character + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + navn (str): The name of the character to fetch information about + """ + + query = """ query ($search: String) { Character (search: $search) { name { @@ -915,7 +985,7 @@ class Anime(commands.Cog): } } } - ''' + """ variables = { 'search': navn } @@ -968,7 +1038,16 @@ class Anime(commands.Cog): @app_commands.checks.cooldown(1, 5) @anilist.command(name='skaper', description='Hent informasjon om en skaper') async def anilist_creator(self, interaction: discord.Interaction, navn: str): - query = ''' + """ + Shows information about a creator + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + navn (str): The name of the creator to fetch information about + """ + + query = """ query ($search: String) { Staff(search: $search) { name { @@ -1007,7 +1086,7 @@ class Anime(commands.Cog): } } } - ''' + """ variables = { 'search': navn } @@ -1061,7 +1140,16 @@ class Anime(commands.Cog): @app_commands.checks.cooldown(1, 5) @anilist.command(name='studio', description='Hent informasjon om et studio') async def anilist_studio(self, interaction: discord.Interaction, navn: str): - query = ''' + """ + Shows information about a studio + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + navn (str): The name of the studio to fetch information about + """ + + query = """ query ($search: String) { Studio(search: $search) { name @@ -1081,7 +1169,7 @@ class Anime(commands.Cog): } } } - ''' + """ variables = { 'search': navn } @@ -1101,7 +1189,7 @@ class Anime(commands.Cog): popular_media.append(f'- [{media_name}]({media_url}){nsfwtag}') popular_media = '\n'.join(popular_media) - query2 = ''' + query2 = """ query ($search: String) { Studio(search: $search) { media(sort: START_DATE_DESC) { @@ -1116,7 +1204,7 @@ class Anime(commands.Cog): } } } - ''' + """ data = requests.post('https://graphql.anilist.co', json={'query': query2, 'variables': variables}, timeout=10).json() data = data['data']['Studio'] @@ -1146,4 +1234,12 @@ class Anime(commands.Cog): async def setup(bot: commands.Bot): + """ + Add the cog to the bot on extension load + + Parameters + ---------- + bot (commands.Bot): Bot instance + """ + await bot.add_cog(Anime(bot)) diff --git a/src/cogs/birthday.py b/src/cogs/birthday.py index 9ee3450..bf1f99b 100644 --- a/src/cogs/birthday.py +++ b/src/cogs/birthday.py @@ -10,12 +10,21 @@ from cogs.utils import discord_utils, embed_templates class Birthday(commands.Cog): + """Save the birthday of Discord users and allow them to see the birthdays of other users""" + def __init__(self, bot: commands.Bot): + """ + Parameters + ---------- + bot (commands.Bot): The bot instance + """ + self.bot = bot self.cursor = self.bot.db_connection.cursor() def init_db(self): - """Create the necessary tables for the birthday cog to work.""" + """Create the necessary tables for the birthday cog to work""" + self.cursor.execute( """ CREATE TABLE IF NOT EXISTS birthdays ( @@ -27,22 +36,43 @@ class Birthday(commands.Cog): self.bot.db_connection.commit() def __fetch_user_birthday(self, user_id: int) -> datetime | None: - """Fetch the birthday of a user from the database""" - self.cursor.execute("SELECT birthday FROM birthdays WHERE discord_id = (%s)", (user_id,)) + """ + Fetch the birthday of a user in the database + + Parameters + ---------- + user_id (int): The Discord user ID + + Returns + ---------- + (datetime | None): The birthday of the user + """ + + self.cursor.execute('SELECT birthday FROM birthdays WHERE discord_id = (%s)', (user_id,)) result = self.cursor.fetchone() if result: birthday = result[0] return datetime(birthday.year, birthday.month, birthday.day) def __fetch_user_next_birthday(self, user_id: int) -> datetime: - """Get date of next birthday of a user""" + """ + Get date of next birthday for a user + + Parameters + ---------- + user_id (int): The Discord user ID + + Returns + ---------- + (datetime): The date of the next birthday + """ + self.cursor.execute( """ SELECT *, CAST(birthday + ((EXTRACT(YEAR FROM AGE(birthday)) + 1) * interval '1' YEAR) AS DATE) AS next_birthday FROM birthdays WHERE discord_id = %s - """, - (user_id,), + """, (user_id,) ) results = self.cursor.fetchone() if results: @@ -52,7 +82,14 @@ class Birthday(commands.Cog): return datetime.now() # This will never happen unless a database error occurs. Keeping the type checker happy def __fetch_next_birthdays(self) -> list[tuple[int, datetime, datetime]]: - """Fetch the 5 first future birthdays of all users in the database""" + """ + Fetch the 5 first future birthdays of all users in the database + + Returns + ---------- + (list[tuple[int, datetime, datetime]]): A list of tuples containing the user ID, the date of birth and the next birthday + """ + self.cursor.execute( """ SELECT *, CAST(birthday + ((EXTRACT(YEAR FROM AGE(birthday)) + 1) * interval '1' YEAR) AS DATE) AS next_birthday @@ -71,8 +108,16 @@ class Birthday(commands.Cog): return results - def __set_user_birthday(self, user_id: int, birthday: datetime) -> None: - """Iset the birthday of a user in the database. If it already exists, update it.""" + def __set_user_birthday(self, user_id: int, birthday: datetime): + """ + Set the birthday of a user in the database. If it already exists, update it. + + Parameters + ---------- + user_id (int): The Discord user ID + birthday (datetime): The birthday of the user + """ + try: self.cursor.execute( """ @@ -82,10 +127,10 @@ class Birthday(commands.Cog): self.bot.db_connection.commit() except psycopg2.errors.UniqueViolation: self.bot.db_connection.rollback() - self.cursor.execute("UPDATE birthdays SET birthday = %s WHERE discord_id = %s", (birthday, user_id)) + self.cursor.execute('UPDATE birthdays SET birthday = %s WHERE discord_id = %s', (birthday, user_id)) self.bot.db_connection.commit() - # @aiocron.crontab("0 0 * * *") + # @aiocron.crontab('0 0 * * *') # async def __check_birthdays(self): # """Check if it's someone's birthday every day at midgnight and send a greeting if it is.""" # await asyncio.sleep(1) # Prevent sending message before date has really changed @@ -106,54 +151,79 @@ class Birthday(commands.Cog): # for birthday in birthdays: # user = self.bot.get_user(birthday[0]) # if user: - # await channel.send(f"Gratulrer med dagen {user.mention}!") + # await channel.send(f'Gratulrer med dagen {user.mention}!') @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) - @app_commands.command() - async def settbursdag(self, interaction: discord.Interaction, dag: int, måned: int, år: int): - """Lagrer din bursdag i databasen""" + @app_commands.command(name='settbursdag', description='Lagrer din bursdag i databasen') + async def birthday_set(self, interaction: discord.Interaction, dag: int, måned: int, år: int): + """ + Allows the user to set their birthday in the database. + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + dag (int): Day of the month + måned (int): Month of the year + år (int): Year + """ + try: date = datetime(år, måned, dag) except ValueError: return await interaction.response.send_message( - embed=embed_templates.error_fatal(interaction, text="ikke en gyldig dato, gjøk!"), + embed=embed_templates.error_fatal(interaction, text='ikke en gyldig dato, gjøk!'), ephemeral=True ) if date.year < 1970: return await interaction.response.send_message( - embed=embed_templates.error_fatal(interaction, text="Ok boomer, men nei. Hvorfor? Unix timestamps. Google it"), + embed=embed_templates.error_fatal(interaction, text='Ok boomer, men nei. Hvorfor? Unix timestamps. Google it'), ephemeral=True ) if date > datetime.now(): return await interaction.response.send_message( - embed=embed_templates.error_fatal(interaction, text="Du kan ikke sette en fremtidig dato!"), + embed=embed_templates.error_fatal(interaction, text='Du kan ikke sette en fremtidig dato!'), ephemeral=True ) self.__set_user_birthday(interaction.user.id, date) - embed = embed_templates.success(interaction, text=f"Bursdag satt til {discord.utils.format_dt(date, style='D')}") + embed = embed_templates.success(interaction, text=f'Bursdag satt til {discord.utils.format_dt(date, style="D")}') await interaction.response.send_message(embed=embed) @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) - @app_commands.command() - async def fjernbursdag(self, interaction: discord.Interaction): - """Fjerner bursdagen din fra databasen""" - self.cursor.execute("DELETE FROM birthdays WHERE discord_id = (%s)", (interaction.user.id,)) + @app_commands.command(name='fjernbursdag', description='Fjerner bursdagen din fra databasen') + async def birthday_remove(self, interaction: discord.Interaction): + """ + Removes the invoking user's birthday from the database + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + """ + + self.cursor.execute('DELETE FROM birthdays WHERE discord_id = (%s)', (interaction.user.id,)) self.bot.db_connection.commit() - embed = embed_templates.success(interaction, text="Bursdag fjernet") + embed = embed_templates.success(interaction, text='Bursdag fjernet') await interaction.response.send_message(embed=embed) @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) - @app_commands.command() - async def bursdag(self, interaction: discord.Interaction, bruker: discord.Member | discord.User | None = None): - """Viser bursdagen til en bruker. Om ingen er gitt, vises din egen bursdag.""" + @app_commands.command(name='bursdag', description='Viser bursdagen til en bruker. Om ingen er gitt, vises din egen') + async def birthday_show(self, interaction: discord.Interaction, bruker: discord.Member | discord.User | None = None): + """ + Shows the birthday of a user. If none is given, shows the invoking user's one. + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + bruker (discord.Member | discord.User | None): The user to show the birthday of. Defaults to None. + """ + if not bruker: bruker = interaction.user @@ -161,7 +231,7 @@ class Birthday(commands.Cog): if not user: return await interaction.response.send_message( - embed=embed_templates.error_fatal(interaction, text="Brukeren har ikke registrert bursdagen sin") + embed=embed_templates.error_fatal(interaction, text='Brukeren har ikke registrert bursdagen sin') ) # Current age information @@ -176,24 +246,31 @@ class Birthday(commands.Cog): embed = discord.Embed(description=bruker.mention, color=discord_utils.get_color(bruker)) embed.set_thumbnail(url=bruker.display_avatar) embed.set_author(name=bruker.name, icon_url=bruker.display_avatar) - embed.add_field(name="Bursdag", value=discord.utils.format_dt(birthday, style="D")) - embed.add_field(name="Hvor gammel?", value=f"{years_old} år\n({days_old} dager)", inline=False) - embed.add_field(name="Neste bursdag om", value=f"{next_birthday_days} dager", inline=False) + embed.add_field(name='Bursdag', value=discord.utils.format_dt(birthday, style='D')) + embed.add_field(name='Hvor gammel?', value=f'{years_old} år\n({days_old} dager)', inline=False) + embed.add_field(name='Neste bursdag om', value=f'{next_birthday_days} dager', inline=False) embed_templates.default_footer(interaction, embed) await interaction.response.send_message(embed=embed) @app_commands.guild_only() @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) - @app_commands.command() - async def kommendebursdager(self, interaction: discord.Interaction): - """Se opptil de 5 neste bursdagene i UiO Gaming""" + @app_commands.command(name='kommendebursdager', description='Viser en liste over de opptil 5 kommende bursdager i serveren') + async def birthdays_upcoming(self, interaction: discord.Interaction): + """ + Lists up to 5 upcoming birthdays of users in the server. + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + """ + next_birthdays = self.__fetch_next_birthdays() if not next_birthdays: - return await interaction.response.send_message(embed=embed_templates.error_fatal(interaction, text="Ingen bursdager")) + return await interaction.response.send_message(embed=embed_templates.error_fatal(interaction, text='Ingen bursdager')) - birthday_string = "" + birthday_string = '' for user in next_birthdays: user_id, _, next_birthday = user try: @@ -201,17 +278,25 @@ class Birthday(commands.Cog): except (discord.NotFound, discord.HTTPException): continue - timestamp = discord.utils.format_dt(next_birthday, style="D") - relative_timestamp = discord.utils.format_dt(next_birthday, style="R") - birthday_string += f"{discord_user.name}#{discord_user.discriminator} - {timestamp} ({relative_timestamp})\n" + timestamp = discord.utils.format_dt(next_birthday, style='D') + relative_timestamp = discord.utils.format_dt(next_birthday, style='R') + birthday_string += f'{discord_user.name}#{discord_user.discriminator} - {timestamp} ({relative_timestamp})\n' if not birthday_string: - return await interaction.response.send_message(embed=embed_templates.error_fatal(interaction, text="Ingen bursdager")) + return await interaction.response.send_message(embed=embed_templates.error_fatal(interaction, text='Ingen bursdager')) - embed = discord.Embed(title="Kommende bursdager", description=birthday_string) + embed = discord.Embed(title='Kommende bursdager', description=birthday_string) embed_templates.default_footer(interaction, embed) await interaction.response.send_message(embed=embed) async def setup(bot: commands.Bot): + """ + Add the cog to the bot on extension load + + Parameters + ---------- + bot (commands.Bot): Bot instance + """ + await bot.add_cog(Birthday(bot)) diff --git a/src/cogs/botinfo.py b/src/cogs/botinfo.py index 7e238df..5e940b5 100644 --- a/src/cogs/botinfo.py +++ b/src/cogs/botinfo.py @@ -11,14 +11,29 @@ from cogs.utils import embed_templates class BotInfo(commands.Cog): + """View information about the bot""" + def __init__(self, bot: commands.Bot): + """ + Parameters + ---------- + bot (commands.Bot): The bot instance + """ + self.bot = bot @app_commands.checks.bot_has_permissions(embed_links=True, external_emojis=True) @app_commands.checks.cooldown(1, 2) - @app_commands.command() + @app_commands.command(name='botinfo', description='Se informajson om botten') async def botinfo(self, interaction: discord.Interaction): - """Viser informasjon om bot""" + """ + View information about the bot + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + """ + # Dev user info dev = await self.bot.fetch_user(170506717140877312) @@ -78,9 +93,15 @@ class BotInfo(commands.Cog): @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) - @app_commands.command() - async def oppetid(self, interaction: discord.Interaction): - """Viser hvor lenge bot har vært kjørende""" + @app_commands.command(name='oppetid', description='Se hvor lenge botten har kjørt') + async def uptime(self, interaction: discord.Interaction): + """ + View the bot's uptime + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + """ embed = discord.Embed(color=interaction.client.user.color) embed.add_field(name='🔌 Oppetid', value=self.__get_uptime()) @@ -89,9 +110,16 @@ class BotInfo(commands.Cog): @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) - @app_commands.command() + @app_commands.command(name='ping', description='Se botten sin nåværende ping') async def ping(self, interaction: discord.Interaction): - """Viser bot sin ping""" + """ + View the bot's ping + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + """ + embed = discord.Embed(color=interaction.client.user.color) embed.add_field( name='📶 Ping', @@ -108,19 +136,18 @@ class BotInfo(commands.Cog): ---------- (int) The bot's ping in milliseconds """ + return int(self.bot.latency * 1000) def __get_uptime(self) -> str: """ Returns the current uptime of the bot in string format - Parameters - ---------- - Returns ---------- (str) The bot's uptime in human readable format """ + now = time() diff = int(now - self.bot.uptime) days, remainder = divmod(diff, 24 * 60 * 60) @@ -131,4 +158,12 @@ class BotInfo(commands.Cog): async def setup(bot: commands.Bot): + """ + Add the cog to the bot on extension load + + Parameters + ---------- + bot (commands.Bot): Bot instance + """ + await bot.add_cog(BotInfo(bot)) diff --git a/src/cogs/deprecated/social_credit.py b/src/cogs/deprecated/social_credit.py index 048d619..4791b08 100644 --- a/src/cogs/deprecated/social_credit.py +++ b/src/cogs/deprecated/social_credit.py @@ -69,7 +69,7 @@ class SocialCredit(commands.Cog): def add_new_citizen(func): async def wrapper(*args, **kwargs): self = args[0] - self.cursor.execute("SELECT user_id FROM social_credit WHERE user_id = %s", (args[1],)) + self.cursor.execute('SELECT user_id FROM social_credit WHERE user_id = %s', (args[1],)) if not self.cursor.fetchone(): self._add_citizen(args[1]) await func(*args, **kwargs) @@ -100,9 +100,9 @@ class SocialCredit(commands.Cog): async def get_leaderboard(self, ctx, page, worst=False): if worst: - keyword = "ASC" + keyword = 'ASC' else: - keyword = "DESC" + keyword = 'DESC' self.cursor.execute( f""" @@ -113,13 +113,13 @@ class SocialCredit(commands.Cog): result = self.cursor.fetchall() if not result: - return await ctx.send(embed=embed_templates.error_fatal(ctx, "Ingen brukere er registrert i databasen")) + return await ctx.send(embed=embed_templates.error_fatal(ctx, 'Ingen brukere er registrert i databasen')) output_list = [] for i, row in enumerate(result): user = ctx.guild.get_member(row[0]) if user: - output_list.append(f"{i+1}. {user.name}#{user.discriminator} - `{row[1]}` poeng") + output_list.append(f'{i+1}. {user.name}#{user.discriminator} - `{row[1]}` poeng') return misc_utils.paginator(output_list, int(page)) @@ -134,7 +134,7 @@ class SocialCredit(commands.Cog): @add_new_citizen async def social_punishment(self, user_id, points): - print(f"{points} points deducted from {user_id}") + print(f'{points} points deducted from {user_id}') self.cursor.execute( """ UPDATE social_credit @@ -147,7 +147,7 @@ class SocialCredit(commands.Cog): @add_new_citizen async def social_reward(self, user_id, points): - print(f"{points} points given to {user_id}") + print(f'{points} points given to {user_id}') self.cursor.execute( """ UPDATE social_credit @@ -164,17 +164,17 @@ class SocialCredit(commands.Cog): if not bruker: bruker = ctx.author - self.cursor.execute("SELECT * FROM social_credit WHERE user_id = %s", (bruker.id,)) + self.cursor.execute('SELECT * FROM social_credit WHERE user_id = %s', (bruker.id,)) result = self.cursor.fetchone() if not result: return await ctx.send( - embed=embed_templates.error_fatal(ctx, f"{bruker.mention} er ikke registrert i databasen") + embed=embed_templates.error_fatal(ctx, f'{bruker.mention} er ikke registrert i databasen') ) db_user = CreditUser(*result) - embed = discord.Embed(description=(f"{bruker.mention} har `{db_user.credit_score}` social credits")) + embed = discord.Embed(description=(f'{bruker.mention} har `{db_user.credit_score}` social credits')) await ctx.send(embed=embed) @commands.command() @@ -185,13 +185,13 @@ class SocialCredit(commands.Cog): page = 0 page_data = await self.get_leaderboard(ctx, page) - output_content = "\n".join(page_data["page_content"]) + output_content = '\n'.join(page_data['page_content']) - embed = discord.Embed(title="Social credit leaderboard", description=output_content) - embed.set_footer(text=f"Side {page_data['page']}/{page_data['pagecount']}") + embed = discord.Embed(title='Social credit leaderboard', description=output_content) + embed.set_footer(text=f'Side {page_data['page']}/{page_data['pagecount']}') await ctx.send(embed=embed) - @commands.command(aliases=["skammeliste"]) + @commands.command(aliases=['skammeliste']) async def shamelist(self, ctx, page=0): try: page = int(page) @@ -199,13 +199,13 @@ class SocialCredit(commands.Cog): page = 0 page_data = await self.get_leaderboard(ctx, page, worst=True) - output_content = "\n".join(page_data["page_content"]) + output_content = '\n'.join(page_data['page_content']) - embed = discord.Embed(title="Social credit skammeliste", description=output_content) - embed.set_footer(text=f"Side {page_data['page']}/{page_data['pagecount']}") + embed = discord.Embed(title='Social credit skammeliste', description=output_content) + embed.set_footer(text=f'Side {page_data['page']}/{page_data['pagecount']}') await ctx.send(embed=embed) - @commands.Cog.listener("on_message") + @commands.Cog.listener('on_message') async def on_message(self, message): if message.author.bot: return @@ -246,12 +246,12 @@ class SocialCredit(commands.Cog): for mention in message.mentions: await self.social_punishment(mention.id, 10) - @commands.Cog.listener("on_reaction_add") + @commands.Cog.listener('on_reaction_add') async def on_star_add(self, reaction, user): if user.bot: return - if reaction.emoji == "⭐": + if reaction.emoji == '⭐': if reaction.message.author == user: await self.social_punishment(user.id, 100) else: @@ -259,12 +259,12 @@ class SocialCredit(commands.Cog): await self.social_punishment(user.id, (len(reaction.message.reactions) - 1) * 25) await self.social_reward(user.id, 25 * len(reaction.message.reactions)) - @commands.Cog.listener("on_reaction_remove") + @commands.Cog.listener('on_reaction_remove') async def on_star_remove(self, reaction, user): if user.bot: return - if reaction.emoji == "⭐": + if reaction.emoji == '⭐': if len(reaction.message.reactions) >= 3: await self.social_punishment(user.id, 25) diff --git a/src/cogs/deprecated/vær.py b/src/cogs/deprecated/vær.py index 84bc9ce..b21499a 100644 --- a/src/cogs/deprecated/vær.py +++ b/src/cogs/deprecated/vær.py @@ -10,7 +10,7 @@ class Vær(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot - @aiocron.crontab("0 6 * * *") + @aiocron.crontab('0 6 * * *') async def forecast(): dev = await self.bot.fetch_user(162337748781367296) @@ -19,8 +19,8 @@ class Vær(commands.Cog): weather = Yr(location_name='Norway/Oslo/Oslo/Oslo', forecast_link='forecast_hour_by_hour') - embed = discord.Embed(title=":white_sun_small_cloud: Værmelding for Oslo :white_sun_small_cloud:") - embed.description = "*Værmeldingen levert til deg av Petter*" + embed = discord.Embed(title=':white_sun_small_cloud: Værmelding for Oslo :white_sun_small_cloud:') + embed.description = '*Værmeldingen levert til deg av Petter*' embed.set_author(name=dev.name, icon_url=dev.display_avatar) half_hour = False @@ -35,11 +35,11 @@ class Vær(commands.Cog): rain = data['precipitation'] regn = rain['@value'] temperatur = temp['@value'] - embed.add_field(name=f"kl {klokkeslett}", value=f"{temperatur}°C - {regn}mm") + embed.add_field(name=f'kl {klokkeslett}', value=f'{temperatur}°C - {regn}mm') klokkeslett += 1 half_hour = True - await channel.send("RISE AND SHINE GAMERS!", embed=embed) + await channel.send('RISE AND SHINE GAMERS!', embed=embed) async def setup(bot: commands.Bot): diff --git a/src/cogs/deprecated/wiki_search.py b/src/cogs/deprecated/wiki_search.py index 5ce9bdc..d594782 100644 --- a/src/cogs/deprecated/wiki_search.py +++ b/src/cogs/deprecated/wiki_search.py @@ -6,7 +6,7 @@ class Wiki(commands.Cog): @commands.command() async def wiki_search(self, ctx, *search_term): - search_term = " ".join(search_term) + search_term = ' '.join(search_term) search = wikipedia.search(search_term) search = search[0] @@ -15,7 +15,7 @@ class Wiki(commands.Cog): summary = wikipedia.summary(search_term, sentences=5) url = search.url - embed = discord.Embed(title=f"Wikipediasøk for {search_term}", url=url) + embed = discord.Embed(title=f'Wikipediasøk for {search_term}', url=url) embed.add_field(name=search_term, value=summary) embed_templates.default_footer(ctx,embed) diff --git a/src/cogs/dev_tools.py b/src/cogs/dev_tools.py index 11137da..dcabf09 100644 --- a/src/cogs/dev_tools.py +++ b/src/cogs/dev_tools.py @@ -9,14 +9,31 @@ from cogs.utils import embed_templates class DevTools(commands.Cog): + """Commands for developers to mangage cogs and other functionality of the bot""" + def __init__(self, bot: commands.Bot): + """ + Parameters + ---------- + bot (commands.Bot): The bot instance + """ + self.bot = bot @commands.is_owner() @commands.bot_has_permissions(embed_links=True) - @commands.command() + @commands.command(name='custommsg', description='Send en melding til hvilken som helst kanal på hvilken som helst server botten er i') async def custommsg(self, ctx: commands.Context, channel: int, *text: tuple[str]): - """Send a message to a specified channel""" + """ + Send a message to any channel on any server the bot is in + + Parameters + ---------- + ctx (commands.Context): Context object + channel (int): The channel ID to send the message to + text (tuple[str]): The text to send + """ + # Send message to the requested channel channel = self.bot.get_channel(channel) custommessage = ' '.join(text) @@ -30,9 +47,19 @@ class DevTools(commands.Cog): @commands.is_owner() @commands.bot_has_permissions(embed_links=True) @commands.cooldown(1, 10, commands.BucketType.guild) - @commands.command() + @commands.command(name='changepresence', description='Endre botten sin nåværende status') async def changepresence(self, ctx: commands.Context, activity_type: str, message: str, status_type: str): - """Changes the bot's presence status""" + """ + Change the bot's presence status + + Parameters + ---------- + ctx (commands.Context): Context object + activity_type (str): The type of activity to set the bot's status to + message (str): The message to set the bot's status to + status_type (str): The type of status to set the bot's status to + """ + activities = { 'playing': 0, 'listening': 2, @@ -52,14 +79,23 @@ class DevTools(commands.Cog): status=status_type, activity=discord.Activity(type=activity_type, name=message) ) + embed = discord.Embed(color=ctx.me.color, description='Endret Presence!') await ctx.send(embed=embed) @commands.is_owner() @commands.bot_has_permissions(embed_links=True, add_reactions=True) - @commands.command() + @commands.command(name='leave', description='Forlater en server. Om ingen er gitt, forlat der det ble sendt fra') async def leave(self, ctx: commands.Context, *guild_id: int): - """Leaves a specified guild""" + """ + Make bot leave a specified guild. If no guild is specified, leave the guild the command was sent from + + Parameters + ---------- + ctx (commands.Context): Context object + guild_id (int): The ID of the guild to leave + """ + # If no guild id is specified, leave the current guild guild_id = guild_id if guild_id else ctx.guild.id @@ -75,7 +111,7 @@ class DevTools(commands.Cog): await confirmation_msg.add_reaction('✅') # Check confirmation - def comfirm(reaction, user): + def comfirm(reaction: discord.Reaction, user: discord.Member): return user == ctx.author and str(reaction.emoji) == '✅' try: @@ -93,9 +129,16 @@ class DevTools(commands.Cog): @commands.is_owner() @commands.bot_has_permissions(embed_links=True) - @commands.command() + @commands.command(name='publicip', description='Hent den offentlige IP-adressen til serveren botten kjører på') async def publicip(self, ctx: commands.Context): - """Sends WAN IP-address. inb4 I leak my IP-address""" + """ + Sends WAN IP-address. Inb4 I leak my IP-address + + Parameters + ---------- + ctx (commands.Context): Context object + """ + data = requests.get('https://wtfismyip.com/json', timeout=10).json() ip = data['YourFuckingIPAddress'] location = data['YourFuckingLocation'] @@ -107,15 +150,30 @@ class DevTools(commands.Cog): @commands.is_owner() @commands.bot_has_permissions(embed_links=True) - @commands.group() + @commands.group(name='cogs', description='Administrer cogs') async def cogs(self, ctx: commands.Context): - """Cog management commands""" + """ + Cog management commands + + Parameters + ---------- + ctx (commands.Context): Context object + """ + if not ctx.invoked_subcommand: await ctx.send_help(ctx.command) - @cogs.command() - async def unload(self, ctx: commands.Context, cog: str): - """Disables a specified cog""" + @cogs.command(name='unload', description='Avslutt en cog') + async def cogs_unload(self, ctx: commands.Context, cog: str): + """ + Disables a specified cog + + Parameters + ---------- + ctx (commands.Context): Context object + cog (str): The name of the cog to disable + """ + for file in listdir('./src/cogs'): if file.endswith('.py'): name = file[:-3] @@ -127,9 +185,17 @@ class DevTools(commands.Cog): embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist') await ctx.send(embed=embed) - @cogs.command() - async def load(self, ctx: commands.Context, cog: str): - """Enables a speicifed cog""" + @cogs.command(name='load', description='Aktiver en cog') + async def cogs_load(self, ctx: commands.Context, cog: str): + """ + Enables a speicifed cog + + Parameters + ---------- + ctx (commands.Context): Context object + cog (str): The name of the cog to enable + """ + for file in listdir('./src/cogs'): if file.endswith('.py'): name = file[:-3] @@ -141,9 +207,16 @@ class DevTools(commands.Cog): embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist') await ctx.send(embed=embed) - @cogs.command() - async def reload(self, ctx: commands.Context, cog: str): - """Reloads a specified cog""" + @cogs.command(name='reload', description='Last inn en cog på nytt') + async def cogs_reload(self, ctx: commands.Context, cog: str): + """ + Reloads a specified cog + + Parameters + ---------- + ctx (commands.Context): Context object + cog (str): The name of the cog to reload + """ for file in listdir('./src/cogs'): if file.endswith('.py'): name = file[:-3] @@ -155,9 +228,16 @@ class DevTools(commands.Cog): embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist') await ctx.send(embed=embed) - @cogs.command() - async def reloadunloaded(self, ctx: commands.Context): - """Reloads all cogs, including previously disabled ones""" + @cogs.command(name='reloadunloaded', description='Last inn alle cogs på nytt, selv de som ikke er lastet inn') + async def cogs_reloadunloaded(self, ctx: commands.Context): + """ + Reloads all cogs, including previously disabled ones + + Parameters + ---------- + ctx (commands.Context): Context object + """ + # Unload all cogs for file in listdir('./src/cogs'): if file.endswith('.py'): @@ -173,9 +253,16 @@ class DevTools(commands.Cog): embed = discord.Embed(color=ctx.me.color, description='Reloaded all cogs') await ctx.send(embed=embed) - @cogs.command() - async def reloadall(self, ctx: commands.Context): - """Reloads all previously enabled cogs""" + @cogs.command(name='reloadall', description='Last inn alle cogs på nytt') + async def cogs_reloadall(self, ctx: commands.Context): + """ + Reloads all previously enabled cogs + + Parameters + ---------- + ctx (commands.Context): Context object + """ + for file in listdir('./src/cogs'): if file.endswith('.py'): name = file[:-3] @@ -186,4 +273,12 @@ class DevTools(commands.Cog): async def setup(bot: commands.Bot): + """ + Add the cog to the bot on extension load + + Parameters + ---------- + bot (commands.Bot): Bot instance + """ + await bot.add_cog(DevTools(bot)) diff --git a/src/cogs/errors.py b/src/cogs/errors.py index d11dd93..60d6ae4 100644 --- a/src/cogs/errors.py +++ b/src/cogs/errors.py @@ -9,13 +9,28 @@ from cogs.utils.misc_utils import ignore_exception class Errors(commands.Cog): + """Error handlers for commands""" + def __init__(self, bot: commands.Bot): + """ + Parameters + ---------- + bot (commands.Bot): The bot instance + """ + self.bot = bot - bot.tree.on_error = self.on_app_command_error + bot.tree.on_error = self.on_app_command_error # Set error handler for slash commands @commands.Cog.listener() async def on_command(self, ctx: commands.Context): - """Logs prefix command execution metadata""" + """ + Logs prefix command execution metadata + + Parameters + ---------- + ctx (commands.Context): Context object + """ + self.bot.logger.info( f'{"❌ " if ctx.command_failed else "✔ "} {ctx.command} | ' + f'{ctx.author.name}#{ctx.author.discriminator} ({ctx.author.id}) | ' + @@ -24,7 +39,15 @@ class Errors(commands.Cog): @commands.Cog.listener() async def on_app_command_completion(self, interaction: discord.Interaction, command: app_commands.Command | app_commands.ContextMenu): - """Logs slash command execution metadata""" + """ + Logs slash command execution metadata + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + command (app_commands.Command | app_commands.ContextMenu): Command object + """ + self.bot.logger.info( f'{"❌ " if interaction.command_failed else "✔ "} {command.name} | ' + f'{interaction.user.name}#{interaction.user.discriminator} ({interaction.user.id}) | ' + @@ -33,7 +56,15 @@ class Errors(commands.Cog): @commands.Cog.listener() async def on_command_error(self, ctx: commands.Context, error: commands.CommandError): - """Handle prefix command errors""" + """ + Handle prefix command errors + + Parameters + ---------- + ctx (commands.Context): Context object + error (commands.CommandError): Eror context object + """ + # Reset cooldown if command throws AttributeError with ignore_exception(AttributeError): self.bot.get_command(f'{ctx.command}').reset_cooldown(ctx) @@ -83,7 +114,7 @@ class Errors(commands.Cog): text = 'Denne kommandoen kan bare utføres i servere' return await ctx.send(text) except discord.errors.Forbidden: # Thrown if bot is blocked by the user or if the user has closed their DMs - print("DM Blocked!") + print('DM Blocked!') elif isinstance(error, commands.DisabledCommand): pass @@ -98,7 +129,15 @@ class Errors(commands.Cog): self.bot.logger.error(''.join(traceback.format_exception(type(error), error, error.__traceback__))) async def on_app_command_error(self, interaction: discord.Interaction, error: app_commands.AppCommandError): - """Handle slash command errors""" + """ + Handle slash command errors + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + error (app_commands.AppCommandError): Eror context object + """ + # Log command usage, just in case await self.on_app_command_completion(interaction, interaction.command) @@ -131,4 +170,12 @@ class Errors(commands.Cog): async def setup(bot: commands.Bot): + """ + Add the cog to the bot on extension load + + Parameters + ---------- + bot (commands.Bot): Bot instance + """ + await bot.add_cog(Errors(bot)) diff --git a/src/cogs/fun.py b/src/cogs/fun.py index b59d3c9..d96cfeb 100644 --- a/src/cogs/fun.py +++ b/src/cogs/fun.py @@ -2,44 +2,107 @@ from discord.ext import commands class Fun(commands.Cog): - def __init__(self, bot: commands.Bot): - self.bot = bot + """Simple commands that return short strings for the memes""" @commands.cooldown(1, 2, commands.BucketType.guild) - @commands.command() + @commands.command(name='norskeuniversiteter') async def norskeuniversiteter(self, ctx: commands.Context): + """ + Sends a link to university meme image album + + Parameters + ---------- + ctx (commands.Context): Context object + """ + await ctx.send('') @commands.cooldown(1, 2, commands.BucketType.guild) - @commands.command() + @commands.command(name='ifi') async def ifi(self, ctx: commands.Context): + """ + ifi meme 1 + + Parameters + ---------- + ctx (commands.Context): Context object + """ + await ctx.send('https://i.imgur.com/ypyK1mi.jpg') @commands.cooldown(1, 2, commands.BucketType.guild) - @commands.command() + @commands.command(name='ifi2') async def ifi2(self, ctx: commands.Context): + """ + ifi meme 2 + + Parameters + ---------- + ctx (commands.Context): Context object + """ + await ctx.send('https://i.imgur.com/ZqgZEEA.jpg') @commands.cooldown(1, 2, commands.BucketType.guild) @commands.command() async def ifi3(self, ctx: commands.Context): + """ + ifi meme 3 + + Parameters + ---------- + ctx (commands.Context): Context object + """ + await ctx.send('https://i.imgur.com/Gx9DQE5.jpg') @commands.cooldown(1, 2, commands.BucketType.guild) @commands.command() async def uio(self, ctx: commands.Context): + """ + uio meme 1 + + Parameters + ---------- + ctx (commands.Context): Context object + """ + await ctx.send('https://i.imgur.com/188MoIV.jpg') @commands.cooldown(1, 2, commands.BucketType.guild) @commands.command() async def ntnu(self, ctx: commands.Context): + """ + ntnu meme 1 + + Parameters + ---------- + ctx (commands.Context): Context object + """ + await ctx.send('https://twitter.com/NTNU/status/970667413564993536') @commands.cooldown(1, 2, commands.BucketType.guild) @commands.command() async def ntnu2(self, ctx: commands.Context): + """ + ntnu meme 2 + + Parameters + ---------- + ctx (commands.Context): Context object + """ + await ctx.send('https://i.imgur.com/h84fknj.jpg') async def setup(bot: commands.Bot): + """ + Add the cog to the bot on extension load + + Parameters + ---------- + bot (commands.Bot): Bot instance + """ + await bot.add_cog(Fun(bot)) diff --git a/src/cogs/funreplies.py b/src/cogs/funreplies.py index fcc42ce..e233087 100644 --- a/src/cogs/funreplies.py +++ b/src/cogs/funreplies.py @@ -7,7 +7,15 @@ from discord.ext import commands class FunReplies(commands.Cog): + """Reply to messages that trigger certain key words/phrases""" + def __init__(self, bot: commands.Bot): + """ + Parameters + ---------- + bot (commands.Bot): The bot instance + """ + self.bot = bot # Cooldowns for trigger words @@ -24,7 +32,14 @@ class FunReplies(commands.Cog): } async def reply_to_triggers(self, message: discord.Message): - """Replies to messages that trigger certain key words/phrases""" + """ + Replies to messages that trigger certain key words/phrases + + Parameters + ---------- + message (discord.Message): Message object to check for triggers to + """ + if message.author.bot: return @@ -62,5 +77,13 @@ class FunReplies(commands.Cog): async def setup(bot: commands.Bot): + """ + Add the cog to the bot on extension load + + Parameters + ---------- + bot (commands.Bot): Bot instance + """ + bot.add_listener(FunReplies(bot).reply_to_triggers, 'on_message') await bot.add_cog(FunReplies(bot)) diff --git a/src/cogs/info.py b/src/cogs/info.py index c989d78..38e0842 100644 --- a/src/cogs/info.py +++ b/src/cogs/info.py @@ -8,24 +8,55 @@ from cogs.utils import discord_utils, embed_templates, misc_utils class Info(commands.Cog): + """View information about Discord object such as guilds, users, roles and channels""" + def __init__(self, bot: commands.Bot): + """ + Parameters + ---------- + bot (commands.Bot): The bot instance + """ + self.bot = bot - guild_group = app_commands.Group(name="guild", description="Se ting om serveren") - user_group = app_commands.Group(name="bruker", description="Se ting om brukeren") + guild_group = app_commands.Group(name='guild', description='Se ting om serveren') + user_group = app_commands.Group(name='bruker', description='Se ting om brukeren') def __construct_role_string(self, roles: list[discord.Role]) -> str: - """Puts all roles except @everyone into a list and joins them to a string""" - if not roles: - return "**Ingen roller**" + """ + Puts all roles except @everyone into a list and joins them to a string - roles = [role.name for role in roles if role.name != "@everyone"] + Parameters + ---------- + roles (list[discord.Role]): List of roles + + Returns + ------- + (str): String of roles + """ + + if not roles: + return '**Ingen roller**' + + roles = [role.name for role in roles if role.name != '@everyone'] roles.reverse() return ', '.join(roles) def __construct_booster_string(self, interaction: discord.Interaction, join_method: callable = ', '.join) -> str: - """Joins all boosters into a string""" + """ + Joins all boosters into a string + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + join_method (callable): Function to join the boosters with + + Returns + ------- + (str): String of boosters + """ + if not interaction.guild.premium_subscribers: return '**Ingen boostere**' @@ -39,7 +70,18 @@ class Info(commands.Cog): return join_method(boosters) def __construct_member_string(self, members: list[discord.Member]) -> str: - """Joins all name#discrim into a string""" + """ + Joins all name#discrim into a string + + Parameters + ---------- + members (list[discord.Member]): List of members + + Returns + ------- + (str): String of members + """ + if not members: return '**Ingen**' @@ -48,7 +90,16 @@ class Info(commands.Cog): return ', '.join(members) async def __send_as_txt_file(self, interaction: discord.Interaction, content: str, file_path: str): - """Sends a string as a txt file and deletes the file afterwards""" + """ + Sends a string as a txt file and deletes the file afterwards + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + content (str): String that's too long to send + file_path (str): Path to file + """ + # Create file with open(file_path, 'w', encoding='utf-8') as file: file.write(content) @@ -64,10 +115,18 @@ class Info(commands.Cog): @app_commands.guild_only() @app_commands.checks.bot_has_permissions(embed_links=True, external_emojis=True) @app_commands.checks.cooldown(1, 2) - @guild_group.command(name="info", description="Hent informasjon om en server") + @guild_group.command(name='info', description='Hent informasjon om en server') async def guild_info(self, interaction: discord.Interaction): + """ + Sends general information about the guild + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + """ + # Days since guild was created - creation_date_timestamp = discord.utils.format_dt(interaction.guild.created_at, style="F") + creation_date_timestamp = discord.utils.format_dt(interaction.guild.created_at, style='F') since_created_days = (interaction.created_at - interaction.guild.created_at).days days_ago = f'{since_created_days} dager' if since_created_days != 1 else f'{since_created_days} dag' @@ -195,8 +254,16 @@ class Info(commands.Cog): @app_commands.guild_only() @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) - @guild_group.command(name="roller", description="Se rollene på serveren") + @guild_group.command(name='roller', description='Se rollene på serveren') async def guild_roles(self, interaction: discord.Interaction): + """ + List all roles in the guild + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + """ + roles = self.__construct_role_string(interaction.guild.roles) # IF roles list is longer than 2048, create text file and send it @@ -211,8 +278,16 @@ class Info(commands.Cog): @app_commands.guild_only() @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) - @guild_group.command(name="boosters", description="Se boostere på serveren") + @guild_group.command(name='boosters', description='Se boostere på serveren') async def guild_boosters(self, interaction: discord.Interaction): + """ + List all boosters in the guild + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + """ + if len(interaction.guild.premium_subscribers) == 0: embed = embed_templates.error_warning(interaction, text='Serveren har ikke noen boosts :(') return await interaction.response.send_message(embed=embed) @@ -227,8 +302,16 @@ class Info(commands.Cog): @app_commands.guild_only() @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) - @guild_group.command(name="ikon", description="Hent serverens ikon") + @guild_group.command(name='ikon', description='Hent serverens ikon') async def guild_icon(self, interaction: discord.Interaction): + """ + Get the guild icon + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + """ + embed = discord.Embed(color=interaction.guild.me.color, description=f'[Lenke]({interaction.guild.icon})') embed.set_author(name=interaction.guild.name, icon_url=interaction.guild.icon) embed.set_image(url=interaction.guild.icon) @@ -238,8 +321,16 @@ class Info(commands.Cog): @app_commands.guild_only() @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) - @guild_group.command(name="splash", description="Hent serverens splash (bakgrunnsbilde på invitasjonsside)") + @guild_group.command(name='splash', description='Hent serverens splash (bakgrunnsbilde på invitasjonsside)') async def guild_splash(self, interaction: discord.Interaction): + """ + Get the guild splash + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + """ + if not interaction.guild.discovery_splash: embed = embed_templates.error_warning(interaction, text='Serveren har ikke en splash') return await interaction.response.send_message(embed=embed) @@ -253,8 +344,16 @@ class Info(commands.Cog): @app_commands.guild_only() @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) - @guild_group.command(name="banner", description="Hen serverens banner") + @guild_group.command(name='banner', description='Hen serverens banner') async def guild_banner(self, interaction: discord.Interaction): + """ + Get the guild banner + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + """ + if not interaction.guild.banner: embed = embed_templates.error_warning(interaction, text='Serveren har ikke et banner :(') return await interaction.response.send_message(embed=embed) @@ -268,14 +367,23 @@ class Info(commands.Cog): @app_commands.guild_only() @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) - @guild_group.command(name="rolle", description="Hent informasjon om en rolle på serveren") + @guild_group.command(name='rolle', description='Hent informasjon om en rolle på serveren') async def guild_role(self, interaction: discord.Interaction, rolle: discord.Role): + """ + Get information about a role + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + rolle (discord.Role): Role to fetch information about + """ + if rolle.name == '@everyone': embed = embed_templates.error_fatal(interaction, text='Skriv inn en annen rolle enn @everyone') return await interaction.response.send_message(embed=embed) # Timestamp and days since creation - created_at_timestamp = discord.utils.format_dt(rolle.created_at, style="F") + created_at_timestamp = discord.utils.format_dt(rolle.created_at, style='F') since_created_days = (interaction.created_at - rolle.created_at).days # List of members with the role @@ -302,8 +410,17 @@ class Info(commands.Cog): @app_commands.guild_only() @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) - @guild_group.command(name="tekstkanal", description="Hent informasjon om en tekstkanal") + @guild_group.command(name='tekstkanal', description='Hent informasjon om en tekstkanal') async def guild_text_channel(self, interaction: discord.Interaction, kanal: discord.TextChannel): + """ + Get information about a text channel + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + kanal (discord.TextChannel): Text channel to fetch information about + """ + members = self.__construct_member_string(kanal.members) if len(members) > 1024: members = 'For mange for å vise her' @@ -323,8 +440,17 @@ class Info(commands.Cog): @app_commands.guild_only() @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) - @guild_group.command(name="talekanal", description="Hent informasjon om en talekanal") + @guild_group.command(name='talekanal', description='Hent informasjon om en talekanal') async def guild_voice_channel(self, interaction: discord.Interaction, kanal: discord.VoiceChannel): + """ + Get information about a voice channel + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + kanal (discord.VoiceChannel): Voice channel to fetch information about + """ + embed = discord.Embed(color=interaction.guild.me.color, title=kanal.name, description=f'ID: {kanal.id}') embed.set_author(name=kanal.guild.name, icon_url=kanal.guild.icon) embed.add_field(name='Opprettet', value=discord.utils.format_dt(kanal.created_at, style='F')) @@ -352,17 +478,25 @@ class Info(commands.Cog): # embed = discord.Embed(color=interaction.guild.me.color, title=emoji.name, description=f'ID: {emoji.id}') # embed.set_author(name=emoji.guild.name, icon_url=emoji.guild.icon) - # embed.add_field(name='Opprettet', value=discord.utils.format_dt(emoji.created_at, style="F")) + # embed.add_field(name='Opprettet', value=discord.utils.format_dt(emoji.created_at, style='F')) # embed.add_field(name='Animert', value='Ja' if emoji.animated else 'Nei') # embed.add_field(name='Lagt til av', value=emoji_creator) # embed.set_image(url=emoji.url) # embed_templates.default_footer(interaction, embed) # await interaction.response.send_message(embed=embed) - guild_oldest_group = app_commands.Group(name="eldst", description="Viser de eldste medlemmene på serveren", parent=guild_group) + guild_oldest_group = app_commands.Group(name='eldst', description='Viser de eldste medlemmene på serveren', parent=guild_group) def __construct_oldest_embed(self, paginator: misc_utils.Paginator, page: list, embed: discord.Embed) -> discord.Embed: - """Constructs the embed for the oldest commands""" + """ + Constructs the embed for the oldest commands + + Parameters + ---------- + paginator (misc_utils.Paginator): Paginator dataclass + page (list): List of members to display + embed (discord.Embed): Embed to add fields to + """ embed.description = '\n'.join(page) embed.set_footer(text=f'Side {paginator.current_page}/{paginator.total_page_count}') return embed @@ -370,8 +504,16 @@ class Info(commands.Cog): @commands.guild_only() @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) - @guild_oldest_group.command(name="lagd", description="Liste over de eldste brukerene på serveren basert på når de ble lagd") + @guild_oldest_group.command(name='lagd', description='Liste over de eldste brukerene på serveren basert på når de ble lagd') async def guild_user_created_oldest(self, interaction: discord.Interaction): + """ + List the oldest users on the server based on when they were created + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + """ + # Sort members by creation date members = sorted(interaction.guild.members, key=lambda m: m.created_at) @@ -392,8 +534,16 @@ class Info(commands.Cog): @commands.guild_only() @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) - @guild_oldest_group.command(name="joined", description="Liste over de eldste brukerene på serveren basert på når de ble med") + @guild_oldest_group.command(name='joined', description='Liste over de eldste brukerene på serveren basert på når de ble med') async def guild_user_joined_oldest(self, interaction: discord.Interaction): + """ + List the oldest users on the server based on when they joined + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + """ + # Sort members by creation date members = sorted(interaction.guild.members, key=lambda m: m.joined_at) @@ -414,8 +564,17 @@ class Info(commands.Cog): @app_commands.guild_only() @app_commands.checks.bot_has_permissions(embed_links=True, external_emojis=True) @app_commands.checks.cooldown(1, 2) - @user_group.command(name="info", description="Hent informasjon om en bruker") + @user_group.command(name='info', description='Hent informasjon om en bruker') async def user_info(self, interaction: discord.Interaction, *, bruker: discord.Member | None = None): + """ + Get information about a user + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + bruker (discord.Member, optional): User to get information about. Defaults to None (invoker). + """ + if not bruker: bruker = interaction.user @@ -489,8 +648,17 @@ class Info(commands.Cog): @app_commands.guild_only() @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) - @user_group.command(name="roller", description="Hent roller til en bruker") + @user_group.command(name='roller', description='Hent roller til en bruker') async def user_roles(self, interaction: discord.Interaction, bruker: discord.Member | None = None): + """ + Get roles of a user + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + bruker (discord.Member, optional): User to get roles of. Defaults to None (invoker). + """ + if not bruker: bruker = interaction.user @@ -507,8 +675,17 @@ class Info(commands.Cog): @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) - @user_group.command(name="avatar", description="Hent avatar til en bruker") + @user_group.command(name='avatar', description='Hent avatar til en bruker') async def user_avatar(self, interaction: discord.Interaction, bruker: discord.User | discord.Member | None = None): + """ + Get avatar of a user + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + bruker (discord.User, optional): User to get avatar of. Defaults to None (invoker). + """ + if not bruker: bruker = interaction.user @@ -520,4 +697,12 @@ class Info(commands.Cog): async def setup(bot: commands.Bot): + """ + Add the cog to the bot on extension load + + Parameters + ---------- + bot (commands.Bot): Bot instance + """ + await bot.add_cog(Info(bot)) diff --git a/src/cogs/mc_whitelist.py b/src/cogs/mc_whitelist.py index ab6fbcc..f773ddc 100644 --- a/src/cogs/mc_whitelist.py +++ b/src/cogs/mc_whitelist.py @@ -8,13 +8,22 @@ from cogs.utils import embed_templates class MCWhitelist(commands.Cog): + """Stand alone cog for handling whitelisting of Discord users on the Minecraft server""" + def __init__(self, bot: commands.Bot): + """ + Parameters + ---------- + bot (commands.Bot): The bot instance + """ + self.bot = bot self.cursor = self.bot.db_connection.cursor() self.init_db() def init_db(self): """Create the necessary tables for the mc_whitelist cog to work.""" + self.cursor.execute( """ CREATE TABLE IF NOT EXISTS mc_whitelist ( @@ -27,15 +36,22 @@ class MCWhitelist(commands.Cog): @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) - @app_commands.command() + @app_commands.command(name='whitelist', description='Whitelist minecraftbrukeren din på serveren vår') async def whitelist(self, interaction: discord.Interaction, minecraftbrukernavn: str): - """Whitelist Minecraftbrukeren din på serveren vår""" + """ + Whitelist a minecraft user on the Minecraft server + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + minecraftbrukernavn (str): Minecraft username + """ # Fetch minecraft uuid from api - data = requests.get(f"https://api.mojang.com/users/profiles/minecraft/{minecraftbrukernavn}", timeout=10).json() + data = requests.get(f'https://api.mojang.com/users/profiles/minecraft/{minecraftbrukernavn}', timeout=10).json() if data.status_code != 200: return await interaction.response.send_message( - embed=embed_templates.error_fatal(interaction, text=f"Brukeren `{minecraftbrukernavn}` finnes ikke på minecraft"), + embed=embed_templates.error_fatal(interaction, text=f'Brukeren `{minecraftbrukernavn}` finnes ikke på minecraft'), ephemeral=True ) @@ -47,21 +63,21 @@ class MCWhitelist(commands.Cog): SELECT * FROM mc_whitelist WHERE minecraft_id = %s OR discord_id = %s - """, (data["id"], interaction.user.id) + """, (data['id'], interaction.user.id) ) if self.cursor.fetchone(): return await interaction.response.send_message( embed=embed_templates.error_fatal( interaction, - text="Du har allerede whitelisted en bruker eller så er brukeren du oppga whitelisted" + text='Du har allerede whitelisted en bruker eller så er brukeren du oppga whitelisted' ) ) # Whitelist user on minecraft server # Unfortunately, this requires an active connection to the server, with correct credentials - with MCRcon(host="127.0.0.1", password=self.bot.mc_rcon_password, port=25575) as mcr: - mcr.command(f"whitelist add {data['name']}") - mcr.command("whitelist reload") + with MCRcon(host='127.0.0.1', password=self.bot.mc_rcon_password, port=25575) as mcr: + mcr.command(f'whitelist add {data["name"]}') + mcr.command('whitelist reload') # Add user to db self.cursor.execute( @@ -69,16 +85,24 @@ class MCWhitelist(commands.Cog): INSERT INTO mc_whitelist (discord_id, minecraft_id) VALUES (%s, %s) """, - (interaction.user.id, data["id"]), + (interaction.user.id, data['id']), ) self.bot.db_connection.commit() await interaction.response.send_message( embed=embed_templates.success( - interaction, text=f"`{data['name']}` er nå tilknyttet din discordbruker og whitelisted!" + interaction, text=f'`{data["name"]}` er nå tilknyttet din discordbruker og whitelisted!' ) ) async def setup(bot: commands.Bot): + """ + Add the cog to the bot on extension load + + Parameters + ---------- + bot (commands.Bot): Bot instance + """ + await bot.add_cog(MCWhitelist(bot)) diff --git a/src/cogs/misc.py b/src/cogs/misc.py index 73cbc24..7ede72c 100644 --- a/src/cogs/misc.py +++ b/src/cogs/misc.py @@ -14,21 +14,43 @@ from cogs.utils.misc_utils import ignore_exception class Misc(commands.Cog): + """Miscellaneous commands that don't fit anywhere else""" + def __init__(self, bot: commands.Bot): + """ + Parameters + ---------- + bot (commands.Bot): The bot instance + """ + self.bot = bot @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) - @app_commands.command() + @app_commands.command(name='weeb', description='Kjeft på weebs') async def weeb(self, interaction: discord.Interaction): - """Kjeft på weebs""" - await interaction.response.send_message("<:sven:762725919604473866> Weebs 👉 <#803596668129509417>") + """ + Call out weebs + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + """ + + await interaction.response.send_message('<:sven:762725919604473866> Weebs 👉 <#803596668129509417>') @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) - @app_commands.command() + @app_commands.command(name='kweeb', description='김정운, 최고도') async def kweeb(self, interaction: discord.Interaction): - """김정운, 최고도""" + """ + Call out kweebs + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + """ + await interaction.response.send_message( """ Melding du nettopp sendte ga muligens følgende vibber for brukeren som brukte denne kommandoen: @@ -52,17 +74,24 @@ https://cdn.discordapp.com/attachments/674726496878723082/1042631569396990004/kw @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) - @app_commands.command() + @app_commands.command(name='owo', description='Oversetter teksten din til owo (we_eb)') async def owo(self, interaction: discord.Interaction, tekst: str): - """Oversetter teksten din til owo (we_eb)""" + """ + Translate text into owo (we_eb) - owo_rules = {"r": "w", "l": "w", "R": "W", "L": "W", "n": "ny", "N": "Ny", "ove": "uv"} + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + tekst (str): Text to translate + """ + + owo_rules = {'r': 'w', 'l': 'w', 'R': 'W', 'L': 'W', 'n': 'ny', 'N': 'Ny', 'ove': 'uv'} for key, value in owo_rules.items(): tekst = re.sub(key, value, tekst) # https://kaomoji.moe/ if len(tekst) >= 1000: - embed = embed_templates.error_warning(interaction, text="Teksten er for lang") + embed = embed_templates.error_warning(interaction, text='Teksten er for lang') return await interaction.response.send_message(embed=embed) embed = discord.Embed(color=interaction.client.user.color, description=tekst) @@ -70,50 +99,74 @@ https://cdn.discordapp.com/attachments/674726496878723082/1042631569396990004/kw await interaction.response.send_message(embed=embed) @app_commands.checks.cooldown(1, 5) - @app_commands.command() - async def emne(self, interaction: discord.Interaction, emnekode: str): - """For NTNU'ere som ikke kan emnekoder på UiO""" - data = requests.get(f"https://data.uio.no/studies/v1/course/{emnekode}", timeout=10) - if data.status_code != 200: - return await interaction.response.send_message(embed=embed_templates.error_fatal(interaction, text="Fant ikke emnekode")) + @app_commands.command(name='emne', description='For NTNU\'ere som ikke kan emnekoder på UiO') + async def course_code(self, interaction: discord.Interaction, emnekode: str): + """ + Translate UiO course codes to their respective course names - await interaction.response.send_message(data.json()["info"]["name"]) + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + emnekode (str): UiO course code + """ + + data = requests.get(f'https://data.uio.no/studies/v1/course/{emnekode}', timeout=10) + if data.status_code != 200: + return await interaction.response.send_message(embed=embed_templates.error_fatal(interaction, text='Fant ikke emnekode')) + + await interaction.response.send_message(data.json()['info']['name']) @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 2) - @app_commands.command() - async def klappifiser(self, interation: discord.Interaction, tekst: str): - """Klapppifiserer teksten din""" + @app_commands.command(name='klappifiser', description='Klappifiser tekst') + async def clapify(self, interation: discord.Interaction, tekst: str): + """ + Add clap emoji between every word of a string + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + tekst (str): Text to clapify + """ + if len(tekst) >= 1000: - embed = embed_templates.error_warning(interation, text="Teksten er for lang") + embed = embed_templates.error_warning(interation, text='Teksten er for lang') return await interation.response.send_message(embed=embed) - tekst = re.sub(" ", "👏", tekst) + tekst = re.sub(' ', '👏', tekst) - embed = discord.Embed(color=interation.client.user.color, description=f"**{tekst.upper()}**") + embed = discord.Embed(color=interation.client.user.color, description=f'**{tekst.upper()}**') embed_templates.default_footer(interation, embed) await interation.response.send_message(embed=embed) @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) - @app_commands.command() + @app_commands.command(name='imdb', description='Søk etter filmer på IMDB') async def imdb(self, interaction: discord.Interaction, tittel: str): - """Se informasjon om en film eller serie""" + """ + Search for movies on IMDB and displays information about the first result + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + tittel (str): Movie title + """ + # Search for movie/show - url = "http://www.omdbapi.com/?" + urllib.parse.urlencode({"s": tittel, "apikey": self.bot.api_keys["omdb"]}) + url = 'http://www.omdbapi.com/?' + urllib.parse.urlencode({'s': tittel, 'apikey': self.bot.api_keys['omdb']}) search = requests.get(url, timeout=10).json() try: - best_result_id = search["Search"][0]["imdbID"] + best_result_id = search['Search'][0]['imdbID'] except KeyError: - embed = embed_templates.error_fatal(interaction, text="Fant ikke filmen!") + embed = embed_templates.error_fatal(interaction, text='Fant ikke filmen!') return await interaction.response.send_message(embed=embed) # Get movie/show info data = requests.get(f'http://www.omdbapi.com/?i={best_result_id}&apikey={self.bot.api_keys["omdb"]}', timeout=10).json() - acceptable_media_types = ["movie", "series", "episode"] - if data["Type"] not in acceptable_media_types: - embed = embed_templates.error_fatal(interaction, text="Fant ikke filmen!") + acceptable_media_types = ['movie', 'series', 'episode'] + if data['Type'] not in acceptable_media_types: + embed = embed_templates.error_fatal(interaction, text='Fant ikke filmen!') return await interaction.response.send_message(embed=embed) embed = discord.Embed( @@ -122,61 +175,78 @@ https://cdn.discordapp.com/attachments/674726496878723082/1042631569396990004/kw url=f'https://www.imdb.com/title/{data["imdbID"]}/', ) embed.set_author(name=interaction.user.name, icon_url=interaction.user.display_avatar) - embed.add_field(name="Type", value=data["Type"].title()) - embed.add_field(name="Sjanger", value=data["Genre"]) - embed.add_field(name="Spilletid", value=data["Runtime"]) - embed.add_field(name="Vurdering på IMDb", value=f'{data["imdbRating"]}/10') - release_date = datetime.strptime(data["Released"], "%d %b %Y") - embed.add_field(name="Utgitt", value=discord.utils.format_dt(release_date, style="D")) + embed.add_field(name='Type', value=data['Type'].title()) + embed.add_field(name='Sjanger', value=data['Genre']) + embed.add_field(name='Spilletid', value=data['Runtime']) + embed.add_field(name='Vurdering på IMDb', value=f'{data["imdbRating"]}/10') + release_date = datetime.strptime(data['Released'], '%d %b %Y') + embed.add_field(name='Utgitt', value=discord.utils.format_dt(release_date, style='D')) embed_templates.default_footer(interaction, embed) - if data["Poster"] != "N/A": - embed.set_thumbnail(url=data["Poster"]) - if data["Director"] != "N/A": - embed.description = data["Director"] - if data["Plot"] != "N/A" and len(data["Plot"]) < 1024: - embed.add_field(name="Sammendrag", value=data["Plot"], inline=False) + if data['Poster'] != 'N/A': + embed.set_thumbnail(url=data['Poster']) + if data['Director'] != 'N/A': + embed.description = data['Director'] + if data['Plot'] != 'N/A' and len(data['Plot']) < 1024: + embed.add_field(name='Sammendrag', value=data['Plot'], inline=False) await interaction.response.send_message(embed=embed) @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) - @app_commands.command() - async def helligdager(self, interaction: discord.Interaction, *, land: str = "NO", år: int = None): - """Se hellidagene i et land""" + @app_commands.command(name='helligdager', description='Se helligdager i et land for et år') + async def holidays(self, interaction: discord.Interaction, *, land: str = 'NO', år: int = None): + """ + Get holidays for a country for a given year + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + land (str): Country code + år (int): Year + """ + land = land.upper() aar = datetime.now().year if not år else int(år) - data = requests.get(f"https://date.nager.at/api/v2/publicholidays/{aar}/{land}", timeout=10) + data = requests.get(f'https://date.nager.at/api/v2/publicholidays/{aar}/{land}', timeout=10) if data.status_code != 200: embed = embed_templates.error_fatal( - interaction, text="Ugyldig land\nHusk å bruke landskoder\n" + "For eksempel: `NO`" + interaction, text='Ugyldig land\nHusk å bruke landskoder\n' + 'For eksempel: `NO`' ) return await interaction.response.send_message(embed=embed) data = data.json() - country = data[0]["countryCode"].lower() + country = data[0]['countryCode'].lower() # Construct output - holiday_str = "" + holiday_str = '' for day in data: - date = discord.utils.format_dt(datetime.strptime(day["date"], "%Y-%m-%d"), style="D") + date = discord.utils.format_dt(datetime.strptime(day['date'], '%Y-%m-%d'), style='D') holiday_str += f'**{date}**: {day["localName"]}\n' - embed = discord.Embed(color=interaction.client.user.color, title=f":flag_{country}: Helligdager {aar} :flag_{country}:") + embed = discord.Embed(color=interaction.client.user.color, title=f':flag_{country}: Helligdager {aar} :flag_{country}:') embed.description = holiday_str embed_templates.default_footer(interaction, embed) await interaction.response.send_message(embed=embed) @app_commands.checks.bot_has_permissions(embed_links=True) @app_commands.checks.cooldown(1, 5) - @app_commands.command() + @app_commands.command(name='match', description='Se hvor mye du matcher med en annen') async def match(self, interaction: discord.Interaction, bruker: discord.Member): - """Se hvor mye du matcher med en annen""" + """ + Get dating compatibility with another user + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + bruker (discord.Member): User to match with + """ + if bruker == interaction.user: embed = embed_templates.error_warning( - interaction, text="Jeg vet du er ensom, men du kan " "ikke matche med deg selv" + interaction, text='Jeg vet du er ensom, men du kan ' 'ikke matche med deg selv' ) return await interaction.response.send_message(embed=embed) @@ -188,39 +258,47 @@ https://cdn.discordapp.com/attachments/674726496878723082/1042631569396990004/kw if bruker.id == self.bot.user.id: match_percent = 100 - await interaction.user.display_avatar.save(fp=f"./src/assets/temp/{interaction.user.id}_raw.png") - await bruker.display_avatar.save(fp=f"./src/assets/temp/{bruker.id}_raw.png") + await interaction.user.display_avatar.save(fp=f'./src/assets/temp/{interaction.user.id}_raw.png') + await bruker.display_avatar.save(fp=f'./src/assets/temp/{bruker.id}_raw.png') - invoker = Image.open(f"./src/assets/temp/{interaction.user.id}_raw.png").convert("RGBA") + invoker = Image.open(f'./src/assets/temp/{interaction.user.id}_raw.png').convert('RGBA') invoker = invoker.resize((389, 389), Image.ANTIALIAS) - user = Image.open(f"./src/assets/temp/{bruker.id}_raw.png").convert("RGBA") + user = Image.open(f'./src/assets/temp/{bruker.id}_raw.png').convert('RGBA') user = user.resize((389, 389), Image.ANTIALIAS) - heart = Image.open("./src/assets/misc/heart.png") - mask = Image.open("./src/assets/misc/heart.png", "r") + heart = Image.open('./src/assets/misc/heart.png') + mask = Image.open('./src/assets/misc/heart.png', 'r') - image = Image.new("RGBA", (1024, 576)) + image = Image.new('RGBA', (1024, 576)) image.paste(invoker, (0, 94)) image.paste(user, (635, 94)) image.paste(heart, (311, 94), mask=mask) draw = ImageDraw.Draw(image) - font = ImageFont.truetype("./src/assets/fonts/RobotoMono-Medium.ttf", 86) - font_size = font.getsize(f"{match_percent}%") + font = ImageFont.truetype('./src/assets/fonts/RobotoMono-Medium.ttf', 86) + font_size = font.getsize(f'{match_percent}%') font_size = ((image.size[0] - font_size[0]) / 2, (image.size[1] - font_size[1]) / 2) - draw.text(font_size, f"{match_percent}%", font=font, fill=(255, 255, 255, 255)) + draw.text(font_size, f'{match_percent}%', font=font, fill=(255, 255, 255, 255)) - image.save(f"./src/assets/temp/{interaction.user.id}_{bruker.id}_edit.png") + image.save(f'./src/assets/temp/{interaction.user.id}_{bruker.id}_edit.png') - f = discord.File(f"./src/assets/temp/{interaction.user.id}_{bruker.id}_edit.png") + f = discord.File(f'./src/assets/temp/{interaction.user.id}_{bruker.id}_edit.png') embed = discord.Embed() - embed.set_image(url=f"attachment://{interaction.user.id}_{bruker.id}_edit.png") + embed.set_image(url=f'attachment://{interaction.user.id}_{bruker.id}_edit.png') embed_templates.default_footer(interaction, embed) await interaction.response.send_message(embed=embed, file=f) with ignore_exception(OSError): - remove(f"./src/assets/temp/{bruker.id}_raw.png") - remove(f"./src/assets/temp/{interaction.user.id}_raw.png") - remove(f"./src/assets/temp/{interaction.user.id}_{bruker.id}_edit.png") + remove(f'./src/assets/temp/{bruker.id}_raw.png') + remove(f'./src/assets/temp/{interaction.user.id}_raw.png') + remove(f'./src/assets/temp/{interaction.user.id}_{bruker.id}_edit.png') async def setup(bot: commands.Bot): + """ + Add the cog to the bot on extension load + + Parameters + ---------- + bot (commands.Bot): Bot instance + """ + await bot.add_cog(Misc(bot)) diff --git a/src/cogs/repeated_messages.py b/src/cogs/repeated_messages.py index 7fa6bc9..e048c38 100644 --- a/src/cogs/repeated_messages.py +++ b/src/cogs/repeated_messages.py @@ -3,34 +3,49 @@ from discord.ext import commands class RepeatedMessages(commands.Cog): + """Send messages at specific points of time""" + def __init__(self, bot: commands.Bot): self.bot = bot - self.uiog_server = self.bot.get_guild(747542543750660178) - @aiocron.crontab("0 0 * * 5") + @aiocron.crontab('0 0 * * 5') async def fredag(): + """Sends a message on Friday at 00:00""" + channel = await self.uiog_server.fetch_channel(747542544291987597) await channel.send( - "NU ÄR DET FREDAG!\n" + - "https://cdn.discordapp.com/attachments/750052141346979850/851216786259181578/video0.mp4" + 'NU ÄR DET FREDAG!\n' + + 'https://cdn.discordapp.com/attachments/750052141346979850/851216786259181578/video0.mp4' ) - @aiocron.crontab("0 0 * * 1") + @aiocron.crontab('0 0 * * 1') async def mandag(): + """Sends a message on Monday at 00:00""" + channel = await self.uiog_server.fetch_channel(747542544291987597) await channel.send( - "ENDELIG MANDAG!\n\n" + - "https://cdn.discordapp.com/attachments/678396498089738250/862853827278929940/hvorfor_de_rike_br_spises.mp4" + 'ENDELIG MANDAG!\n\n' + + 'https://cdn.discordapp.com/attachments/678396498089738250/862853827278929940/hvorfor_de_rike_br_spises.mp4' ) - @aiocron.crontab("0 0 1 1 *") + @aiocron.crontab('0 0 1 1 *') async def new_year(): + """Sends a message on New Year's Day at 00:00""" + channel = await self.uiog_server.fetch_channel(747542544291987597) await channel.send( - "GODT NYTTÅR!\n\nNå, meld dere inn i UiO Gaming igjen <:ThisIsAThreat:874998234072903710>" + 'GODT NYTTÅR!\n\nNå, meld dere inn i UiO Gaming igjen <:ThisIsAThreat:874998234072903710>' ) async def setup(bot: commands.Bot): + """ + Add the cog to the bot on extension load + + Parameters + ---------- + bot (commands.Bot): Bot instance + """ + await bot.add_cog(RepeatedMessages(bot)) diff --git a/src/cogs/utils/discord_utils.py b/src/cogs/utils/discord_utils.py index ae107ae..1dd9847 100644 --- a/src/cogs/utils/discord_utils.py +++ b/src/cogs/utils/discord_utils.py @@ -15,27 +15,55 @@ def get_color(discord_object: discord.User | discord.Member | discord.Role) -> d ----------- (discord.Color): The user's displayed color """ - if hasattr(discord_object, "color") and str(discord_object.color) != '#000000': + + if hasattr(discord_object, 'color') and str(discord_object.color) != '#000000': return discord_object.color return discord.Colour(0x99AAB5) class ScrollerButton(discord.ui.Button): + """Button that scrolls through pages in a scroller view""" + def __init__(self, paginator: Paginator, button_action: callable, content_constructor: callable, label: str, disabled: bool = False): + """ + Parameters + ----------- + paginator (Paginator): The paginator object that contains the data to be paginated + button_action (callable): The function that returns the requested page + content_constructor (callable): A function that takes a paginator object and a page number and returns an embed + """ + super().__init__(label=label, disabled=disabled) self.paginator = paginator self.button_action = button_action self.content_constructor = content_constructor async def callback(self, interaction: discord.Interaction): + """ + What to do when the button is pressed + + Parameters + ----------- + interaction (discord.Interaction): Slash command context object + """ + await interaction.response.defer() content = self.content_constructor(self.paginator, self.button_action(), interaction.message.embeds[0]) await interaction.message.edit(embed=content, view=Scroller(self.paginator, self.content_constructor)) class Scroller(discord.ui.View): + """View that allows scrolling through pages of data using the pagination module""" + def __init__(self, paginatior: Paginator, content_constructor: callable): + """ + Parameters + ----------- + paginator (Paginator): The paginator object that contains the data to be paginated + content_constructor (callable): A function that takes a paginator object and a page number and returns an embed + """ + super().__init__() self.paginator = paginatior diff --git a/src/cogs/utils/embed_templates.py b/src/cogs/utils/embed_templates.py index 21afa99..ce41af3 100644 --- a/src/cogs/utils/embed_templates.py +++ b/src/cogs/utils/embed_templates.py @@ -7,16 +7,17 @@ def default_footer(interaction: discord.Interaction, embed: discord.Embed) -> di Parameters ----------- - interaction (discord.Interaction): Discord interaction context + interaction (discord.Interaction): Slash command context object embed (discord.Embed): An embed object to add the footer to Returns ----------- (discord.Embed): The passed in embed + footer """ + return embed.set_footer( icon_url=interaction.user.avatar, - text=f"{interaction.user.name}#{interaction.user.discriminator}" + text=f'{interaction.user.name}#{interaction.user.discriminator}' ) @@ -26,14 +27,15 @@ def error_warning(interaction: discord.Interaction, text: str) -> discord.Embed: Parameters ----------- - interaction (discord.Interaction): Discord interaction context + interaction (discord.Interaction): Slash command context object text (str): The error message Returns ----------- (discord.Embed): An embed object based on the template with the specified text """ - embed = discord.Embed(color=discord.Color.gold(), description=f"⚠️ {text}") + + embed = discord.Embed(color=discord.Color.gold(), description=f'⚠️ {text}') default_footer(interaction, embed) return embed @@ -45,14 +47,15 @@ def error_fatal(interaction: discord.Interaction, text: str) -> discord.Embed: Parameters ----------- - interaction (discord.Interaction): Discord interaction context + interaction (discord.Interaction): Slash command context object text (str): The error message Returns ----------- (discord.Embed): An embed object based on the template with the specified text """ - embed = discord.Embed(color=discord.Color.red(), description=f"❌ {text}") + + embed = discord.Embed(color=discord.Color.red(), description=f'❌ {text}') default_footer(interaction, embed) return embed @@ -64,14 +67,15 @@ def success(interaction: discord.Interaction, text: str) -> discord.Embed: Parameters ----------- - interaction (discord.Interaction): Discord interaction context + interaction (discord.Interaction): Slash command context object text (str): The message Returns ----------- discord.Embed: An embed object based on the template with the specified text """ - embed = discord.Embed(color=discord.Color.green(), description=f"✅ {text}") + + embed = discord.Embed(color=discord.Color.green(), description=f'✅ {text}') default_footer(interaction, embed) return embed diff --git a/src/cogs/utils/misc_utils.py b/src/cogs/utils/misc_utils.py index bb97244..868bb8f 100644 --- a/src/cogs/utils/misc_utils.py +++ b/src/cogs/utils/misc_utils.py @@ -3,18 +3,15 @@ from math import ceil @contextmanager -def ignore_exception(*exceptions) -> None: +def ignore_exception(*exceptions: Exception): """ Ignores the given exceptions Parameters ---------- - *exceptions: The exceptions you want to ignore - - Returns - ---------- - None + *exceptions tuple[Exception]: The exceptions you want to ignore """ + try: yield except exceptions: diff --git a/src/logger.py b/src/logger.py index d0eb52a..0d0d493 100644 --- a/src/logger.py +++ b/src/logger.py @@ -3,6 +3,8 @@ import logging.handlers class BotLogger: + """Logging config for the bot""" + def __init__(self): logger = logging.getLogger('discord') logger.setLevel(logging.INFO) diff --git a/src/run.py b/src/run.py index 25f3bf9..9017af1 100644 --- a/src/run.py +++ b/src/run.py @@ -11,14 +11,16 @@ from logger import BotLogger # Load config file -with codecs.open("./src/config/config.yaml", "r", encoding="utf8") as f: +with codecs.open('./src/config/config.yaml', 'r', encoding='utf8') as f: config = yaml.load(f, Loader=yaml.SafeLoader) class Bot(commands.Bot): - def __init__(self) -> None: + """UiO Gaming Bot""" + + def __init__(self): super().__init__( - command_prefix=commands.when_mentioned_or(config["bot"]["prefix"]), + command_prefix=commands.when_mentioned_or(config['bot']['prefix']), case_insensitive=True, intents=discord.Intents.all() ) @@ -26,28 +28,28 @@ class Bot(commands.Bot): self.logger = BotLogger().logger # Initialize logger # Connect to database - db = config.get("database", {}) + db = config.get('database', {}) self.db_connection = psycopg2.connect( - host=db["host"], - dbname=db["dbname"], - user=db["username"], - password=db["password"], + host=db['host'], + dbname=db['dbname'], + user=db['username'], + password=db['password'], ) # Fetch misc config values - self.mc_rcon_password = config["minecraft"]["rcon_password"] - self.presence = config["bot"].get("presence", {}) - self.api_keys = config.get("api", {}) - self.emoji = config.get("emoji", {}) - self.misc = config.get("misc", {}) - self.config_mode = config.get("mode") + self.mc_rcon_password = config['minecraft']['rcon_password'] + self.presence = config['bot'].get('presence', {}) + self.api_keys = config.get('api', {}) + self.emoji = config.get('emoji', {}) + self.misc = config.get('misc', {}) + self.config_mode = config.get('mode') - async def setup_hook(self) -> None: + async def setup_hook(self): # Load cogs - for file in listdir("./src/cogs"): - if file.endswith(".py"): + for file in listdir('./src/cogs'): + if file.endswith('.py'): name = file[:-3] - await bot.load_extension(f"cogs.{name}") + await bot.load_extension(f'cogs.{name}') # Sync slash commands to Discord if self.config_mode == 'prod': @@ -63,33 +65,33 @@ bot = Bot() @bot.event async def on_ready(): - if not hasattr(bot, "uptime"): + if not hasattr(bot, 'uptime'): bot.uptime = time() # Print bot info - print(f"Username: {bot.user.name}") - print(f"ID: {bot.user.id}") - print(f"Version: {discord.__version__}") - print("." * 50 + "\n") + print(f'Username: {bot.user.name}') + print(f'ID: {bot.user.id}') + print(f'Version: {discord.__version__}') + print('.' * 50 + '\n') # Set initial presence # Presence status status_types = { - "online": discord.Status.online, - "dnd": discord.Status.dnd, - "idle": discord.Status.idle, - "offline": discord.Status.offline, + 'online': discord.Status.online, + 'dnd': discord.Status.dnd, + 'idle': discord.Status.idle, + 'offline': discord.Status.offline, } - status_type = status_types.get(bot.presence["type"].lower(), discord.Status.online) + status_type = status_types.get(bot.presence['type'].lower(), discord.Status.online) # Presence actitivity - activities = {"playing": 0, "listening": 2, "watching": 3} - activity_type = activities.get(bot.presence["activity"].lower(), 0) + activities = {'playing': 0, 'listening': 2, 'watching': 3} + activity_type = activities.get(bot.presence['activity'].lower(), 0) await bot.change_presence( - activity=discord.Activity(type=activity_type, name=bot.presence["message"]), + activity=discord.Activity(type=activity_type, name=bot.presence['message']), status=status_type ) -bot.run(config["bot"]["token"], reconnect=True, log_handler=None) +bot.run(config['bot']['token'], reconnect=True, log_handler=None) From ebf5a849f850b770b0b4d0bacd5c06ad0600229c Mon Sep 17 00:00:00 2001 From: LBlend Date: Thu, 17 Nov 2022 23:18:27 +0100 Subject: [PATCH 52/54] add (some) documentation --- README.md | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ff2f805..f98c6d7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,41 @@ # UiO Gaming Discord Bot -UiO Gaming Discord Bot er en chattebot for ulike formål. Den skal hjelpe oss med ulike oppgaver og ha mange ulike funksjoner +UiO Gaming Discord Bot er en chattebot for ulike formål. Hvilke formål det er, er jeg ikke helt sikker på. -Mer dokumentasjon kommer snart™ +## Installasjon + +1. Fjern `.example` fra [config.yaml.example](src/config/config.yaml) i configmappen + +2. Fyll inn verdiene i denne configfilen + +Du kan så bevege deg videre til ett av to alternativer. + +### Valgmulighet 1 - Manuelt + +Du bør ha gjort følgende: + +- Installert Python 3.10+ +- Ha en Postgresql database satt opp + + +1. Installer avhengigheter + +``` +pip install -r requirements.txt -U +``` + +2. Kjør bot + +``` +python src/run.py +``` + +### Valgmulighet 2 - Docker + +``` +docker-compose up +``` + +## Bidra + +Se [CONTRIBUTING.md](CONTRIBUTING.md) From c60064542bdce1287e2e5a04fadb03182be061d2 Mon Sep 17 00:00:00 2001 From: LBlend Date: Fri, 18 Nov 2022 03:46:06 +0100 Subject: [PATCH 53/54] simplify ScrollerButton disabling code --- src/cogs/utils/discord_utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cogs/utils/discord_utils.py b/src/cogs/utils/discord_utils.py index 1dd9847..4d02ebf 100644 --- a/src/cogs/utils/discord_utils.py +++ b/src/cogs/utils/discord_utils.py @@ -67,7 +67,7 @@ class Scroller(discord.ui.View): super().__init__() self.paginator = paginatior - self.add_item(ScrollerButton(self.paginator, self.paginator.first_page, content_constructor, label='<<', disabled=True if self.paginator.current_page == 1 else False)) - self.add_item(ScrollerButton(self.paginator, self.paginator.previous_page, content_constructor, label='<', disabled=True if self.paginator.current_page == 1 else False)) - self.add_item(ScrollerButton(self.paginator, self.paginator.next_page, content_constructor, label='>', disabled=True if self.paginator.current_page == self.paginator.total_page_count else False)) - self.add_item(ScrollerButton(self.paginator, self.paginator.last_page, content_constructor, label='>>', disabled=True if self.paginator.current_page == self.paginator.total_page_count else False)) + self.add_item(ScrollerButton(self.paginator, self.paginator.first_page, content_constructor, label='<<', disabled=self.paginator.current_page == 1)) + self.add_item(ScrollerButton(self.paginator, self.paginator.previous_page, content_constructor, label='<', disabled=self.paginator.current_page == 1)) + self.add_item(ScrollerButton(self.paginator, self.paginator.next_page, content_constructor, label='>', disabled=self.paginator.current_page == self.paginator.total_page_count)) + self.add_item(ScrollerButton(self.paginator, self.paginator.last_page, content_constructor, label='>>', disabled=self.paginator.current_page == self.paginator.total_page_count)) From 642c0aaa42ad08d45dc13455274875b743955383 Mon Sep 17 00:00:00 2001 From: LBlend Date: Fri, 18 Nov 2022 03:49:16 +0100 Subject: [PATCH 54/54] readd emoji info command with ctx framework --- src/cogs/info.py | 50 ++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/src/cogs/info.py b/src/cogs/info.py index 38e0842..f35ae28 100644 --- a/src/cogs/info.py +++ b/src/cogs/info.py @@ -462,28 +462,36 @@ class Info(commands.Cog): embed_templates.default_footer(interaction, embed) await interaction.response.send_message(embed=embed) - # @app_commands.checks.bot_has_permissions(embed_links=True) - # @app_commands.guild_only() - # @app_commands.checks.cooldown(1, 2) - # @app_commands.command() - # async def emoji(self, interaction: discord.Interaction, emoji: discord.Emoji): - # """ - # Viser info om en CUSTOM emoji som tilhører serveren - # """ - # emoji = await emoji.guild.fetch_emoji(emoji.id) - # try: - # emoji_creator = f'{emoji.user.mention}\n{emoji.user.name}#{emoji.user.discriminator}' - # except AttributeError: - # emoji_creator = 'Jeg trenger `manage_emojis`-tillatelsen på serveren den er fra for å hente dette' + # NOTE: This command is implemented using the old command framework + # This is due to lack of emoji support in the new framework + # TODO: take a look at this command when/if the new framework supports emojis + @commands.guild_only() + @commands.bot_has_permissions(embed_links=True) + @commands.cooldown(1, 2) + @commands.command(name='emoji', description='Hent informasjon om en emoji i serveren') + async def emoji(self, ctx: commands.Context, emoji: discord.Emoji): + """ + Get information about an emoji in the server - # embed = discord.Embed(color=interaction.guild.me.color, title=emoji.name, description=f'ID: {emoji.id}') - # embed.set_author(name=emoji.guild.name, icon_url=emoji.guild.icon) - # embed.add_field(name='Opprettet', value=discord.utils.format_dt(emoji.created_at, style='F')) - # embed.add_field(name='Animert', value='Ja' if emoji.animated else 'Nei') - # embed.add_field(name='Lagt til av', value=emoji_creator) - # embed.set_image(url=emoji.url) - # embed_templates.default_footer(interaction, embed) - # await interaction.response.send_message(embed=embed) + Parameters + ---------- + ctx (commands.Context): Command context object + emoji (discord.Emoji): Emoji to fetch information about + """ + + emoji = await emoji.guild.fetch_emoji(emoji.id) + try: + emoji_creator = f'{emoji.user.mention}\n{emoji.user.name}#{emoji.user.discriminator}' + except AttributeError: + emoji_creator = 'Jeg trenger `manage_emojis`-tillatelsen på serveren den er fra for å hente dette' + + embed = discord.Embed(color=ctx.me.color, title=emoji.name, description=f'ID: {emoji.id}') + embed.set_author(name=emoji.guild.name, icon_url=emoji.guild.icon) + embed.add_field(name='Opprettet', value=discord.utils.format_dt(emoji.created_at, style='F')) + embed.add_field(name='Animert', value='Ja' if emoji.animated else 'Nei') + embed.add_field(name='Lagt til av', value=emoji_creator) + embed.set_image(url=emoji.url) + await ctx.send(embed=embed) guild_oldest_group = app_commands.Group(name='eldst', description='Viser de eldste medlemmene på serveren', parent=guild_group)