import asyncio import builtins import collections import datetime import functools import importlib import inspect import logging import os import re import sys import textwrap import time import traceback import types from contextvars import ContextVar from functools import partial from io import BytesIO, StringIO import aiohttp import discord.ext import redbot import rich from AAA3A_utils.cog import Cog from AAA3A_utils.cogsutils import CogsUtils from AAA3A_utils.context import Context, is_dev from AAA3A_utils.loop import Loop from AAA3A_utils.menus import Menu, Reactions from AAA3A_utils.sentry import SentryHelper from AAA3A_utils.settings import Settings from AAA3A_utils.shared_cog import SharedCog from AAA3A_utils.views import ( Buttons, ChannelSelect, ConfirmationAskView, Dropdown, MentionableSelect, Modal, RoleSelect, Select, UserSelect, ) # NOQA from redbot.core import Config, dev_commands from redbot.core import utils as redutils from redbot.core.utils import chat_formatting as cf from redbot.core.utils.chat_formatting import box from rich.console import Console from rich.table import Table from redbot.core import commands # isort:skip from redbot.core.bot import Red # isort:skip import discord # isort:skip import typing # isort:skip ctxconsole = ContextVar[rich.console.Console]("ctxconsole") class Exit(BaseException): pass def no_colour_rich_markup( *objects: typing.Any, lang: str = "", no_box: typing.Optional[bool] = False ) -> str: """ Slimmed down version of rich_markup which ensure no colours (/ANSI) can exist https://github.com/Cog-Creators/Red-DiscordBot/pull/5538/files (Kowlin) """ temp_console = Console( # Prevent messing with STDOUT's console color_system=None, file=StringIO(), force_terminal=True, width=80, ) temp_console.print(*objects) if no_box: return temp_console.file.getvalue() return box(temp_console.file.getvalue(), lang=lang) # type: ignore class DevSpace: def __init__(self, **kwargs) -> None: self.__dict__.update(**kwargs) def __repr__(self) -> str: items = [f"{k}={v!r}" for k, v in self.__dict__.items()] return ( f"<{self.__class__.__name__} {' '.join(items)}>" if items else f"<{self.__class__.__name__} [Nothing]>" ) def __eq__(self, other: object) -> bool: if isinstance(self, self.__class__) and isinstance(other, self.__class__): return self.__dict__ == other.__dict__ return NotImplemented def __len__(self) -> int: return len(self.__dict__) def __contains__(self, key: str) -> typing.Any: return key in self.__dict__ def __iter__(self) -> typing.Iterator[typing.Tuple[str, typing.Any]]: yield from self.__dict__.items() def __reversed__(self) -> typing.Dict: return self.__dict__.__reversed__() def __getattr__(self, attr: str) -> typing.Any: raise AttributeError(attr) def __setattr__(self, attr: str, value: typing.Any) -> None: self.__dict__[attr] = value def __delattr__(self, attr: str) -> None: del self.__dict__[attr] def __getitem__(self, key: str) -> typing.Any: return self.__dict__[key] def __setitem__(self, key: str, value: typing.Any) -> None: self.__dict__[key] = value def __delitem__(self, key: str) -> None: del self.__dict__[key] def clear(self) -> None: self.__dict__.clear() def update(self, **kwargs) -> None: self.__dict__.update(**kwargs) def copy(self) -> typing.Any: # typing_extensions.Self return self.__class__(**self.__dict__) def items(self): return self.__dict__.items() def keys(self): return self.__dict__.keys() def values(self): return self.__dict__.values() def get(self, key: str, _default: typing.Optional[typing.Any] = None) -> typing.Any: return self.__dict__.get(key, _default) def pop(self, key: str, _default: typing.Optional[typing.Any] = None) -> typing.Any: return self.__dict__.pop(key, _default) def popitem(self) -> typing.Any: return self.__dict__.popitem() def _update_with_defaults( self, defaults: typing.Iterable[typing.Tuple[str, typing.Any]] ) -> None: for key, value in defaults: self.__dict__.setdefault(key, value) class DevEnv(typing.Dict[str, typing.Any]): is_dev = is_dev def __init__(self, *args, **kwargs) -> None: # self.__dict__ = {} super().__init__(*args, **kwargs) self.imported: typing.List[typing.Union[str, typing.Tuple[str, str]]] = [] @classmethod def get_environment( cls, ctx: commands.Context, use_extended_environment: bool = True ) -> typing.Dict[str, typing.Any]: env = cls( # In Dev cog by Zeph. **{ "me": ctx.me, # Redirect builtin console functions to rich. "print": rich.print, "help": functools.partial(rich.inspect, help=True), # Eval and exec automatically put this in, but types.FunctionType does not. "__builtins__": builtins, # Fill in various other environment keys that some code might expect. "__builtin__": builtins, "__doc__": ctx.command.help, "__package__": None, "__loader__": None, "__spec__": None, } ) env["interaction"] = ctx.interaction if getattr(ctx.channel, "category", None) is not None: env["category"] = ctx.channel.category Dev = ctx.bot.get_cog("Dev") if use_extended_environment: env.update(cls.get_env(ctx.bot, ctx)) # My nice env! base_env = dev_commands.Dev.get_environment(Dev, ctx) # In Dev in Core. # del base_env["_"] env.update(base_env) env.update({"dev_env": env, "devenv": env}) return env def get_formatted_env( self, ctx: typing.Optional[commands.Context] = None, show_values: typing.Optional[bool] = True, ) -> str: if show_values: raw_table = Table( "Key", "Value", title="------------------------------ DevEnv ------------------------------", ) else: raw_table = Table( "Key", title="------------------------------ DevEnv ------------------------------" ) for name, value in self.items(): if name in self.imported: continue if show_values: raw_table.add_row(str(name), repr(value)) else: raw_table.add_row(str(name)) raw_table_str = no_colour_rich_markup(raw_table, no_box=True) raw_table_str = self.sanitize_output(self.get("ctx", ctx), raw_table_str) if ctx is not None: asyncio.create_task(Menu(pages=raw_table_str, lang="py").start(ctx)) return raw_table_str def get_formatted_imports(self) -> str: if not (imported := self.imported): return "" imported.sort(key=lambda x: x if isinstance(x, str) else f"z{x[0]}") message = "".join( ( f">>> import {_import}\n" if isinstance(_import, str) else f">>> from {_import[0]} import {_import[1]}\n" ) for _import in imported ) imported.clear() return message def __missing__(self, key: str) -> typing.Any: try: if (value := self["devspace"].get(key)) is not None: return value except (KeyError, AttributeError): pass if not self.get("auto_imports", True): raise KeyError(key) if key in {"exit", "quit"}: raise Exit() try: # this is called implicitly after KeyError, but # some modules would overwrite builtins (e.g. bin) return getattr(builtins, key) except AttributeError: pass try: module = importlib.import_module(key) except ImportError: pass else: self.imported.append(key) self[key] = module return module try: if (cog := self["bot"].get_cog(key)) is not None: self[key] = cog return cog except (KeyError, AttributeError): pass if key.lower().startswith("id"): _id = key[2:] if key[2] != "_" else key[3:] try: _id = int(_id) except ValueError: pass else: try: if (member := self["guild"].get_member(_id)) is not None: self[key] = member return member except (KeyError, AttributeError): pass try: if (user := self["bot"].get_user(_id)) is not None: self[key] = user return user except (KeyError, AttributeError): pass try: if (guild := self["bot"].get_guild(_id)) is not None: self[key] = guild return guild except (KeyError, AttributeError): pass try: if (channel := self["guild"].get_channel(_id)) is not None: self[key] = channel return channel except (KeyError, AttributeError): pass try: if (role := self["guild"].get_role(_id)) is not None: self[key] = role return role except (KeyError, AttributeError): pass try: if (message := self["channel"].get_partial_message(_id)) is not None: self[key] = message return message except (KeyError, AttributeError): pass if (attr := getattr(discord, key, None)) is not None: self.imported.append(("discord", key)) self[key] = attr return attr if (attr := getattr(typing, key, None)) is not None: self.imported.append(("typing", key)) self[key] = attr return attr if (attr := getattr(cf, key, None)) is not None: self.imported.append(("redbot.core.utils.chat_formatting", key)) self[key] = attr return attr try: if is_dev(bot=self["bot"]) and (attr := getattr(CogsUtils, key, None)) is not None: self.imported.append(("AAA3A_utils.CogsUtils", key)) self[key] = attr return attr except (KeyError, AttributeError): pass raise KeyError(key) @staticmethod def sanitize_output(ctx: commands.Context, input_: str) -> str: """Hides the bot's token from a string.""" token = ctx.bot.http.token input_ = CogsUtils.replace_var_paths(input_) return re.sub(re.escape(token), "[EXPUNGED]", input_, re.I) @classmethod def get_env( cls, bot: Red, ctx: typing.Optional[commands.Context] = None ) -> typing.Dict[str, typing.Any]: logger = CogsUtils.get_logger(name="Test") def where(name_or_module: typing.Union[str, types.MethodType]) -> str: name = ( name_or_module if isinstance(name_or_module, str) else (getattr(name_or_module, "__module__", None) or name_or_module.__name__) ).replace("-", "_") spec = importlib.util.find_spec(name) if spec is None: raise RuntimeError("Module `{name}` not found.".format(name=name)) return spec.origin async def _rtfs(ctx: commands.Context, object): code = inspect.getsource(object) await Menu(pages=code, lang="py").start(ctx) def reference(ctx: commands.Context) -> typing.Optional[discord.Message]: if ctx.message.reference is not None and isinstance( ctx.message.reference.resolved, discord.Message ): return ctx.message.reference.resolved # def _console_custom(ctx: commands.Context): # return {"width": 80, "color_system": None} def search_attribute( a, b: typing.Optional[str] = "", startswith: typing.Optional[str] = "" ) -> typing.List[str]: return [ x for x in dir(a) if b.lower() in x.lower() and x.lower().startswith(startswith.lower()) ] async def run_converter( converter: typing.Any, argument: str, label: typing.Optional[str] = "test" ) -> typing.Any: param = discord.ext.commands.parameters.Parameter( name=label, kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=converter ) return await discord.ext.commands.converter.run_converters( ctx, converter=param.converter, argument=argument, param=param ) async def run_converters( command_or_function: typing.Union[str, commands.Command, typing.Callable], arguments: str, ) -> typing.Dict[str, typing.Any]: if isinstance(command_or_function, str): if (command := ctx.bot.get_command(command_or_function)) is None: raise RuntimeError() elif isinstance(command_or_function, commands.Command): command = command_or_function else: command = commands.Command(command_or_function) view = discord.ext.commands.view.StringView(arguments) fake_context = type( "FakeContext", (), { "bot": ctx.bot, "command": command, "message": ctx.message, "prefix": None, "view": view, }, ) await command._parse_arguments(fake_context) kwargs = {} for i, arg in enumerate(fake_context.args[1:]): kwargs[list(command.params.keys())[i]] = arg kwargs.update(fake_context.kwargs) return kwargs def get_internal(ctx: commands.Context): def _get_internal( name: typing.Literal["events", "listeners", "loggers", "parsers", "converters"], b: typing.Optional[str] = "", startswith: typing.Optional[str] = "", ): if name == "events": if b == "": result = ctx.bot.extra_events.copy() else: return ctx.bot.extra_events[b] elif name == "listeners": if b == "": result = ctx.bot._listeners.copy() else: return ctx.bot._listeners[b] elif name == "loggers": result = logging.Logger.manager.loggerDict.copy() elif name == "parsers": result = ctx.bot._get_websocket(0)._discord_parsers.copy() elif name == "converters": result = discord.ext.commands.converter.CONVERTER_MAPPING.copy() else: raise ValueError(name) result = { name: value for name, value in result.items() if b.lower() in name.lower() and name.lower().startswith(startswith.lower()) } return result return _get_internal def set_loggers_level( level: typing.Optional[str] = logging.DEBUG, loggers: typing.Optional[typing.List] = None, exclusions: typing.Optional[typing.List] = None, b: typing.Optional[str] = "", startswith: typing.Optional[str] = "", ) -> int: __loggers = logging.Logger.manager.loggerDict if loggers is not None: _loggers = [ logger for name, logger in __loggers.items() if name in loggers and isinstance(logger, logging.Logger) ] else: _loggers = [ logger for logger in __loggers.values() if isinstance(logger, logging.Logger) ] _loggers = [ logger for logger in _loggers if b.lower() in logger.name and logger.name.lower().startswith(startswith.lower()) ] if exclusions is not None: _loggers = [logger for logger in _loggers if logger.name not in exclusions] for logger in _loggers: logger.setLevel(level) return len(_loggers) def params(_object: typing.Any) -> None: return {param.name: param for param in inspect.signature(_object).parameters.values()} def find_all(predicate, iterable: collections.abc.Iterable) -> typing.List[typing.Any]: if hasattr(iterable, "__aiter__"): async def _a_find_all(): return [element async for element in iterable if predicate(element)] return _a_find_all() else: return [element for element in iterable if predicate(element)] def get_all(iterable: collections.abc.Iterable, **attrs) -> typing.List[typing.Any]: attrget = discord.utils.attrgetter converted = [ (attrget(attr.replace("__", ".")), value) for attr, value in attrs.items() ] if hasattr(iterable, "__aiter__"): async def _a_get_all(): return [ element async for element in iterable if all(pred(element) == value for pred, value in converted) ] return _a_get_all() else: return [ element for element in iterable if all(pred(element) == value for pred, value in converted) ] dev_space: DevSpace = getattr(ctx.bot.get_cog("Dev"), "dev_space", AttributeError()) def get_url(ctx: commands.Context): async def _get_url(url: str, **kwargs): async with ctx.bot.get_cog("Dev")._session.get(url=url, **kwargs) as r: return r return _get_url if is_dev(bot=bot): env = { # CogsUtils "CogsUtils": lambda ctx: CogsUtils, "Loop": lambda ctx: Loop, "Reactions": lambda ctx: Reactions, "Menu": lambda ctx: Menu, "SharedCog": lambda ctx: SharedCog, "Cog": lambda ctx: Cog, "Context": lambda ctx: Context, "Settings": lambda ctx: Settings, "SentryHelper": lambda ctx: SentryHelper, "logger": lambda ctx: logger, "_rtfs": lambda ctx: partial(_rtfs, ctx), "DevEnv": lambda ctx: cls, "DevSpace": lambda ctx: DevSpace, "Cogs": lambda ctx: CogsCommands.Cogs( bot=ctx.bot, Cog=CogsCommands.Cog, Command=CogsCommands.Command ), "Commands": lambda ctx: CogsCommands.Commands( bot=ctx.bot, Cog=CogsCommands.Cog, Command=CogsCommands.Command ), } # Dpy2 things env.update( { "ConfirmationAskView": lambda ctx: ConfirmationAskView, "Buttons": lambda ctx: Buttons, "Dropdown": lambda ctx: Dropdown, "Select": lambda ctx: Select, "ChannelSelect": lambda ctx: ChannelSelect, "MentionableSelect": lambda ctx: MentionableSelect, "RoleSelect": lambda ctx: RoleSelect, "UserSelect": lambda ctx: UserSelect, "Modal": lambda ctx: Modal, } ) else: env = {} env.update( { # Dpy & Red "discord": lambda ctx: discord, "redbot": lambda ctx: redbot, "Red": lambda ctx: Red, "redutils": lambda ctx: redutils, "cf": lambda ctx: cf, "Config": lambda ctx: Config, "run_converter": lambda ctx: run_converter, "run_converters": lambda ctx: run_converters, "Route": lambda ctx: discord.http.Route, "websocket": lambda ctx: ctx.bot._get_websocket(ctx.guild.id), "get_internal": get_internal, "set_loggers_level": lambda ctx: set_loggers_level, "find": lambda ctx: discord.utils.find, "get": lambda ctx: discord.utils.get, "MISSING": lambda ctx: discord.utils.MISSING, "escape_markdown": lambda ctx: discord.utils.escape_markdown, "as_chunks": lambda ctx: discord.utils.as_chunks, "format_dt": lambda ctx: discord.utils.format_dt, # Ty Lemon. "params": lambda ctx: params, "find_all": lambda ctx: find_all, "get_all": lambda ctx: get_all, # Dev Space "dev_space": lambda ctx: dev_space, "devspace": lambda ctx: dev_space, # Typing "typing": lambda ctx: typing, # Inspect "inspect": lambda ctx: inspect, "source": lambda ctx: lambda _object: rich.print( textwrap.dedent(inspect.getsource(_object)).strip() ), "gs": lambda ctx: lambda _object: rich.print( textwrap.dedent(inspect.getsource(_object)).strip() ), # "gs": lambda ctx: inspect.getsource, # logging "logging": lambda ctx: logging, # Date & Time "datetime": lambda ctx: datetime, "time": lambda ctx: time, "utc_now": lambda ctx: datetime.datetime.now(tz=datetime.timezone.utc), "local_now": lambda ctx: datetime.datetime.now(), "get_utc_now": lambda ctx: functools.partial( datetime.datetime.now, tz=datetime.timezone.utc ), "get_local_now": lambda ctx: datetime.datetime.now, # Os & Sys "os": lambda ctx: os, "sys": lambda ctx: sys, # Aiohttp "aiohttp": lambda ctx: aiohttp, "session": lambda ctx: ctx.bot.get_cog("Dev")._session, "get_url": get_url, "BytesIO": lambda ctx: BytesIO, # TextWrap "textwrap": lambda ctx: textwrap, # Search attributes "search_attrs": lambda ctx: search_attribute, # search python library "where": lambda ctx: where, # `reference` "reference": reference, # No color (Dev cog from fluffy-cogs in mobile). # "_console_custom": _console_custom, # Dpy get "get_cog": lambda ctx: ctx.bot.get_cog, "get_command": lambda ctx: ctx.bot.get_command, "get_guild": lambda ctx: ctx.bot.get_guild, "get_channel": lambda ctx: ctx.guild.get_channel, "fetch_message": lambda ctx: ctx.channel.fetch_message, # Fake "token": lambda ctx: "[EXPUNGED]", } ) if ctx is not None: _env = {} for name, value in env.items(): try: _env[name] = value(ctx) except Exception as exc: traceback.clear_frames(exc.__traceback__) _env[name] = exc else: _env = env return _env class CogsCommands: class Cog(commands.Cog): def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) @classmethod def _setup(cls, bot: Red, Cog, Command, cog) -> typing.Any: # typing_extensions.Self c = cls() c.__dict__ = cog.__dict__ c.__cog_name__ = cog.__cog_name__ c.bot = bot c.Cog = Cog c.Command = Command c.original_object = cog return c def __repr__(self) -> str: if getattr(self, "original_object", None) is not None: return repr(self.original_object) return super().__repr__() def __len__(self) -> int: cog = self source = { command.name: command for command in self.bot.all_commands.values() if getattr(command.cog, "qualified_name", None) == getattr(cog, "qualified_name", None) } return len(source) def __contains__(self, key: str) -> bool: cog = self source = { command.name: command for command in self.bot.all_commands.values() if getattr(command.cog, "qualified_name", None) == getattr(cog, "qualified_name", None) } return key in source def __iter__(self) -> typing.Iterator[typing.Tuple[str, typing.Any]]: cog = self source = { command.name: command for command in self.bot.all_commands.values() if getattr(command.cog, "qualified_name", None) == getattr(cog, "qualified_name", None) } _items = source items = { key: self.Command._setup( bot=self.bot, Cog=self.Cog, Command=self.Command, command=value ) for key, value in _items.items() } yield from items.items() def __getitem__(self, key: str) -> typing.Any: cog = self source = { command.name: command for command in self.bot.all_commands.values() if getattr(command.cog, "qualified_name", None) == getattr(cog, "qualified_name", None) } _item = source[key] return self.Command._setup( bot=self.bot, Cog=self.Cog, Command=self.Command, command=_item ) def items(self): cog = self source = { command.name: command for command in self.bot.all_commands.values() if getattr(command.cog, "qualified_name", None) == getattr(cog, "qualified_name", None) } _items = source return { key: self.Command._setup( bot=self.bot, Cog=self.Cog, Command=self.Command, command=value ) for key, value in _items.items() } def keys(self): cog = self source = { command.name: command for command in self.bot.all_commands.values() if getattr(command.cog, "qualified_name", None) == getattr(cog, "qualified_name", None) } return source.keys() def values(self): cog = self source = { command.name: command for command in self.bot.all_commands.values() if getattr(command.cog, "qualified_name", None) == getattr(cog, "qualified_name", None) } _items = source items = { key: self.Command._setup( bot=self.bot, Cog=self.Cog, Command=self.Command, command=value ) for key, value in _items.items() } return items.values() class Command(commands.Command): def __init__(func, *args, **kwargs): super().__init__(func=func, *args, **kwargs) @classmethod def _setup(cls, bot: Red, Cog, Command, command) -> typing.Any: # typing_extensions.Self c = cls(command.callback) c.__dict__ = command.__dict__ c.bot = bot c.Cog = Cog c.Command = Command c.original_object = command return c def __repr__(self) -> str: if getattr(self, "original_object", None) is not None: return repr(self.original_object) return super().__repr__() def __eq__(self, other) -> bool: return ( isinstance(other, commands.Command) and other.qualified_name == self.qualified_name and other.callback == self.callback ) def __len__(self) -> int: command = self source = { c.name: c for c in self.bot.walk_commands() if getattr(c.parent, "qualified_name", None) == command.qualified_name } return len(source) def __contains__(self, key: str) -> bool: command = self source = { c.name: c for c in self.bot.walk_commands() if getattr(c.parent, "qualified_name", None) == command.qualified_name } return key in source def __iter__(self) -> typing.Iterator[typing.Tuple[str, typing.Any]]: command = self source = { c.name: c for c in self.bot.walk_commands() if getattr(c.parent, "qualified_name", None) == command.qualified_name } _items = source items = { key: self.Command._setup( bot=self.bot, Cog=self.Cog, Command=self.Command, command=value ) for key, value in _items.items() } yield from items.items() def __getitem__(self, key: str) -> typing.Any: command = self source = { c.name: c for c in self.bot.walk_commands() if getattr(c.parent, "qualified_name", None) == command.qualified_name } _item = source[key] return self.Command._setup( bot=self.bot, Cog=self.Cog, Command=self.Command, command=_item ) def items(self): command = self source = { c.name: c for c in self.bot.walk_commands() if getattr(c.parent, "qualified_name", None) == command.qualified_name } _items = source return { key: self.Command._setup( bot=self.bot, Cog=self.Cog, Command=self.Command, command=value ) for key, value in _items.items() } def keys(self): command = self source = { c.name: c for c in self.bot.walk_commands() if getattr(c.parent, "qualified_name", None) == command.qualified_name } return source.keys() def values(self): command = self source = { c.name: c for c in self.bot.walk_commands() if getattr(c.parent, "qualified_name", None) == command.qualified_name } _items = source items = { key: self.Command._setup( bot=self.bot, Cog=self.Cog, Command=self.Command, command=value ) for key, value in _items.items() } return items.values() class Cogs: def __init__(self, bot: Red, Cog, Command) -> None: self.bot: Red = bot self.Cog = Cog self.Command = Command def __eq__(self, other: object) -> bool: return isinstance(self, self.__class__) and isinstance(other, self.__class__) def __len__(self) -> int: source = {cog.qualified_name: cog for cog in self.bot.cogs.values()} return len(source) def __contains__(self, key: str) -> bool: source = {cog.qualified_name: cog for cog in self.bot.cogs.values()} return key in source def __iter__(self) -> typing.Iterator[typing.Tuple[str, typing.Any]]: source = {cog.qualified_name: cog for cog in self.bot.cogs.values()} _items = source.items() yield from { key: self.Cog._setup(bot=self.bot, Cog=self.Cog, Command=self.Command, cog=value) for key, value in _items } def __getitem__(self, key: str) -> typing.Any: source = {cog.qualified_name: cog for cog in self.bot.cogs.values()} _item = source[key] return self.Cog._setup(bot=self.bot, Cog=self.Cog, Command=self.Command, cog=_item) def items(self): source = {cog.qualified_name: cog for cog in self.bot.cogs.values()} _items = source.items() return { key: self.Cog._setup(bot=self.bot, Cog=self.Cog, Command=self.Command, cog=value) for key, value in _items } def keys(self): source = {cog.qualified_name: cog for cog in self.bot.cogs.values()} return source.keys() def values(self): source = {cog.qualified_name: cog for cog in self.bot.cogs.values()} _items = source.items() items = { key: self.Cog._setup(bot=self.bot, Cog=self.Cog, Command=self.Command, cog=value) for key, value in _items } return items.values() class Commands: def __init__(self, bot: Red, Cog, Command) -> None: self.bot: Red = bot self.Cog = Cog self.Command = Command def __eq__(self, other: object) -> bool: return isinstance(self, self.__class__) and isinstance(other, self.__class__) def __len__(self) -> int: source = {command.name: command for command in self.bot.all_commands.values()} return len(source) def __contains__(self, key: str) -> bool: source = {command.name: command for command in self.bot.all_commands.values()} return key in source def __iter__(self) -> typing.Iterator[typing.Tuple[str, typing.Any]]: source = {command.name: command for command in self.bot.all_commands.values()} _items = source.items() yield from { key: self.Command._setup( bot=self.bot, Cog=self.Cog, Command=self.Command, command=value ) for key, value in _items.items() } def __getitem__(self, key: str) -> typing.Any: source = {command.name: command for command in self.bot.all_commands.values()} _item = source[key] return self.Command._setup( bot=self.bot, Cog=self.Cog, Command=self.Command, command=_item ) def items(self): source = {command.name: command for command in self.bot.all_commands.values()} _items = source.items() return { key: self.Command._setup( bot=self.bot, Cog=self.Cog, Command=self.Command, command=value ) for key, value in _items.items() } def keys(self): source = {command.name: command for command in self.bot.all_commands.values()} return source.keys() def values(self): source = {command.name: command for command in self.bot.all_commands.values()} _items = source.items() items = { key: self.Command._setup( bot=self.bot, Cog=self.Cog, Command=self.Command, command=value ) for key, value in _items.items() } return items.values()