You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Mar 2, 2025. It is now read-only.
I tried to integrate myself the third parties in Red-Dashboard. I personally think that every cog should be able to add their own pages to the dashboard, easily, without having to modify the source code of Red-Dashboard or cog Dashboard.
I haven't done any documentation.
How does it work in practice:
On the Red-Dashboard side:
An API endpoint point has been added: /third_party/<cog_name>/[page]?**[params]. The local Dashboard cog sends the list of third parties and pages to Red-Dashboard, in the get_variables RPC method, which is already called at regular intervals. Thus, the code checks if the cog, and the page exist. Depending on the parameters provided by the cog creator, the code will deny requests if the method used is not one of the allowed ones (HEAD, GET, OPTIONS, POST, PATH and DELETE). If user_id is a required parameter, then the Dashboard will request the OAuth login of the current user. If guild_id is required, then the current dashboard.html page will be displayed to allow the choice of a server: the html file has been modified to allow the BASE_GUILD_URL variable to be changed optionally. user_id, guild_id, member_id, role_id and channel_id are context variables, which should be `int': at the moment, choice is not possible for members, roles and channels, but these parameters could be provided by cogs manually in Discord. If parameters are required, the Dashboard will display an error on the browser. A web request will be sent to the local cog Dashboard which will dispatch the data correctly and get a response.
Types of responses from third parties:
The third parties would return to the local cog Dashboard a dict like a real RPC method would.
Several keys are supported by the API endpoint:
status: Any request response should have it, but it is not used.
web-content: The Flask/Django/Jinja2 template will be displayed on the browser. It can contain HTML, CSS and JavaScript, and should start with {% extends "base-site.html" %} to display the base dashboard layout. The variables in the response will be passed in.
error_message: Using the new html file error_message.html, the provided message will be displayed directly to the user, without having to code a different html content.
redirect : The existing template with its name will be displayed. The variables of the response will be passed on.
If the request methods are other than HEAD and GET, the data will be returned directly as JSON.
\app\base\static\assets\js\plugins\utils.js:
A new JavaScript script has been added as a utility to the Dashboard. It contains several methods that can be called in a template.
$.sendNotification: Easily display notifications on the Dashboard, with only the type and message as required parameters.
$.postData: Easily send data to the bot with the POST method. By default, the current URL window.location.href will be used for the request.
$.showTableRegular : Allows to display the provided data in tabular form.
$.generateForm: Generates a form with many features and supporting all possible inputs (name, label, type, placeholder, required, validate, error). By default, data is sent to the bot with $.postData.
On the Dashboard local cog side:
A DashboardRPC_ThirdParties extension has been added and is accessible at Dashboard.rpc.third_parties_handler. A third party is linked to a commands.Cog object which must be loaded, in order to be used.
The DashboardRPC_ThirdParties.add_third_party method must be used to add a cog as a third party. The page parameters are stored in DashboardRPC_ThirdParties.third_parties.
The decorator dashboard.rpc.thirdparties.dashboard_page allows to provide parameters for each page. All attributes of the cog class that have a __dashboard_params__ attribute will be automatically added to the Dashboard when the add third party method is called. Context parameters (user_id/user, guild_id/guild, member_id/member, role_id/role, channel_id/channel) and required parameters are detected in the parameter names.
Here are its parameters:
name: None so that the user does not have to specify the name to get this page. A name will have the same limitations as the Discord slash command names for ease of use.
methods: The web request methods allowed to call the third party page.
required_kwargs : To manually specify required parameters.
permissions_required : The user's required permissions on the server.
hidden: A parameter not used at this time. Maybe the pages will be listed somewhere someday.
The RPC method DashboardRPC_ThirdParties.data_receive receives the data from Red-Dashboard for the endpoint API I mentioned earlier. In case, the existence of the third party and the page is checked as new. If the cog is no longer loaded, the request is "refused" with an error message. If a context_ids variable is provided (user_id, guild_id, member_id, role_id or channel_id), the code checks if the bot has access to it and if the Discord objects exist. The parameters user, guild, member, role and channel are then added eventually.
The parameters received from the Red-Dashboard (and passed to cogs) are method, **context_ids, **kwargs and lang_code. Cogs should use **kwargs last, as the user (or Flask) is free to add whatever parameters they wish to the pages.
Quick cog example:
from redbot.core import commands
try:
from dashboard.rpc.thirdparties import dashboard_page
except ImportError:
def dashboard_page(*args, **kwargs): # fake decorator
def decorator(func: typing.Callable):
return func
return decorator
class Cog(commands.Cog):
def __init__(self, bot: Red) -> None:
self.bot: Red = bot
self.cache: typing.Dict[discord.Guild, dict] = {}
async def cog_load(self) -> None:
if (dashboard_cog := self.bot.get_cog("Dashboard")) is not None and dashboard_page is not None:
dashboard_cog.rpc.third_parties_handler.add_third_party(self)
async def cog_unload(self) -> None:
if (dashboard_cog := self.bot.get_cog("Dashboard")) is not None and dashboard_page is not None:
dashboard_cog.rpc.third_parties_handler.remove_third_party(self)
@dashboard_page(name=None, methods=["GET", "POST"])
async def rpc_callback(self, method: str, user: discord.User, guild: discord.Guild, **kwargs) -> None:
if method == "GET":
return {"status": 0, "data": self.cache.get(guild.id, {})}
elif method == "POST":
self.cache[guild] = kwargs.get("data", {})
return {"status": 0, "data": self.cache[guild.id]}
await ctx.bot.add_cog(Cog(bot))
Minor corrections:
Say that the guild was not found if the parameter cannot be converted to str.
Replace all except: with except Exception.
Added highlight filter to Flask to allow formatted code to be displayed in multiple languages. (<code class="language-python">' + hljs.highlight('python', item).value + '</code>)
Thank you very much in advance,
Have a nice day,
AAA3A
I tried to integrate two of my cogs with this PR as a third party to Dashboard, and I noticed a small problem.
The cogs would use decorators from the dashboarď module, but this one can load afterwards when the bot starts. Also, the Dashboard cog could not be reloaded without losing all third parties.
To solve this problem, I added the dashboard_cog_add event which will be dispatched when the cog Dashboard is loaded. When a new cog is loaded, as dpy does, the cog will look for events for that cog and call the associated methods. This way, the pages will always work on the bot.
from redbot.core import commands
from redbot.core.bot import Red
import discord
import typing
def dashboard_page(*args, **kwargs):
def decorator(func: typing.Callable):
func.__dashboard_decorator_params__ = (args, kwargs)
return func
return decorator
class DashboardIntegration:
bot: Red
@commands.Cog.listener()
async def on_dashboard_cog_add(self, dashboard_cog: commands.Cog) -> None:
try:
from dashboard.rpc.thirdparties import dashboard_page
except ImportError: # Should never happen because the event would not be dispatched by the Dashboard cog.
return
for attr in dir(self):
if hasattr((func := getattr(self, attr)), "__dashboard_decorator_params__"):
setattr(self, attr, func.__class__(dashboard_page(*func.__dashboard_decorator_params__[0], **func.__dashboard_decorator_params__[1])(func.__func__), func.__self__))
dashboard_cog.rpc.third_parties_handler.add_third_party(self)
async def cog_unload(self) -> None:
if (dashboard_cog := self.bot.get_cog("Dashboard")) is not None:
dashboard_cog.rpc.third_parties_handler.remove_third_party(self)
@dashboard_page(name=None)
async def rpc_callback(self, user: discord.User, **kwargs) -> None:
return {"status": 0, "web-content": web_content}
web_content = """
{% extends "base-site.html" %}
{% block title %} {{ _('TicketTool cog') }} {% endblock title %}
{% block content %}
<h2>TicketToolCog</h2>
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-body">
<button class="btn", onclick="window.location.href = window.location.origin + window.location.pathname + '/settings';">Access to Settings</button>
</div>
</div>
</div>
</div>
{% endblock content %}
"""
When Cog-Creators/Red-DiscordBot#5570 is merged into the Red repo, the cog Dashboard will use the new on_cog_remove event to remove third parties, without cogs having to implement the cog_unload method.
I also added a new "Third Parties" page that appears once you log in.
The menu is similar to the one in the command list. The Dashboard will retrieve the cog description from this list based on the name of the third party.
The hidden kwarg I mentioned earlier allows you to hide pages in this menu. If all pages are hidden, the third party will not appear.
AAA3A-AAA3A
added a commit
to AAA3A-AAA3A/AAA3A_utils
that referenced
this pull request
Apr 15, 2023
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Labels
None yet
1 participant
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Type
Description of the changes
Hello,
I tried to integrate myself the third parties in Red-Dashboard. I personally think that every cog should be able to add their own pages to the dashboard, easily, without having to modify the source code of Red-Dashboard or cog Dashboard.
I haven't done any documentation.
How does it work in practice:
On the Red-Dashboard side:
An API endpoint point has been added:
/third_party/<cog_name>/[page]?**[params]. The local Dashboard cog sends the list of third parties and pages to Red-Dashboard, in theget_variablesRPC method, which is already called at regular intervals. Thus, the code checks if the cog, and the page exist. Depending on the parameters provided by the cog creator, the code will deny requests if the method used is not one of the allowed ones (HEAD,GET,OPTIONS,POST,PATHandDELETE). Ifuser_idis a required parameter, then the Dashboard will request the OAuth login of the current user. Ifguild_idis required, then the currentdashboard.htmlpage will be displayed to allow the choice of a server: the html file has been modified to allow theBASE_GUILD_URLvariable to be changed optionally.user_id,guild_id,member_id,role_idandchannel_idare context variables, which should be `int': at the moment, choice is not possible for members, roles and channels, but these parameters could be provided by cogs manually in Discord. If parameters are required, the Dashboard will display an error on the browser. A web request will be sent to the local cog Dashboard which will dispatch the data correctly and get a response.Types of responses from third parties:
The third parties would return to the local cog Dashboard a
dictlike a real RPC method would.Several keys are supported by the API endpoint:
status: Any request response should have it, but it is not used.web-content: The Flask/Django/Jinja2 template will be displayed on the browser. It can contain HTML, CSS and JavaScript, and should start with{% extends "base-site.html" %}to display the base dashboard layout. The variables in the response will be passed in.error_message: Using the new html fileerror_message.html, the provided message will be displayed directly to the user, without having to code a different html content.redirect: The existing template with its name will be displayed. The variables of the response will be passed on.If the request methods are other than
HEADandGET, the data will be returned directly as JSON.\app\base\static\assets\js\plugins\utils.js:A new JavaScript script has been added as a utility to the Dashboard. It contains several methods that can be called in a template.
$.sendNotification: Easily display notifications on the Dashboard, with only the type and message as required parameters.$.postData: Easily send data to the bot with thePOSTmethod. By default, the current URLwindow.location.hrefwill be used for the request.$.showTableRegular: Allows to display the provided data in tabular form.$.generateForm: Generates a form with many features and supporting all possible inputs (name,label,type,placeholder,required,validate,error). By default, data is sent to the bot with$.postData.On the Dashboard local cog side:
A
DashboardRPC_ThirdPartiesextension has been added and is accessible atDashboard.rpc.third_parties_handler. A third party is linked to acommands.Cogobject which must be loaded, in order to be used.The
DashboardRPC_ThirdParties.add_third_partymethod must be used to add a cog as a third party. The page parameters are stored inDashboardRPC_ThirdParties.third_parties.The decorator
dashboard.rpc.thirdparties.dashboard_pageallows to provide parameters for each page. All attributes of the cog class that have a__dashboard_params__attribute will be automatically added to the Dashboard when the add third party method is called. Context parameters (user_id/user,guild_id/guild,member_id/member,role_id/role,channel_id/channel) and required parameters are detected in the parameter names.Here are its parameters:
name:Noneso that the user does not have to specify the name to get this page. A name will have the same limitations as the Discord slash command names for ease of use.methods: The web request methods allowed to call the third party page.required_kwargs: To manually specify required parameters.permissions_required: The user's required permissions on the server.hidden: A parameter not used at this time. Maybe the pages will be listed somewhere someday.The RPC method
DashboardRPC_ThirdParties.data_receivereceives the data from Red-Dashboard for the endpoint API I mentioned earlier. In case, the existence of the third party and the page is checked as new. If the cog is no longer loaded, the request is "refused" with an error message. If acontext_idsvariable is provided (user_id,guild_id,member_id,role_idorchannel_id), the code checks if the bot has access to it and if the Discord objects exist. The parametersuser,guild,member,roleandchannelare then added eventually.The parameters received from the Red-Dashboard (and passed to cogs) are
method,**context_ids,**kwargsandlang_code. Cogs should use**kwargslast, as the user (or Flask) is free to add whatever parameters they wish to the pages.Quick cog example:
Minor corrections:
str.except:withexcept Exception.highlightfilter to Flask to allow formatted code to be displayed in multiple languages. (<code class="language-python">' + hljs.highlight('python', item).value + '</code>)Thank you very much in advance,
Have a nice day,
AAA3A