From 6153ad18474da74ad3bf2b0e3d8bbcc785f3f834 Mon Sep 17 00:00:00 2001 From: AAA3A <89632044+AAA3A-AAA3A@users.noreply.github.com> Date: Tue, 4 Apr 2023 19:44:15 +0200 Subject: [PATCH 01/11] [Dashboard] Add third parties. --- dashboard/baserpc.py | 69 +++++++---------- dashboard/rpc/thirdparties.py | 139 ++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 43 deletions(-) create mode 100644 dashboard/rpc/thirdparties.py diff --git a/dashboard/baserpc.py b/dashboard/baserpc.py index 95f1f7c3..3d8f7381 100644 --- a/dashboard/baserpc.py +++ b/dashboard/baserpc.py @@ -18,6 +18,7 @@ from .rpc.permissions import DashboardRPC_Permissions from .rpc.utils import rpccheck from .rpc.webhooks import DashboardRPC_Webhooks +from .rpc.thirdparties import DashboardRPC_ThirdParties HUMANIZED_PERMISSIONS = { "view": "View server on dashboard", @@ -51,6 +52,8 @@ def __init__(self, cog: commands.Cog): self.extensions.append(DashboardRPC_Permissions(self.cog)) self.extensions.append(DashboardRPC_AliasCC(self.cog)) self.extensions.append(DashboardRPC_Webhooks(self.cog)) + self.third_parties_handler = DashboardRPC_ThirdParties(self.cog) + self.extensions.append(self.third_parties_handler) # To make sure that both RPC server and client are on the same "version" self.version = random.randint(1, 10000) @@ -199,15 +202,12 @@ async def get_variables(self): "uptime": uptime_str, }, }, + "third_parties": await self.third_parties_handler.get_third_parties(), } if self.owner is None: app_info = await self.bot.application_info() - if app_info.team: - self.owner = str(app_info.team.name) - else: - self.owner = str(app_info.owner) - + self.owner = str(app_info.team.name) if app_info.team else str(app_info.owner) returning["bot"]["owner"] = self.owner return returning @@ -220,12 +220,7 @@ async def get_commands(self): returning = [] downloader = self.bot.get_cog("Downloader") for name, cog in self.bot.cogs.copy().items(): - stripped = [] - - for c in cog.__cog_commands__: - if not c.parent: - stripped.append(c) - + stripped = [c for c in cog.__cog_commands__ if not c.parent] cmds = await self.build_cmd_list(stripped, do_escape=False) if not cmds: continue @@ -234,28 +229,23 @@ async def get_commands(self): repo = "Unknown" # Taken from Trusty's downloader fuckery, # https://gist.github.com/TrustyJAID/784c8c32dd45b1cc8155ed42c0c56591 - if name not in self.cog_info_cache: - if downloader: - module = downloader.cog_name_from_instance(cog) - installed, cog_info = await downloader.is_installed(module) - if installed: - author = humanize_list(cog_info.author) if cog_info.author else "Unknown" - try: - repo = ( - cog_info.repo.clean_url if cog_info.repo.clean_url else "Unknown" - ) - except AttributeError: - repo = "Unknown (Removed from Downloader)" - elif cog.__module__.startswith("redbot."): - author = "Cog Creators" - repo = "https://github.com/Cog-Creators/Red-DiscordBot" - self.cog_info_cache[name] = {} - self.cog_info_cache[name]["author"] = author - self.cog_info_cache[name]["repo"] = repo - else: + if name in self.cog_info_cache: author = self.cog_info_cache[name]["author"] repo = self.cog_info_cache[name]["repo"] + elif downloader: + module = downloader.cog_name_from_instance(cog) + installed, cog_info = await downloader.is_installed(module) + if installed: + author = humanize_list(cog_info.author) if cog_info.author else "Unknown" + try: + repo = cog_info.repo.clean_url or "Unknown" + except AttributeError: + repo = "Unknown (Removed from Downloader)" + elif cog.__module__.startswith("redbot."): + author = "Cog Creators" + repo = "https://github.com/Cog-Creators/Red-DiscordBot" + self.cog_info_cache[name] = {"author": author, "repo": repo} returning.append( { "name": escape(name or ""), @@ -265,8 +255,7 @@ async def get_commands(self): "repo": repo, } ) - returning = sorted(returning, key=lambda k: k["name"]) - return returning + return sorted(returning, key=lambda k: k["name"]) @rpccheck() async def get_users_servers(self, userid: int, page: int): @@ -347,13 +336,9 @@ async def get_server(self, userid: int, serverid: int): user = guild.get_member(userid) baseuser = self.bot.get_user(userid) - is_owner = False - if await self.bot.is_owner(baseuser): - is_owner = True - - if not user: - if not baseuser and not is_owner: - return {"status": 0} + is_owner = bool(await self.bot.is_owner(baseuser)) + if not user and not baseuser and not is_owner: + return {"status": 0} if is_owner: humanized = ["Everything (Bot Owner)"] @@ -409,15 +394,13 @@ async def get_server(self, userid: int, serverid: int): adminroles = [] ar = await self.bot._config.guild(guild).admin_role() for rid in ar: - r = guild.get_role(rid) - if r: + if r := guild.get_role(rid): adminroles.append((rid, r.name)) modroles = [] mr = await self.bot._config.guild(guild).mod_role() for rid in mr: - r = guild.get_role(rid) - if r: + if r := guild.get_role(rid): modroles.append((rid, r.name)) all_roles = [(r.id, r.name) for r in guild.roles] diff --git a/dashboard/rpc/thirdparties.py b/dashboard/rpc/thirdparties.py new file mode 100644 index 00000000..da68c80b --- /dev/null +++ b/dashboard/rpc/thirdparties.py @@ -0,0 +1,139 @@ +from redbot.core.bot import Red +from redbot.core.commands import commands + +import discord +import inspect +import typing + +from .utils import rpccheck + + +def dashboard_page(name: typing.Optional[str] = None, methods: typing.List[str] = ["GET"], required_kwargs: typing.List[str] = None, permissions_required: typing.List[str] = ["view"], hidden: bool = False): + if required_kwargs is None: + required_kwargs = [] + def decorator(func: typing.Callable): + if name is not None and not isinstance(name, str): + raise TypeError("Name of a page must be a string.") + if name is not None: + discord.app_commands.commands.validate_name(name) + if not inspect.iscoroutinefunction(func): + raise TypeError("Func must be a coroutine.") + params = {"name": name, "methods": methods, "context_ids": [], "required_kwargs": required_kwargs, "permissions_required": permissions_required, "hidden": hidden} + for key, value in inspect.signature(func).parameters.items(): + if value.name == "self" or value.kind in [inspect._ParameterKind.POSITIONAL_ONLY, inspect._ParameterKind.VAR_KEYWORD]: + continue + if key in ["user_id", "guild_id", "member_id", "role_id", "channel_id"] and key not in params["context_ids"]: + params["context_ids"].append(key) + if value.default is inspect._empty: + continue + elif f"{key}_id" in ["user_id", "guild_id", "member_id", "role_id", "channel_id"] and f"{key}_id" not in params["context_ids"]: + params["context_ids"].append(f"{key}_id") + elif key not in ["method", "lang_code"]: + params["required_kwargs"].append(key) + # A guild must be chose for these kwargs. + for key in ["member_id", "role_id", "channel_id"]: + if key in params["context_ids"] and "guild_id" not in params["context_ids"]: + params["context_ids"].append("guild_id") + # No guild available without user connection. + if "guild_id" in params["context_ids"]: + if "user_id" not in params["context_ids"]: + params["context_ids"].append("user_id") + else: + params["required_kwargs"] = [] + func.__dashboard_params__ = params.copy() + return func + return decorator + + +class DashboardRPC_ThirdParties: + def __init__(self, cog: commands.Cog): + self.bot: Red = cog.bot + self.cog: commands.Cog = cog + + self.third_parties: typing.Dict[str, typing.Dict[str, typing.Tuple[typing.Callable, typing.Dict[str, bool]]]] = {} + self.third_parties_cogs: typing.Dict[str, commands.Cog] = {} + + self.bot.register_rpc_handler(self.data_receive) + + def unload(self): + self.bot.unregister_rpc_handler(self.data_receive) + + def add_third_party(self, cog: commands.Cog, overwrite: bool = False): + cog_name = cog.qualified_name.lower() + if cog_name in self.third_parties and not overwrite: + raise RuntimeError(f"The cog {cog_name} is already an existing third party ") + _pages = {} + for attr in dir(cog): + if hasattr((func := getattr(cog, attr)), "__dashboard_params__"): + page = func.__dashboard_params__["name"] + if page in _pages: + raise RuntimeError(f"The page {page} is already an existing page for this third party ") + _pages[page] = (func, func.__dashboard_params__) + if not _pages: + raise RuntimeError("No page found.") + self.third_parties[cog_name] = _pages + self.third_parties_cogs[cog_name] = cog + + def remove_third_party(self, cog: str): + cog_name = cog.qualified_name.lower() + try: + del self.third_parties_cogs[cog_name] + except KeyError: + pass + return self.third_parties.pop(cog_name, None) + + @rpccheck() + async def get_third_parties(self): + return {key: {k: v[1] for k, v in value.items()} for key, value in self.third_parties.items()} + + @rpccheck() + async def data_receive(self, method: str, cog_name: str, page: str, context_ids: typing.Optional[typing.Dict[str, int]] = None, kwargs: typing.Dict[str, typing.Any] = None, lang_code: typing.Optional[str] = None) -> typing.Dict[str, typing.Any]: + if context_ids is None: + context_ids = {} + if kwargs is None: + kwargs = {} + cog_name = cog_name.lower() + if not cog_name or cog_name not in self.third_parties or cog_name not in self.third_parties_cogs: + return {"status": 1, "message": "Third party not found.", "error_message": "404: Looks like that third party doesn't exist... Strange..."} + if self.bot.get_cog(self.third_parties_cogs[cog_name].qualified_name) is None: + return {"status": 1, "message": "Third party not loaded.", "error_message": "404: Looks like that third party doesn't exist... Strange..."} + page = page.lower() if page is not None else page + if page not in self.third_parties[cog_name]: + return {"status": 1, "message": "Page not found.", "error_message": "404: Looks like that page doesn't exist... Strange..."} + kwargs["method"] = method + if "user_id" in self.third_parties[cog_name][page][1]["context_ids"]: + if (user := self.bot.get_user(context_ids["user_id"])) is None: + return {"status": 1, "message": "Page not found.", "error_message": "404: Looks like that I do not share any server with you..."} + kwargs["user_id"] = context_ids["user_id"] + kwargs["user"] = user + if "guild_id" in self.third_parties[cog_name][page][1]["context_ids"] and "user_id" in self.third_parties[cog_name][page][1]["context_ids"]: + if (guild := self.bot.get_guild(context_ids["guild_id"])) is None: + return {"status": 1, "message": "Page not found.", "error_message": "404: Looks like that I'm not in this server..."} + if (m := guild.get_member(context_ids["user_id"])) is None: + return {"status": 1, "message": "Page not found.", "error_message": "403: Looks like that you're not in this server..."} + if m.id != guild.owner.id: + perms = self.cog.rpc.get_perms(guildid=guild.id, m=m) + if perms is None: + return {"status": 1, "message": "Page not found.", "error_message": "403: Looks like that you haven't permissions in this server..."} + for permission in self.third_parties[cog_name][page][1]["permissions_required"]: + if permission not in perms: + return {"status": 1, "message": "Page not found.", "error_message": "403: Looks like that you haven't permissions in this server..."} + kwargs["guild_id"] = context_ids["guild_id"] + kwargs["guild"] = guild + if "member_id" in self.third_parties[cog_name][page][1]["context_ids"]: + if (member := guild.get_member(context_ids["member_id"])) is None: + return {"status": 1, "message": "Page not found.", "error_message": "404: Looks like that this member is not found in this guild..."} + kwargs["member_id"] = context_ids["member_id"] + kwargs["member"] = member + if "role_id" in self.third_parties[cog_name][page][1]["context_ids"]: + if (role := guild.get_role(context_ids["role_id"])) is None: + return {"status": 1, "message": "Page not found.", "error_message": "404: Looks like that this role is not found in this guild..."} + kwargs["role_id"] = context_ids["role_id"] + kwargs["role"] = role + if "channel_id" in self.third_parties[cog_name][page][1]["context_ids"]: + if (channel := guild.get_channel(context_ids["channel_id"])) is None: + return {"status": 1, "message": "Page not found.", "error_message": "404: Looks like that this channel is not found in this guild..."} + kwargs["channel_id"] = context_ids["channel_id"] + kwargs["channel"] = channel + kwargs["lang_code"] = lang_code or "en-EN" + return await self.third_parties[cog_name][page][0](**kwargs) From c4be96088f91846600d4b5ce6318a247cdf4c133 Mon Sep 17 00:00:00 2001 From: AAA3A <89632044+AAA3A-AAA3A@users.noreply.github.com> Date: Wed, 5 Apr 2023 16:57:50 +0200 Subject: [PATCH 02/11] Update thirdparties.py --- dashboard/rpc/thirdparties.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dashboard/rpc/thirdparties.py b/dashboard/rpc/thirdparties.py index da68c80b..a1246b40 100644 --- a/dashboard/rpc/thirdparties.py +++ b/dashboard/rpc/thirdparties.py @@ -11,6 +11,7 @@ def dashboard_page(name: typing.Optional[str] = None, methods: typing.List[str] = ["GET"], required_kwargs: typing.List[str] = None, permissions_required: typing.List[str] = ["view"], hidden: bool = False): if required_kwargs is None: required_kwargs = [] + def decorator(func: typing.Callable): if name is not None and not isinstance(name, str): raise TypeError("Name of a page must be a string.") @@ -22,10 +23,10 @@ def decorator(func: typing.Callable): for key, value in inspect.signature(func).parameters.items(): if value.name == "self" or value.kind in [inspect._ParameterKind.POSITIONAL_ONLY, inspect._ParameterKind.VAR_KEYWORD]: continue + if value.default is not inspect._empty: + continue if key in ["user_id", "guild_id", "member_id", "role_id", "channel_id"] and key not in params["context_ids"]: params["context_ids"].append(key) - if value.default is inspect._empty: - continue elif f"{key}_id" in ["user_id", "guild_id", "member_id", "role_id", "channel_id"] and f"{key}_id" not in params["context_ids"]: params["context_ids"].append(f"{key}_id") elif key not in ["method", "lang_code"]: From 400c4accc6fddc235bd5d3618480ac1f30f5d2a0 Mon Sep 17 00:00:00 2001 From: AAA3A <89632044+AAA3A-AAA3A@users.noreply.github.com> Date: Fri, 7 Apr 2023 16:55:15 +0200 Subject: [PATCH 03/11] Update baserpc.py --- dashboard/baserpc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dashboard/baserpc.py b/dashboard/baserpc.py index 3d8f7381..db33ff95 100644 --- a/dashboard/baserpc.py +++ b/dashboard/baserpc.py @@ -222,8 +222,8 @@ async def get_commands(self): for name, cog in self.bot.cogs.copy().items(): stripped = [c for c in cog.__cog_commands__ if not c.parent] cmds = await self.build_cmd_list(stripped, do_escape=False) - if not cmds: - continue + # if not cmds: + # continue author = "Unknown" repo = "Unknown" From bacdf7ae8f7b26cea06035c2b14519e5cf483968 Mon Sep 17 00:00:00 2001 From: AAA3A <89632044+AAA3A-AAA3A@users.noreply.github.com> Date: Fri, 7 Apr 2023 17:18:13 +0200 Subject: [PATCH 04/11] Update thirdparties.py --- dashboard/rpc/thirdparties.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/dashboard/rpc/thirdparties.py b/dashboard/rpc/thirdparties.py index a1246b40..7d63e7d0 100644 --- a/dashboard/rpc/thirdparties.py +++ b/dashboard/rpc/thirdparties.py @@ -8,7 +8,7 @@ from .utils import rpccheck -def dashboard_page(name: typing.Optional[str] = None, methods: typing.List[str] = ["GET"], required_kwargs: typing.List[str] = None, permissions_required: typing.List[str] = ["view"], hidden: bool = False): +def dashboard_page(name: typing.Optional[str] = None, methods: typing.List[str] = ["GET"], required_kwargs: typing.List[str] = None, permissions_required: typing.List[str] = ["view"], hidden: typing.Optional[bool] = None): if required_kwargs is None: required_kwargs = [] @@ -19,7 +19,7 @@ def decorator(func: typing.Callable): discord.app_commands.commands.validate_name(name) if not inspect.iscoroutinefunction(func): raise TypeError("Func must be a coroutine.") - params = {"name": name, "methods": methods, "context_ids": [], "required_kwargs": required_kwargs, "permissions_required": permissions_required, "hidden": hidden} + params = {"name": name, "methods": methods, "context_ids": [], "required_kwargs": required_kwargs, "permissions_required": permissions_required, "hidden": None} for key, value in inspect.signature(func).parameters.items(): if value.name == "self" or value.kind in [inspect._ParameterKind.POSITIONAL_ONLY, inspect._ParameterKind.VAR_KEYWORD]: continue @@ -36,13 +36,16 @@ def decorator(func: typing.Callable): if key in params["context_ids"] and "guild_id" not in params["context_ids"]: params["context_ids"].append("guild_id") # No guild available without user connection. - if "guild_id" in params["context_ids"]: - if "user_id" not in params["context_ids"]: - params["context_ids"].append("user_id") - else: - params["required_kwargs"] = [] + if ( + "guild_id" in params["context_ids"] + and "user_id" not in params["context_ids"] + ): + params["context_ids"].append("user_id") + if params["hidden"] is None: + params["hidden"] = bool(params["required_kwargs"]) func.__dashboard_params__ = params.copy() return func + return decorator @@ -75,7 +78,7 @@ def add_third_party(self, cog: commands.Cog, overwrite: bool = False): self.third_parties[cog_name] = _pages self.third_parties_cogs[cog_name] = cog - def remove_third_party(self, cog: str): + def remove_third_party(self, cog: commands.Cog): cog_name = cog.qualified_name.lower() try: del self.third_parties_cogs[cog_name] From e113019a0a728b9f588e71a33c91df07733e7b38 Mon Sep 17 00:00:00 2001 From: AAA3A <89632044+AAA3A-AAA3A@users.noreply.github.com> Date: Fri, 7 Apr 2023 18:09:02 +0200 Subject: [PATCH 05/11] Update baserpc.py --- dashboard/baserpc.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dashboard/baserpc.py b/dashboard/baserpc.py index db33ff95..40fbd3dd 100644 --- a/dashboard/baserpc.py +++ b/dashboard/baserpc.py @@ -174,7 +174,7 @@ async def get_variables(self): try: botavatar = str(self.bot.user.avatar_url_as(static_format="png")) except AttributeError: - botavatar = str(self.bot.user.avatar) + botavatar = str(self.bot.user.display_avatar) returning = { "bot": { @@ -297,6 +297,7 @@ async def get_users_servers(self, userid: int, page: int): )(), "go": False, } + if is_owner: guilds.append(sgd) continue From a0f2ed4e52960d849457fba507d01ead0d326fae Mon Sep 17 00:00:00 2001 From: AAA3A <89632044+AAA3A-AAA3A@users.noreply.github.com> Date: Sat, 15 Apr 2023 13:38:51 +0200 Subject: [PATCH 06/11] [Dashboard] Dispatch `dashboard_cog_add` event when cog loading. --- dashboard/baserpc.py | 6 +----- dashboard/rpc/thirdparties.py | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/dashboard/baserpc.py b/dashboard/baserpc.py index 40fbd3dd..ad2e0d5b 100644 --- a/dashboard/baserpc.py +++ b/dashboard/baserpc.py @@ -387,11 +387,7 @@ async def get_server(self, userid: int, serverid: int): else: vl = "Unknown" - if not self.cog.configcache.get(serverid, {"roles": []})["roles"]: - warn = True - else: - warn = False - + warn = not self.cog.configcache.get(serverid, {"roles": []})["roles"] adminroles = [] ar = await self.bot._config.guild(guild).admin_role() for rid in ar: diff --git a/dashboard/rpc/thirdparties.py b/dashboard/rpc/thirdparties.py index 7d63e7d0..7034fe09 100644 --- a/dashboard/rpc/thirdparties.py +++ b/dashboard/rpc/thirdparties.py @@ -42,7 +42,7 @@ def decorator(func: typing.Callable): ): params["context_ids"].append("user_id") if params["hidden"] is None: - params["hidden"] = bool(params["required_kwargs"]) + params["hidden"] = params["required_kwargs"] or [x for x in params["context_ids"] if x not in ["user_id", "guild_id"]] func.__dashboard_params__ = params.copy() return func @@ -58,9 +58,25 @@ def __init__(self, cog: commands.Cog): self.third_parties_cogs: typing.Dict[str, commands.Cog] = {} self.bot.register_rpc_handler(self.data_receive) + self.bot.add_listener(self.on_cog_add) + self.bot.dispatch("dashboard_cog_add", self.cog) def unload(self): self.bot.unregister_rpc_handler(self.data_receive) + self.bot.remove_listener(self.on_cog_add) + + @commands.Cog.listener() + async def on_cog_add(self, cog: commands.Cog): + ev = "on_dashboard_cog_add" + funcs = [listener[1] for listener in cog.get_listeners() if listener[0] == ev] + for func in funcs: + self.bot._schedule_event(func, ev, self.cog) # like in bot.dispatch + + @commands.Cog.listener() + async def on_cog_remove(self, cog: commands.Cog): + if cog not in self.third_parties_cogs.values(): + return + self.remove_third_party(cog) def add_third_party(self, cog: commands.Cog, overwrite: bool = False): cog_name = cog.qualified_name.lower() From af258b8657f577623152a1c672aa11501a8b36ad Mon Sep 17 00:00:00 2001 From: AAA3A <89632044+AAA3A-AAA3A@users.noreply.github.com> Date: Sat, 15 Apr 2023 23:23:24 +0200 Subject: [PATCH 07/11] Update thirdparties.py --- dashboard/rpc/thirdparties.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dashboard/rpc/thirdparties.py b/dashboard/rpc/thirdparties.py index 7034fe09..1c14b1bd 100644 --- a/dashboard/rpc/thirdparties.py +++ b/dashboard/rpc/thirdparties.py @@ -19,7 +19,7 @@ def decorator(func: typing.Callable): discord.app_commands.commands.validate_name(name) if not inspect.iscoroutinefunction(func): raise TypeError("Func must be a coroutine.") - params = {"name": name, "methods": methods, "context_ids": [], "required_kwargs": required_kwargs, "permissions_required": permissions_required, "hidden": None} + params = {"name": name, "methods": methods, "context_ids": [], "required_kwargs": required_kwargs, "permissions_required": permissions_required, "hidden": None, "real_cog_name": None} for key, value in inspect.signature(func).parameters.items(): if value.name == "self" or value.kind in [inspect._ParameterKind.POSITIONAL_ONLY, inspect._ParameterKind.VAR_KEYWORD]: continue @@ -88,6 +88,7 @@ def add_third_party(self, cog: commands.Cog, overwrite: bool = False): page = func.__dashboard_params__["name"] if page in _pages: raise RuntimeError(f"The page {page} is already an existing page for this third party ") + func.__dashboard_params__["real_cog_name"] = cog.qualified_name _pages[page] = (func, func.__dashboard_params__) if not _pages: raise RuntimeError("No page found.") From db0b4744b180ca5536b567620ff266841d7c4152 Mon Sep 17 00:00:00 2001 From: AAA3A <89632044+AAA3A-AAA3A@users.noreply.github.com> Date: Sun, 23 Apr 2023 13:32:30 +0200 Subject: [PATCH 08/11] Update thirdparties.py --- dashboard/rpc/thirdparties.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dashboard/rpc/thirdparties.py b/dashboard/rpc/thirdparties.py index 1c14b1bd..3fb5ce75 100644 --- a/dashboard/rpc/thirdparties.py +++ b/dashboard/rpc/thirdparties.py @@ -19,7 +19,7 @@ def decorator(func: typing.Callable): discord.app_commands.commands.validate_name(name) if not inspect.iscoroutinefunction(func): raise TypeError("Func must be a coroutine.") - params = {"name": name, "methods": methods, "context_ids": [], "required_kwargs": required_kwargs, "permissions_required": permissions_required, "hidden": None, "real_cog_name": None} + params = {"name": name, "methods": methods, "context_ids": [], "required_kwargs": required_kwargs, "permissions_required": permissions_required, "hidden": hidden, "real_cog_name": None} for key, value in inspect.signature(func).parameters.items(): if value.name == "self" or value.kind in [inspect._ParameterKind.POSITIONAL_ONLY, inspect._ParameterKind.VAR_KEYWORD]: continue @@ -81,13 +81,13 @@ async def on_cog_remove(self, cog: commands.Cog): def add_third_party(self, cog: commands.Cog, overwrite: bool = False): cog_name = cog.qualified_name.lower() if cog_name in self.third_parties and not overwrite: - raise RuntimeError(f"The cog {cog_name} is already an existing third party ") + raise RuntimeError(f"The cog {cog_name} is already an existing third party.") _pages = {} for attr in dir(cog): if hasattr((func := getattr(cog, attr)), "__dashboard_params__"): page = func.__dashboard_params__["name"] if page in _pages: - raise RuntimeError(f"The page {page} is already an existing page for this third party ") + raise RuntimeError(f"The page {page} is already an existing page for this third party.") func.__dashboard_params__["real_cog_name"] = cog.qualified_name _pages[page] = (func, func.__dashboard_params__) if not _pages: From d613c0f0a0f3994b4c69848f6e2383f0ea0672e1 Mon Sep 17 00:00:00 2001 From: AAA3A <89632044+AAA3A-AAA3A@users.noreply.github.com> Date: Sun, 23 Apr 2023 13:54:54 +0200 Subject: [PATCH 09/11] Update thirdparties.py --- dashboard/rpc/thirdparties.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dashboard/rpc/thirdparties.py b/dashboard/rpc/thirdparties.py index 3fb5ce75..6426aaa3 100644 --- a/dashboard/rpc/thirdparties.py +++ b/dashboard/rpc/thirdparties.py @@ -8,7 +8,9 @@ from .utils import rpccheck -def dashboard_page(name: typing.Optional[str] = None, methods: typing.List[str] = ["GET"], required_kwargs: typing.List[str] = None, permissions_required: typing.List[str] = ["view"], hidden: typing.Optional[bool] = None): +def dashboard_page(name: typing.Optional[str] = None, methods: typing.List[str] = ["GET"], context_ids: typing.List[str] = None, required_kwargs: typing.List[str] = None, permissions_required: typing.List[str] = ["view"], hidden: typing.Optional[bool] = None): + if context_ids is None: + context_ids = [] if required_kwargs is None: required_kwargs = [] @@ -19,7 +21,7 @@ def decorator(func: typing.Callable): discord.app_commands.commands.validate_name(name) if not inspect.iscoroutinefunction(func): raise TypeError("Func must be a coroutine.") - params = {"name": name, "methods": methods, "context_ids": [], "required_kwargs": required_kwargs, "permissions_required": permissions_required, "hidden": hidden, "real_cog_name": None} + params = {"name": name, "methods": methods, "context_ids": context_ids, "required_kwargs": required_kwargs, "permissions_required": permissions_required, "hidden": hidden, "real_cog_name": None} for key, value in inspect.signature(func).parameters.items(): if value.name == "self" or value.kind in [inspect._ParameterKind.POSITIONAL_ONLY, inspect._ParameterKind.VAR_KEYWORD]: continue From 3e0e15541a8476fb063cb7af53cc5ff6485f9abf Mon Sep 17 00:00:00 2001 From: AAA3A <89632044+AAA3A-AAA3A@users.noreply.github.com> Date: Sun, 23 Apr 2023 20:56:44 +0200 Subject: [PATCH 10/11] Update thirdparties.py --- dashboard/rpc/thirdparties.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dashboard/rpc/thirdparties.py b/dashboard/rpc/thirdparties.py index 6426aaa3..3a167062 100644 --- a/dashboard/rpc/thirdparties.py +++ b/dashboard/rpc/thirdparties.py @@ -61,11 +61,13 @@ def __init__(self, cog: commands.Cog): self.bot.register_rpc_handler(self.data_receive) self.bot.add_listener(self.on_cog_add) + self.bot.add_listener(self.on_cog_remove) self.bot.dispatch("dashboard_cog_add", self.cog) def unload(self): self.bot.unregister_rpc_handler(self.data_receive) self.bot.remove_listener(self.on_cog_add) + self.bot.remove_listener(self.on_cog_remove) @commands.Cog.listener() async def on_cog_add(self, cog: commands.Cog): From f682b9c67d38870117b785aad24bcbc4b760d0c3 Mon Sep 17 00:00:00 2001 From: AAA3A <89632044+AAA3A-AAA3A@users.noreply.github.com> Date: Sun, 18 Jun 2023 16:31:45 +0200 Subject: [PATCH 11/11] Update thirdparties.py --- dashboard/rpc/thirdparties.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dashboard/rpc/thirdparties.py b/dashboard/rpc/thirdparties.py index 3a167062..a7be4d0a 100644 --- a/dashboard/rpc/thirdparties.py +++ b/dashboard/rpc/thirdparties.py @@ -74,7 +74,7 @@ async def on_cog_add(self, cog: commands.Cog): ev = "on_dashboard_cog_add" funcs = [listener[1] for listener in cog.get_listeners() if listener[0] == ev] for func in funcs: - self.bot._schedule_event(func, ev, self.cog) # like in bot.dispatch + self.bot._schedule_event(func, ev, self.cog) # like in `bot.dispatch` @commands.Cog.listener() async def on_cog_remove(self, cog: commands.Cog):