diff --git a/tags/core.py b/tags/core.py index 4a915af..690242d 100644 --- a/tags/core.py +++ b/tags/core.py @@ -42,6 +42,7 @@ from TagScriptEngine import __version__ as tse_version from .abc import CompositeMetaClass +from .dashboard_integration import DashboardIntegration from .errors import MissingTagPermissions, TagCharacterLimitReached from .mixins import Commands, OwnerCommands, Processor from .objects import Tag @@ -57,6 +58,7 @@ class Tags( OwnerCommands, Processor, commands.Cog, + DashboardIntegration, metaclass=CompositeMetaClass, ): """ diff --git a/tags/dashboard_integration.py b/tags/dashboard_integration.py new file mode 100644 index 0000000..8a6f8f6 --- /dev/null +++ b/tags/dashboard_integration.py @@ -0,0 +1,196 @@ +from redbot.core import commands +from redbot.core.bot import Red +import discord +import typing + +import datetime + +from .converters import TagConverter +from .errors import TagFeedbackError +from .objects import Tag + +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: + dashboard_cog.rpc.third_parties_handler.add_third_party(self) + + + async def get_dashboard_page(self, user: discord.User, guild: typing.Optional[discord.Guild], **kwargs) -> typing.Dict[str, typing.Any]: + import wtforms + from markupsafe import Markup + class MarkdownTextAreaField(wtforms.TextAreaField): + def __call__( + self, + disable_toolbar: bool = False, + **kwargs, + ) -> Markup: + if "class" not in kwargs: + kwargs["class"] = "markdown-text-area-field" + else: + kwargs["class"] += " markdown-text-area-field" + if disable_toolbar: + kwargs["class"] += " markdown-text-area-field-toolbar-disabled" + return super().__call__(**kwargs) + class TagForm(kwargs["Form"]): + tag_name: wtforms.StringField = wtforms.StringField("Name", validators=[wtforms.validators.InputRequired(), wtforms.validators.Regexp(r"^[^\s]+$"), wtforms.validators.Length(max=300)]) + tagscript: MarkdownTextAreaField = MarkdownTextAreaField("Script", validators=[wtforms.validators.InputRequired(), wtforms.validators.Length(max=1700), kwargs["DpyObjectConverter"](TagConverter)]) + class TagsForm(kwargs["Form"]): + def __init__(self, tags: typing.Dict[str, str]) -> None: + super().__init__(prefix="tags_form_") + for name, tagscript in tags.items(): + self.tags.append_entry({"tag_name": name, "tagscript": tagscript}) + self.tags.default = [entry for entry in self.tags.entries if entry.csrf_token.data is None] + self.tags.entries = [entry for entry in self.tags.entries if entry.csrf_token.data is not None] + tags: wtforms.FieldList = wtforms.FieldList(wtforms.FormField(TagForm)) + submit: wtforms.SubmitField = wtforms.SubmitField("Save Modifications") + + if guild is not None: + existing_tags = self.guild_tag_cache[guild.id].copy() + else: + existing_tags = self.global_tag_cache.copy() + tags_form: TagsForm = TagsForm({tag.name: tag.tagscript for tag in sorted(existing_tags.values(), key=lambda tag: tag.name)}) + if tags_form.validate_on_submit() and await tags_form.validate_dpy_converters(): + tags = {tag.tag_name.data: tag.tagscript.data for tag in tags_form.tags} + for tag_name, tagscript in tags.items(): + if tag_name not in existing_tags: + __import__ + try: + self.validate_tag_count(guild) + except TagFeedbackError as e: + return {"status": 1, "notifications": [{"message": str(e), "category": "warning"}]} + tag = Tag( + self, + tag_name, + tagscript, + guild_id=guild.id if guild is not None else None, + author_id=user.id, + created_at=datetime.datetime.now(tz=datetime.timezone.utc), + ) + await tag.initialize() + elif tagscript != existing_tags[tag_name].tagscript: + await existing_tags[tag_name].edit_tagscript(tagscript) + for tag_name in existing_tags: + if tag_name not in tags: + await existing_tags[tag_name].delete() + return { + "status": 0, + "notifications": [{"message": "Successfully saved the modifications.", "category": "success"}], + "redirect_url": kwargs["request_url"], + } + + html_form = [ + '
", + ] + ) + tags_form_str = Markup("\n".join(html_form)) + + return { + "status": 0, + "web_content": { + "source": WEB_CONTENT, + "tags_form": tags_form_str, + "tags_form_length": len(tags_form.tags.default), + }, + } + + @dashboard_page(name=None, description="Manage global Tags.", is_owner=True, methods=("GET", "POST")) + async def dashboard_global_page(self, user: discord.User, **kwargs) -> typing.Dict[str, typing.Any]: + if user.id not in self.bot.owner_ids: + return {"status": 1} + return await self.get_dashboard_page(user=user, guild=None, **kwargs) + + @dashboard_page(name="guild", description="Manage guild Tags.", methods=("GET", "POST")) + async def dashboard_guild_page( + self, user: discord.User, guild: discord.Guild, **kwargs + ) -> typing.Dict[str, typing.Any]: + member = guild.get_member(user.id) + if ( + user.id not in self.bot.owner_ids + and ( + member is None + or not (await self.bot.is_mod(member) or member.guild_permissions.manage_guild) + ) + ): + return {"status": 1} + return await self.get_dashboard_page(user=user, guild=guild, **kwargs) + +WEB_CONTENT = """ + {{ tags_form|safe }} + + +"""