From 614434aebf5cf78c0e2d65f37255c9eb02bc7297 Mon Sep 17 00:00:00 2001 From: Romain J Date: Sun, 16 May 2021 00:32:14 +0200 Subject: [PATCH] feat(commands:*|Tag): feat tag system --- setup.cfg | 1 + tuxbot/cogs/Custom/locales/messages.pot | 2 +- tuxbot/cogs/Linux/locales/messages.pot | 2 +- tuxbot/cogs/Logs/locales/messages.pot | 2 +- tuxbot/cogs/Mod/locales/messages.pot | 22 +-- tuxbot/cogs/Network/locales/messages.pot | 2 +- tuxbot/cogs/Polls/locales/messages.pot | 2 +- tuxbot/cogs/Tags/__init__.py | 19 +++ tuxbot/cogs/Tags/config.py | 12 ++ tuxbot/cogs/Tags/functions/__init__.py | 0 tuxbot/cogs/Tags/functions/converters.py | 36 +++++ tuxbot/cogs/Tags/functions/exceptions.py | 13 ++ tuxbot/cogs/Tags/functions/paginator.py | 17 ++ tuxbot/cogs/Tags/functions/utils.py | 47 ++++++ tuxbot/cogs/Tags/locales/en-US.po | 74 +++++++++ tuxbot/cogs/Tags/locales/fr-FR.po | 75 +++++++++ tuxbot/cogs/Tags/locales/messages.pot | 74 +++++++++ tuxbot/cogs/Tags/models/__init__.py | 1 + tuxbot/cogs/Tags/models/tags.py | 31 ++++ tuxbot/cogs/Tags/tags.py | 189 +++++++++++++++++++++++ tuxbot/cogs/Utils/locales/messages.pot | 2 +- tuxbot/core/bot.py | 1 + tuxbot/core/utils/paginator.py | 171 ++++++++++++++++++++ 23 files changed, 778 insertions(+), 17 deletions(-) create mode 100644 tuxbot/cogs/Tags/__init__.py create mode 100644 tuxbot/cogs/Tags/config.py create mode 100644 tuxbot/cogs/Tags/functions/__init__.py create mode 100644 tuxbot/cogs/Tags/functions/converters.py create mode 100644 tuxbot/cogs/Tags/functions/exceptions.py create mode 100644 tuxbot/cogs/Tags/functions/paginator.py create mode 100644 tuxbot/cogs/Tags/functions/utils.py create mode 100644 tuxbot/cogs/Tags/locales/en-US.po create mode 100644 tuxbot/cogs/Tags/locales/fr-FR.po create mode 100644 tuxbot/cogs/Tags/locales/messages.pot create mode 100644 tuxbot/cogs/Tags/models/__init__.py create mode 100644 tuxbot/cogs/Tags/models/tags.py create mode 100644 tuxbot/cogs/Tags/tags.py create mode 100644 tuxbot/core/utils/paginator.py diff --git a/setup.cfg b/setup.cfg index b79a3b8..45993e9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,6 +20,7 @@ install_requires = Babel>=2.8.0 beautifulsoup4>=4.9.3 discord.py @ git+https://github.com/Rapptz/discord.py + discord-ext-menus humanize>=2.6.0 ipinfo>=4.1.0 ipwhois>=1.2.0 diff --git a/tuxbot/cogs/Custom/locales/messages.pot b/tuxbot/cogs/Custom/locales/messages.pot index 1a89c56..5eb7438 100644 --- a/tuxbot/cogs/Custom/locales/messages.pot +++ b/tuxbot/cogs/Custom/locales/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Tuxbot-bot\n" "Report-Msgid-Bugs-To: rick@gnous.eu\n" -"POT-Creation-Date: 2021-05-15 21:32+0200\n" +"POT-Creation-Date: 2021-05-16 00:28+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/tuxbot/cogs/Linux/locales/messages.pot b/tuxbot/cogs/Linux/locales/messages.pot index 465dd15..3e61664 100644 --- a/tuxbot/cogs/Linux/locales/messages.pot +++ b/tuxbot/cogs/Linux/locales/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Tuxbot-bot\n" "Report-Msgid-Bugs-To: rick@gnous.eu\n" -"POT-Creation-Date: 2021-05-15 21:32+0200\n" +"POT-Creation-Date: 2021-05-16 00:28+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/tuxbot/cogs/Logs/locales/messages.pot b/tuxbot/cogs/Logs/locales/messages.pot index 76c59f0..2822dd1 100644 --- a/tuxbot/cogs/Logs/locales/messages.pot +++ b/tuxbot/cogs/Logs/locales/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Tuxbot-bot\n" "Report-Msgid-Bugs-To: rick@gnous.eu\n" -"POT-Creation-Date: 2021-05-15 21:32+0200\n" +"POT-Creation-Date: 2021-05-16 00:28+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/tuxbot/cogs/Mod/locales/messages.pot b/tuxbot/cogs/Mod/locales/messages.pot index d1769bd..f8e6df6 100644 --- a/tuxbot/cogs/Mod/locales/messages.pot +++ b/tuxbot/cogs/Mod/locales/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Tuxbot-bot\n" "Report-Msgid-Bugs-To: rick@gnous.eu\n" -"POT-Creation-Date: 2021-05-15 21:32+0200\n" +"POT-Creation-Date: 2021-05-16 00:28+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,50 +17,50 @@ msgstr "" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: tuxbot/cogs/Mod/mod.py:71 +#: tuxbot/cogs/Mod/mod.py:75 #, python-brace-format msgid "Locale changed to {lang} successfully" msgstr "" -#: tuxbot/cogs/Mod/mod.py:82 +#: tuxbot/cogs/Mod/mod.py:86 msgid "List of available locales: " msgstr "" -#: tuxbot/cogs/Mod/mod.py:107 +#: tuxbot/cogs/Mod/mod.py:111 msgid "" "{}please read the following rule: \n" "{}" msgstr "" -#: tuxbot/cogs/Mod/mod.py:125 tuxbot/cogs/Mod/mod.py:225 +#: tuxbot/cogs/Mod/mod.py:129 tuxbot/cogs/Mod/mod.py:229 msgid "No rules found for this server" msgstr "" -#: tuxbot/cogs/Mod/mod.py:129 tuxbot/cogs/Mod/mod.py:229 +#: tuxbot/cogs/Mod/mod.py:133 tuxbot/cogs/Mod/mod.py:233 msgid "Rules for {}" msgstr "" -#: tuxbot/cogs/Mod/mod.py:135 tuxbot/cogs/Mod/mod.py:235 +#: tuxbot/cogs/Mod/mod.py:139 tuxbot/cogs/Mod/mod.py:239 msgid "Latest change: {}" msgstr "" -#: tuxbot/cogs/Mod/mod.py:149 tuxbot/cogs/Mod/mod.py:249 +#: tuxbot/cogs/Mod/mod.py:153 tuxbot/cogs/Mod/mod.py:253 msgid "Rules for {} ({}/{})" msgstr "" -#: tuxbot/cogs/Mod/mod.py:168 +#: tuxbot/cogs/Mod/mod.py:172 msgid "" "Following rule added: \n" "{}" msgstr "" -#: tuxbot/cogs/Mod/mod.py:191 +#: tuxbot/cogs/Mod/mod.py:195 msgid "" "Following rule updated: \n" "{}" msgstr "" -#: tuxbot/cogs/Mod/mod.py:209 +#: tuxbot/cogs/Mod/mod.py:213 msgid "" "Following rule deleted: \n" "{}" diff --git a/tuxbot/cogs/Network/locales/messages.pot b/tuxbot/cogs/Network/locales/messages.pot index d4953b9..4e34ff7 100644 --- a/tuxbot/cogs/Network/locales/messages.pot +++ b/tuxbot/cogs/Network/locales/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Tuxbot-bot\n" "Report-Msgid-Bugs-To: rick@gnous.eu\n" -"POT-Creation-Date: 2021-05-15 21:32+0200\n" +"POT-Creation-Date: 2021-05-16 00:28+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/tuxbot/cogs/Polls/locales/messages.pot b/tuxbot/cogs/Polls/locales/messages.pot index 329e906..b03876f 100644 --- a/tuxbot/cogs/Polls/locales/messages.pot +++ b/tuxbot/cogs/Polls/locales/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Tuxbot-bot\n" "Report-Msgid-Bugs-To: rick@gnous.eu\n" -"POT-Creation-Date: 2021-05-15 21:32+0200\n" +"POT-Creation-Date: 2021-05-16 00:28+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/tuxbot/cogs/Tags/__init__.py b/tuxbot/cogs/Tags/__init__.py new file mode 100644 index 0000000..cf77b72 --- /dev/null +++ b/tuxbot/cogs/Tags/__init__.py @@ -0,0 +1,19 @@ +from collections import namedtuple + +from tuxbot.core.bot import Tux +from .tags import Tags +from .config import TagsConfig, HAS_MODELS + +VersionInfo = namedtuple("VersionInfo", "major minor micro release_level") +version_info = VersionInfo(major=1, minor=0, micro=0, release_level="alpha") + +__version__ = "v{}.{}.{}-{}".format( + version_info.major, + version_info.minor, + version_info.micro, + version_info.release_level, +).replace("\n", "") + + +def setup(bot: Tux): + bot.add_cog(Tags(bot)) diff --git a/tuxbot/cogs/Tags/config.py b/tuxbot/cogs/Tags/config.py new file mode 100644 index 0000000..654dfbc --- /dev/null +++ b/tuxbot/cogs/Tags/config.py @@ -0,0 +1,12 @@ +from typing import Dict + +from structured_config import Structure + +HAS_MODELS = True + + +class TagsConfig(Structure): + pass + + +extra: Dict[str, Dict] = {} diff --git a/tuxbot/cogs/Tags/functions/__init__.py b/tuxbot/cogs/Tags/functions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tuxbot/cogs/Tags/functions/converters.py b/tuxbot/cogs/Tags/functions/converters.py new file mode 100644 index 0000000..64ad8d3 --- /dev/null +++ b/tuxbot/cogs/Tags/functions/converters.py @@ -0,0 +1,36 @@ +from discord.ext import commands +from discord.ext.commands import Context + +from tuxbot.cogs.Tags.functions.exceptions import ( + UnknownTagException, + ExistingTagException, +) +from tuxbot.cogs.Tags.models import Tag + + +def _(x): + return x + + +class TagConverter(commands.Converter): + async def convert(self, ctx: Context, argument: str): # skipcq: PYL-W0613 + arg = argument.lower() + + tag_row = await Tag.get_or_none(server_id=ctx.guild.id, name=arg) + + if not tag_row: + raise UnknownTagException(_("Unknown tag")) + + return arg + + +class NewTagConverter(commands.Converter): + async def convert(self, ctx: Context, argument: str): # skipcq: PYL-W0613 + arg = argument.lower() + + tag_row = await Tag.get_or_none(server_id=ctx.guild.id, name=arg) + + if tag_row: + raise ExistingTagException(_("Tag already exists")) + + return arg diff --git a/tuxbot/cogs/Tags/functions/exceptions.py b/tuxbot/cogs/Tags/functions/exceptions.py new file mode 100644 index 0000000..75cea57 --- /dev/null +++ b/tuxbot/cogs/Tags/functions/exceptions.py @@ -0,0 +1,13 @@ +from discord.ext import commands + + +class TagsException(commands.BadArgument): + pass + + +class UnknownTagException(TagsException): + pass + + +class ExistingTagException(TagsException): + pass diff --git a/tuxbot/cogs/Tags/functions/paginator.py b/tuxbot/cogs/Tags/functions/paginator.py new file mode 100644 index 0000000..be3c7dc --- /dev/null +++ b/tuxbot/cogs/Tags/functions/paginator.py @@ -0,0 +1,17 @@ +from tuxbot.core.utils.paginator import SimplePages + + +class TagPage: + def __init__(self, entry): + self.name = entry.name + self.uses = entry.uses + + def __str__(self): + return f"{self.name} ({self.uses})" + + +class TagPages(SimplePages): + def __init__(self, entries, per_page=15): + converted = [TagPage(entry) for entry in entries] + + super().__init__(converted, per_page=per_page) diff --git a/tuxbot/cogs/Tags/functions/utils.py b/tuxbot/cogs/Tags/functions/utils.py new file mode 100644 index 0000000..5cc9aeb --- /dev/null +++ b/tuxbot/cogs/Tags/functions/utils.py @@ -0,0 +1,47 @@ +from typing import Optional + +import discord + +from tuxbot.cogs.Tags.models import Tag +from tuxbot.core.utils.functions.extra import ContextPlus + + +async def get_tag(guild_id: int, name: str) -> Tag: + return await Tag.get(server_id=guild_id, name=name) + + +async def get_all_tags( + guild_id: int, author: Optional[discord.Member] = None +) -> list[Tag]: + if author is not None: + return ( + await Tag.filter(server_id=guild_id, author_id=author.id) + .all() + .order_by("-uses") + ) + + return await Tag.filter(server_id=guild_id).all().order_by("-uses") + + +async def search_tags(guild_id: int, q: str) -> list[Tag]: + return await Tag.filter(server_id=guild_id, name__icontains=q).all().order_by("-uses") + + +async def create_tag(ctx: ContextPlus, name: str, content: str): + tag_row = await Tag() + + tag_row.server_id = ctx.guild.id + tag_row.author_id = ctx.author.id + + tag_row.name = name # type: ignore + tag_row.content = content # type: ignore + + await tag_row.save() + + +async def edit_tag(ctx: ContextPlus, name: str, content: str): + tag_row = await get_tag(ctx.guild.id, name) + + tag_row.content = content # type: ignore + + await tag_row.save() diff --git a/tuxbot/cogs/Tags/locales/en-US.po b/tuxbot/cogs/Tags/locales/en-US.po new file mode 100644 index 0000000..8e55f4e --- /dev/null +++ b/tuxbot/cogs/Tags/locales/en-US.po @@ -0,0 +1,74 @@ +# English translations for Tuxbot-bot package. +# Copyright (C) 2020 THE Tuxbot-bot'S COPYRIGHT HOLDER +# This file is distributed under the same license as the Tuxbot-bot package. +# Automatically generated, 2020. +# +msgid "" +msgstr "" +"Project-Id-Version: Tuxbot-bot\n" +"Report-Msgid-Bugs-To: rick@gnous.eu\n" +"POT-Creation-Date: 2021-01-19 14:42+0100\n" +"PO-Revision-Date: 2020-06-10 00:38+0200\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: en_US\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: tuxbot/cogs/Tags/functions/converters.py:22 +msgid "Unknown tag" +msgstr "" + +#: tuxbot/cogs/Tags/functions/converters.py:34 +msgid "Tag already exists" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:71 +msgid "Tag successfully deleted" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:74 +msgid "Your can't delete this tag" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:82 +msgid "Tag successfully created" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:98 +msgid "Tag successfully edited" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:101 +msgid "Your can't edit this tag" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:120 +msgid "Owner" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:122 +msgid "Uses" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:135 +msgid "The search must be at least 3 characters" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:150 +msgid "No tags found" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:171 +msgid "No tags found for {}" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:180 +msgid "Tag owner is on this server." +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:188 +msgid "This tag is now owned by you." +msgstr "" diff --git a/tuxbot/cogs/Tags/locales/fr-FR.po b/tuxbot/cogs/Tags/locales/fr-FR.po new file mode 100644 index 0000000..cdd4578 --- /dev/null +++ b/tuxbot/cogs/Tags/locales/fr-FR.po @@ -0,0 +1,75 @@ +# French translations for Tuxbot-bot package +# Traductions françaises du paquet Tuxbot-bot. +# Copyright (C) 2020 THE Tuxbot-bot'S COPYRIGHT HOLDER +# This file is distributed under the same license as the Tuxbot-bot package. +# Automatically generated, 2020. +# +msgid "" +msgstr "" +"Project-Id-Version: Tuxbot-bot\n" +"Report-Msgid-Bugs-To: rick@gnous.eu\n" +"POT-Creation-Date: 2021-01-19 14:42+0100\n" +"PO-Revision-Date: 2020-06-10 00:38+0200\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: tuxbot/cogs/Tags/functions/converters.py:22 +msgid "Unknown tag" +msgstr "Tag inconnu" + +#: tuxbot/cogs/Tags/functions/converters.py:34 +msgid "Tag already exists" +msgstr "Tag déjà existant" + +#: tuxbot/cogs/Tags/tags.py:71 +msgid "Tag successfully deleted" +msgstr "Tag supprimé avec succès" + +#: tuxbot/cogs/Tags/tags.py:74 +msgid "Your can't delete this tag" +msgstr "Vous ne pouvez pas supprimer ce tag" + +#: tuxbot/cogs/Tags/tags.py:82 +msgid "Tag successfully created" +msgstr "Tag créé avec succès" + +#: tuxbot/cogs/Tags/tags.py:98 +msgid "Tag successfully edited" +msgstr "Tag édité avec succès" + +#: tuxbot/cogs/Tags/tags.py:101 +msgid "Your can't edit this tag" +msgstr "Vous ne pouvez pas éditer ce tag" + +#: tuxbot/cogs/Tags/tags.py:120 +msgid "Owner" +msgstr "Propriétaire" + +#: tuxbot/cogs/Tags/tags.py:122 +msgid "Uses" +msgstr "Utilisations" + +#: tuxbot/cogs/Tags/tags.py:135 +msgid "The search must be at least 3 characters" +msgstr "La recherche doit faire au moins 3 caracteres" + +#: tuxbot/cogs/Tags/tags.py:150 +msgid "No tags found" +msgstr "Aucun tag trouvé" + +#: tuxbot/cogs/Tags/tags.py:171 +msgid "No tags found for {}" +msgstr "Aucun tag trouvé pour {}" + +#: tuxbot/cogs/Tags/tags.py:180 +msgid "Tag owner is on this server." +msgstr "Le propriétaire du tag est sur ce serveur" + +#: tuxbot/cogs/Tags/tags.py:188 +msgid "This tag is now owned by you." +msgstr "Ce tag est désormais en votre possession" diff --git a/tuxbot/cogs/Tags/locales/messages.pot b/tuxbot/cogs/Tags/locales/messages.pot new file mode 100644 index 0000000..e0a32fa --- /dev/null +++ b/tuxbot/cogs/Tags/locales/messages.pot @@ -0,0 +1,74 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the Tuxbot-bot package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Tuxbot-bot\n" +"Report-Msgid-Bugs-To: rick@gnous.eu\n" +"POT-Creation-Date: 2021-05-16 00:28+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: tuxbot/cogs/Tags/functions/converters.py:22 +msgid "Unknown tag" +msgstr "" + +#: tuxbot/cogs/Tags/functions/converters.py:34 +msgid "Tag already exists" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:71 +msgid "Tag successfully deleted" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:74 +msgid "Your can't delete this tag" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:82 +msgid "Tag successfully created" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:98 +msgid "Tag successfully edited" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:101 +msgid "Your can't edit this tag" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:120 +msgid "Owner" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:122 +msgid "Uses" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:135 +msgid "The search must be at least 3 characters" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:150 +msgid "No tags found" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:171 +msgid "No tags found for {}" +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:180 +msgid "Tag owner is on this server." +msgstr "" + +#: tuxbot/cogs/Tags/tags.py:188 +msgid "This tag is now owned by you." +msgstr "" diff --git a/tuxbot/cogs/Tags/models/__init__.py b/tuxbot/cogs/Tags/models/__init__.py new file mode 100644 index 0000000..11b89a3 --- /dev/null +++ b/tuxbot/cogs/Tags/models/__init__.py @@ -0,0 +1 @@ +from .tags import * diff --git a/tuxbot/cogs/Tags/models/tags.py b/tuxbot/cogs/Tags/models/tags.py new file mode 100644 index 0000000..445528a --- /dev/null +++ b/tuxbot/cogs/Tags/models/tags.py @@ -0,0 +1,31 @@ +import tortoise +from tortoise import fields + + +class Tag(tortoise.Model): + id = fields.BigIntField(pk=True) + server_id = fields.BigIntField() + author_id = fields.BigIntField() + + name = fields.TextField() + content = fields.TextField() + + uses = fields.IntField(default=0) + + created_at = fields.DatetimeField(auto_now_add=True) + + class Meta: + table = "tags" + + def __str__(self): + return ( + f"" + ) + + __repr__ = __str__ diff --git a/tuxbot/cogs/Tags/tags.py b/tuxbot/cogs/Tags/tags.py new file mode 100644 index 0000000..304eb9c --- /dev/null +++ b/tuxbot/cogs/Tags/tags.py @@ -0,0 +1,189 @@ +import logging +from typing import Optional + +import discord +from discord.ext import commands, menus + +from tuxbot.cogs.Tags.functions.converters import TagConverter, NewTagConverter +from tuxbot.cogs.Tags.functions.exceptions import ( + UnknownTagException, + ExistingTagException, +) +from tuxbot.cogs.Tags.functions.paginator import TagPages +from tuxbot.cogs.Tags.functions.utils import ( + get_tag, + get_all_tags, + search_tags, + create_tag, + edit_tag, +) +from tuxbot.core.bot import Tux + +from tuxbot.core.i18n import ( + Translator, +) +from tuxbot.core.utils.functions.extra import ( + group_extra, + ContextPlus, +) + +log = logging.getLogger("tuxbot.cogs.Tags") +_ = Translator("Tags", __file__) + + +class Tags(commands.Cog): + def __init__(self, bot: Tux): + self.bot = bot + + async def cog_command_error(self, ctx: ContextPlus, error): + if isinstance( + error, + (UnknownTagException, ExistingTagException), + ): + await ctx.send(_(str(error), ctx, self.bot.config)) + + # ========================================================================= + # ========================================================================= + + @group_extra(name="tag", invoke_without_command=True, deletable=False) + @commands.guild_only() + async def _tag(self, ctx: ContextPlus, *, name: TagConverter): + tag_row = await get_tag(ctx.guild.id, str(name)) + + await ctx.send(tag_row.content) + + tag_row.uses += 1 # type: ignore + + await tag_row.save() + + @_tag.command(name="delete") + async def _tag_delete(self, ctx: ContextPlus, *, name: TagConverter): + tag_row = await get_tag(ctx.guild.id, str(name)) + + backdoor = ( + await ctx.bot.is_owner(ctx.author) + or ctx.author.guild_permissions.manage_messages + ) + + if backdoor or ctx.author.id == tag_row.id: + await tag_row.delete() + return await ctx.send( + _("Tag successfully deleted", ctx, self.bot.config) + ) + + await ctx.send(_("Your can't delete this tag", ctx, self.bot.config)) + + @_tag.command(name="create", aliases=["add"]) + async def _tag_create( + self, ctx, name: NewTagConverter, *, content: commands.clean_content + ): + await create_tag(ctx, str(name), str(content)) + + await ctx.send(_("Tag successfully created", ctx, self.bot.config)) + + @_tag.command(name="edit") + async def _tag_edit( + self, ctx, name: TagConverter, *, content: commands.clean_content + ): + tag_row = await get_tag(ctx.guild.id, str(name)) + + backdoor = ( + await ctx.bot.is_owner(ctx.author) + or ctx.author.guild_permissions.manage_messages + ) + + if backdoor or ctx.author.id == tag_row.id: + await edit_tag(ctx, str(name), str(content)) + return await ctx.send( + _("Tag successfully edited", ctx, self.bot.config) + ) + + await ctx.send(_("Your can't edit this tag", ctx, self.bot.config)) + + @_tag.command(name="info", aliases=["owner"]) + async def _tag_info(self, ctx: ContextPlus, *, name: TagConverter): + tag_row = await get_tag(ctx.guild.id, str(name)) + + e = discord.Embed(color=discord.Colour.blue()) + + owner_id = tag_row.author_id + e.title = tag_row.name + e.timestamp = tag_row.created_at + + user = self.bot.get_user(owner_id) or ( + await self.bot.fetch_user(owner_id) + ) + + e.set_author(name=str(user), icon_url=user.avatar.url) + + e.add_field( + name=_("Owner", ctx, self.bot.config), value=f"<@{owner_id}>" + ) + e.add_field(name=_("Uses", ctx, self.bot.config), value=tag_row.uses) + + await ctx.send(embed=e) + + @_tag.command(name="search", aliases=["find"]) + async def _tag_search( + self, ctx: ContextPlus, *, name: commands.clean_content + ): + q = str(name) + + if len(q) < 3: + return await ctx.send( + _( + "The search must be at least 3 characters", + ctx, + self.bot.config, + ) + ) + + tags = await search_tags(ctx.guild.id, q) + + if tags: + try: + p = TagPages(entries=tags) + await p.start(ctx) + except menus.MenuError as e: + await ctx.send(e) + else: + await ctx.send(_("No tags found", ctx, self.bot.config)) + + @_tag.command(name="list") + async def _tag_list( + self, ctx: ContextPlus, author: Optional[discord.Member] + ): + author = author or ctx.author + + tags = await get_all_tags(ctx.guild.id, author) + + if tags: + try: + p = TagPages(entries=tags) + p.embed.set_author( + name=str(author), icon_url=author.avatar.url + ) + await p.start(ctx) + except menus.MenuError as e: + await ctx.send(e) + else: + await ctx.send( + _("No tags found for {}", ctx, self.bot.config).format(author) + ) + + @_tag.command(name="claim") + async def _tag_claim(self, ctx: ContextPlus, *, name: TagConverter): + tag_row = await get_tag(ctx.guild.id, str(name)) + + if await ctx.guild.fetch_member(tag_row.author_id) is not None: + return await ctx.send( + _("Tag owner is on this server.", ctx, self.bot.config) + ) + + tag_row.author_id = ctx.author.id + + await tag_row.save() + + await ctx.send( + _("This tag is now owned by you.", ctx, self.bot.config) + ) diff --git a/tuxbot/cogs/Utils/locales/messages.pot b/tuxbot/cogs/Utils/locales/messages.pot index 098c9a3..3a641a7 100644 --- a/tuxbot/cogs/Utils/locales/messages.pot +++ b/tuxbot/cogs/Utils/locales/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Tuxbot-bot\n" "Report-Msgid-Bugs-To: rick@gnous.eu\n" -"POT-Creation-Date: 2021-05-15 21:32+0200\n" +"POT-Creation-Date: 2021-05-16 00:28+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/tuxbot/core/bot.py b/tuxbot/core/bot.py index f425a08..810b04d 100644 --- a/tuxbot/core/bot.py +++ b/tuxbot/core/bot.py @@ -44,6 +44,7 @@ packages: Tuple = ( "tuxbot.cogs.Network", "tuxbot.cogs.Linux", "tuxbot.cogs.Mod", + "tuxbot.cogs.Tags", ) diff --git a/tuxbot/core/utils/paginator.py b/tuxbot/core/utils/paginator.py new file mode 100644 index 0000000..0375ca2 --- /dev/null +++ b/tuxbot/core/utils/paginator.py @@ -0,0 +1,171 @@ +""" +Credits to Rapptz/RoboDanny + +https://github.com/Rapptz/RoboDanny/blob/0dfa21599da76e84c2f8e7fde0c132ec93c840a8/cogs/utils/paginator.py +""" +import asyncio +import discord +from discord.ext.commands import Paginator as CommandPaginator +from discord.ext import menus + + +class RoboPages(menus.MenuPages): + def __init__(self, source): + super().__init__(source=source, check_embeds=True) + self.input_lock = asyncio.Lock() + + async def finalize(self, timed_out): + try: + if timed_out: + await self.message.clear_reactions() + else: + await self.message.delete() + except discord.HTTPException: + pass + + @menus.button("\N{INFORMATION SOURCE}\ufe0f", position=menus.Last(3)) + async def show_help(self): + """shows this message""" + embed = discord.Embed( + title="Paginator help", + description="Hello! Welcome to the help page.", + ) + messages = [] + for (emoji, button) in self.buttons.items(): + messages.append(f"{emoji}: {button.action.__doc__}") + + embed.add_field( + name="What are these reactions for?", + value="\n".join(messages), + inline=False, + ) + embed.set_footer( + text=f"We were on page {self.current_page + 1} before this message." + ) + await self.message.edit(content=None, embed=embed) + + async def go_back_to_current_page(): + await asyncio.sleep(30.0) + await self.show_page(self.current_page) + + self.bot.loop.create_task(go_back_to_current_page()) + + @menus.button( + "\N{INPUT SYMBOL FOR NUMBERS}", position=menus.Last(1.5), lock=False + ) + async def numbered_page(self, payload): + """lets you type a page number to go to""" + if self.input_lock.locked(): + return + + async with self.input_lock: + channel = self.message.channel + author_id = payload.user_id + to_delete = [] + to_delete.append( + await channel.send("What page do you want to go to?") + ) + + def message_check(m): + return ( + m.author.id == author_id + and channel == m.channel + and m.content.isdigit() + ) + + try: + msg = await self.bot.wait_for( + "message", check=message_check, timeout=30.0 + ) + except asyncio.TimeoutError: + to_delete.append(await channel.send("Took too long.")) + await asyncio.sleep(5) + else: + page = int(msg.content) + to_delete.append(msg) + await self.show_checked_page(page - 1) + + try: + await channel.delete_messages(to_delete) + except Exception: + pass + + +class FieldPageSource(menus.ListPageSource): + """A page source that requires (field_name, field_value) tuple items.""" + + def __init__(self, entries, *, per_page=12): + super().__init__(entries, per_page=per_page) + self.embed = discord.Embed(colour=discord.Colour.blurple()) + + # pylint: disable=arguments-differ + async def format_page(self, menu, entries): + self.embed.clear_fields() + self.embed.description = discord.Embed.Empty + + for key, value in entries: + self.embed.add_field(name=key, value=value, inline=False) + + maximum = self.get_max_pages() + if maximum > 1: + text = f"Page {menu.current_page + 1}/{maximum} ({len(self.entries)} entries)" + self.embed.set_footer(text=text) + + return self.embed + + +class TextPageSource(menus.ListPageSource): + def __init__(self, text, *, prefix="```", suffix="```", max_size=2000): + pages = CommandPaginator( + prefix=prefix, suffix=suffix, max_size=max_size - 200 + ) + for line in text.split("\n"): + pages.add_line(line) + + super().__init__(entries=pages.pages, per_page=1) + + # pylint: disable=arguments-differ + async def format_page(self, menu, content): + maximum = self.get_max_pages() + if maximum > 1: + return f"{content}\nPage {menu.current_page + 1}/{maximum}" + return content + + +class SimplePageSource(menus.ListPageSource): + def __init__(self, entries, *, per_page=12): + super().__init__(entries, per_page=per_page) + self.initial_page = True + + # pylint: disable=arguments-differ + async def format_page(self, menu, entries): + pages = [] + for index, entry in enumerate( + entries, start=menu.current_page * self.per_page + ): + pages.append(f"{index + 1}. {entry}") + + maximum = self.get_max_pages() + if maximum > 1: + footer = f"Page {menu.current_page + 1}/{maximum} ({len(self.entries)} entries)" + menu.embed.set_footer(text=footer) + + if self.initial_page and self.is_paginating(): + pages.append("") + pages.append( + "Confused? React with \N{INFORMATION SOURCE} for more info." + ) + self.initial_page = False + + menu.embed.description = "\n".join(pages) + return menu.embed + + +class SimplePages(RoboPages): + """A simple pagination session reminiscent of the old Pages interface. + Basically an embed with some normal formatting. + """ + + def __init__(self, entries, *, per_page=12): + super().__init__(SimplePageSource(entries, per_page=per_page)) + self.embed = discord.Embed(colour=discord.Colour.blurple())