From 93759287ca518e0af8aeac7fb659ae3ab694bad8 Mon Sep 17 00:00:00 2001 From: Zevaryx Date: Fri, 6 Aug 2021 20:24:50 -0600 Subject: [PATCH] Pass all flake8 checks --- .pre-commit-config.yaml | 6 ---- jarvis/__init__.py | 24 ++++++++++---- jarvis/cogs/admin/__init__.py | 6 +++- jarvis/cogs/admin/ban.py | 47 +++++++++++++++------------ jarvis/cogs/admin/kick.py | 8 +++-- jarvis/cogs/admin/lock.py | 26 +++++++-------- jarvis/cogs/admin/lockdown.py | 16 ++++----- jarvis/cogs/admin/mute.py | 13 +++++--- jarvis/cogs/admin/purge.py | 15 +++++---- jarvis/cogs/admin/roleping.py | 30 +++++++++-------- jarvis/cogs/admin/warning.py | 22 +++++++------ jarvis/cogs/autoreact.py | 28 +++++++++------- jarvis/cogs/ctc2.py | 31 ++++++++++-------- jarvis/cogs/dbrand.py | 59 ++++++++++++++++++++-------------- jarvis/cogs/dev.py | 52 +++++++++++++++--------------- jarvis/cogs/error.py | 14 +++++--- jarvis/cogs/gitlab.py | 52 ++++++++++++++++-------------- jarvis/cogs/image.py | 15 ++++++--- jarvis/cogs/jokes.py | 12 ++++--- jarvis/cogs/modlog/__init__.py | 6 +++- jarvis/cogs/modlog/command.py | 10 ++++-- jarvis/cogs/modlog/member.py | 38 +++++++++++++++------- jarvis/cogs/modlog/message.py | 15 ++++++--- jarvis/cogs/modlog/utils.py | 10 ++++-- jarvis/cogs/owner.py | 44 +++++++++++++------------ jarvis/cogs/remindme.py | 32 ++++++++++-------- jarvis/cogs/rolegiver.py | 28 +++++++++------- jarvis/cogs/settings.py | 44 ++++++++++++++----------- jarvis/cogs/starboard.py | 24 ++++++++------ jarvis/cogs/util.py | 28 ++++++++-------- jarvis/cogs/verify.py | 23 +++++++------ jarvis/config.py | 21 +++++++++--- jarvis/data/dbrand.py | 1 + jarvis/data/robotcamo.py | 1 + jarvis/data/unicode.py | 1 + jarvis/db/models.py | 37 +++++++++++++++++++++ jarvis/events/guild.py | 16 ++++++--- jarvis/events/member.py | 9 ++++-- jarvis/events/message.py | 44 ++++++++++++++++--------- jarvis/logo.py | 7 ++-- jarvis/tasks/__init__.py | 4 ++- jarvis/tasks/unban.py | 4 ++- jarvis/tasks/unlock.py | 4 ++- jarvis/tasks/unmute.py | 4 ++- jarvis/tasks/unwarn.py | 5 +-- jarvis/utils/__init__.py | 32 +++++++++++------- jarvis/utils/cachecog.py | 16 +++++---- jarvis/utils/field.py | 6 +++- jarvis/utils/permissions.py | 14 +++++--- run.py | 1 + 50 files changed, 625 insertions(+), 380 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a015e5a..c180e5b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -35,9 +35,3 @@ repos: - flake8-bandit~=2.1 - flake8-docstrings~=1.5 args: [--max-line-length=120, --ignore=ANN101 D107 ANN102 ANN206 D105 ANN204] - - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.910 - hooks: - - id: mypy - args: [--install-types, --non-interactive] diff --git a/jarvis/__init__.py b/jarvis/__init__.py index d3d9968..968183b 100644 --- a/jarvis/__init__.py +++ b/jarvis/__init__.py @@ -1,7 +1,8 @@ +"""Main J.A.R.V.I.S. package.""" import asyncio import logging from pathlib import Path -from typing import Union +from typing import Optional from discord import Intents from discord.ext import commands @@ -10,7 +11,7 @@ from discord_slash import SlashCommand from mongoengine import connect from psutil import Process -from jarvis import logo +from jarvis import logo # noqa: F401 from jarvis import tasks from jarvis import utils from jarvis.config import get_config @@ -18,6 +19,14 @@ from jarvis.events import guild from jarvis.events import member from jarvis.events import message +jconfig = get_config() + +logger = logging.getLogger("discord") +logger.setLevel(logging.getLevelName(jconfig.log_level)) +file_handler = logging.FileHandler(filename="jarvis.log", encoding="UTF-8", mode="w") +file_handler.setFormatter(logging.Formatter("[%(asctime)s][%(levelname)s][%(name)s] %(message)s")) +logger.addHandler(file_handler) + if asyncio.get_event_loop().is_closed(): asyncio.set_event_loop(asyncio.new_event_loop()) @@ -25,7 +34,6 @@ intents = Intents.default() intents.members = True restart_ctx = None -jconfig = get_config() jarvis = commands.Bot( command_prefix=utils.get_prefix, @@ -40,7 +48,8 @@ __version__ = "1.10.3" @jarvis.event -async def on_ready(): +async def on_ready() -> None: + """d.py on_ready override.""" global restart_ctx print(" Logged in as {0.user}".format(jarvis)) print(" Connected to {} guild(s)".format(len(jarvis.guilds))) @@ -60,7 +69,8 @@ async def on_ready(): restart_ctx = None -def run(ctx=None): +def run(ctx: dict = None) -> Optional[dict]: + """Run J.A.R.V.I.S.""" global restart_ctx if ctx: restart_ctx = ctx @@ -81,7 +91,7 @@ def run(ctx=None): jarvis.load_extension(extension) print( " https://discord.com/api/oauth2/authorize?client_id=" - + "{}&permissions=8&scope=bot%20applications.commands".format(jconfig.client_id) + + "{}&permissions=8&scope=bot%20applications.commands".format(jconfig.client_id) # noqa: W503 ) jarvis.max_messages = jconfig.max_messages @@ -89,7 +99,7 @@ def run(ctx=None): # Add event listeners if jconfig.events: - listeners = [ + _ = [ guild.GuildEventHandler(jarvis), member.MemberEventHandler(jarvis), message.MessageEventHandler(jarvis), diff --git a/jarvis/cogs/admin/__init__.py b/jarvis/cogs/admin/__init__.py index d8c13a2..1235b8a 100644 --- a/jarvis/cogs/admin/__init__.py +++ b/jarvis/cogs/admin/__init__.py @@ -1,3 +1,6 @@ +"""J.A.R.V.I.S. Admin Cogs.""" +from discord.ext.commands import Bot + from jarvis.cogs.admin import ban from jarvis.cogs.admin import kick from jarvis.cogs.admin import lock @@ -8,7 +11,8 @@ from jarvis.cogs.admin import roleping from jarvis.cogs.admin import warning -def setup(bot): +def setup(bot: Bot) -> None: + """Add admin cogs to J.A.R.V.I.S.""" bot.add_cog(ban.BanCog(bot)) bot.add_cog(kick.KickCog(bot)) bot.add_cog(lock.LockCog(bot)) diff --git a/jarvis/cogs/admin/ban.py b/jarvis/cogs/admin/ban.py index 5da80ae..58365a1 100644 --- a/jarvis/cogs/admin/ban.py +++ b/jarvis/cogs/admin/ban.py @@ -1,3 +1,4 @@ +"""J.A.R.V.I.S. BanCog.""" import re from datetime import datetime from datetime import timedelta @@ -21,6 +22,8 @@ from jarvis.utils.permissions import admin_or_permissions class BanCog(CacheCog): + """J.A.R.V.I.S. BanCog.""" + def __init__(self, bot: commands.Bot): super().__init__(bot) @@ -32,7 +35,8 @@ class BanCog(CacheCog): duration: int, active: bool, fields: list, - ): + ) -> None: + """Apply a Discord ban.""" await ctx.guild.ban(user, reason=reason) _ = Ban( user=user.id, @@ -61,7 +65,8 @@ class BanCog(CacheCog): await ctx.send(embed=embed) - async def discord_apply_unban(self, ctx: SlashContext, user: User, reason: str): + async def discord_apply_unban(self, ctx: SlashContext, user: User, reason: str) -> None: + """Apply a Discord unban.""" await ctx.guild.unban(user, reason=reason) _ = Unban( user=user.id, @@ -128,7 +133,7 @@ class BanCog(CacheCog): reason: str = None, type: str = "perm", duration: int = 4, - ): + ) -> None: if not user or user == ctx.author: await ctx.send("You cannot ban yourself.", hidden=True) return @@ -221,7 +226,7 @@ class BanCog(CacheCog): ctx: SlashContext, user: str, reason: str, - ): + ) -> None: if len(reason) > 100: await ctx.send("Reason must be < 100 characters", hidden=True) return @@ -252,12 +257,8 @@ class BanCog(CacheCog): active_bans = [] for ban in bans: active_bans.append("{0} ({1}): {2}".format(ban.user.name, ban.user.id, ban.reason)) - message = ( - "More than one result. " - + "Please use one of the following IDs:\n```" - + "\n".join(active_bans) - + "\n```" - ) + ab_message = "\n".join(active_bans) + message = f"More than one result. Please use one of the following IDs:\n```{ab_message}\n```" await ctx.send(message) return else: @@ -331,13 +332,13 @@ class BanCog(CacheCog): ], ) @admin_or_permissions(ban_members=True) - async def _bans_list(self, ctx: SlashContext, type: int = 0, active: int = 1): + async def _bans_list(self, ctx: SlashContext, type: int = 0, active: int = 1) -> None: active = bool(active) exists = self.check_cache(ctx, type=type, active=active) if exists: await ctx.defer(hidden=True) await ctx.send( - "Please use existing interaction: " + f"{exists['paginator']._message.jump_url}", + f"Please use existing interaction: {exists['paginator']._message.jump_url}", hidden=True, ) return @@ -357,10 +358,12 @@ class BanCog(CacheCog): fields.append( Field( name=f"Username: {ban.username}#{ban.discrim}", - value=f"Date: {ban.created_at.strftime('%d-%m-%Y')}\n" - + f"User ID: {ban.user}\n" - + f"Reason: {ban.reason}\n" - + f"Type: {ban.type}\n\u200b", + value=( + f"Date: {ban.created_at.strftime('%d-%m-%Y')}\n" + f"User ID: {ban.user}\n" + f"Reason: {ban.reason}\n" + f"Type: {ban.type}\n\u200b" + ), inline=False, ) ) @@ -372,10 +375,12 @@ class BanCog(CacheCog): fields.append( Field( name=f"Username: {ban.user.name}#" + f"{ban.user.discriminator}", - value="Date: [unknown]\n" - + f"User ID: {ban.user.id}\n" - + f"Reason: {ban.reason}\n" - + "Type: manual\n\u200b", + value=( + f"Date: [unknown]\n" + f"User ID: {ban.user.id}\n" + f"Reason: {ban.reason}\n" + "Type: manual\n\u200b" + ), inline=False, ) ) @@ -397,7 +402,7 @@ class BanCog(CacheCog): pages.append(embed) else: for i in range(0, len(bans), 5): - embed = build_embed(title=title, description="", fields=fields[i : i + 5]) + embed = build_embed(title=title, description="", fields=fields[i : i + 5]) # noqa: E203 embed.set_thumbnail(url=ctx.guild.icon_url) pages.append(embed) diff --git a/jarvis/cogs/admin/kick.py b/jarvis/cogs/admin/kick.py index 121e2cc..70d1905 100644 --- a/jarvis/cogs/admin/kick.py +++ b/jarvis/cogs/admin/kick.py @@ -1,4 +1,6 @@ +"""J.A.R.V.I.S. KickCog.""" from discord import User +from discord.ext.commands import Bot from discord_slash import cog_ext from discord_slash import SlashContext from discord_slash.utils.manage_commands import create_option @@ -11,7 +13,9 @@ from jarvis.utils.permissions import admin_or_permissions class KickCog(CacheCog): - def __init__(self, bot): + """J.A.R.V.I.S. KickCog.""" + + def __init__(self, bot: Bot): super().__init__(bot) @cog_ext.cog_slash( @@ -33,7 +37,7 @@ class KickCog(CacheCog): ], ) @admin_or_permissions(kick_members=True) - async def _kick(self, ctx: SlashContext, user: User, reason=None): + async def _kick(self, ctx: SlashContext, user: User, reason: str = None) -> None: if not user or user == ctx.author: await ctx.send("You cannot kick yourself.", hidden=True) return diff --git a/jarvis/cogs/admin/lock.py b/jarvis/cogs/admin/lock.py index 9a458ee..6649409 100644 --- a/jarvis/cogs/admin/lock.py +++ b/jarvis/cogs/admin/lock.py @@ -1,10 +1,12 @@ +"""J.A.R.V.I.S. LockCog.""" +from contextlib import suppress from typing import Union from discord import Role from discord import TextChannel from discord import User from discord import VoiceChannel -from discord.ext import commands +from discord.ext.commands import Bot from discord_slash import cog_ext from discord_slash import SlashContext from discord_slash.utils.manage_commands import create_option @@ -15,7 +17,9 @@ from jarvis.utils.permissions import admin_or_permissions class LockCog(CacheCog): - def __init__(self, bot: commands.Bot): + """J.A.R.V.I.S. LockCog.""" + + def __init__(self, bot: Bot): super().__init__(bot) async def _lock_channel( @@ -24,8 +28,8 @@ class LockCog(CacheCog): role: Role, admin: User, reason: str, - allow_send=False, - ): + allow_send: bool = False, + ) -> None: overrides = channel.overwrites_for(role) if isinstance(channel, TextChannel): overrides.send_messages = allow_send @@ -38,7 +42,7 @@ class LockCog(CacheCog): channel: Union[TextChannel, VoiceChannel], role: Role, admin: User, - ): + ) -> None: overrides = channel.overwrites_for(role) if isinstance(channel, TextChannel): overrides.send_messages = None @@ -77,7 +81,7 @@ class LockCog(CacheCog): reason: str, duration: int = 10, channel: Union[TextChannel, VoiceChannel] = None, - ): + ) -> None: await ctx.defer(hidden=True) if duration <= 0: await ctx.send("Duration must be > 0", hidden=True) @@ -91,10 +95,8 @@ class LockCog(CacheCog): if not channel: channel = ctx.channel for role in ctx.guild.roles: - try: + with suppress(Exception): await self._lock_channel(channel, role, ctx.author, reason) - except Exception: - continue # Just continue on error _ = Lock( channel=channel.id, guild=ctx.guild.id, @@ -121,7 +123,7 @@ class LockCog(CacheCog): self, ctx: SlashContext, channel: Union[TextChannel, VoiceChannel] = None, - ): + ) -> None: if not channel: channel = ctx.channel lock = Lock.objects(guild=ctx.guild.id, channel=channel.id, active=True).first() @@ -129,10 +131,8 @@ class LockCog(CacheCog): await ctx.send(f"{channel.mention} not locked.", hidden=True) return for role in ctx.guild.roles: - try: + with suppress(Exception): await self._unlock_channel(channel, role, ctx.author) - except Exception: - continue # Just continue on error lock.active = False lock.save() await ctx.send(f"{channel.mention} unlocked") diff --git a/jarvis/cogs/admin/lockdown.py b/jarvis/cogs/admin/lockdown.py index 40a5150..dc91dde 100644 --- a/jarvis/cogs/admin/lockdown.py +++ b/jarvis/cogs/admin/lockdown.py @@ -1,3 +1,5 @@ +"""J.A.R.V.I.S. LockdownCog.""" +from contextlib import suppress from datetime import datetime from discord.ext import commands @@ -11,6 +13,8 @@ from jarvis.utils.permissions import admin_or_permissions class LockdownCog(CacheCog): + """J.A.R.V.I.S. LockdownCog.""" + def __init__(self, bot: commands.Bot): super().__init__(bot) @@ -39,7 +43,7 @@ class LockdownCog(CacheCog): ctx: SlashContext, reason: str, duration: int = 10, - ): + ) -> None: await ctx.defer(hidden=True) if duration <= 0: await ctx.send("Duration must be > 0", hidden=True) @@ -52,10 +56,8 @@ class LockdownCog(CacheCog): updates = [] for channel in channels: for role in roles: - try: + with suppress(Exception): await self._lock_channel(channel, role, ctx.author, reason) - except Exception: - continue # Just continue on error updates.append( Lock( channel=channel.id, @@ -80,7 +82,7 @@ class LockdownCog(CacheCog): async def _lockdown_end( self, ctx: SlashContext, - ): + ) -> None: channels = ctx.guild.channels roles = ctx.guild.roles update = False @@ -91,10 +93,8 @@ class LockdownCog(CacheCog): await ctx.defer() for channel in channels: for role in roles: - try: + with suppress(Exception): await self._unlock_channel(channel, role, ctx.author) - except Exception: - continue # Just continue on error update = True if update: Lock.objects(guild=ctx.guild.id, active=True).update(set__active=False) diff --git a/jarvis/cogs/admin/mute.py b/jarvis/cogs/admin/mute.py index 0dce394..aaf6e2a 100644 --- a/jarvis/cogs/admin/mute.py +++ b/jarvis/cogs/admin/mute.py @@ -1,3 +1,4 @@ +"""J.A.R.V.I.S. MuteCog.""" from discord import Member from discord.ext import commands from discord.utils import get @@ -13,7 +14,9 @@ from jarvis.utils.permissions import admin_or_permissions class MuteCog(commands.Cog): - def __init__(self, bot): + """J.A.R.V.I.S. MuteCog.""" + + def __init__(self, bot: commands.Bot): self.bot = bot @cog_ext.cog_slash( @@ -41,7 +44,7 @@ class MuteCog(commands.Cog): ], ) @admin_or_permissions(mute_members=True) - async def _mute(self, ctx: SlashContext, user: Member, reason: str, duration: int = 30): + async def _mute(self, ctx: SlashContext, user: Member, reason: str, duration: int = 30) -> None: if user == ctx.author: await ctx.send("You cannot mute yourself.", hidden=True) return @@ -54,7 +57,7 @@ class MuteCog(commands.Cog): mute_setting = Setting.objects(guild=ctx.guild.id, setting="mute").first() if not mute_setting: await ctx.send( - "Please configure a mute role " + "with /settings mute first", + "Please configure a mute role with /settings mute first", hidden=True, ) return @@ -100,11 +103,11 @@ class MuteCog(commands.Cog): ], ) @admin_or_permissions(mute_members=True) - async def _unmute(self, ctx: SlashContext, user: Member): + async def _unmute(self, ctx: SlashContext, user: Member) -> None: mute_setting = Setting.objects(guild=ctx.guild.id, setting="mute").first() if not mute_setting: await ctx.send( - "Please configure a mute role with " + "/settings mute first.", + "Please configure a mute role with /settings mute first.", hidden=True, ) return diff --git a/jarvis/cogs/admin/purge.py b/jarvis/cogs/admin/purge.py index 1a7da64..2eba321 100644 --- a/jarvis/cogs/admin/purge.py +++ b/jarvis/cogs/admin/purge.py @@ -1,3 +1,4 @@ +"""J.A.R.V.I.S. PurgeCog.""" from discord import TextChannel from discord.ext import commands from discord_slash import cog_ext @@ -10,7 +11,9 @@ from jarvis.utils.permissions import admin_or_permissions class PurgeCog(commands.Cog): - def __init__(self, bot): + """J.A.R.V.I.S. PurgeCog.""" + + def __init__(self, bot: commands.Bot): self.bot = bot @cog_ext.cog_slash( @@ -26,7 +29,7 @@ class PurgeCog(commands.Cog): ], ) @admin_or_permissions(manage_messages=True) - async def _purge(self, ctx: SlashContext, amount: int = 10): + async def _purge(self, ctx: SlashContext, amount: int = 10) -> None: if amount < 1: await ctx.send("Amount must be >= 1", hidden=True) return @@ -63,7 +66,7 @@ class PurgeCog(commands.Cog): ], ) @admin_or_permissions(manage_messages=True) - async def _autopurge_add(self, ctx: SlashContext, channel: TextChannel, delay: int = 30): + async def _autopurge_add(self, ctx: SlashContext, channel: TextChannel, delay: int = 30) -> None: if not isinstance(channel, TextChannel): await ctx.send("Channel must be a TextChannel", hidden=True) return @@ -83,7 +86,7 @@ class PurgeCog(commands.Cog): admin=ctx.author.id, delay=delay, ).save() - await ctx.send(f"Autopurge set up on {channel.mention}, " + f"delay is {delay} seconds") + await ctx.send(f"Autopurge set up on {channel.mention}, delay is {delay} seconds") @cog_ext.cog_subcommand( base="autopurge", @@ -99,7 +102,7 @@ class PurgeCog(commands.Cog): ], ) @admin_or_permissions(manage_messages=True) - async def _autopurge_remove(self, ctx: SlashContext, channel: TextChannel): + async def _autopurge_remove(self, ctx: SlashContext, channel: TextChannel) -> None: autopurge = Autopurge.objects(guild=ctx.guild.id, channel=channel.id) if not autopurge: await ctx.send("Autopurge does not exist.", hidden=True) @@ -127,7 +130,7 @@ class PurgeCog(commands.Cog): ], ) @admin_or_permissions(manage_messages=True) - async def _autopurge_update(self, ctx: SlashContext, channel: TextChannel, delay: int): + async def _autopurge_update(self, ctx: SlashContext, channel: TextChannel, delay: int) -> None: autopurge = Autopurge.objects(guild=ctx.guild.id, channel=channel.id) if not autopurge: await ctx.send("Autopurge does not exist.", hidden=True) diff --git a/jarvis/cogs/admin/roleping.py b/jarvis/cogs/admin/roleping.py index bc67acd..002442d 100644 --- a/jarvis/cogs/admin/roleping.py +++ b/jarvis/cogs/admin/roleping.py @@ -1,9 +1,11 @@ +"""J.A.R.V.I.S. RolepingCog.""" from datetime import datetime from datetime import timedelta from ButtonPaginator import Paginator from discord import Member from discord import Role +from discord.ext.commands import Bot from discord_slash import cog_ext from discord_slash import SlashContext from discord_slash.model import ButtonStyle @@ -17,7 +19,9 @@ from jarvis.utils.permissions import admin_or_permissions class RolepingCog(CacheCog): - def __init__(self, bot): + """J.A.R.V.I.S. RolepingCog.""" + + def __init__(self, bot: Bot): super().__init__(bot) @cog_ext.cog_subcommand( @@ -34,7 +38,7 @@ class RolepingCog(CacheCog): ], ) @admin_or_permissions(manage_guild=True) - async def _roleping_add(self, ctx: SlashContext, role: Role): + async def _roleping_add(self, ctx: SlashContext, role: Role) -> None: roleping = Roleping.objects(guild=ctx.guild.id, role=role.id).first() if roleping: await ctx.send(f"Role `{role.name}` already in roleping.", hidden=True) @@ -62,7 +66,7 @@ class RolepingCog(CacheCog): ], ) @admin_or_permissions(manage_guild=True) - async def _roleping_remove(self, ctx: SlashContext, role: Role): + async def _roleping_remove(self, ctx: SlashContext, role: Role) -> None: roleping = Roleping.objects(guild=ctx.guild.id, role=role.id) if not roleping: await ctx.send("Roleping does not exist", hidden=True) @@ -76,12 +80,12 @@ class RolepingCog(CacheCog): name="list", description="List all blocklisted roles", ) - async def _roleping_list(self, ctx: SlashContext): + async def _roleping_list(self, ctx: SlashContext) -> None: exists = self.check_cache(ctx) if exists: await ctx.defer(hidden=True) await ctx.send( - "Please use existing interaction: " + f"{exists['paginator']._message.jump_url}", + f"Please use existing interaction: {exists['paginator']._message.jump_url}", hidden=True, ) return @@ -176,7 +180,7 @@ class RolepingCog(CacheCog): ], ) @admin_or_permissions(manage_guild=True) - async def _roleping_bypass_user(self, ctx: SlashContext, user: Member, rping: Role): + async def _roleping_bypass_user(self, ctx: SlashContext, user: Member, rping: Role) -> None: roleping = Roleping.objects(guild=ctx.guild.id, role=rping.id).first() if not roleping: await ctx.send(f"Roleping not configured for {rping.mention}", hidden=True) @@ -188,7 +192,7 @@ class RolepingCog(CacheCog): if len(roleping.bypass["users"]) == 10: await ctx.send( - "Already have 10 users in bypass. " "Please consider using roles for roleping bypass", + "Already have 10 users in bypass. Please consider using roles for roleping bypass", hidden=True, ) return @@ -197,7 +201,7 @@ class RolepingCog(CacheCog): if matching_role: await ctx.send( - f"{user.mention} already has bypass " f"via {matching_role[0].mention}", + f"{user.mention} already has bypass via {matching_role[0].mention}", hidden=True, ) return @@ -229,7 +233,7 @@ class RolepingCog(CacheCog): ], ) @admin_or_permissions(manage_guild=True) - async def _roleping_bypass_role(self, ctx: SlashContext, role: Role, rping: Role): + async def _roleping_bypass_role(self, ctx: SlashContext, role: Role, rping: Role) -> None: roleping = Roleping.objects(guild=ctx.guild.id, role=rping.id).first() if not roleping: await ctx.send(f"Roleping not configured for {rping.mention}", hidden=True) @@ -241,7 +245,7 @@ class RolepingCog(CacheCog): if len(roleping.bypass["roles"]) == 10: await ctx.send( - "Already have 10 roles in bypass. " "Please consider consolidating roles for roleping bypass", + "Already have 10 roles in bypass. Please consider consolidating roles for roleping bypass", hidden=True, ) return @@ -273,7 +277,7 @@ class RolepingCog(CacheCog): ], ) @admin_or_permissions(manage_guild=True) - async def _roleping_restore_user(self, ctx: SlashContext, user: Member, rping: Role): + async def _roleping_restore_user(self, ctx: SlashContext, user: Member, rping: Role) -> None: roleping = Roleping.objects(guild=ctx.guild.id, role=rping.id).first() if not roleping: await ctx.send(f"Roleping not configured for {rping.mention}", hidden=True) @@ -310,7 +314,7 @@ class RolepingCog(CacheCog): ], ) @admin_or_permissions(manage_guild=True) - async def _roleping_restore_role(self, ctx: SlashContext, role: Role, rping: Role): + async def _roleping_restore_role(self, ctx: SlashContext, role: Role, rping: Role) -> None: roleping = Roleping.objects(guild=ctx.guild.id, role=rping.id).first() if not roleping: await ctx.send(f"Roleping not configured for {rping.mention}", hidden=True) @@ -322,7 +326,7 @@ class RolepingCog(CacheCog): if len(roleping.bypass["roles"]) == 10: await ctx.send( - "Already have 10 roles in bypass. " "Please consider consolidating roles for roleping bypass", + "Already have 10 roles in bypass. Please consider consolidating roles for roleping bypass", hidden=True, ) return diff --git a/jarvis/cogs/admin/warning.py b/jarvis/cogs/admin/warning.py index 25a6bdd..bd65732 100644 --- a/jarvis/cogs/admin/warning.py +++ b/jarvis/cogs/admin/warning.py @@ -1,8 +1,10 @@ +"""J.A.R.V.I.S. WarningCog.""" from datetime import datetime from datetime import timedelta from ButtonPaginator import Paginator from discord import User +from discord.ext.commands import Bot from discord_slash import cog_ext from discord_slash import SlashContext from discord_slash.model import ButtonStyle @@ -17,7 +19,9 @@ from jarvis.utils.permissions import admin_or_permissions class WarningCog(CacheCog): - def __init__(self, bot): + """J.A.R.V.I.S. WarningCog.""" + + def __init__(self, bot: Bot): super().__init__(bot) @cog_ext.cog_slash( @@ -45,7 +49,7 @@ class WarningCog(CacheCog): ], ) @admin_or_permissions(manage_guild=True) - async def _warn(self, ctx: SlashContext, user: User, reason: str, duration: int = 24): + async def _warn(self, ctx: SlashContext, user: User, reason: str, duration: int = 24) -> None: if len(reason) > 100: await ctx.send("Reason must be < 100 characters", hidden=True) return @@ -101,13 +105,13 @@ class WarningCog(CacheCog): ], ) @admin_or_permissions(manage_guild=True) - async def _warnings(self, ctx: SlashContext, user: User, active: bool = 1): + async def _warnings(self, ctx: SlashContext, user: User, active: bool = 1) -> None: active = bool(active) exists = self.check_cache(ctx, user_id=user.id, active=active) if exists: await ctx.defer(hidden=True) await ctx.send( - "Please use existing interaction: " + f"{exists['paginator']._message.jump_url}", + f"Please use existing interaction: {exists['paginator']._message.jump_url}", hidden=True, ) return @@ -138,15 +142,15 @@ class WarningCog(CacheCog): fields.append( Field( name=warn.created_at.strftime("%Y-%m-%d %H:%M:%S UTC"), - value=f"{warn.reason}\n" + f"Admin: {admin_name}\n" + "\u200b", + value=f"{warn.reason}\nAdmin: {admin_name}\n\u200b", inline=False, ) ) for i in range(0, len(fields), 5): embed = build_embed( title="Warnings", - description=f"{warnings.count()} total | " + f"{active_warns.count()} currently active", - fields=fields[i : i + 5], + description=f"{warnings.count()} total | {active_warns.count()} currently active", + fields=fields[i : i + 5], # noqa: E203 ) embed.set_author( name=user.name + "#" + user.discriminator, @@ -170,8 +174,8 @@ class WarningCog(CacheCog): for i in range(0, len(fields), 5): embed = build_embed( title="Warnings", - description=f"{warnings.count()} total | " + f"{active_warns.count()} currently active", - fields=fields[i : i + 5], + description=f"{warnings.count()} total | {active_warns.count()} currently active", + fields=fields[i : i + 5], # noqa: E203 ) embed.set_author( name=user.name + "#" + user.discriminator, diff --git a/jarvis/cogs/autoreact.py b/jarvis/cogs/autoreact.py index d220a45..a291ca7 100644 --- a/jarvis/cogs/autoreact.py +++ b/jarvis/cogs/autoreact.py @@ -1,3 +1,4 @@ +"""J.A.R.V.I.S. Autoreact Cog.""" import re from discord import TextChannel @@ -13,7 +14,9 @@ from jarvis.utils.permissions import admin_or_permissions class AutoReactCog(commands.Cog): - def __init__(self, bot): + """J.A.R.V.I.S. Autoreact Cog.""" + + def __init__(self, bot: commands.Bot): self.bot = bot self.custom_emote = re.compile(r"^<:\w+:(\d+)>$") @@ -31,7 +34,7 @@ class AutoReactCog(commands.Cog): ], ) @admin_or_permissions(manage_guild=True) - async def _autoreact_create(self, ctx: SlashContext, channel: TextChannel): + async def _autoreact_create(self, ctx: SlashContext, channel: TextChannel) -> None: if not isinstance(channel, TextChannel): await ctx.send("Channel must be a text channel", hidden=True) return @@ -62,7 +65,7 @@ class AutoReactCog(commands.Cog): ], ) @admin_or_permissions(manage_guild=True) - async def _autoreact_delete(self, ctx, channel: TextChannel): + async def _autoreact_delete(self, ctx: SlashContext, channel: TextChannel) -> None: exists = Autoreact.objects(guild=ctx.guild.id, channel=channel.id).delete() if exists: await ctx.send(f"Autoreact removed from {channel.mention}") @@ -89,13 +92,13 @@ class AutoReactCog(commands.Cog): ], ) @admin_or_permissions(manage_guild=True) - async def _autoreact_add(self, ctx, channel: TextChannel, emote: str): + async def _autoreact_add(self, ctx: SlashContext, channel: TextChannel, emote: str) -> None: await ctx.defer() custom_emoji = self.custom_emote.match(emote) standard_emoji = emote in emoji_list if not custom_emoji and not standard_emoji: await ctx.send( - "Please use either an emote from this server" + " or a unicode emoji.", + "Please use either an emote from this server or a unicode emoji.", hidden=True, ) return @@ -106,7 +109,7 @@ class AutoReactCog(commands.Cog): return exists = Autoreact.objects(guild=ctx.guild.id, channel=channel.id).first() if not exists: - await ctx.send("Please create autoreact first with " + f"/autoreact create {channel.mention}") + await ctx.send(f"Please create autoreact first with /autoreact create {channel.mention}") return if emote in exists.reactions: await ctx.send( @@ -116,7 +119,7 @@ class AutoReactCog(commands.Cog): return if len(exists.reactions) >= 5: await ctx.send( - "Max number of reactions hit. " + "Remove a different one to add this one", + "Max number of reactions hit. Remove a different one to add this one", hidden=True, ) return @@ -144,11 +147,11 @@ class AutoReactCog(commands.Cog): ], ) @admin_or_permissions(manage_guild=True) - async def _autoreact_remove(self, ctx, channel: TextChannel, emote: str): + async def _autoreact_remove(self, ctx: SlashContext, channel: TextChannel, emote: str) -> None: exists = Autoreact.objects(guild=ctx.guild.id, channel=channel.id).first() if not exists: await ctx.send( - "Please create autoreact first with " + f"/autoreact create {channel.mention}", + f"Please create autoreact first with /autoreact create {channel.mention}", hidden=True, ) return @@ -176,11 +179,11 @@ class AutoReactCog(commands.Cog): ], ) @admin_or_permissions(manage_guild=True) - async def _autoreact_list(self, ctx, channel: TextChannel): + async def _autoreact_list(self, ctx: SlashContext, channel: TextChannel) -> None: exists = Autoreact.objects(guild=ctx.guild.id, channel=channel.id).first() if not exists: await ctx.send( - "Please create autoreact first with " + f"/autoreact create {channel.mention}", + f"Please create autoreact first with /autoreact create {channel.mention}", hidden=True, ) return @@ -192,5 +195,6 @@ class AutoReactCog(commands.Cog): await ctx.send(message) -def setup(bot): +def setup(bot: commands.Bot) -> None: + """Add AutoReactCog to J.A.R.V.I.S.""" bot.add_cog(AutoReactCog(bot)) diff --git a/jarvis/cogs/ctc2.py b/jarvis/cogs/ctc2.py index 9686620..7c410cc 100644 --- a/jarvis/cogs/ctc2.py +++ b/jarvis/cogs/ctc2.py @@ -1,15 +1,14 @@ +"""J.A.R.V.I.S. Complete the Code 2 Cog.""" import re from datetime import datetime from datetime import timedelta import aiohttp -import pymongo from ButtonPaginator import Paginator from discord import Member from discord import User +from discord.commands.ext import Bot from discord.ext import commands -from discord.ext.tasks import loop -from discord.utils import find from discord_slash import cog_ext from discord_slash import SlashContext from discord_slash.model import ButtonStyle @@ -29,11 +28,16 @@ invites = re.compile( class CTCCog(CacheCog): - def __init__(self, bot): + """J.A.R.V.I.S. Complete the Code 2 Cog.""" + + def __init__(self, bot: Bot): super().__init__(bot) self._session = aiohttp.ClientSession() self.url = "https://completethecodetwo.cards/pw" + def __del__(self): + self._session.close() + @cog_ext.cog_subcommand( base="ctc2", name="about", @@ -41,7 +45,7 @@ class CTCCog(CacheCog): guild_ids=guild_ids, ) @commands.cooldown(1, 30, commands.BucketType.channel) - async def _about(self, ctx): + async def _about(self, ctx: SlashContext) -> None: await ctx.send("See https://completethecode.com for more information") @cog_ext.cog_subcommand( @@ -51,22 +55,22 @@ class CTCCog(CacheCog): guild_ids=guild_ids, ) @commands.cooldown(1, 2, commands.BucketType.user) - async def _pw(self, ctx: SlashContext, guess: str): + async def _pw(self, ctx: SlashContext, guess: str) -> None: if len(guess) > 800: await ctx.send( - "Listen here, dipshit. Don't be like " + "<@256110768724901889>. Make your guesses < 800 characters.", + "Listen here, dipshit. Don't be like <@256110768724901889>. Make your guesses < 800 characters.", hidden=True, ) return elif not valid.fullmatch(guess): await ctx.send( - "Listen here, dipshit. Don't be like " + "<@256110768724901889>. Make your guesses *readable*.", + "Listen here, dipshit. Don't be like <@256110768724901889>. Make your guesses *readable*.", hidden=True, ) return elif invites.search(guess): await ctx.send( - "Listen here, dipshit. " + "No using this to bypass sending invite links.", + "Listen here, dipshit. No using this to bypass sending invite links.", hidden=True, ) return @@ -90,12 +94,12 @@ class CTCCog(CacheCog): guild_ids=guild_ids, ) @commands.cooldown(1, 2, commands.BucketType.user) - async def _guesses(self, ctx: SlashContext): + async def _guesses(self, ctx: SlashContext) -> None: exists = self.check_cache(ctx) if exists: await ctx.defer(hidden=True) await ctx.send( - "Please use existing interaction: " + f"{exists['paginator']._message.jump_url}", + f"Please use existing interaction: {exists['paginator']._message.jump_url}", hidden=True, ) return @@ -124,7 +128,7 @@ class CTCCog(CacheCog): embed = build_embed( title="completethecodetwo.cards guesses", description=f"{len(fields)} guesses so far", - fields=fields[i : i + 5], + fields=fields[i : i + 5], # noqa: E203 url="https://completethecodetwo.cards", ) embed.set_thumbnail(url="https://dev.zevaryx.com/db_logo.png") @@ -158,5 +162,6 @@ class CTCCog(CacheCog): await paginator.start() -def setup(bot): +def setup(bot: Bot) -> None: + """Add CTCCog to J.A.R.V.I.S.""" bot.add_cog(CTCCog(bot)) diff --git a/jarvis/cogs/dbrand.py b/jarvis/cogs/dbrand.py index 86af75a..32e835c 100644 --- a/jarvis/cogs/dbrand.py +++ b/jarvis/cogs/dbrand.py @@ -1,8 +1,10 @@ +"""J.A.R.V.I.S. dbrand cog.""" import re import aiohttp from discord.ext import commands from discord_slash import cog_ext +from discord_slash import SlashContext from discord_slash.utils.manage_commands import create_option from jarvis.config import get_config @@ -20,7 +22,7 @@ class DbrandCog(commands.Cog): Mostly support functions. Credit @cpixl for the shipping API """ - def __init__(self, bot): + def __init__(self, bot: commands.Bot): self.bot = bot self.base_url = "https://dbrand.com/" self._session = aiohttp.ClientSession() @@ -28,6 +30,9 @@ class DbrandCog(commands.Cog): self.api_url = get_config().urls["dbrand_shipping"] self.cache = {} + def __del__(self): + self._session.close() + @cog_ext.cog_subcommand( base="db", name="skin", @@ -35,7 +40,7 @@ class DbrandCog(commands.Cog): description="See what skins are available", ) @commands.cooldown(1, 30, commands.BucketType.channel) - async def _skin(self, ctx): + async def _skin(self, ctx: SlashContext) -> None: await ctx.send(self.base_url + "shop/skins") @cog_ext.cog_subcommand( @@ -45,7 +50,7 @@ class DbrandCog(commands.Cog): description="Get some robot camo. Make Tony Stark proud", ) @commands.cooldown(1, 30, commands.BucketType.channel) - async def _camo(self, ctx): + async def _camo(self, ctx: SlashContext) -> None: await ctx.send(self.base_url + "shop/special-edition/robot-camo") @cog_ext.cog_subcommand( @@ -55,7 +60,7 @@ class DbrandCog(commands.Cog): description="See devices with Grip support", ) @commands.cooldown(1, 30, commands.BucketType.channel) - async def _grip(self, ctx): + async def _grip(self, ctx: SlashContext) -> None: await ctx.send(self.base_url + "shop/grip/#grip-devices") @cog_ext.cog_subcommand( @@ -65,7 +70,7 @@ class DbrandCog(commands.Cog): description="Contact support", ) @commands.cooldown(1, 30, commands.BucketType.channel) - async def _contact(self, ctx): + async def _contact(self, ctx: SlashContext) -> None: await ctx.send("Contact dbrand support here: " + self.base_url + "contact") @cog_ext.cog_subcommand( @@ -75,7 +80,7 @@ class DbrandCog(commands.Cog): description="Contact support", ) @commands.cooldown(1, 30, commands.BucketType.channel) - async def _support(self, ctx): + async def _support(self, ctx: SlashContext) -> None: await ctx.send("Contact dbrand support here: " + self.base_url + "contact") @cog_ext.cog_subcommand( @@ -85,7 +90,7 @@ class DbrandCog(commands.Cog): description="Get your order status", ) @commands.cooldown(1, 30, commands.BucketType.channel) - async def _orderstat(self, ctx): + async def _orderstat(self, ctx: SlashContext) -> None: await ctx.send(self.base_url + "order-status") @cog_ext.cog_subcommand( @@ -95,7 +100,7 @@ class DbrandCog(commands.Cog): description="Get your order status", ) @commands.cooldown(1, 30, commands.BucketType.channel) - async def _orders(self, ctx): + async def _orders(self, ctx: SlashContext) -> None: await ctx.send(self.base_url + "order-status") @cog_ext.cog_subcommand( @@ -105,7 +110,7 @@ class DbrandCog(commands.Cog): description="dbrand status", ) @commands.cooldown(1, 30, commands.BucketType.channel) - async def _status(self, ctx): + async def _status(self, ctx: SlashContext) -> None: await ctx.send(self.base_url + "status") @cog_ext.cog_subcommand( @@ -115,7 +120,7 @@ class DbrandCog(commands.Cog): description="Give us your money!", ) @commands.cooldown(1, 30, commands.BucketType.channel) - async def _buy(self, ctx): + async def _buy(self, ctx: SlashContext) -> None: await ctx.send("Give us your money! " + self.base_url + "shop") @cog_ext.cog_subcommand( @@ -125,7 +130,7 @@ class DbrandCog(commands.Cog): description="(not) extortion", ) @commands.cooldown(1, 30, commands.BucketType.channel) - async def _extort(self, ctx): + async def _extort(self, ctx: SlashContext) -> None: await ctx.send("Be (not) extorted here: " + self.base_url + "not-extortion") @cog_ext.cog_subcommand( @@ -135,7 +140,7 @@ class DbrandCog(commands.Cog): guild_ids=guild_ids, ) @commands.cooldown(1, 30, commands.BucketType.channel) - async def _wallpapers(self, ctx): + async def _wallpapers(self, ctx: SlashContext) -> None: await ctx.send("Get robot camo wallpapers here: https://db.io/wallpapers") @cog_ext.cog_subcommand( @@ -147,7 +152,7 @@ class DbrandCog(commands.Cog): ( create_option( name="search", - description="Country search query (2 character code, " + "country name, emoji)", + description="Country search query (2 character code, country name, emoji)", option_type=3, required=True, ) @@ -155,7 +160,7 @@ class DbrandCog(commands.Cog): ], ) @commands.cooldown(1, 2, commands.BucketType.user) - async def _shipping(self, ctx, *, search: str): + async def _shipping(self, ctx: SlashContext, search: str) -> None: await ctx.defer() if not re.match(r"^[A-Z- ]+$", search, re.IGNORECASE): if re.match( @@ -203,10 +208,12 @@ class DbrandCog(commands.Cog): ) country = "-".join(x for x in data["country"].split(" ") if x != "the") country_urlsafe = country.replace("-", "%20") - description = "Click the link above to " + "see shipping time to {data['country']}." - description += "\n[View all shipping destinations]" + "(https://dbrand.com/shipping)" - description += " | [Check shipping status]" - description += "(https://dbrand.com/status" + f"#main-content:~:text={country_urlsafe})" + description = ( + f"Click the link above to see shipping time to {data['country']}." + "\n[View all shipping destinations](https://dbrand.com/shipping)" + " | [Check shipping status]" + f"(https://dbrand.com/status#main-content:~:text={country_urlsafe})" + ) embed = build_embed( title="Shipping to {}".format(data["country"]), description=description, @@ -223,8 +230,9 @@ class DbrandCog(commands.Cog): elif not data["is_valid"]: embed = build_embed( title="Check Shipping Times", - description="Country not found.\nYou can [view all shipping " - + "destinations here](https://dbrand.com/shipping)", + description=( + "Country not found.\nYou can [view all shipping " "destinations here](https://dbrand.com/shipping)" + ), fields=[], url="https://dbrand.com/shipping", color="#FFBB00", @@ -238,9 +246,11 @@ class DbrandCog(commands.Cog): elif not data["shipping_available"]: embed = build_embed( title="Shipping to {}".format(data["country"]), - description="No shipping available.\nTime to move to a country" - + " that has shipping available.\nYou can [find a new country " - + "to live in here](https://dbrand.com/shipping)", + description=( + "No shipping available.\nTime to move to a country" + " that has shipping available.\nYou can [find a new country " + "to live in here](https://dbrand.com/shipping)" + ), fields=[], url="https://dbrand.com/shipping", color="#FFBB00", @@ -253,5 +263,6 @@ class DbrandCog(commands.Cog): await ctx.send(embed=embed) -def setup(bot): +def setup(bot: commands.Bot) -> None: + """Add dbrandcog to J.A.R.V.I.S.""" bot.add_cog(DbrandCog(bot)) diff --git a/jarvis/cogs/dev.py b/jarvis/cogs/dev.py index 85b394a..13a18ff 100644 --- a/jarvis/cogs/dev.py +++ b/jarvis/cogs/dev.py @@ -1,13 +1,17 @@ +"""J.A.R.V.I.S. Developer Cog.""" import base64 import hashlib import re -import subprocess +import subprocess # noqa: S404 import uuid as uuidpy +from typing import Any +from typing import Union import ulid as ulidpy from bson import ObjectId from discord.ext import commands from discord_slash import cog_ext +from discord_slash import SlashContext from discord_slash.utils.manage_commands import create_choice from discord_slash.utils.manage_commands import create_option @@ -18,13 +22,13 @@ from jarvis.utils.field import Field supported_hashes = {x for x in hashlib.algorithms_guaranteed if "shake" not in x} OID_VERIFY = re.compile(r"^([1-9][0-9]{0,3}|0)(\.([1-9][0-9]{0,3}|0)){5,13}$") -URL_VERIFY = re.compile(r"http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]" + r"|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+") +URL_VERIFY = re.compile(r"http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+") DN_VERIFY = re.compile( - r"^(?:(?PCN=(?P[^,]*)),)" + r"?(?:(?P(?:(?:CN|OU)=[^,]+,?)+),)" + r"?(?P(?:DC=[^,]+,?)+)$" + r"^(?:(?PCN=(?P[^,]*)),)?(?:(?P(?:(?:CN|OU)=[^,]+,?)+),)?(?P(?:DC=[^,]+,?)+)$" ) ULID_VERIFY = re.compile(r"^[0-9a-z]{26}$", re.IGNORECASE) UUID_VERIFY = re.compile( - ("[a-f0-9]{8}-" + "[a-f0-9]{4}-" + "[1-5]" + "[a-f0-9]{3}-" + "[89ab][a-f0-9]{3}-" + "[a-f0-9]{12}$"), + r"[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$", re.IGNORECASE, ) @@ -36,9 +40,8 @@ invites = re.compile( UUID_GET = {3: uuidpy.uuid3, 5: uuidpy.uuid5} -def hash_obj(hash, data, text: bool = True) -> str: - """ - Hash data with hash object +def hash_obj(hash: Any, data: Union[str, bytes], text: bool = True) -> str: + """Hash data with hash object. Data can be text or binary """ @@ -48,14 +51,16 @@ def hash_obj(hash, data, text: bool = True) -> str: BSIZE = 65536 block_idx = 0 while block_idx * BSIZE < len(data): - block = data[BSIZE * block_idx : BSIZE * (block_idx + 1)] + block = data[BSIZE * block_idx : BSIZE * (block_idx + 1)] # noqa: E203 hash.update(block) block_idx += 1 return hash.hexdigest() class DevCog(commands.Cog): - def __init__(self, bot): + """J.A.R.V.I.S. Developer Cog.""" + + def __init__(self, bot: commands.Bot): self.bot = bot @cog_ext.cog_slash( @@ -78,7 +83,7 @@ class DevCog(commands.Cog): ], ) @commands.cooldown(1, 2, commands.BucketType.user) - async def _hash(self, ctx, method: str, data: str): + async def _hash(self, ctx: SlashContext, method: str, data: str) -> None: if not data: await ctx.send( "No data to hash", @@ -119,7 +124,7 @@ class DevCog(commands.Cog): ), ], ) - async def _uuid(self, ctx, version: str, data: str = None): + async def _uuid(self, ctx: SlashContext, version: str, data: str = None) -> None: version = int(version) if version in [3, 5] and not data: await ctx.send(f"UUID{version} requires data.", hidden=True) @@ -143,8 +148,7 @@ class DevCog(commands.Cog): description="Generate an ObjectID", ) @commands.cooldown(1, 2, commands.BucketType.user) - async def _objectid(self, ctx): - """Generates new bson.ObjectId""" + async def _objectid(self, ctx: SlashContext) -> None: await ctx.send(f"ObjectId: `{str(ObjectId())}`") @cog_ext.cog_slash( @@ -152,8 +156,7 @@ class DevCog(commands.Cog): description="Generate a ULID", ) @commands.cooldown(1, 2, commands.BucketType.user) - async def _ulid(self, ctx): - """Generates a new ULID""" + async def _ulid(self, ctx: SlashContext) -> None: await ctx.send(f"ULID: `{ulidpy.new().str}`") @cog_ext.cog_slash( @@ -161,8 +164,7 @@ class DevCog(commands.Cog): description="Convert a UUID to a ULID", ) @commands.cooldown(1, 2, commands.BucketType.user) - async def _uuid2ulid(self, ctx: commands.Context, uuid): - """Converts a UUID to a ULID""" + async def _uuid2ulid(self, ctx: SlashContext, uuid: str) -> None: if UUID_VERIFY.match(uuid): u = ulidpy.parse(uuid) await ctx.send(f"ULID: `{u.str}`") @@ -174,8 +176,7 @@ class DevCog(commands.Cog): description="Convert a ULID to a UUID", ) @commands.cooldown(1, 2, commands.BucketType.user) - async def _ulid2uuid(self, ctx: commands.Context, ulid): - """Converts a ULID to a UUID""" + async def _ulid2uuid(self, ctx: SlashContext, ulid: str) -> None: if ULID_VERIFY.match(ulid): ulid = ulidpy.parse(ulid) await ctx.send(f"UUID: `{ulid.uuid}`") @@ -203,8 +204,7 @@ class DevCog(commands.Cog): ), ], ) - async def _encode(self, ctx, method: str, data: str): - """Encodes text with specified encoding method""" + async def _encode(self, ctx: SlashContext, method: str, data: str) -> None: method = getattr(base64, method + "encode") await ctx.send(f"`{method(data.encode('UTF-8')).decode('UTF-8')}`") @@ -227,8 +227,7 @@ class DevCog(commands.Cog): ), ], ) - async def _decode(self, ctx, method: str, data: str): - """Decodes text with specified encoding method""" + async def _decode(self, ctx: SlashContext, method: str, data: str) -> None: method = getattr(base64, method + "decode") decoded = method(data.encode("UTF-8")).decode("UTF-8") if invites.search(decoded): @@ -244,10 +243,11 @@ class DevCog(commands.Cog): description="Get J.A.R.V.I.S. lines of code", ) @commands.cooldown(1, 30, commands.BucketType.channel) - async def _cloc(self, ctx): - output = subprocess.check_output(["tokei", "-C", "--sort", "code"]).decode("UTF-8") + async def _cloc(self, ctx: SlashContext) -> None: + output = subprocess.check_output(["tokei", "-C", "--sort", "code"]).decode("UTF-8") # noqa: S603, S607 await ctx.send(f"```\n{output}\n```") -def setup(bot): +def setup(bot: commands.Bot) -> None: + """Add DevCog to J.A.R.V.I.S.""" bot.add_cog(DevCog(bot)) diff --git a/jarvis/cogs/error.py b/jarvis/cogs/error.py index cc78a74..86234ad 100644 --- a/jarvis/cogs/error.py +++ b/jarvis/cogs/error.py @@ -1,3 +1,4 @@ +"""J.A.R.V.I.S. error handling cog.""" from discord.ext import commands from discord_slash import SlashContext @@ -5,11 +6,14 @@ from jarvis import slash class ErrorHandlerCog(commands.Cog): - def __init__(self, bot): + """J.A.R.V.I.S. error handling cog.""" + + def __init__(self, bot: commands.Bot): self.bot = bot @commands.Cog.listener() - async def on_command_error(self, ctx: commands.Context, error): + async def on_command_error(self, ctx: commands.Context, error: Exception) -> None: + """d.py on_command_error override.""" if isinstance(error, commands.errors.MissingPermissions): await ctx.send("I'm afraid I can't let you do that.") elif isinstance(error, commands.errors.CommandNotFound): @@ -23,7 +27,8 @@ class ErrorHandlerCog(commands.Cog): ctx.command.reset_cooldown(ctx) @commands.Cog.listener() - async def on_slash_command_error(self, ctx: SlashContext, error): + async def on_slash_command_error(self, ctx: SlashContext, error: Exception) -> None: + """discord_slash on_slash_command_error override.""" if isinstance(error, commands.errors.MissingPermissions) or isinstance(error, commands.errors.CheckFailure): await ctx.send("I'm afraid I can't let you do that.", hidden=True) elif isinstance(error, commands.errors.CommandNotFound): @@ -41,5 +46,6 @@ class ErrorHandlerCog(commands.Cog): slash.commands[ctx.command].reset_cooldown(ctx) -def setup(bot): +def setup(bot: commands.Bot) -> None: + """Add ErrorHandlerCog to J.A.R.V.I.S.""" bot.add_cog(ErrorHandlerCog(bot)) diff --git a/jarvis/cogs/gitlab.py b/jarvis/cogs/gitlab.py index 920be8e..2765e7d 100644 --- a/jarvis/cogs/gitlab.py +++ b/jarvis/cogs/gitlab.py @@ -1,11 +1,11 @@ +"""J.A.R.V.I.S. GitLab Cog.""" from datetime import datetime from datetime import timedelta import gitlab from ButtonPaginator import Paginator +from discord import Embed from discord.ext import commands -from discord.ext.tasks import loop -from discord.utils import find from discord_slash import cog_ext from discord_slash import SlashContext from discord_slash.model import ButtonStyle @@ -21,7 +21,9 @@ guild_ids = [862402786116763668] class GitlabCog(CacheCog): - def __init__(self, bot): + """J.A.R.V.I.S. GitLab Cog.""" + + def __init__(self, bot: commands.Bot): super().__init__(bot) config = get_config() self._gitlab = gitlab.Gitlab("https://git.zevaryx.com", private_token=config.gitlab_token) @@ -35,7 +37,7 @@ class GitlabCog(CacheCog): guild_ids=guild_ids, options=[create_option(name="id", description="Issue ID", option_type=4, required=True)], ) - async def _issue(self, ctx: SlashContext, id: int): + async def _issue(self, ctx: SlashContext, id: int) -> None: try: issue = self.project.issues.get(int(id)) except gitlab.exceptions.GitlabGetError: @@ -69,7 +71,7 @@ class GitlabCog(CacheCog): fields.append( Field( name="Milestone", - value=f"[{issue.milestone['title']}]" + f"({issue.milestone['web_url']})", + value=f"[{issue.milestone['title']}]({issue.milestone['web_url']})", inline=False, ) ) @@ -87,7 +89,7 @@ class GitlabCog(CacheCog): icon_url=issue.author["avatar_url"], url=issue.author["web_url"], ) - embed.set_thumbnail(url="https://about.gitlab.com/images" + "/press/logo/png/gitlab-icon-rgb.png") + embed.set_thumbnail(url="https://about.gitlab.com/images/press/logo/png/gitlab-icon-rgb.png") await ctx.send(embed=embed) @cog_ext.cog_subcommand( @@ -104,7 +106,7 @@ class GitlabCog(CacheCog): ) ], ) - async def _milestone(self, ctx: SlashContext, id: int): + async def _milestone(self, ctx: SlashContext, id: int) -> None: try: milestone = self.project.milestones.get(int(id)) except gitlab.exceptions.GitlabGetError: @@ -142,9 +144,9 @@ class GitlabCog(CacheCog): embed.set_author( name="J.A.R.V.I.S.", url="https://git.zevaryx.com/jarvis", - icon_url="https://git.zevaryx.com/uploads/-" + "/system/user/avatar/11/avatar.png", + icon_url="https://git.zevaryx.com/uploads/-/system/user/avatar/11/avatar.png", ) - embed.set_thumbnail(url="https://about.gitlab.com/images" + "/press/logo/png/gitlab-icon-rgb.png") + embed.set_thumbnail(url="https://about.gitlab.com/images/press/logo/png/gitlab-icon-rgb.png") await ctx.send(embed=embed) @cog_ext.cog_subcommand( @@ -161,7 +163,7 @@ class GitlabCog(CacheCog): ) ], ) - async def _mergerequest(self, ctx: SlashContext, id: int): + async def _mergerequest(self, ctx: SlashContext, id: int) -> None: try: mr = self.project.mergerequests.get(int(id)) except gitlab.exceptions.GitlabGetError: @@ -201,7 +203,7 @@ class GitlabCog(CacheCog): fields.append( Field( name="Milestone", - value=f"[{mr.milestone['title']}]" + f"({mr.milestone['web_url']})", + value=f"[{mr.milestone['title']}]({mr.milestone['web_url']})", inline=False, ) ) @@ -219,10 +221,11 @@ class GitlabCog(CacheCog): icon_url=mr.author["avatar_url"], url=mr.author["web_url"], ) - embed.set_thumbnail(url="https://about.gitlab.com/images" + "/press/logo/png/gitlab-icon-rgb.png") + embed.set_thumbnail(url="https://about.gitlab.com/images/press/logo/png/gitlab-icon-rgb.png") await ctx.send(embed=embed) - def build_embed_page(self, api_list: list, t_state: str, name: str): + def build_embed_page(self, api_list: list, t_state: str, name: str) -> Embed: + """Build an embed page for the paginator.""" title = "" if t_state: title = f"{t_state} " @@ -241,14 +244,14 @@ class GitlabCog(CacheCog): title=title, description="", fields=fields, - url="https://git.zevaryx.com/stark-industries/j.a.r.v.i.s./" + f"{name.replace(' ', '_')}s", + url=f"https://git.zevaryx.com/stark-industries/j.a.r.v.i.s./{name.replace(' ', '_')}s", ) embed.set_author( name="J.A.R.V.I.S.", url="https://git.zevaryx.com/jarvis", - icon_url="https://git.zevaryx.com/uploads/-" + "/system/user/avatar/11/avatar.png", + icon_url="https://git.zevaryx.com/uploads/-/system/user/avatar/11/avatar.png", ) - embed.set_thumbnail(url="https://about.gitlab.com/images" + "/press/logo/png/gitlab-icon-rgb.png") + embed.set_thumbnail(url="https://about.gitlab.com/images/press/logo/png/gitlab-icon-rgb.png") return embed @cog_ext.cog_subcommand( @@ -270,7 +273,7 @@ class GitlabCog(CacheCog): ) ], ) - async def _issues(self, ctx: SlashContext, state: str = "opened"): + async def _issues(self, ctx: SlashContext, state: str = "opened") -> None: exists = self.check_cache(ctx, state=state) if exists: await ctx.defer(hidden=True) @@ -311,7 +314,7 @@ class GitlabCog(CacheCog): pages = [] t_state = t_state[0].upper() + t_state[1:] for i in range(0, len(issues), 5): - pages.append(self.build_embed_page(issues[i : i + 5], t_state=t_state, name="issue")) + pages.append(self.build_embed_page(issues[i : i + 5], t_state=t_state, name="issue")) # noqa: E203 paginator = Paginator( bot=self.bot, @@ -357,7 +360,7 @@ class GitlabCog(CacheCog): ) ], ) - async def _mergerequests(self, ctx: SlashContext, state: str = "opened"): + async def _mergerequests(self, ctx: SlashContext, state: str = "opened") -> None: exists = self.check_cache(ctx, state=state) if exists: await ctx.defer(hidden=True) @@ -398,7 +401,7 @@ class GitlabCog(CacheCog): pages = [] t_state = t_state[0].upper() + t_state[1:] for i in range(0, len(merges), 5): - pages.append(self.build_embed_page(merges[i : i + 5], t_state=t_state, name="merge request")) + pages.append(self.build_embed_page(merges[i : i + 5], t_state=t_state, name="merge request")) # noqa: E203 paginator = Paginator( bot=self.bot, @@ -430,12 +433,12 @@ class GitlabCog(CacheCog): description="Get open issues from GitLab", guild_ids=guild_ids, ) - async def _milestones(self, ctx: SlashContext): + async def _milestones(self, ctx: SlashContext) -> None: exists = self.check_cache(ctx) if exists: await ctx.defer(hidden=True) await ctx.send( - "Please use existing interaction: " + f"{exists['paginator']._message.jump_url}", + f"Please use existing interaction: {exists['paginator']._message.jump_url}", hidden=True, ) return @@ -463,7 +466,7 @@ class GitlabCog(CacheCog): pages = [] for i in range(0, len(milestones), 5): - pages.append(self.build_embed_page(milestones[i : i + 5], t_state=None, name="milestone")) + pages.append(self.build_embed_page(milestones[i : i + 5], t_state=None, name="milestone")) # noqa: E203 paginator = Paginator( bot=self.bot, @@ -489,6 +492,7 @@ class GitlabCog(CacheCog): await paginator.start() -def setup(bot): +def setup(bot: commands.Bot) -> None: + """Add GitlabCog to J.A.R.V.I.S. if Gitlab token exists.""" if get_config().gitlab_token: bot.add_cog(GitlabCog(bot)) diff --git a/jarvis/cogs/image.py b/jarvis/cogs/image.py index 5c9c4f1..ac1a2d1 100644 --- a/jarvis/cogs/image.py +++ b/jarvis/cogs/image.py @@ -1,3 +1,4 @@ +"""J.A.R.V.I.S. image processing cog.""" import re from io import BytesIO @@ -20,19 +21,22 @@ class ImageCog(commands.Cog): May be categorized under util later """ - def __init__(self, bot): + def __init__(self, bot: commands.Bot): self.bot = bot self._session = aiohttp.ClientSession() self.tgt_match = re.compile(r"([0-9]*\.?[0-9]*?) ?([KMGTP]?B)", re.IGNORECASE) - async def _resize(self, ctx, target: str, url: str = None): + def __del__(self): + self._session.close() + + async def _resize(self, ctx: commands.Context, target: str, url: str = None) -> None: if not target: await ctx.send("Missing target size, i.e. 200KB.") return tgt = self.tgt_match.match(target) if not tgt: - await ctx.send("Invalid target format ({}).".format(target) + " Expected format like 200KB") + await ctx.send(f"Invalid target format ({target}). Expected format like 200KB") return tgt_size = unconvert_bytesize(float(tgt.groups()[0]), tgt.groups()[1]) @@ -95,9 +99,10 @@ class ImageCog(commands.Cog): @commands.command(name="resize", help="Resize an image") @commands.cooldown(1, 60, commands.BucketType.user) - async def _resize_pref(self, ctx, target: str, url: str = None): + async def _resize_pref(self, ctx: commands.Context, target: str, url: str = None) -> None: await self._resize(ctx, target, url) -def setup(bot): +def setup(bot: commands.Bot) -> None: + """Add ImageCog to J.A.R.V.I.S.""" bot.add_cog(ImageCog(bot)) diff --git a/jarvis/cogs/jokes.py b/jarvis/cogs/jokes.py index eca3898..b1be215 100644 --- a/jarvis/cogs/jokes.py +++ b/jarvis/cogs/jokes.py @@ -1,8 +1,9 @@ +"""J.A.R.V.I.S. Jokes module.""" import html import re import traceback from datetime import datetime -from random import randint +from secrets import randint from discord.ext import commands from discord_slash import cog_ext @@ -20,7 +21,7 @@ class JokeCog(commands.Cog): May adapt over time to create jokes using machine learning """ - def __init__(self, bot): + def __init__(self, bot: commands.Bot): self.bot = bot # TODO: Make this a command group with subcommands @@ -29,8 +30,8 @@ class JokeCog(commands.Cog): description="Hear a joke", ) @commands.cooldown(1, 10, commands.BucketType.channel) - async def _joke(self, ctx: SlashContext, id: str = None): - """Get a joke from the database""" + async def _joke(self, ctx: SlashContext, id: str = None) -> None: + """Get a joke from the database.""" try: if randint(1, 100_000) == 5779 and id is None: await ctx.send(f"<@{ctx.message.author.id}>") @@ -109,5 +110,6 @@ class JokeCog(commands.Cog): # await ctx.send(f"**{result['title']}**\n\n{result['body']}") -def setup(bot): +def setup(bot: commands.Bot) -> None: + """Add JokeCog to J.A.R.V.I.S.""" bot.add_cog(JokeCog(bot)) diff --git a/jarvis/cogs/modlog/__init__.py b/jarvis/cogs/modlog/__init__.py index 0d7f9d4..8816800 100644 --- a/jarvis/cogs/modlog/__init__.py +++ b/jarvis/cogs/modlog/__init__.py @@ -1,9 +1,13 @@ +"""J.A.R.V.I.S. Modlog Cogs.""" +from discord.ext.commands import Bot + from jarvis.cogs.modlog import command from jarvis.cogs.modlog import member from jarvis.cogs.modlog import message -def setup(bot): +def setup(bot: Bot) -> None: + """Add modlog cogs to J.A.R.V.I.S.""" bot.add_cog(command.ModlogCommandCog(bot)) bot.add_cog(member.ModlogMemberCog(bot)) bot.add_cog(message.ModlogMessageCog(bot)) diff --git a/jarvis/cogs/modlog/command.py b/jarvis/cogs/modlog/command.py index c25cb8c..e5ccd4b 100644 --- a/jarvis/cogs/modlog/command.py +++ b/jarvis/cogs/modlog/command.py @@ -1,3 +1,4 @@ +"""J.A.R.V.I.S. ModlogCommandCog.""" from discord import DMChannel from discord.ext import commands from discord_slash import SlashContext @@ -8,11 +9,14 @@ from jarvis.utils.field import Field class ModlogCommandCog(commands.Cog): - def __init__(self, bot): + """J.A.R.V.I.S. ModlogCommandCog.""" + + def __init__(self, bot: commands.Bot): self.bot = bot @commands.Cog.listener() - async def on_slash_command(self, ctx: SlashContext): + async def on_slash_command(self, ctx: SlashContext) -> None: + """Process on_slash_command events.""" if not isinstance(ctx.channel, DMChannel) and ctx.name not in ["pw"]: modlog = Setting.objects(guild=ctx.guild.id, setting="modlog").first() if modlog: @@ -41,5 +45,5 @@ class ModlogCommandCog(commands.Cog): name=ctx.author.name, icon_url=ctx.author.avatar_url, ) - embed.set_footer(text=f"{ctx.author.name}#{ctx.author.discriminator}" + f" | {ctx.author.id}") + embed.set_footer(text=f"{ctx.author.name}#{ctx.author.discriminator} | {ctx.author.id}") await channel.send(embed=embed) diff --git a/jarvis/cogs/modlog/member.py b/jarvis/cogs/modlog/member.py index 1f641e6..8d41352 100644 --- a/jarvis/cogs/modlog/member.py +++ b/jarvis/cogs/modlog/member.py @@ -1,3 +1,4 @@ +"""J.A.R.V.I.S. ModlogMemberCog.""" import asyncio from datetime import datetime from datetime import timedelta @@ -19,12 +20,15 @@ from jarvis.utils.field import Field class ModlogMemberCog(commands.Cog): - def __init__(self, bot): + """J.A.R.V.I.S. ModlogMemberCog.""" + + def __init__(self, bot: commands.Bot): self.bot = bot self.cache = [] @commands.Cog.listener() - async def on_member_ban(self, guild: discord.Guild, user: discord.User): + async def on_member_ban(self, guild: discord.Guild, user: discord.User) -> None: + """Process on_member_ban events.""" modlog = Setting.objects(guild=guild.id, setting="modlog").first() if modlog: channel = guild.get_channel(modlog.value) @@ -61,7 +65,8 @@ class ModlogMemberCog(commands.Cog): await channel.send(embed=embed) @commands.Cog.listener() - async def on_member_unban(self, guild: discord.Guild, user: discord.User): + async def on_member_unban(self, guild: discord.Guild, user: discord.User) -> None: + """Process on_member_unban events.""" modlog = Setting.objects(guild=guild.id, setting="modlog").first() if modlog: channel = guild.get_channel(modlog.value) @@ -96,7 +101,8 @@ class ModlogMemberCog(commands.Cog): await channel.send(embed=embed) @commands.Cog.listener() - async def on_member_remove(self, user: discord.Member): + async def on_member_remove(self, user: discord.Member) -> None: + """Process on_member_remove events.""" modlog = Setting.objects(guild=user.guild.id, setting="modlog").first() if modlog: channel = user.guild.get_channel(modlog.value) @@ -140,7 +146,8 @@ class ModlogMemberCog(commands.Cog): await channel.send(embed=embed) - async def process_mute(self, before, after) -> discord.Embed: + async def process_mute(self, before: discord.Member, after: discord.Member) -> discord.Embed: + """Process mute event.""" await asyncio.sleep(0.5) # Need to wait for audit log auditlog = await before.guild.audit_logs( limit=50, @@ -171,7 +178,8 @@ class ModlogMemberCog(commands.Cog): desc=f"{before.mention} was muted", ) - async def process_unmute(self, before, after) -> discord.Embed: + async def process_unmute(self, before: discord.Member, after: discord.Member) -> discord.Embed: + """Process unmute event.""" await asyncio.sleep(0.5) # Need to wait for audit log auditlog = await before.guild.audit_logs( limit=50, @@ -202,7 +210,8 @@ class ModlogMemberCog(commands.Cog): desc=f"{before.mention} was muted", ) - async def process_verify(self, before, after) -> discord.Embed: + async def process_verify(self, before: discord.Member, after: discord.Member) -> discord.Embed: + """Process verification event.""" await asyncio.sleep(0.5) # Need to wait for audit log auditlog = await before.guild.audit_logs( limit=50, @@ -220,7 +229,8 @@ class ModlogMemberCog(commands.Cog): desc=f"{before.mention} was verified", ) - async def process_rolechange(self, before, after) -> discord.Embed: + async def process_rolechange(self, before: discord.Member, after: discord.Member) -> discord.Embed: + """Process rolechange event.""" await asyncio.sleep(0.5) # Need to wait for audit log auditlog = await before.guild.audit_logs( limit=50, @@ -249,7 +259,11 @@ class ModlogMemberCog(commands.Cog): ) @commands.Cog.listener() - async def on_member_update(self, before: discord.Member, after: discord.Member): + async def on_member_update(self, before: discord.Member, after: discord.Member) -> None: + """Process on_member_update events. + + Caches events due to double-send bug + """ h = hash(hash(before) * hash(after)) if h not in self.cache: self.cache.append(h) @@ -287,18 +301,18 @@ class ModlogMemberCog(commands.Cog): fields = [ Field( name="Before", - value=f"{bname} ({before.name}" + f"#{before.discriminator})", + value=f"{bname} ({before.name}#{before.discriminator})", ), Field( name="After", - value=f"{aname} ({after.name}" + f"#{after.discriminator})", + value=f"{aname} ({after.name}#{after.discriminator})", ), ] if log.user.id != before.id: fields.append( Field( name="Moderator", - value=f"{log.user.mention} ({log.user.name}" + f"#{log.user.discriminator})", + value=f"{log.user.mention} ({log.user.name}#{log.user.discriminator})", ) ) if log.reason: diff --git a/jarvis/cogs/modlog/message.py b/jarvis/cogs/modlog/message.py index be5e81a..8c779a6 100644 --- a/jarvis/cogs/modlog/message.py +++ b/jarvis/cogs/modlog/message.py @@ -1,3 +1,4 @@ +"""J.A.R.V.I.S. ModlogMessageCog.""" import discord from discord.ext import commands @@ -7,11 +8,14 @@ from jarvis.utils.field import Field class ModlogMessageCog(commands.Cog): - def __init__(self, bot): + """J.A.R.V.I.S. ModlogMessageCog.""" + + def __init__(self, bot: commands.Bot): self.bot = bot @commands.Cog.listener() - async def on_message_edit(self, before: discord.Message, after: discord.Message): + async def on_message_edit(self, before: discord.Message, after: discord.Message) -> None: + """Process on_message_edit events.""" if not before.author.bot: modlog = Setting.objects(guild=after.guild.id, setting="modlog").first() if modlog: @@ -43,11 +47,12 @@ class ModlogMessageCog(commands.Cog): icon_url=before.author.avatar_url, url=after.jump_url, ) - embed.set_footer(text=f"{before.author.name}#{before.author.discriminator}" + f" | {before.author.id}") + embed.set_footer(text=f"{before.author.name}#{before.author.discriminator} | {before.author.id}") await channel.send(embed=embed) @commands.Cog.listener() - async def on_message_delete(self, message: discord.Message): + async def on_message_delete(self, message: discord.Message) -> None: + """Process on_message_delete events.""" modlog = Setting.objects(guild=message.guild.id, setting="modlog").first() if modlog: fields = [Field("Original Message", message.content or "N/A", False)] @@ -95,5 +100,5 @@ class ModlogMessageCog(commands.Cog): icon_url=message.author.avatar_url, url=message.jump_url, ) - embed.set_footer(text=f"{message.author.name}#{message.author.discriminator}" + f" | {message.author.id}") + embed.set_footer(text=f"{message.author.name}#{message.author.discriminator} | {message.author.id}") await channel.send(embed=embed) diff --git a/jarvis/cogs/modlog/utils.py b/jarvis/cogs/modlog/utils.py index 43273be..0742e0d 100644 --- a/jarvis/cogs/modlog/utils.py +++ b/jarvis/cogs/modlog/utils.py @@ -1,7 +1,11 @@ +"""J.A.R.V.I.S. Modlog Cog Utilities.""" from datetime import datetime from datetime import timedelta +from typing import List import discord +from discord import AuditLogEntry +from discord import Member from discord.utils import find from jarvis.utils import build_embed @@ -15,10 +19,11 @@ def modlog_embed( title: str, desc: str, ) -> discord.Embed: + """Get modlog embed.""" fields = [ Field( name="Moderator", - value=f"{admin.mention} ({admin.name}" + f"#{admin.discriminator})", + value=f"{admin.mention} ({admin.name}#{admin.discriminator})", ), ] if log.reason: @@ -38,7 +43,8 @@ def modlog_embed( return embed -def get_latest_log(auditlog, target): +def get_latest_log(auditlog: List[AuditLogEntry], target: Member) -> AuditLogEntry: + """Filter AuditLog to get latest entry.""" before = datetime.utcnow() - timedelta(seconds=10) return find( lambda x: x.target.id == target.id and x.created_at > before, diff --git a/jarvis/cogs/owner.py b/jarvis/cogs/owner.py index ff063dc..55e1b83 100644 --- a/jarvis/cogs/owner.py +++ b/jarvis/cogs/owner.py @@ -1,8 +1,10 @@ +"""J.A.R.V.I.S. Owner Cog.""" import os import sys import traceback from inspect import getsource from time import time +from typing import Any import discord from discord import DMChannel @@ -18,18 +20,18 @@ from jarvis.utils.permissions import user_is_bot_admin class OwnerCog(commands.Cog): """ - J.A.R.V.I.S. management cog + J.A.R.V.I.S. management cog. Used by admins to control core J.A.R.V.I.S. systems """ - def __init__(self, bot): + def __init__(self, bot: commands.Cog): self.bot = bot self.admins = Config.objects(key="admins").first() @commands.command(name="load", hidden=True) @user_is_bot_admin() - async def _load_cog(self, ctx, *, cog: str): + async def _load_cog(self, ctx: commands.Context, *, cog: str) -> None: info = await self.bot.application_info() if ctx.message.author == info.owner or ctx.message.author.id in self.admins.value: try: @@ -47,7 +49,7 @@ class OwnerCog(commands.Cog): @commands.command(name="unload", hidden=True) @user_is_bot_admin() - async def _unload_cog(self, ctx, *, cog: str): + async def _unload_cog(self, ctx: commands.Context, *, cog: str) -> None: if cog in ["jarvis.cogs.owner", "owner"]: await ctx.send("Cannot unload `owner` cog") return @@ -68,7 +70,7 @@ class OwnerCog(commands.Cog): @commands.command(name="reload", hidden=True) @user_is_bot_admin() - async def _cog_reload(self, ctx, *, cog: str): + async def _cog_reload(self, ctx: commands.Context, *, cog: str) -> None: if cog in ["jarvis.cogs.owner", "owner"]: await ctx.send("Cannot reload `owner` cog") return @@ -91,13 +93,13 @@ class OwnerCog(commands.Cog): @commands.group(name="system", hidden=True, pass_context=True) @user_is_bot_admin() - async def _system(self, ctx): + async def _system(self, ctx: commands.Context) -> None: if ctx.invoked_subcommand is None: await ctx.send("Usage: `system `\n" + "Subcommands: `restart`, `update`") @_system.command(name="restart", hidden=True) @user_is_bot_admin() - async def _restart(self, ctx): + async def _restart(self, ctx: commands.Context) -> None: info = await self.bot.application_info() if ctx.message.author == info.owner or ctx.message.author.id in self.admins.value: await ctx.send("Restarting core systems...") @@ -117,7 +119,7 @@ class OwnerCog(commands.Cog): @_system.command(name="update", hidden=True) @user_is_bot_admin() - async def _update(self, ctx): + async def _update(self, ctx: commands.Context) -> None: info = await self.bot.application_info() if ctx.message.author == info.owner or ctx.message.author.id in self.admins.value: await ctx.send("Updating core systems...") @@ -144,19 +146,19 @@ class OwnerCog(commands.Cog): @_system.command(name="refresh", hidden=True) @user_is_bot_admin() - async def _refresh(self, ctx): + async def _refresh(self, ctx: commands.Context) -> None: reload_config() await ctx.send("System refreshed") @commands.group(name="admin", hidden=True, pass_context=True) @commands.is_owner() - async def _admin(self, ctx): + async def _admin(self, ctx: commands.Context) -> None: if ctx.invoked_subcommand is None: await ctx.send("Usage: `admin `\n" + "Subcommands: `add`, `remove`") @_admin.command(name="add", hidden=True) @commands.is_owner() - async def _add(self, ctx, user: User): + async def _add(self, ctx: commands.Context, user: User) -> None: if user.id in self.admins.value: await ctx.send(f"{user.mention} is already an admin.") return @@ -167,7 +169,7 @@ class OwnerCog(commands.Cog): @_admin.command(name="remove", hidden=True) @commands.is_owner() - async def _remove(self, ctx, user: User): + async def _remove(self, ctx: commands.Context, user: User) -> None: if user.id not in self.admins.value: await ctx.send(f"{user.mention} is not an admin.") return @@ -176,7 +178,8 @@ class OwnerCog(commands.Cog): reload_config() await ctx.send(f"{user.mention} is no longer an admin.") - def resolve_variable(self, variable): + def resolve_variable(self, variable: Any) -> Any: + """Resolve a variable from eval.""" if hasattr(variable, "__iter__"): var_length = len(list(variable)) if (var_length > 100) and (not isinstance(variable, str)): @@ -192,7 +195,8 @@ class OwnerCog(commands.Cog): else f"" ) - def prepare(self, string): + def prepare(self, string: str) -> str: + """Prepare string for eval.""" arr = string.strip("```").replace("py\n", "").replace("python\n", "").split("\n") if not arr[::-1][0].replace(" ", "").startswith("return"): arr[len(arr) - 1] = "return " + arr[::-1][0] @@ -200,7 +204,7 @@ class OwnerCog(commands.Cog): @commands.command(pass_context=True, aliases=["eval", "exec", "evaluate"]) @user_is_bot_admin() - async def _eval(self, ctx, *, code: str): + async def _eval(self, ctx: commands.Context, *, code: str) -> None: if not isinstance(ctx.message.channel, DMChannel): return code = self.prepare(code) @@ -215,13 +219,13 @@ class OwnerCog(commands.Cog): } try: - exec( + exec( # noqa: S102 f"async def func():{code}", globals().update(args), locals(), ) a = time() - response = await eval("func()", globals().update(args), locals()) + response = await eval("func()", globals().update(args), locals()) # noqa: S307 if response is None or isinstance(response, discord.Message): del args, code return @@ -230,8 +234,7 @@ class OwnerCog(commands.Cog): response = response.replace("`", "") await ctx.send( - f"```py\n{self.resolve_variable(response)}```" - + f"`{type(response).__name__} | {(time() - a) / 1000} ms`" + f"```py\n{self.resolve_variable(response)}```\n`{type(response).__name__} | {(time() - a) / 1000} ms`" ) except Exception: await ctx.send(f"Error occurred:```\n{traceback.format_exc()}```") @@ -239,5 +242,6 @@ class OwnerCog(commands.Cog): del args, code -def setup(bot): +def setup(bot: commands.Bot) -> None: + """Add OwnerCog to J.A.R.V.I.S.""" bot.add_cog(OwnerCog(bot)) diff --git a/jarvis/cogs/remindme.py b/jarvis/cogs/remindme.py index 045564a..f9132d6 100644 --- a/jarvis/cogs/remindme.py +++ b/jarvis/cogs/remindme.py @@ -1,13 +1,15 @@ +"""J.A.R.V.I.S. Remind Me Cog.""" import asyncio import re from datetime import datetime from datetime import timedelta +from typing import List from typing import Optional from bson import ObjectId -from discord.ext import commands +from discord import Embed +from discord.ext.commands import Bot from discord.ext.tasks import loop -from discord.utils import find from discord_slash import cog_ext from discord_slash import SlashContext from discord_slash.utils.manage_commands import create_option @@ -29,7 +31,9 @@ invites = re.compile( class RemindmeCog(CacheCog): - def __init__(self, bot): + """J.A.R.V.I.S. Remind Me Cog.""" + + def __init__(self, bot: Bot): super().__init__(bot) self._remind.start() @@ -77,7 +81,7 @@ class RemindmeCog(CacheCog): days: Optional[int] = 0, hours: Optional[int] = 0, minutes: Optional[int] = 0, - ): + ) -> None: if len(message) > 100: await ctx.send("Reminder cannot be > 100 characters.", hidden=True) return @@ -120,7 +124,7 @@ class RemindmeCog(CacheCog): if reminders >= 5: await ctx.send( "You already have 5 (or more) active reminders. " - + "Please either remove an old one, or wait for one to pass", + "Please either remove an old one, or wait for one to pass", hidden=True, ) return @@ -162,7 +166,8 @@ class RemindmeCog(CacheCog): await ctx.send(embed=embed) - async def get_reminders_embed(self, ctx, reminders): + async def get_reminders_embed(self, ctx: SlashContext, reminders: List[Reminder]) -> Embed: + """Build embed for paginator.""" fields = [] for reminder in reminders: fields.append( @@ -175,7 +180,7 @@ class RemindmeCog(CacheCog): embed = build_embed( title=f"{len(reminders)} Active Reminder(s)", - description="All active reminders for " + f"{ctx.author.mention}", + description=f"All active reminders for {ctx.author.mention}", fields=fields, ) @@ -192,12 +197,12 @@ class RemindmeCog(CacheCog): name="list", description="List reminders for a user", ) - async def _list(self, ctx: SlashContext): + async def _list(self, ctx: SlashContext) -> None: exists = self.check_cache(ctx) if exists: await ctx.defer(hidden=True) await ctx.send( - "Please use existing interaction: " + f"{exists['paginator']._message.jump_url}", + f"Please use existing interaction: {exists['paginator']._message.jump_url}", hidden=True, ) return @@ -215,7 +220,7 @@ class RemindmeCog(CacheCog): name="delete", description="Delete a reminder", ) - async def _delete(self, ctx: SlashContext): + async def _delete(self, ctx: SlashContext) -> None: reminders = Reminder.objects(user=ctx.author.id, active=True) if not reminders: await ctx.send("You have no reminders set", hidden=True) @@ -293,7 +298,7 @@ class RemindmeCog(CacheCog): await message.edit(components=components) @loop(seconds=15) - async def _remind(self): + async def _remind(self) -> None: reminders = Reminder.objects(remind_at__lte=datetime.utcnow() + timedelta(seconds=30)) for reminder in reminders: if reminder.remind_at <= datetime.utcnow(): @@ -313,7 +318,7 @@ class RemindmeCog(CacheCog): embed.set_thumbnail(url=user.avatar_url) try: await user.send(embed=embed) - except: + except Exception: guild = self.bot.fetch_guild(reminder.guild) channel = guild.get_channel(reminder.channel) if guild else None if channel: @@ -322,5 +327,6 @@ class RemindmeCog(CacheCog): reminder.delete() -def setup(bot): +def setup(bot: Bot) -> None: + """Add RemindmeCog to J.A.R.V.I.S.""" bot.add_cog(RemindmeCog(bot)) diff --git a/jarvis/cogs/rolegiver.py b/jarvis/cogs/rolegiver.py index cd6e560..0548b3b 100644 --- a/jarvis/cogs/rolegiver.py +++ b/jarvis/cogs/rolegiver.py @@ -1,3 +1,4 @@ +"""J.A.R.V.I.S. Role Giver Cog.""" from discord import Role from discord.ext import commands from discord_slash import cog_ext @@ -11,7 +12,9 @@ from jarvis.utils.permissions import admin_or_permissions class RolegiverCog(commands.Cog): - def __init__(self, bot): + """J.A.R.V.I.S. Role Giver Cog.""" + + def __init__(self, bot: commands.Bot): self.bot = bot @cog_ext.cog_subcommand( @@ -28,7 +31,7 @@ class RolegiverCog(commands.Cog): ], ) @admin_or_permissions(manage_guild=True) - async def _rolegiver_add(self, ctx: SlashContext, role: Role): + async def _rolegiver_add(self, ctx: SlashContext, role: Role) -> None: setting = Setting.objects(guild=ctx.guild.id, setting="rolegiver").first() if setting and role.id in setting.value: await ctx.send("Role already in rolegiver", hidden=True) @@ -64,7 +67,7 @@ class RolegiverCog(commands.Cog): icon_url=ctx.author.avatar_url, ) - embed.set_footer(text=f"{ctx.author.name}#{ctx.author.discriminator} " + f"| {ctx.author.id}") + embed.set_footer(text=f"{ctx.author.name}#{ctx.author.discriminator} | {ctx.author.id}") await ctx.send(embed=embed) @@ -82,7 +85,7 @@ class RolegiverCog(commands.Cog): ], ) @admin_or_permissions(manage_guild=True) - async def _rolegiver_remove(self, ctx: SlashContext, role: Role): + async def _rolegiver_remove(self, ctx: SlashContext, role: Role) -> None: setting = Setting.objects(guild=ctx.guild.id, setting="rolegiver").first() if not setting or (setting and not setting.value): await ctx.send("Rolegiver has no roles", hidden=True) @@ -120,7 +123,7 @@ class RolegiverCog(commands.Cog): icon_url=ctx.author.avatar_url, ) - embed.set_footer(text=f"{ctx.author.name}#{ctx.author.discriminator} " + f"| {ctx.author.id}") + embed.set_footer(text=f"{ctx.author.name}#{ctx.author.discriminator} | {ctx.author.id}") await ctx.send(embed=embed) @@ -129,7 +132,7 @@ class RolegiverCog(commands.Cog): name="list", description="List roles rolegiver", ) - async def _rolegiver_list(self, ctx: SlashContext): + async def _rolegiver_list(self, ctx: SlashContext) -> None: setting = Setting.objects(guild=ctx.guild.id, setting="rolegiver").first() if not setting or (setting and not setting.value): await ctx.send("Rolegiver has no roles", hidden=True) @@ -157,7 +160,7 @@ class RolegiverCog(commands.Cog): icon_url=ctx.author.avatar_url, ) - embed.set_footer(text=f"{ctx.author.name}#{ctx.author.discriminator} " + f"| {ctx.author.id}") + embed.set_footer(text=f"{ctx.author.name}#{ctx.author.discriminator} | {ctx.author.id}") await ctx.send(embed=embed) @@ -175,7 +178,7 @@ class RolegiverCog(commands.Cog): ], ) @commands.cooldown(1, 10, commands.BucketType.user) - async def _role_get(self, ctx: SlashContext, role: Role): + async def _role_get(self, ctx: SlashContext, role: Role) -> None: setting = Setting.objects(guild=ctx.guild.id, setting="rolegiver").first() if not setting or (setting and not setting.value): await ctx.send("Rolegiver has no roles", hidden=True) @@ -212,7 +215,7 @@ class RolegiverCog(commands.Cog): icon_url=ctx.author.avatar_url, ) - embed.set_footer(text=f"{ctx.author.name}#{ctx.author.discriminator} " + f"| {ctx.author.id}") + embed.set_footer(text=f"{ctx.author.name}#{ctx.author.discriminator} | {ctx.author.id}") await ctx.send(embed=embed) @@ -230,7 +233,7 @@ class RolegiverCog(commands.Cog): ], ) @commands.cooldown(1, 10, commands.BucketType.user) - async def _role_forfeit(self, ctx: SlashContext, role: Role): + async def _role_forfeit(self, ctx: SlashContext, role: Role) -> None: setting = Setting.objects(guild=ctx.guild.id, setting="rolegiver").first() if not setting or (setting and not setting.value): await ctx.send("Rolegiver has no roles", hidden=True) @@ -267,10 +270,11 @@ class RolegiverCog(commands.Cog): icon_url=ctx.author.avatar_url, ) - embed.set_footer(text=f"{ctx.author.name}#{ctx.author.discriminator} " + f"| {ctx.author.id}") + embed.set_footer(text=f"{ctx.author.name}#{ctx.author.discriminator} | {ctx.author.id}") await ctx.send(embed=embed) -def setup(bot): +def setup(bot: commands.Bot) -> None: + """Add RolegiverCog to J.A.R.V.I.S.""" bot.add_cog(RolegiverCog(bot)) diff --git a/jarvis/cogs/settings.py b/jarvis/cogs/settings.py index d2e0bcb..f17b300 100644 --- a/jarvis/cogs/settings.py +++ b/jarvis/cogs/settings.py @@ -1,3 +1,6 @@ +"""J.A.R.V.I.S. Settings Management Cog.""" +from typing import Any + from discord import Role from discord import TextChannel from discord.ext import commands @@ -13,10 +16,13 @@ from jarvis.utils.permissions import admin_or_permissions class SettingsCog(commands.Cog): - def __init__(self, bot): + """J.A.R.V.I.S. Settings Management Cog.""" + + def __init__(self, bot: commands.Bot): self.bot = bot - def update_settings(self, setting, value, guild): + def update_settings(self, setting: str, value: Any, guild: int) -> bool: + """Update a guild setting.""" setting = Setting.objects(setting=setting, guild=guild).first() if not setting: setting = Setting(setting=setting, guild=guild, value=value) @@ -24,7 +30,8 @@ class SettingsCog(commands.Cog): return updated is not None - def delete_settings(self, setting, guild): + def delete_settings(self, setting: str, guild: int) -> bool: + """Delete a guild setting.""" return Setting.objects(setting=setting, guild=guild).delete() @cog_ext.cog_subcommand( @@ -44,7 +51,7 @@ class SettingsCog(commands.Cog): ], ) @admin_or_permissions(manage_guild=True) - async def _set_mute(self, ctx, role: Role): + async def _set_mute(self, ctx: SlashContext, role: Role) -> None: await ctx.defer() self.update_settings("mute", role.id, ctx.guild.id) await ctx.send(f"Settings applied. New mute role is `{role.name}`") @@ -64,7 +71,7 @@ class SettingsCog(commands.Cog): ], ) @admin_or_permissions(manage_guild=True) - async def _set_modlog(self, ctx, channel: TextChannel): + async def _set_modlog(self, ctx: SlashContext, channel: TextChannel) -> None: if not isinstance(channel, TextChannel): await ctx.send("Channel must be a TextChannel", hidden=True) return @@ -86,7 +93,7 @@ class SettingsCog(commands.Cog): ], ) @admin_or_permissions(manage_guild=True) - async def _set_userlog(self, ctx, channel: TextChannel): + async def _set_userlog(self, ctx: SlashContext, channel: TextChannel) -> None: if not isinstance(channel, TextChannel): await ctx.send("Channel must be a TextChannel", hidden=True) return @@ -108,7 +115,7 @@ class SettingsCog(commands.Cog): ], ) @admin_or_permissions(manage_guild=True) - async def _set_massmention(self, ctx, amount: int): + async def _set_massmention(self, ctx: SlashContext, amount: int) -> None: await ctx.defer() self.update_settings("massmention", amount, ctx.guild.id) await ctx.send(f"Settings applied. New massmention limit is {amount}") @@ -128,7 +135,7 @@ class SettingsCog(commands.Cog): ], ) @admin_or_permissions(manage_guild=True) - async def _set_verified(self, ctx, role: Role): + async def _set_verified(self, ctx: SlashContext, role: Role) -> None: await ctx.defer() self.update_settings("verified", role.id, ctx.guild.id) await ctx.send(f"Settings applied. New verified role is `{role.name}`") @@ -148,7 +155,7 @@ class SettingsCog(commands.Cog): ], ) @admin_or_permissions(manage_guild=True) - async def _set_unverified(self, ctx, role: Role): + async def _set_unverified(self, ctx: SlashContext, role: Role) -> None: await ctx.defer() self.update_settings("unverified", role.id, ctx.guild.id) await ctx.send(f"Settings applied. New unverified role is `{role.name}`") @@ -161,7 +168,7 @@ class SettingsCog(commands.Cog): description="Unset mute role", ) @admin_or_permissions(manage_guild=True) - async def _unset_mute(self, ctx): + async def _unset_mute(self, ctx: SlashContext) -> None: await ctx.defer() self.delete_settings("mute", ctx.guild.id) await ctx.send("Setting removed.") @@ -173,7 +180,7 @@ class SettingsCog(commands.Cog): description="Unset modlog channel", ) @admin_or_permissions(manage_guild=True) - async def _unset_modlog(self, ctx): + async def _unset_modlog(self, ctx: SlashContext) -> None: self.delete_settings("modlog", ctx.guild.id) await ctx.send("Setting removed.") @@ -184,7 +191,7 @@ class SettingsCog(commands.Cog): description="Unset userlog channel", ) @admin_or_permissions(manage_guild=True) - async def _unset_userlog(self, ctx): + async def _unset_userlog(self, ctx: SlashContext) -> None: self.delete_settings("userlog", ctx.guild.id) await ctx.send("Setting removed.") @@ -195,7 +202,7 @@ class SettingsCog(commands.Cog): description="Unet massmention amount", ) @admin_or_permissions(manage_guild=True) - async def _massmention(self, ctx): + async def _massmention(self, ctx: SlashContext) -> None: await ctx.defer() self.delete_settings("massmention", ctx.guild.id) await ctx.send("Setting removed.") @@ -207,7 +214,7 @@ class SettingsCog(commands.Cog): description="Unset verified role", ) @admin_or_permissions(manage_guild=True) - async def _verified(self, ctx): + async def _verified(self, ctx: SlashContext) -> None: await ctx.defer() self.delete_settings("verified", ctx.guild.id) await ctx.send("Setting removed.") @@ -219,14 +226,14 @@ class SettingsCog(commands.Cog): description="Unset unverified role", ) @admin_or_permissions(manage_guild=True) - async def _unverified(self, ctx): + async def _unverified(self, ctx: SlashContext) -> None: await ctx.defer() self.delete_settings("unverified", ctx.guild.id) await ctx.send("Setting removed.") @cog_ext.cog_subcommand(base="settings", name="view", description="View settings") @admin_or_permissions(manage_guild=True) - async def _view(self, ctx: SlashContext): + async def _view(self, ctx: SlashContext) -> None: settings = Setting.objects(guild=ctx.guild.id) fields = [] @@ -260,10 +267,11 @@ class SettingsCog(commands.Cog): @cog_ext.cog_subcommand(base="settings", name="clear", description="Clear all settings") @admin_or_permissions(manage_guild=True) - async def _clear(self, ctx: SlashContext): + async def _clear(self, ctx: SlashContext) -> None: deleted = Setting.objects(guild=ctx.guild.id).delete() await ctx.send(f"Guild settings cleared: `{deleted is not None}`") -def setup(bot): +def setup(bot: commands.Bot) -> None: + """Add SettingsCog to J.A.R.V.I.S.""" bot.add_cog(SettingsCog(bot)) diff --git a/jarvis/cogs/starboard.py b/jarvis/cogs/starboard.py index 8b2436a..12e3063 100644 --- a/jarvis/cogs/starboard.py +++ b/jarvis/cogs/starboard.py @@ -1,3 +1,4 @@ +"""J.A.R.V.I.S. Starboard Cog.""" from discord import TextChannel from discord.ext import commands from discord.utils import find @@ -24,7 +25,9 @@ supported_images = [ class StarboardCog(commands.Cog): - def __init__(self, bot): + """J.A.R.V.I.S. Starboard Cog.""" + + def __init__(self, bot: commands.Bot): self.bot = bot @cog_ext.cog_subcommand( @@ -33,7 +36,7 @@ class StarboardCog(commands.Cog): description="Lists all Starboards", ) @admin_or_permissions(manage_guild=True) - async def _list(self, ctx): + async def _list(self, ctx: SlashContext) -> None: starboards = Starboard.objects(guild=ctx.guild.id) if starboards != []: message = "Available Starboards:\n" @@ -57,7 +60,7 @@ class StarboardCog(commands.Cog): ], ) @admin_or_permissions(manage_guild=True) - async def _create(self, ctx, channel: TextChannel): + async def _create(self, ctx: SlashContext, channel: TextChannel) -> None: if channel not in ctx.guild.channels: await ctx.send( "Channel not in guild. Choose an existing channel.", @@ -99,7 +102,7 @@ class StarboardCog(commands.Cog): ], ) @admin_or_permissions(manage_guild=True) - async def _delete(self, ctx, channel: TextChannel): + async def _delete(self, ctx: SlashContext, channel: TextChannel) -> None: deleted = Starboard.objects(channel=channel.id, guild=ctx.guild.id).delete() if deleted: _ = Star.objects(starboard=channel.id).delete() @@ -120,7 +123,7 @@ class StarboardCog(commands.Cog): ), create_option( name="channel", - description="Channel that has the message, " + "required if different than command message", + description="Channel that has the message, required if different than command message", option_type=7, required=False, ), @@ -132,7 +135,7 @@ class StarboardCog(commands.Cog): ctx: SlashContext, message: str, channel: TextChannel = None, - ): + ) -> None: if not channel: channel = ctx.channel starboards = Starboard.objects(guild=ctx.guild.id) @@ -230,7 +233,7 @@ class StarboardCog(commands.Cog): components[0]["components"][0]["disabled"] = True await com_ctx.edit_origin( - content="Message saved to Starboard.\n" + f"See it in {starboard.mention}", + content=f"Message saved to Starboard.\nSee it in {starboard.mention}", components=components, ) @@ -259,14 +262,14 @@ class StarboardCog(commands.Cog): ctx: SlashContext, id: int, starboard: TextChannel, - ): + ) -> None: if not isinstance(starboard, TextChannel): await ctx.send("Channel must be a TextChannel", hidden=True) return exists = Starboard.objects(channel=starboard.id, guild=ctx.guild.id).first() if not exists: await ctx.send( - f"Starboard does not exist in {starboard.mention}. " + "Please create it first", + f"Starboard does not exist in {starboard.mention}. Please create it first", hidden=True, ) return @@ -291,5 +294,6 @@ class StarboardCog(commands.Cog): await ctx.send(f"Star {id} deleted") -def setup(bot): +def setup(bot: commands.Bot) -> None: + """Add StarboardCog to J.A.R.V.I.S.""" bot.add_cog(StarboardCog(bot)) diff --git a/jarvis/cogs/util.py b/jarvis/cogs/util.py index 0dd19ad..53e2bcd 100644 --- a/jarvis/cogs/util.py +++ b/jarvis/cogs/util.py @@ -1,3 +1,4 @@ +"""J.A.R.V.I.S. Utility Cog.""" import re import secrets import string @@ -35,7 +36,7 @@ class UtilCog(commands.Cog): Mostly system utility functions, but may change over time """ - def __init__(self, bot): + def __init__(self, bot: commands.Cog): self.bot = bot self.config = get_config() @@ -44,7 +45,7 @@ class UtilCog(commands.Cog): description="Retrieve J.A.R.V.I.S. status", ) @commands.cooldown(1, 30, commands.BucketType.channel) - async def _status(self, ctx): + async def _status(self, ctx: SlashContext) -> None: title = "J.A.R.V.I.S. Status" desc = "All systems online" color = "#98CCDA" @@ -68,12 +69,12 @@ class UtilCog(commands.Cog): description="Get the current logo", ) @commands.cooldown(1, 30, commands.BucketType.channel) - async def _logo(self, ctx): + async def _logo(self, ctx: SlashContext) -> None: lo = logo.get_logo(self.config.logo) await ctx.send(f"```\n{lo}\n```") @cog_ext.cog_slash(name="rchk", description="Robot Camo HK416") - async def _rchk(self, ctx: SlashContext): + async def _rchk(self, ctx: SlashContext) -> None: await ctx.send(content=hk) @cog_ext.cog_slash( @@ -88,7 +89,7 @@ class UtilCog(commands.Cog): ) ], ) - async def _rcauto(self, ctx: SlashContext, text: str): + async def _rcauto(self, ctx: SlashContext, text: str) -> None: to_send = "" if len(text) == 1 and not re.match(r"^[A-Z0-9-()$@!?^'#. ]$", text.upper()): await ctx.send("Please use ASCII characters.", hidden=True) @@ -120,7 +121,7 @@ class UtilCog(commands.Cog): ], ) @commands.cooldown(1, 5, commands.BucketType.user) - async def _avatar(self, ctx, user: User = None): + async def _avatar(self, ctx: SlashContext, user: User = None) -> None: if not user: user = ctx.author @@ -142,7 +143,7 @@ class UtilCog(commands.Cog): ) ], ) - async def _roleinfo(self, ctx: SlashContext, role: Role): + async def _roleinfo(self, ctx: SlashContext, role: Role) -> None: fields = [ Field(name="ID", value=role.id), @@ -193,7 +194,7 @@ class UtilCog(commands.Cog): ) ], ) - async def _userinfo(self, ctx: SlashContext, user: User = None): + async def _userinfo(self, ctx: SlashContext, user: User = None) -> None: if not user: user = ctx.author user_roles = user.roles @@ -230,7 +231,7 @@ class UtilCog(commands.Cog): await ctx.send(embed=embed) @cog_ext.cog_slash(name="serverinfo", description="Get server info") - async def _server_info(self, ctx: SlashContext): + async def _server_info(self, ctx: SlashContext) -> None: guild: Guild = ctx.guild owner = f"{guild.owner.name}#{guild.owner.discriminator}" if guild.owner else "||`[redacted]`||" @@ -291,7 +292,7 @@ class UtilCog(commands.Cog): ], ) @commands.cooldown(1, 15, type=commands.BucketType.user) - async def _pw_gen(self, ctx: SlashContext, length: int = 32, chars: int = 3): + async def _pw_gen(self, ctx: SlashContext, length: int = 32, chars: int = 3) -> None: if length > 256: await ctx.send("Please limit password to 256 characters", hidden=True) return @@ -305,11 +306,12 @@ class UtilCog(commands.Cog): pw = "".join(secrets.choice(choices[chars]) for i in range(length)) await ctx.send( f"Generated password:\n`{pw}`\n\n" - + '**WARNING: Once you press "Dismiss Message", ' - + "*the password is lost forever***", + '**WARNING: Once you press "Dismiss Message", ' + "*the password is lost forever***", hidden=True, ) -def setup(bot): +def setup(bot: commands.Bot) -> None: + """Add UtilCog to J.A.R.V.I.S.""" bot.add_cog(UtilCog(bot)) diff --git a/jarvis/cogs/verify.py b/jarvis/cogs/verify.py index 6eebfba..f3f02f5 100644 --- a/jarvis/cogs/verify.py +++ b/jarvis/cogs/verify.py @@ -1,4 +1,5 @@ -from random import randint +"""J.A.R.V.I.S. Verify Cog.""" +from secrets import randint from discord.ext import commands from discord_slash import cog_ext @@ -10,7 +11,8 @@ from discord_slash.utils import manage_components from jarvis.db.models import Setting -def create_layout(): +def create_layout() -> list: + """Create verify component layout.""" buttons = [] yes = randint(0, 2) for i in range(3): @@ -29,7 +31,9 @@ def create_layout(): class VerifyCog(commands.Cog): - def __init__(self, bot): + """J.A.R.V.I.S. Verify Cog.""" + + def __init__(self, bot: commands.Bot): self.bot = bot @cog_ext.cog_slash( @@ -37,7 +41,7 @@ class VerifyCog(commands.Cog): description="Verify that you've read the rules", ) @commands.cooldown(1, 15, commands.BucketType.user) - async def _verify(self, ctx: SlashContext): + async def _verify(self, ctx: SlashContext) -> None: await ctx.defer() role = Setting.objects(guild=ctx.guild.id, setting="verified").first() if not role: @@ -48,13 +52,13 @@ class VerifyCog(commands.Cog): return components = create_layout() message = await ctx.send( - content=f"{ctx.author.mention}, " + "please press the button that says `YES`.", + content=f"{ctx.author.mention}, please press the button that says `YES`.", components=components, ) await message.delete(delay=15) @cog_ext.cog_component(components=create_layout()) - async def _process(self, ctx: ComponentContext): + async def _process(self, ctx: ComponentContext) -> None: await ctx.defer(edit_origin=True) try: if ctx.author.id != ctx.origin_message.mentions[0].id: @@ -75,15 +79,16 @@ class VerifyCog(commands.Cog): role = ctx.guild.get_role(setting.value) await ctx.author.remove_roles(role, reason="Verification passed") await ctx.edit_origin( - content=f"Welcome, {ctx.author.mention}. " + "Please enjoy your stay.", + content=f"Welcome, {ctx.author.mention}. Please enjoy your stay.", components=manage_components.spread_to_rows(*components, max_in_row=5), ) await ctx.origin_message.delete(delay=5) else: await ctx.edit_origin( - content=f"{ctx.author.mention}, incorrect. " + "Please press the button that says `YES`", + content=f"{ctx.author.mention}, incorrect. Please press the button that says `YES`", ) -def setup(bot): +def setup(bot: commands.Bot) -> None: + """Add VerifyCog to J.A.R.V.I.S.""" bot.add_cog(VerifyCog(bot)) diff --git a/jarvis/config.py b/jarvis/config.py index dc0dfaa..459f9f6 100644 --- a/jarvis/config.py +++ b/jarvis/config.py @@ -1,3 +1,4 @@ +"""Load the config for J.A.R.V.I.S.""" from yaml import load from jarvis.db.models import Config as DBConfig @@ -9,7 +10,10 @@ except ImportError: class Config(object): - def __new__(cls, *args, **kwargs): + """Config singleton object for J.A.R.V.I.S.""" + + def __new__(cls, *args: list, **kwargs: dict): + """Get the singleton config, or creates a new one.""" it = cls.__dict__.get("it") if it is not None: return it @@ -24,33 +28,39 @@ class Config(object): logo: str, mongo: dict, urls: dict, + log_level: str = "WARNING", cogs: list = None, events: bool = True, gitlab_token: str = None, max_messages: int = 1000, - ): + ) -> None: + """Initialize the config object.""" self.token = token self.client_id = client_id self.logo = logo self.mongo = mongo self.urls = urls + self.log_level = log_level self.cogs = cogs self.events = events self.max_messages = max_messages self.gitlab_token = gitlab_token - def get_db_config(self): + def get_db_config(self) -> None: + """Load the database config objects.""" config = DBConfig.objects() for item in config: setattr(self, item.key, item.value) @classmethod - def from_yaml(cls, y): + def from_yaml(cls, y: dict) -> "Config": + """Load the yaml config file.""" instance = cls(**y) return instance def get_config(path: str = "config.yaml") -> Config: + """Get the config from the specified yaml file.""" if Config.__dict__.get("it"): return Config() with open(path) as f: @@ -59,6 +69,7 @@ def get_config(path: str = "config.yaml") -> Config: return Config.from_yaml(y) -def reload_config(): +def reload_config() -> None: + """Force reload of the config singleton on next call.""" if "it" in Config.__dict__: Config.__dict__.pop("it") diff --git a/jarvis/data/dbrand.py b/jarvis/data/dbrand.py index c4e245c..f9c4952 100644 --- a/jarvis/data/dbrand.py +++ b/jarvis/data/dbrand.py @@ -1,3 +1,4 @@ +"""dbrand-specific data.""" shipping_lookup = [ {"country": "afghanistan", "code": "AF"}, {"country": "albania", "code": "AL"}, diff --git a/jarvis/data/robotcamo.py b/jarvis/data/robotcamo.py index a72486a..cf70d63 100644 --- a/jarvis/data/robotcamo.py +++ b/jarvis/data/robotcamo.py @@ -1,3 +1,4 @@ +"""Robot Camo emote lookups.""" emotes = { "A": 852317928572715038, "B": 852317954975727679, diff --git a/jarvis/data/unicode.py b/jarvis/data/unicode.py index 01e8a3e..ed0fba5 100644 --- a/jarvis/data/unicode.py +++ b/jarvis/data/unicode.py @@ -1,3 +1,4 @@ +"""Unicode emoji data.""" import json from os import getcwd from os import sep as s diff --git a/jarvis/db/models.py b/jarvis/db/models.py index 826ef11..573daff 100644 --- a/jarvis/db/models.py +++ b/jarvis/db/models.py @@ -1,3 +1,4 @@ +"""J.A.R.V.I.S. database object for mongoengine.""" from datetime import datetime from mongoengine import Document @@ -12,10 +13,14 @@ from mongoengine.fields import StringField class SnowflakeField(LongField): + """Snowflake LongField Override.""" + pass class Autopurge(Document): + """Autopurge database object.""" + guild = SnowflakeField(required=True) channel = SnowflakeField(required=True) delay = IntField(min_value=1, max_value=300, default=30) @@ -26,6 +31,8 @@ class Autopurge(Document): class Autoreact(Document): + """Autoreact database object.""" + guild = SnowflakeField(required=True) channel = SnowflakeField(required=True) reactions = ListField(field=StringField()) @@ -36,6 +43,8 @@ class Autoreact(Document): class Ban(Document): + """Ban database object.""" + active = BooleanField(default=True) admin = SnowflakeField(required=True) user = SnowflakeField(required=True) @@ -51,6 +60,8 @@ class Ban(Document): class Config(Document): + """Config database object.""" + key = StringField(required=True) value = DynamicField(required=True) @@ -58,6 +69,8 @@ class Config(Document): class Guess(Document): + """Guess database object.""" + correct = BooleanField(default=False) guess = StringField(max_length=800, required=True) user = SnowflakeField(required=True) @@ -66,6 +79,8 @@ class Guess(Document): class Joke(Document): + """Joke database object.""" + rid = StringField() body = StringField() title = StringField() @@ -77,6 +92,8 @@ class Joke(Document): class Kick(Document): + """Kick database object.""" + admin = SnowflakeField(required=True) guild = SnowflakeField(required=True) reason = StringField(max_length=100, required=True) @@ -87,6 +104,8 @@ class Kick(Document): class Lock(Document): + """Lock database object.""" + active = BooleanField(default=True) admin = SnowflakeField(required=True) channel = SnowflakeField(required=True) @@ -99,6 +118,8 @@ class Lock(Document): class Mute(Document): + """Mute database object.""" + active = BooleanField(default=True) user = SnowflakeField(required=True) admin = SnowflakeField(required=True) @@ -111,6 +132,8 @@ class Mute(Document): class Purge(Document): + """Purge database object.""" + admin = SnowflakeField(required=True) channel = SnowflakeField(required=True) guild = SnowflakeField(required=True) @@ -121,6 +144,8 @@ class Purge(Document): class Reminder(Document): + """Reminder database object.""" + active = BooleanField(default=True) user = SnowflakeField(required=True) guild = SnowflakeField(required=True) @@ -133,6 +158,8 @@ class Reminder(Document): class Roleping(Document): + """Roleping database object.""" + active = BooleanField(default=True) role = SnowflakeField(required=True) guild = SnowflakeField(required=True) @@ -144,6 +171,8 @@ class Roleping(Document): class Setting(Document): + """Setting database object.""" + guild = SnowflakeField(required=True) setting = StringField(required=True) value = DynamicField() @@ -152,6 +181,8 @@ class Setting(Document): class Star(Document): + """Star database object.""" + active = BooleanField(default=True) index = IntField(required=True) message = SnowflakeField(required=True) @@ -166,6 +197,8 @@ class Star(Document): class Starboard(Document): + """Starboard database object.""" + channel = SnowflakeField(required=True) guild = SnowflakeField(required=True) admin = SnowflakeField(required=True) @@ -175,6 +208,8 @@ class Starboard(Document): class Unban(Document): + """Unban database object.""" + user = SnowflakeField(required=True) username = StringField(required=True) discrim = IntField(min_value=1, max_value=9999, required=True) @@ -187,6 +222,8 @@ class Unban(Document): class Warning(Document): + """Warning database object.""" + active = BooleanField(default=True) admin = SnowflakeField(required=True) user = SnowflakeField(required=True) diff --git a/jarvis/events/guild.py b/jarvis/events/guild.py index 1208f5a..70866e2 100644 --- a/jarvis/events/guild.py +++ b/jarvis/events/guild.py @@ -1,24 +1,30 @@ +"""J.A.R.V.I.S. guild event handler.""" import asyncio +from discord import Guild +from discord.ext.commands import Bot from discord.utils import find from jarvis.db.models import Setting class GuildEventHandler(object): - def __init__(self, bot): + """J.A.R.V.I.S. guild event handler.""" + + def __init__(self, bot: Bot): self.bot = bot self.bot.add_listener(self.on_guild_join) - async def on_guild_join(self, guild): + async def on_guild_join(self, guild: Guild) -> None: + """Handle on_guild_join event.""" general = find(lambda x: x.name == "general", guild.channels) if general and general.permissions_for(guild.me).send_messages: user = self.bot.user await general.send( f"Allow me to introduce myself. I am {user.mention}, a virtual " - + "artificial intelligence, and I'm here to assist you with a " - + "variety of tasks as best I can, " - + "24 hours a day, seven days a week." + "artificial intelligence, and I'm here to assist you with a " + "variety of tasks as best I can, " + "24 hours a day, seven days a week." ) await asyncio.sleep(1) await general.send("Importing all preferences from home interface...") diff --git a/jarvis/events/member.py b/jarvis/events/member.py index 2d86393..e54f719 100644 --- a/jarvis/events/member.py +++ b/jarvis/events/member.py @@ -1,15 +1,20 @@ +"""J.A.R.V.I.S. Member event handler.""" from discord import Member +from discord.ext.commands import Bot from jarvis.db.models import Mute from jarvis.db.models import Setting class MemberEventHandler(object): - def __init__(self, bot): + """J.A.R.V.I.S. Member event handler.""" + + def __init__(self, bot: Bot): self.bot = bot self.bot.add_listener(self.on_member_join) - async def on_member_join(self, user: Member): + async def on_member_join(self, user: Member) -> None: + """Handle on_member_join event.""" guild = user.guild mute = Mute.objects(guild=guild.id, user=user.id, active=True).first() if mute: diff --git a/jarvis/events/message.py b/jarvis/events/message.py index f0403a4..8985f14 100644 --- a/jarvis/events/message.py +++ b/jarvis/events/message.py @@ -1,7 +1,9 @@ +"""J.A.R.V.I.S. Message event handler.""" import re from discord import DMChannel from discord import Message +from discord.ext.commands import Bot from discord.utils import find from jarvis.config import get_config @@ -20,17 +22,21 @@ invites = re.compile( class MessageEventHandler(object): - def __init__(self, bot): + """J.A.R.V.I.S. Message event handler.""" + + def __init__(self, bot: Bot): self.bot = bot self.bot.add_listener(self.on_message) self.bot.add_listener(self.on_message_edit) - async def autopurge(self, message: Message): + async def autopurge(self, message: Message) -> None: + """Handle autopurge events.""" autopurge = Autopurge.objects(guild=message.guild.id, channel=message.channel.id).first() if autopurge: await message.delete(delay=autopurge.delay) - async def autoreact(self, message: Message): + async def autoreact(self, message: Message) -> None: + """Handle autoreact events.""" autoreact = Autoreact.objects( guild=message.guild.id, channel=message.channel.id, @@ -39,12 +45,13 @@ class MessageEventHandler(object): for reaction in autoreact.reactions: await message.add_reaction(reaction) - async def checks(self, message: Message): + async def checks(self, message: Message) -> None: + """Other message checks.""" # #tech channel = find(lambda x: x.id == 599068193339736096, message.channel_mentions) if channel and message.author.id == 293795462752894976: await channel.send( - content="https://cdn.discordapp.com/attachments/" + "664621130044407838/805218508866453554/tech.gif" + content="https://cdn.discordapp.com/attachments/664621130044407838/805218508866453554/tech.gif" ) content = re.sub(r"\s+", "", message.content) match = invites.search(content) @@ -81,20 +88,20 @@ class MessageEventHandler(object): name=message.author.nick if message.author.nick else message.author.name, icon_url=message.author.avatar_url, ) - embed.set_footer( - text=f"{message.author.name}#" + f"{message.author.discriminator} " + f"| {message.author.id}" - ) + embed.set_footer(text=f"{message.author.name}#{message.author.discriminator} | {message.author.id}") await message.channel.send(embed=embed) - async def massmention(self, message: Message): + async def massmention(self, message: Message) -> None: + """Handle massmention events.""" massmention = Setting.objects( guild=message.guild.id, setting="massmention", ).first() if ( massmention - and massmention.value > 0 - and len(message.mentions) - (1 if message.author in message.mentions else 0) > massmention.value + and massmention.value > 0 # noqa: W503 + and len(message.mentions) - (1 if message.author in message.mentions else 0) # noqa: W503 + > massmention.value # noqa: W503 ): _ = Warning( active=True, @@ -114,10 +121,11 @@ class MessageEventHandler(object): name=message.author.nick if message.author.nick else message.author.name, icon_url=message.author.avatar_url, ) - embed.set_footer(text=f"{message.author.name}#{message.author.discriminator} " + f"| {message.author.id}") + embed.set_footer(text=f"{message.author.name}#{message.author.discriminator} | {message.author.id}") await message.channel.send(embed=embed) - async def roleping(self, message: Message): + async def roleping(self, message: Message) -> None: + """Handle roleping events.""" rolepings = Roleping.objects(guild=message.guild.id, active=True) if not rolepings: @@ -181,10 +189,11 @@ class MessageEventHandler(object): name=message.author.nick if message.author.nick else message.author.name, icon_url=message.author.avatar_url, ) - embed.set_footer(text=f"{message.author.name}#{message.author.discriminator} " + f"| {message.author.id}") + embed.set_footer(text=f"{message.author.name}#{message.author.discriminator} | {message.author.id}") await message.channel.send(embed=embed) - async def on_message(self, message: Message): + async def on_message(self, message: Message) -> None: + """Handle on_message event. Calls other event handlers.""" if not isinstance(message.channel, DMChannel) and not message.author.bot: await self.autoreact(message) await self.massmention(message) @@ -192,8 +201,11 @@ class MessageEventHandler(object): await self.autopurge(message) await self.checks(message) - async def on_message_edit(self, before: Message, after: Message): + async def on_message_edit(self, before: Message, after: Message) -> None: + """Handle on_message_edit event. Calls other event handlers.""" if not isinstance(after.channel, DMChannel) and not after.author.bot: await self.massmention(after) await self.roleping(after) await self.checks(after) + await self.roleping(after) + await self.checks(after) diff --git a/jarvis/logo.py b/jarvis/logo.py index 794f2e4..79e550c 100644 --- a/jarvis/logo.py +++ b/jarvis/logo.py @@ -1,3 +1,5 @@ +"""Logos for J.A.R.V.I.S.""" + logo_doom = r""" ___ ___ ______ _ _ _____ _____ |_ | / _ \ | ___ \ | | | | |_ _| / ___| @@ -82,7 +84,7 @@ logo_alligator = r""" #+# #+# #+# #+# #+# #+# #+# #+# #+# #+#+#+# #+# #+# #+# #+# #+# #+# ##### ### ### ### ### ### ### ### ### ### ########### ### ######## ### -""" +""" # noqa: E501 logo_alligator2 = r""" @@ -97,7 +99,8 @@ logo_alligator2 = r""" """ -def get_logo(lo): +def get_logo(lo: str) -> str: + """Get a logo.""" if "logo_" not in lo: lo = "logo_" + lo return globals()[lo] if lo in globals() else logo_alligator2 diff --git a/jarvis/tasks/__init__.py b/jarvis/tasks/__init__.py index cd87d10..4ef40ea 100644 --- a/jarvis/tasks/__init__.py +++ b/jarvis/tasks/__init__.py @@ -1,10 +1,12 @@ +"""J.A.R.V.I.S. background task handlers.""" from jarvis.tasks import unban from jarvis.tasks import unlock from jarvis.tasks import unmute from jarvis.tasks import unwarn -def init(): +def init() -> None: + """Start the background task handlers.""" unban.unban.start() unlock.unlock.start() unmute.unmute.start() diff --git a/jarvis/tasks/unban.py b/jarvis/tasks/unban.py index 84b89ce..521a249 100644 --- a/jarvis/tasks/unban.py +++ b/jarvis/tasks/unban.py @@ -1,3 +1,4 @@ +"""J.A.R.V.I.S. unban background task handler.""" from datetime import datetime from datetime import timedelta @@ -12,7 +13,8 @@ jarvis_id = get_config().client_id @loop(minutes=10) -async def unban(): +async def unban() -> None: + """J.A.R.V.I.S. unban background task.""" bans = Ban.objects(type="temp", active=True) unbans = [] for ban in bans: diff --git a/jarvis/tasks/unlock.py b/jarvis/tasks/unlock.py index c0a24f9..a1a1762 100644 --- a/jarvis/tasks/unlock.py +++ b/jarvis/tasks/unlock.py @@ -1,3 +1,4 @@ +"""J.A.R.V.I.S. unlock background task handler.""" from datetime import datetime from datetime import timedelta @@ -8,7 +9,8 @@ from jarvis.db.models import Lock @loop(minutes=1) -async def unlock(): +async def unlock() -> None: + """J.A.R.V.I.S. unlock background task.""" locks = Lock.objects(active=True) for lock in locks: if lock.created_at + timedelta(minutes=lock.duration) < datetime.utcnow(): diff --git a/jarvis/tasks/unmute.py b/jarvis/tasks/unmute.py index 7fb6dda..9a00bdc 100644 --- a/jarvis/tasks/unmute.py +++ b/jarvis/tasks/unmute.py @@ -1,3 +1,4 @@ +"""J.A.R.V.I.S. unmute background task handler.""" from datetime import datetime from datetime import timedelta @@ -9,7 +10,8 @@ from jarvis.db.models import Setting @loop(minutes=1) -async def unmute(): +async def unmute() -> None: + """J.A.R.V.I.S. unmute background task.""" mutes = Mute.objects(duration__gt=0, active=True) mute_roles = Setting.objects(setting="mute") for mute in mutes: diff --git a/jarvis/tasks/unwarn.py b/jarvis/tasks/unwarn.py index 455c41a..d4754c3 100644 --- a/jarvis/tasks/unwarn.py +++ b/jarvis/tasks/unwarn.py @@ -1,14 +1,15 @@ +"""J.A.R.V.I.S. unwarn background task handler.""" from datetime import datetime from datetime import timedelta from discord.ext.tasks import loop -import jarvis from jarvis.db.models import Warning @loop(hours=1) -async def unwarn(): +async def unwarn() -> None: + """J.A.R.V.I.S. unwarn background task.""" warns = Warning.objects(active=True) for warn in warns: if warn.created_at + timedelta(hours=warn.duration) < datetime.utcnow(): diff --git a/jarvis/utils/__init__.py b/jarvis/utils/__init__.py index 29df3cf..925a07e 100644 --- a/jarvis/utils/__init__.py +++ b/jarvis/utils/__init__.py @@ -1,9 +1,11 @@ +"""J.A.R.V.I.S. Utility Functions.""" from datetime import datetime from pkgutil import iter_modules import git from discord import Color from discord import Embed +from discord import Message from discord.ext import commands import jarvis.cogs @@ -13,17 +15,19 @@ from jarvis.config import get_config __all__ = ["field", "db", "cachecog", "permissions"] -def convert_bytesize(bytes: int) -> str: - bytes = float(bytes) +def convert_bytesize(b: int) -> str: + """Convert bytes amount to human readable.""" + b = float(b) sizes = ["B", "KB", "MB", "GB", "TB", "PB"] size = 0 - while bytes >= 1024 and size < len(sizes) - 1: - bytes = bytes / 1024 + while b >= 1024 and size < len(sizes) - 1: + b = b / 1024 size += 1 - return "{:0.3f} {}".format(bytes, sizes[size]) + return "{:0.3f} {}".format(b, sizes[size]) -def unconvert_bytesize(size, ending: str): +def unconvert_bytesize(size: int, ending: str) -> int: + """Convert human readable to bytes.""" ending = ending.upper() sizes = ["B", "KB", "MB", "GB", "TB", "PB"] if ending == "B": @@ -32,7 +36,8 @@ def unconvert_bytesize(size, ending: str): return round(size * (1024 ** sizes.index(ending))) -def get_prefix(bot, message): +def get_prefix(bot: commands.Bot, message: Message) -> list: + """Get bot prefixes.""" prefixes = ["!", "-", "%"] # if not message.guild: # return "?" @@ -40,13 +45,15 @@ def get_prefix(bot, message): return commands.when_mentioned_or(*prefixes)(bot, message) -def get_extensions(path=jarvis.cogs.__path__) -> list: +def get_extensions(path: str = jarvis.cogs.__path__) -> list: + """Get J.A.R.V.I.S. cogs.""" config = get_config() vals = config.cogs or [x.name for x in iter_modules(path)] return ["jarvis.cogs.{}".format(x) for x in vals] def parse_color_hex(hex: str) -> Color: + """Convert a hex color to a d.py Color.""" hex = hex.lstrip("#") rgb = tuple(int(hex[i : i + 2], 16) for i in (0, 2, 4)) # noqa: E203 return Color.from_rgb(*rgb) @@ -58,8 +65,9 @@ def build_embed( fields: list, color: str = "#FF0000", timestamp: datetime = None, - **kwargs, + **kwargs: dict, ) -> Embed: + """Embed builder utility function.""" if not timestamp: timestamp = datetime.utcnow() embed = Embed( @@ -74,7 +82,8 @@ def build_embed( return embed -def update(): +def update() -> int: + """J.A.R.V.I.S. update utility.""" repo = git.Repo(".") dirty = repo.is_dirty() current_hash = repo.head.object.hexsha @@ -88,6 +97,7 @@ def update(): return 1 -def get_repo_hash(): +def get_repo_hash() -> str: + """J.A.R.V.I.S. current branch hash.""" repo = git.Repo(".") return repo.head.object.hexsha diff --git a/jarvis/utils/cachecog.py b/jarvis/utils/cachecog.py index 0a7b13f..5cfec4f 100644 --- a/jarvis/utils/cachecog.py +++ b/jarvis/utils/cachecog.py @@ -1,3 +1,4 @@ +"""Cog wrapper for command caching.""" from datetime import datetime from datetime import timedelta @@ -8,24 +9,27 @@ from discord_slash import SlashContext class CacheCog(commands.Cog): + """Cog wrapper for command caching.""" + def __init__(self, bot: commands.Bot): self.bot = bot self.cache = {} self._expire_interaction.start() - def check_cache(self, ctx: SlashContext, **kwargs): + def check_cache(self, ctx: SlashContext, **kwargs: dict) -> dict: + """Check the cache.""" if not kwargs: kwargs = {} return find( - lambda x: x["command"] == ctx.subcommand_name - and x["user"] == ctx.author.id - and x["guild"] == ctx.guild.id - and all(x[k] == v for k, v in kwargs.items()), + lambda x: x["command"] == ctx.subcommand_name # noqa: W503 + and x["user"] == ctx.author.id # noqa: W503 + and x["guild"] == ctx.guild.id # noqa: W503 + and all(x[k] == v for k, v in kwargs.items()), # noqa: W503 self.cache.values(), ) @loop(minutes=1) - async def _expire_interaction(self): + async def _expire_interaction(self) -> None: keys = list(self.cache.keys()) for key in keys: if self.cache[key]["timeout"] <= datetime.utcnow() + timedelta(minutes=1): diff --git a/jarvis/utils/field.py b/jarvis/utils/field.py index 8fb683f..8339f65 100644 --- a/jarvis/utils/field.py +++ b/jarvis/utils/field.py @@ -1,12 +1,16 @@ +"""Embed field helper.""" from dataclasses import dataclass from typing import Any @dataclass class Field: + """Embed Field.""" + name: Any value: Any inline: bool = True - def to_dict(self): + def to_dict(self) -> dict: + """Convert Field to d.py field dict.""" return {"name": self.name, "value": self.value, "inline": self.inline} diff --git a/jarvis/utils/permissions.py b/jarvis/utils/permissions.py index 0d1fc69..fc1c86b 100644 --- a/jarvis/utils/permissions.py +++ b/jarvis/utils/permissions.py @@ -1,10 +1,14 @@ +"""Permissions wrappers.""" from discord.ext import commands from jarvis.config import get_config -def user_is_bot_admin(): - def predicate(ctx): +def user_is_bot_admin() -> bool: + """Check if a user is a J.A.R.V.I.S. admin.""" + + def predicate(ctx: commands.Context) -> bool: + """Command check predicate.""" if getattr(get_config(), "admins", None): return ctx.author.id in get_config().admins else: @@ -13,10 +17,12 @@ def user_is_bot_admin(): return commands.check(predicate) -def admin_or_permissions(**perms): +def admin_or_permissions(**perms: dict) -> bool: + """Check if a user is an admin or has other perms.""" original = commands.has_permissions(**perms).predicate - async def extended_check(ctx): + async def extended_check(ctx: commands.Context) -> bool: + """Extended check predicate.""" # noqa: D401 return await commands.has_permissions(administrator=True).predicate(ctx) or await original(ctx) return commands.check(extended_check) diff --git a/run.py b/run.py index 95ddec1..a83a96b 100755 --- a/run.py +++ b/run.py @@ -1,4 +1,5 @@ #!/bin/python3 +# flake8: noqa from importlib import reload as ireload from multiprocessing import freeze_support from multiprocessing import Process