diff --git a/.github/workflows/codacy.yml b/.github/workflows/codacy.yml index 377f986..fac464a 100644 --- a/.github/workflows/codacy.yml +++ b/.github/workflows/codacy.yml @@ -51,7 +51,7 @@ jobs: # Adjust severity of non-security issues gh-code-scanning-compat: true # Force 0 exit code to allow SARIF file generation - # This will handover control about PR rejection to the GitHub side + # This will hand over control about PR rejection to the GitHub side max-allowed-issues: 2147483647 # Upload the SARIF file generated in the previous step diff --git a/Discord-Bot-main/Bot/DebugBot.py b/Discord-Bot-main/Bot/DebugBot.py deleted file mode 100644 index 3115930..0000000 --- a/Discord-Bot-main/Bot/DebugBot.py +++ /dev/null @@ -1,42 +0,0 @@ - - -from importlib import reload -import asyncio -import event -import types - - - -# décaration d'un décorateur a fonction (@update_module(module_name)) -def update_module(module): - def __update__(func): - async def callback(*args, **kwargs): - reload(__import__(module,globals(), locals(), [], 0)) # (recherche le module pour voir si il est déà chargé sinon il import dynamiquement) puis la fonction reload recharge le module - return await func(*args, **kwargs) - - return (callback) - - return __update__ - - -def update_all_modules(func): - async def __update__(*args, **kwargs): - for name, val in globals().items(): - if isinstance(val, types.ModuleType): - print(val.__name__) - reload(__import__(val.__name__,globals(), locals(), [], 0)) - - reload(__import__("DebugBot",globals(), locals(), [], 0)) - - return await func(*args, **kwargs) - - return __update__ - -async def debug_on_message(*args,**kwargs): - return await event.on_message(*args,**kwargs) - -async def debug_on_prez(*args,**kwargs): - return await event.on_prez(*args,**kwargs) - -async def debug_on_help(*args,**kwargs): - return await event.on_help(*args,**kwargs) diff --git a/Discord-Bot-main/Bot/UR-Bot.py b/Discord-Bot-main/Bot/UR-Bot.py deleted file mode 100644 index 3e617ed..0000000 --- a/Discord-Bot-main/Bot/UR-Bot.py +++ /dev/null @@ -1,61 +0,0 @@ -import discord -from dotenv import load_dotenv -import DebugBot -from discord.ext import commands -import os - -load_dotenv() -TOKEN = os.getenv('TOKEN') - - -class UR_BOT(commands.Bot): - async def on_ready(self): - print('--- We have successfully logged in as {0.user}'.format(self)) - - async def on_message(self, message): - if message.author == self.user: - return - - await DebugBot.debug_on_message(message) - - return await bot.process_commands(message) - - -intent = discord.Intents.default() -intent.members = True -intent.messages = True - -bot = UR_BOT(command_prefix=DebugBot.event.BOT_PREFIX, intents=intent) -bot.remove_command('help') - - -@bot.command() -@commands.guild_only() -async def ping(ctx): - latency = round(bot.latency * 1000) - await ctx.send(f"Pong ! {latency}ms") - - -@bot.command(name="help") -async def help(ctx): - await DebugBot.debug_on_help(ctx) - - -@bot.command(name="prez") -async def prez(ctx): - await DebugBot.debug_on_prez(ctx) - - -@DebugBot.update_all_modules -async def reload_module(): - return 0 - - -@bot.command(aliases=['reload', 'rld']) -async def reload_module(ctx): - await reload_module() - await ctx.channel.send("The scripts has been reloaded.") - return 0 - - -bot.run(TOKEN) diff --git a/Discord-Bot-main/Bot/debug_bot.py b/Discord-Bot-main/Bot/debug_bot.py new file mode 100644 index 0000000..4ea32c6 --- /dev/null +++ b/Discord-Bot-main/Bot/debug_bot.py @@ -0,0 +1,41 @@ +from importlib import reload +import event +import types + + +# Déclaration d'un décorateur à fonction (@update_module(module_name)) +def update_module(module): + def __update__(func): + # Recherche du module pour voir s'il est déjà chargé ; si ce n'est pas le cas : + # il importe dynamiquement et ensuite, la fonction 'reload' recharge le module. + async def callback(*args, **kwargs): + reload(__import__(module, globals(), locals(), [], + 0)) + return await func(*args, **kwargs) + return callback + return __update__ + + +def update_all_modules(func): + async def __update__(*args, **kwargs): + for name, val in globals().items(): + if isinstance(val, types.ModuleType): + print(val.__name__) + reload(__import__(val.__name__, globals(), locals(), [], 0)) + + reload(__import__("DebugBot", globals(), locals(), [], 0)) + + return await func(*args, **kwargs) + return __update__ + + +async def debug_on_message(*args, **kwargs): + return await event.on_message(*args, **kwargs) + + +async def debug_on_prez(*args, **kwargs): + return await event.on_prez(*args, **kwargs) + + +async def debug_on_help(*args, **kwargs): + return await event.on_help(*args, **kwargs) diff --git a/Discord-Bot-main/Bot/description.txt b/Discord-Bot-main/Bot/description.txt deleted file mode 100644 index 8638553..0000000 --- a/Discord-Bot-main/Bot/description.txt +++ /dev/null @@ -1 +0,0 @@ -$help \ No newline at end of file diff --git a/Discord-Bot-main/Bot/event.py b/Discord-Bot-main/Bot/event.py index b8450da..a04bb56 100644 --- a/Discord-Bot-main/Bot/event.py +++ b/Discord-Bot-main/Bot/event.py @@ -1,132 +1,128 @@ -import asyncio import discord BOT_PREFIX = "$" - -HELP_DATA = { - "About": - { - "cmd_0": - { - "cmd": "credit", - "help": "Affiche les crédits" - }, - - "cmd_1": - { - "cmd": "version", - "help": "Affiche les numéros de version" - }, - }, - - "General": - { - "cmd_0": - { - "cmd": "cancel", - "help": "Annule l'action en cours" - }, - - "cmd_1": - { - "cmd": "done", - "help": "Confirme l'action en cours" - }, - - "cmd_2": - { - "cmd": "edit", - "help": "Édite un message" - }, - - "cmd_3": +help_data = { + "About": { - "cmd": "lang", - "help": "Change la langue de l'utilisateur" + "cmd_0": + { + "cmd": "credit", + "help": "Affiche les crédits" + }, + "cmd_1": + { + "cmd": "version", + "help": "Affiche les numéros de version" + }, }, - }, - - "Planning": - { - "cmd_0": + "General": { - "cmd": "cal", - "help": "Permet d'accéder au calendrier" + "cmd_0": + { + "cmd": "cancel", + "help": "Annule l'action en cours" + }, + "cmd_1": + { + "cmd": "done", + "help": "Confirme l'action en cours" + }, + "cmd_2": + { + "cmd": "edit", + "help": "Édite un message" + }, + "cmd_3": + { + "cmd": "lang", + "help": "Change la langue de l'utilisateur" + }, }, - - "cmd_1": - { - "cmd": "jdr", - "help": "Envoie un lien pour créer une partie" - }, - - "cmd_2": + "Planning": { - "cmd": "site", - "help": "Permet d'accéder au calendrier" + "cmd_0": + { + "cmd": "cal", + "help": "Permet d'accéder au calendrier" + }, + "cmd_1": + { + "cmd": "jdr", + "help": "Envoie un lien pour créer une partie" + }, + "cmd_2": + { + "cmd": "site", + "help": "Permet d'accéder au calendrier" + }, }, - - }, - - "Presentation": - { - "cmd_0": + "Presentation": { - "cmd": "prez", - "help": "Envoie un lien pour se présenter" + "cmd_0": + { + "cmd": "prez", + "help": "Envoie un lien pour se présenter" + }, }, - }, - "No Category": - { - "cmd_0": { - "cmd": "help", - "help": "Affiche ce message" - }, - } - + "cmd_0": + { + "cmd": "help", + "help": "Affiche ce message" + }, + } } -def GetMaxStrSizeInArray(array:dict,callback=None): - _size=0 +def GetMaxStrSizeInArray(array: dict, callback=None): + _size = 0 for cmd in array: _r = callback(cmd) - if(_r > _size): - _size = _r + if _r > _size: + _size = _r return _size - -async def on_message(event,*args,**kwargs): + +async def on_message(event, *args): if event.content.startswith('hi'): await event.channel.send(f'Hello! Mis a jour : {args}') -async def on_prez(event,*args,**kwargs): - embed = discord.Embed(url="http://presentation.unionrolistes.fr/?webhook=https://discord.com/api/webhooks/875068900612665396/DJusy0eGs9Xyx2os-dodBVfWia2fbhfBzfmnDM9g-30ozoFYAuZBHVXaD9TKaC1wwBwg", description="⬆️ Here is the link to create your presentation.", title="Union Roliste - Presentation", color= 0x0CC1EE) +async def on_prez(event): + embed = discord.Embed( + url="http://presentation.unionrolistes.fr/?webhook=https://discord.com/api/webhooks/875068900612665396/DJusy0eGs9Xyx2os-dodBVfWia2fbhfBzfmnDM9g-30ozoFYAuZBHVXaD9TKaC1wwBwg", + description="⬆️ Here is the link to create your presentation.", title="Union Roliste - Presentation", + color=0x0CC1EE) embed.set_author(name=event.author.display_name, icon_url=event.author.avatar_url) - DATA = ["**:pen_ballpoint: Nom\n**","**:pen_ballpoint: Prenom\n**",":round_pushpin: **Address\n**",":telephone: **N° Telephone\n**", ":postbox: **Code postal\n**","**:computer: Support (Windows / Linux / Mac)\n**","**Expérience en programmation\n**"] + # DATA = ["**:pen_ballpoint: Nom\n**", "**:pen_ballpoint: Prenom\n**", ":round_pushpin: **Address\n**", + # ":telephone: **N° Telephone\n**", ":postbox: **Code postal\n**", + # "**:computer: Support (Windows / Linux / Mac)\n**", "**Expérience en programmation\n**"] + # La variable n'est jamais utilisée. Ceci dit, je la laisse en commentaire, au cas où un futur dev l'utiliserait. + embed.add_field(name="**\n**", value="**───────────────────────────────**", inline=False) - embed.set_footer(text="Union Roliste dev presentation.", icon_url="https://avatars.githubusercontent.com/u/62179928?s=200&v=4") + embed.set_footer(text="Union Roliste dev presentation.", + icon_url="https://avatars.githubusercontent.com/u/62179928?s=200&v=4") embed.set_thumbnail(url="https://avatars.githubusercontent.com/u/62179928?s=200&v=4") - await event.author.send(embed=embed) # envoie un message de presentation privée à l'auteur qui a fait a commandes + await event.author.send(embed=embed) # envoie un message de presentation privée à l'auteur qui a fait a commandes async def on_help(event): embed = discord.Embed(title="Assistance UR-Bot", color=0x000000) - embed.set_author(name="Union des Rôlistes", icon_url="https://cdn.discordapp.com/avatars/1040275175687606372/33d5a8782c1d658caeeae59799e722b0.webp?size=32") + embed.set_author(name="Union des Rôlistes", + icon_url="https://cdn.discordapp.com/avatars/1040275175687606372/33d5a8782c1d658caeeae59799e722b0.webp?size=32") embed.set_footer(text="Soutenez le JDR, Soutenez l'UR !") - embed.set_thumbnail(url="https://avatars.githubusercontent.com/u/62179928?s=200&v=4") # Initialiser le logo de l'UR en haut à droite + embed.set_thumbnail( + url="https://avatars.githubusercontent.com/u/62179928?s=200&v=4") # Initialiser le logo de l'UR en haut à droite first_category = True # Voir le bloc if/else pour comprendre - for category, commands in HELP_DATA.items(): + for category, commands in help_data.items(): # Si l'écriture de l'embed n'en est pas à sa première catégorie (About, General, Présentation, etc.), alors : if not first_category: # Ajouter une ligne qui sépare les catégories entre-elles. diff --git a/Discord-Bot-main/Bot/ur_bot.py b/Discord-Bot-main/Bot/ur_bot.py new file mode 100644 index 0000000..9848c5c --- /dev/null +++ b/Discord-Bot-main/Bot/ur_bot.py @@ -0,0 +1,86 @@ +import debug_bot +import os +import discord +from discord.ext import commands + +TOKEN = os.getenv('TOKEN') + + +class UrBot(commands.Bot): + """ + Classe initialisée à chaque lancement du BOT. + """ + async def on_ready(self): + """ + Éxécution d'une action au lancement du BOT. + """ + print('--- We have successfully logged in as {0.user}'.format(self)) + + async def on_message(self, message): + """ + Est une méthode qui permet : + - d'ignorer les messages envoyés par le BOT lui-même ; + - d'appeler une fonction de débogage pour chaque message entrant ; + - de traiter et d'exécuter les commandes des utilisateurs. + """ + if message.author == self.user: + return + + await debug_bot.debug_on_message(message) + + return await BOT.process_commands(message) + + +INTENT = discord.Intents.default() +INTENT.members = True +INTENT.messages = True +BOT = UrBot(command_prefix=debug_bot.event.BOT_PREFIX, intents=INTENT) +BOT.remove_command('help') + + +@BOT.command() +@commands.guild_only() +async def ping(ctx): + """ + Calcul de la latence de l'utilisateur (additionnée à celle des serveurs discord) et renvoie de cette dernière. + """ + latency = round(BOT.latency * 1000) + await ctx.send(f"Pong ! {latency}ms") + + +@BOT.command(name="help") +async def _help(ctx): # Ajouter un underscore évite l'erreur "Shadows built-in name 'help'", sans changer la commande. + """ + Appel d'une méthode quand la commande est reçue. + """ + await debug_bot.debug_on_help(ctx) + + +@BOT.command(name="prez") +async def prez(ctx): + """ + Appel d'une méthode quand la commande est reçue. + """ + await debug_bot.debug_on_prez(ctx) + + +# Fonction utilisée dans "reload_module". +@debug_bot.update_all_modules +async def reload_all_modules(): + """ + Retourne 0. + """ + return 0 + + +# Commande Discord pour recharger les modules +@BOT.command(aliases=['reload', 'rld']) +async def reload_module(ctx): + """ + Appel d'une méthode quand la commande est reçue. + """ + await reload_all_modules() + await ctx.channel.send("The scripts have been reloaded.") + return 0 + +BOT.run(TOKEN) diff --git a/Discord-Bot-main/README.md b/Discord-Bot-main/README.md index 91c1d98..17240e0 100644 --- a/Discord-Bot-main/README.md +++ b/Discord-Bot-main/README.md @@ -2,18 +2,17 @@ ###### doc : https://discordpy.readthedocs.io/en/stable/api.html?highlight=on_message#discord.Guild.get_channel -# Instalation +## Installation -> **Télécharger** ![Docker](docker.com) +> **Télécharger** [Docker](docker.com) > -> **Télécharger** ![Discord-Bot-main](https://github.com/UnionRolistes/Bot_Base/tree/11-cree-une-application-docker-pour-facilit%C3%A9-le-developpement/Discord-Bot-main) +> **Télécharger** [Discord-Bot-main](https://github.com/UnionRolistes/Bot_Base/tree/11-cree-une-application-docker-pour-facilit%C3%A9-le-developpement/Discord-Bot-main) > -> **Lancer le script** ![LaunchInstall.bat](https://github.com/UnionRolistes/Bot_Base/blob/11-cree-une-application-docker-pour-facilit%C3%A9-le-developpement/Discord-Bot-main/Install/LaunchInstall.bat) dans [Powershell](https://learn.microsoft.com/fr-fr/powershell/scripting/overview?view=powershell-7.3) +> **Lancer le script** [LaunchInstall.bat](https://github.com/UnionRolistes/Bot_Base/blob/11-cree-une-application-docker-pour-facilit%C3%A9-le-developpement/Discord-Bot-main/Install/LaunchInstall.bat) dans [Powershell](https://learn.microsoft.com/fr-fr/powershell/scripting/overview?view=powershell-7.3) > -> **Fini !. Le script (LaunchInstall.bat) lancera automatiquement le container et par concequant le bot discord** -> +> **Fini ! Le script (LaunchInstall.bat) lancera automatiquement votre conteneur (puis le bot discord).** > -> **Commandes disponible :** +> **Commandes disponibles :** > > - **$prez** > - **$help** diff --git a/SECURITY.md b/SECURITY.md index 034e848..d5b3b33 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,7 +6,7 @@ Use this section to tell people about which versions of your project are currently being supported with security updates. | Version | Supported | -| ------- | ------------------ | +|---------|--------------------| | 5.1.x | :white_check_mark: | | 5.0.x | :x: | | 4.0.x | :white_check_mark: | diff --git a/archives/functions.py b/archives/functions.py index 0b60b0b..3dcc46a 100644 --- a/archives/functions.py +++ b/archives/functions.py @@ -2,7 +2,7 @@ Ci-dessous se trouvent les trois commandes qui étaient demandées dans un ticket. Voici une description de ces dernières : • name : change le nickname du bot sur le serveur où est tapée la commande. Le nom initial (qui apparaît sur chaque serveur) ne change pas. Pourtant, il est possible de changer ce dernier. Cependant, Discord y impose une limite (deux changements par heure). ---> Faudrait-il garder la première option ou coder la deuxième ; faire les deux ? +→ Faudrait-il garder la première option ou coder la deuxième ; faire les deux ? • description : change le message d'état de présence du bot. On ne peut malheureusement pas changer la section "À propos de moi" par une commande. diff --git a/bot/extends/base/base.py b/bot/extends/base/base.py index 499fee1..73630e5 100644 --- a/bot/extends/base/base.py +++ b/bot/extends/base/base.py @@ -1,9 +1,7 @@ import os import asyncio from discord.ext import commands -from dotenv import load_dotenv -load_dotenv() class Base(commands.Cog, name='Base'): def __init__(self, bot: commands.Bot): @@ -21,11 +19,11 @@ async def _ping(self, ctx): await self._send_pong_response(ctx) # Commande d'affichage des crédits - @commands.command(name="credits", help='affiche les crédits', aliases=['credit', 'c']) + @commands.command(name="credentials", help='affiche les crédits', aliases=['credit', 'c']) async def _credits(self, ctx): - credits = await self._get_credits() - if credits: - await ctx.send(credits) + credentials = await self._get_credits() + if credentials: + await ctx.send(credentials) else: await ctx.send("Aucune information de crédits disponible.") @@ -53,19 +51,20 @@ async def _send_pong_response(self, ctx): ) # Récupère les informations de crédits depuis les fichiers - async def _get_credits(self): - credits = "" + @staticmethod + async def _get_credits(): + credentials = "" parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) for directory in os.listdir(parent_dir): credits_file_path = os.path.join(parent_dir, directory, 'credits.txt') if os.path.exists(credits_file_path): try: with open(credits_file_path, 'r') as f: - credits += f.read() + '\n\n' + credentials += f.read() + '\n\n' except Exception as e: print(e) - if credits: - credits_lines = credits.splitlines() + if credentials: + credits_lines = credentials.splitlines() formatted_credits = ( f"```ansi\n" f"\x1b[1;34m{credits_lines[0]}\x1b[0m\n" # Titre en bleu foncé @@ -79,7 +78,8 @@ async def _get_credits(self): return None # Récupère les informations de versions depuis les fichiers - async def _get_versions(self): + @staticmethod + async def _get_versions(): versions = "" parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) directory_names = { @@ -138,18 +138,20 @@ def _get_commands_by_category(self): return dict(sorted(commands_by_category.items())) # Génère la liste de commandes pour une catégorie - def _get_command_list(self, commands): + def _get_command_list(self, instructions): command_list = "" - for command in commands: + for command in instructions: aliases = self._get_command_aliases(command) msg = f' -- \x1b[2;36m{command.help}\x1b[0m' if command.help is not None else '' command_list += f"\x1b[2;33m{command.name}\x1b[0m{aliases}{msg}\n" return command_list # Récupère les alias d'une commande - def _get_command_aliases(self, command): + @staticmethod + def _get_command_aliases(command): return f'''{' ' + str(command.aliases).replace("'", "") if command.aliases != [] else ''}''' + # Fonction d'initialisation du Cog async def setup(bot): bot.remove_command('help') diff --git a/changelog.txt b/changelog.txt index a7bbadf..aebdb51 100755 --- a/changelog.txt +++ b/changelog.txt @@ -1,7 +1,7 @@ 02/12/2022 - Bot python 0.3.3 =========================== - Le bot ne donne plus son token en mp. - - Cree une application docker pour facilité le developpement. + - Cree une application docker pour faciliter le developpement. - ping fonctionne. 13/08/2021 - Bot python 0.3.2 diff --git a/src/bot/__init__.py b/src/bot/__init__.py index a78b127..f85f88c 100755 --- a/src/bot/__init__.py +++ b/src/bot/__init__.py @@ -1,6 +1,5 @@ -import gettext -from urpy import Localization +from Bot_Base.src.urpy.localization import Localization import platform @@ -18,4 +17,4 @@ localization.add_translation('bot_base', ['special-rp', 'fr']) -_ = localization.gettext \ No newline at end of file +_ = localization.gettext diff --git a/src/bot/cog_General.py b/src/bot/cog_General.py deleted file mode 100644 index 1e684f6..0000000 --- a/src/bot/cog_General.py +++ /dev/null @@ -1,54 +0,0 @@ -from discord.ext import commands -import bot -from bot import localization, _, strings -import urpy - -#UR_Bot © 2020 by "Association Union des Rôlistes & co" is licensed under Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA) -#To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ -#Ask a derogation at Contact.unionrolistes@gmail.com - - -class General(commands.Cog): - __doc__ = strings.General_descr - - def __init__(self, URBot: urpy.MyBot): - self.bot = URBot - self.callbacks = { - 'edit': [], - 'done': [], - 'cancel': [] - } - - async def call_callbacks(self, command: str, ctx: commands.Context): - for callback in self.callbacks[command]: - await callback(ctx) - - @commands.command(brief=strings.edit_brief, help=strings.edit_help) - async def edit(self, ctx): - """ Call callbacks bound to the edit command. """ - await self.call_callbacks('edit', ctx) - - @commands.command(brief=strings.done_brief, help=strings.done_help) - async def done(self, ctx): - """ Call callbacks bound to the done command. """ - await self.call_callbacks('done', ctx) - - @commands.command(brief=strings.cancel_brief) - async def cancel(self, ctx): - """ Call callbacks bound to the cancel command. """ - await self.call_callbacks('cancel', ctx) - - @commands.command(brief=strings.lang_brief, help=strings.lang_help) - async def lang(self, ctx: commands.Context, language): - """ Switches to specified language """ - if language in localization.languages: - localization.set_user_language(language) - await ctx.send(_("Your language has successfully been set to english !")) - - else: - await ctx.send(_("Sorry, i don't know this language !")) - - def add_to_command(self, command: str, *callbacks): - """ Adds a callback to the specified command. It will be called on command invokation.""" - for callback in callbacks: - self.callbacks[command].append(callback) diff --git a/src/bot/cog_About.py b/src/bot/cog_about.py similarity index 67% rename from src/bot/cog_About.py rename to src/bot/cog_about.py index d7f6b77..17fd658 100644 --- a/src/bot/cog_About.py +++ b/src/bot/cog_about.py @@ -1,28 +1,29 @@ from discord.ext import commands -from bot import templates -from bot import _, strings -import urpy -from urpy.utils import * +from Bot_Base.src.bot import templates +from Bot_Base.src.urpy.my_commands import MyBot, MyCog +from Bot_Base.src.urpy.utils import * +import strings -#UR_Bot © 2020 by "Association Union des Rôlistes & co" is licensed under Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA) -#To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ -#Ask a derogation at Contact.unionrolistes@gmail.com + +# UR_Bot © 2020 by "Association Union des Rôlistes & co" is licensed under Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA) +# To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ +# Ask a derogation at Contact.unionrolistes@gmail.com class About(commands.Cog): """ This cog contains commands used to get general information about the bot. """ - __doc__ = strings.About_descr + __doc__ = strings.ABOUT_DESCR - def __init__(self, owner: urpy.MyBot): - """ Creates an about cog. """ + def __init__(self, owner: MyBot): + """ Creates an 'about cog'. """ super(About, self).__init__() self.bot = owner - @commands.command(brief=strings.version_brief, help=strings.version_help) + @commands.command(brief=strings.VERSION_BRIEF, help=strings.VERSION_HELP) async def version(self, ctx: commands.Context): """ Displays the version numbers. """ await self.send_info_msg(ctx) - @commands.command(brief=strings.credit_brief, help=strings.credit_help) + @commands.command(brief=strings.CREDIT_BRIEF, help=strings.CREDIT_HELP) async def credit(self, ctx: commands.Context): """ Displays the credits. """ await self.send_info_msg(ctx, with_credits=True) @@ -36,7 +37,7 @@ async def send_info_msg(self, ctx, with_credits=False): version=cog.get_version(), credits=cog.get_credits() if with_credits else "") - for name, cog in self.bot.cogs.items() if isinstance(cog, urpy.MyCog) + for name, cog in self.bot.cogs.items() if isinstance(cog, MyCog) ) # sends the message diff --git a/src/bot/cog_general.py b/src/bot/cog_general.py new file mode 100755 index 0000000..f92b67e --- /dev/null +++ b/src/bot/cog_general.py @@ -0,0 +1,62 @@ +import gettext +from discord.ext import commands +from Bot_Base.src.urpy.localization import Localization +from Bot_Base.src.urpy.my_commands import MyBot +import strings + +# UR_Bot © 2020 by "Association Union des Rôlistes & co" is licensed under Attribution-NonCommercial-ShareAlike 4.0 +# International (CC BY-NC-SA). To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ +# Ask a derogation at Contact.unionrolistes@gmail.com + +# Configuration de gettext pour la traduction +gettext.bindtextdomain('bot_base', 'locales') +gettext.textdomain('bot_base') +_ = gettext.gettext + + +class General(commands.Cog): + """Cog contenant des commandes générales pour le bot.""" + __doc__ = strings.General_descr + + def __init__(self, ur_bot: MyBot): + """Initialise une instance spécifiée plus haut.""" + self.bot = ur_bot + self.localization = Localization() + self.callbacks = { + 'edit': [], + 'done': [], + 'cancel': [] + } + + async def call_callbacks(self, command: str, ctx: commands.Context): + """Appelle-les callbacks liés à une commande spécifique.""" + for callback in self.callbacks[command]: + await callback(ctx) + + @commands.command(brief=strings.edit_brief, help=strings.edit_help) + async def edit(self, ctx): + """Call callbacks bound to the edit command.""" + await self.call_callbacks('edit', ctx) + + @commands.command(brief=strings.done_brief, help=strings.done_help) + async def done(self, ctx): + """Call callbacks bound to the done command.""" + await self.call_callbacks('done', ctx) + + @commands.command(brief=strings.cancel_brief) + async def cancel(self, ctx): + """Call callbacks bound to the cancel command.""" + await self.call_callbacks('cancel', ctx) + + @commands.command(brief=strings.lang_brief, help=strings.lang_help) + async def lang(self, ctx: commands.Context, language): + """Switches to specified language""" + if self.localization.set_user_language(ctx.author.id): + await ctx.send(_("Your language has successfully been set to {language}!").format(language=language)) + else: + await ctx.send(_("Sorry, I don't know this language!")) + + def add_to_command(self, command: str, *callbacks): + """Adds a callback to the specified command. It will be called on command invocation.""" + for callback in callbacks: + self.callbacks[command].append(callback) diff --git a/src/bot/settings.py b/src/bot/settings.py old mode 100644 new mode 100755 index 7c9c702..7030620 --- a/src/bot/settings.py +++ b/src/bot/settings.py @@ -1 +1,4 @@ -command_prefix = '$' \ No newline at end of file +COMMAND_PREFIX = '$' +""" +Préfixe utilisé par le bot. C'est ce qui lui permet de reconnaître les commandes tapées par l'utilisateur. +""" diff --git a/src/bot/strings.py b/src/bot/strings.py index 3c2c804..50b7211 100644 --- a/src/bot/strings.py +++ b/src/bot/strings.py @@ -1,8 +1,13 @@ -from urpy.localization import lcl +""" +Ce module contient les chaînes de caractères utilisées pour les messages +et les descriptions envoyés par le bot Discord. +""" -bot_title = lcl('URbot - The discord bot of "l\'Union des Rôlistes"') -lang_brief = lcl('Switches to specified language') -lang_help = lcl( +from Bot_Base.src.urpy.localization import lcl + +BOT_TITLE = lcl('URbot - The discord bot of "l\'Union des Rôlistes"') +LANG_BRIEF = lcl('Switches to specified language') +LANG_HELP = lcl( """\ Switches to specified language @@ -11,15 +16,15 @@ - fr - special-rp\ """) -done_brief = lcl('Confirms the current action') -done_help = lang_brief -edit_brief = lcl('Edits a message') -edit_help = edit_brief -cancel_brief = lcl('Cancels the current action') -cancel_help = cancel_brief -version_brief = lcl('Displays the version numbers') -version_help = version_brief -credit_brief = lcl('Displays the credits') -credit_help = credit_brief -About_descr = lcl('This category groups various commands to display general information about the bot.') -General_descr = lcl('This category groups various commands whose utility depends on the context.') +DONE_BRIEF = lcl('Confirms the current action') +DONE_HELP = LANG_BRIEF +EDIT_BRIEF = lcl('Edits a message') +EDIT_HELP = EDIT_BRIEF +CANCEL_BRIEF = lcl('Cancels the current action') +CANCEL_HELP = CANCEL_BRIEF +VERSION_BRIEF = lcl('Displays the version numbers') +VERSION_HELP = VERSION_BRIEF +CREDIT_BRIEF = lcl('Displays the credits') +CREDIT_HELP = CREDIT_BRIEF +ABOUT_DESCR = lcl('This category groups various commands to display general information about the bot.') +GENERAL_DESCR = lcl('This category groups various commands whose utility depends on the context.') diff --git a/src/bot/URbot.py b/src/bot/urbot.py similarity index 51% rename from src/bot/URbot.py rename to src/bot/urbot.py index e87c3b7..f38c513 100755 --- a/src/bot/URbot.py +++ b/src/bot/urbot.py @@ -1,35 +1,32 @@ #!/opt/virtualenv/URBot/bin/python +"""Ce programme initialise et lance le bot URBot.""" -import inspect -import logging import os import platform import sys -import importlib -from importlib import resources - +import logging +import inspect +from importlib import resources, import_module from discord.ext import commands - -from urpy import MyHelpCommand -from urpy.utils import error_log, code_block, log -from bot import _, strings -from bot import localization - -import urpy +from Bot_Base.src.urpy import localization +from Bot_Base.src.urpy.help import MyHelpCommand +from Bot_Base.src.urpy.my_commands import MyBot +from Bot_Base.src.urpy.utils import error_log, code_block, log +from Bot_Base.src.bot import info +from cog_about import About +from cog_general import General +import settings +import strings debug = platform.system() == 'Windows' -from bot import info -from bot import settings -from bot.cog_General import General -from bot.cog_About import About -#UR_Bot © 2020 by "Association Union des Rôlistes & co" is licensed under Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA) -#To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ -#Ask a derogation at Contact.unionrolistes@gmail.com +# UR_Bot © 2020 by "Association Union des Rôlistes & co" is licensed under Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA) +# To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ +# Ask a derogation at Contact.unionrolistes@gmail.com -class URBot(urpy.MyBot): +class URBot(MyBot): """ Discord bot for "l'Union des Rôlistes". Contains global settings and functions. @@ -43,14 +40,14 @@ def __init__(self): Creates an instance of URBot. Use the run method to start it. """ self.localization = localization # TODO move localization to urpy - super(URBot, self).__init__(settings.command_prefix, help_command=MyHelpCommand(self.localization)) + super(URBot, self).__init__(settings.COMMAND_PREFIX, help_command=MyHelpCommand(self.localization)) self.cog_general = General(self) # adds base cogs to the bot self.add_cog(self.cog_general) self.add_cog(About(self)) - self.isDebugMode = debug + self.is_debug_mode = debug @staticmethod def get_credits(): @@ -65,21 +62,22 @@ def get_version(): @staticmethod def get_name(): """ Return title of the bot.""" - return _(strings.bot_title) + return strings.BOT_TITLE async def on_ready(self): """ Listener to the on_ready event. """ error_log("We have logged in as {}!".format(self.user)) async def invoke(self, ctx: commands.Context): - self.localization.set_current_user(ctx.author.id) + localisation = localization.Localization() + self.localization.Localization.set_current_user(localisation, ctx.author.id) await super(URBot, self).invoke(ctx) async def on_command_error(self, context, exception: commands.CommandError): """ Listener to the on_command_error event. """ # TODO better docstring if isinstance(exception, commands.UserInputError): - await context.send(_("{err_msg}\nIncorrect usage ☹ Check help of the command for more information.").format( - err_msg=code_block(exception))) + err_msg = code_block(exception) + await context.send(f"{err_msg}\nIncorrect usage ☹ Check help of the command for more information.") else: raise exception @@ -96,23 +94,47 @@ def add_to_command(self, command: str, *callbacks): if debug: - cogs_path = os.path.abspath('cogs') - token_path = '../../../bot_token' + COGS_PATH = os.path.abspath('cogs') + TOKEN_PATH = '../../../bot_token' else: - cogs_path = '/usr/local/src/URbot/bot/cogs' - token_path = '/usr/local/src/URbot/.bot_token' + COGS_PATH = '/usr/local/src/URbot/bot/cogs' + TOKEN_PATH = '/usr/local/src/URbot/.bot_token' def main(): + """ + Initialise et lance le bot Discord URBot. + + Cette fonction configure le système de logging, charge tous les cogs Discord + depuis le répertoire spécifié (COGS_PATH), les ajoute au bot, lit le jeton du + bot depuis TOKEN_PATH, et démarre le bot avec le jeton récupéré. + + Étapes : + 1. Configure le logging au niveau INFO. + 2. Initialise une instance de URBot. + 3. Ajoute le dossier 'cogs' au chemin d'importation Python. + 4. Analyse le dossier 'cogs' pour trouver les modules Python contenant des cogs Discord. + 5. Importe et ajoute chaque cog trouvé à l'instance du bot. + 6. Enregistre le chargement réussi de chaque cog. + 7. Lit le jeton du bot Discord depuis TOKEN_PATH. + 8. Lance le bot en utilisant le jeton récupéré. + + Lance : + ValueError : Si un module dans COGS_PATH ne contient pas de fichier 'cog.py'. + + Remarque : + Assurez-vous que COGS_PATH et TOKEN_PATH sont correctement définis avant d'exécuter cette fonction. + """ + logging.basicConfig(level=logging.INFO) ur_bot = URBot() log("Loading cogs...") # adds the "cogs" folder to the import path - sys.path.append(cogs_path) + sys.path.append(COGS_PATH) - if os.path.exists(cogs_path): + if os.path.exists(COGS_PATH): # scans the "cogs" folder for cogs to add to the bot - for dir_entry in os.scandir(cogs_path): + for dir_entry in os.scandir(COGS_PATH): dir_entry: os.DirEntry # imports and adds all found cogs @@ -120,19 +142,20 @@ def main(): # tries to import the cog module try: - module = importlib.import_module(f"{dir_entry.name}.cog") + module = import_module(f"{dir_entry.name}.cog") except ValueError as e: # TODO fix error handling error_log( - f"The package \'{dir_entry.name}\' does not contain a module named \'cog.py\' (in {dir_entry.path}).") + f"The package \'{dir_entry.name}\' does not contain a module named \'cog.py\' (in {dir_entry.path}. //{e}") else: # retrieves all discord.Cog based classes - cog_classes = filter(lambda member: inspect.isclass(member[1]) and issubclass(member[1], commands.Cog), - inspect.getmembers(module)) - for Cog in cog_classes: + cog_classes = filter( + lambda member: inspect.isclass(member[1]) and issubclass(member[1], commands.Cog), + inspect.getmembers(module)) + for cog in cog_classes: # adds cog to the bot - cog = Cog[1](ur_bot) + cog = cog[1](ur_bot) ur_bot.add_cog(cog) log(f"Loaded : {cog.qualified_name}") log("Done") @@ -140,7 +163,7 @@ def main(): log("No cogs found") # reads the bot token - with open(token_path) as f: + with open(TOKEN_PATH) as f: bot_token = f.read() # starts the bot diff --git a/src/start.py b/src/start.py old mode 100644 new mode 100755 index b303b77..707cfd5 --- a/src/start.py +++ b/src/start.py @@ -1,5 +1,8 @@ -#!/opt/virtualenv/URBot/bin/python -from bot.URbot import main +#!/opt/virtualenv/ur_bot/bin/python +from bot.urbot import main if __name__ == '__main__': + """ + Démarrage du programme. + """ main() diff --git a/src/urpy/__init__.py b/src/urpy/__init__.py index 6c3e23b..2af3cae 100644 --- a/src/urpy/__init__.py +++ b/src/urpy/__init__.py @@ -1,7 +1,7 @@ -from urpy.localization import Localization, lcl -from urpy.help import MyHelpCommand -from urpy.my_commands import MyBot, MyCog, MyContext -from urpy.get_ressources import * +from Bot_Base.src.urpy.localization import Localization, lcl +from Bot_Base.src.urpy.help import MyHelpCommand +from Bot_Base.src.urpy.my_commands import MyBot, MyCog, MyContext +from Bot_Base.src.urpy.get_ressources import * base_localization = Localization('../locale') # TODO path for linux _ = base_localization.gettext diff --git a/src/urpy/get_ressources.py b/src/urpy/get_ressources.py index 82d18be..dee8ac3 100644 --- a/src/urpy/get_ressources.py +++ b/src/urpy/get_ressources.py @@ -1,4 +1,5 @@ from importlib import resources + def get_planning_anncmnt_mdl() -> str: return resources.read_text('urpy.templates', "planning_annoucement_template.txt") diff --git a/src/urpy/help.py b/src/urpy/help.py index 4f17ef1..811ef33 100644 --- a/src/urpy/help.py +++ b/src/urpy/help.py @@ -1,11 +1,11 @@ import discord from discord.ext import commands -#UR_Bot © 2020 by "Association Union des Rôlistes & co" is licensed under Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA) -#To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ -#Ask a derogation at Contact.unionrolistes@gmail.com +# UR_Bot © 2020 by "Association Union des Rôlistes & co" is licensed under Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA) +# To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ +# Ask a derogation at Contact.unionrolistes@gmail.com -from urpy.localization import lcl +from Bot_Base.src.urpy.localization import lcl _ = lcl @@ -17,7 +17,7 @@ def __init__(self, localization, **options): commands_heading=lcl('Commands:'), no_category=lcl('No Category'), **options) # TODO 'No Category' in french global _ - # This check is necessary cause HelpCommand is deep copied every time in discord api. """ + # This check is necessary cause HelpCommand is deeply copied every time in discord api. """ if _ is lcl: _ = localization.gettext @@ -35,10 +35,11 @@ def add_command_formatting(self, command): signature = self.get_command_signature(command) self.paginator.add_line(signature, empty=True) - domain = getattr(command.cog, 'domain', None) + # 'domain = getattr(command.cog, 'domain', None)' ; la variable n'est jamais utilisée. À laisser au cas où ça + # servirait un futur développeur. if command.help: try: - self.paginator.add_line(_(command.help, domain), empty=True) + self.paginator.add_line(_(command.help), empty=True) except RuntimeError: for line in command.help.splitlines(): self.paginator.add_line(line) # TODO localization here @@ -47,32 +48,35 @@ def add_command_formatting(self, command): def get_ending_note(self): """:class:`str`: Returns help command's ending note. This is mainly useful to override for i18n purposes.""" command_name = self.invoked_with - return _("Type {0}{1} command for more info on a command.\n" - "You can also type {0}{1} category for more info on a category.").format(self.clean_prefix, - command_name) + return _(f"Type {self.clean_prefix}{command_name} command for more info on a command.\n" + f"You can also type {self.clean_prefix}{command_name} category for more info on a category.") + # Une erreur se produit sur 'self.clean_prefix'. + # En effet, l'IDE demande la création de cette méthode dans la classe. - def add_indented_commands(self, commands, *, heading, max_size=None): - if not commands: + def add_indented_commands(self, instructions, *, heading, max_size=None): + if not instructions: return if heading.startswith('\u200b'): heading = _(heading[1:-1]) + ':' self.paginator.add_line(_(heading[:-1]) + heading[-1]) - max_size = max_size or self.get_max_size(commands) + max_size = max_size or self.get_max_size(instructions) - get_width = discord.utils._string_width - for command in commands: - domain = getattr(command.cog, 'domain', None) + get_width = discord.utils._string_width # Erreur : 'Access to a protected member _string_width of a module'. + for command in instructions: + # domain = getattr(command.cog, 'domain', None) ; la variable n'est jamais utilisée. À laisser au cas où ça + # servirait un futur développeur. name = command.name width = max_size - (get_width(name) - len(name)) - entry = '{0}{1:<{width}} {2}'.format(self.indent * ' ', name, _(command.short_doc, domain), width=width) + entry = '{0}{1:<{width}} {2}'.format(self.indent * ' ', name, _(command.short_doc), width=width) self.paginator.add_line(self.shorten_text(entry)) async def send_cog_help(self, cog): - domain = getattr(cog, 'domain', None) + # 'domain = getattr(cog, 'domain', None)' ; la variable n'est jamais utilisée. À laisser au cas où ça + # servirait un futur développeur. if cog.description: - self.paginator.add_line(_(cog.description, domain), empty=True) + self.paginator.add_line(_(cog.description), empty=True) filtered = await self.filter_commands(cog.get_commands(), sort=self.sort_commands) - self.add_indented_commands(filtered, heading=_(self.commands_heading, domain)) + self.add_indented_commands(filtered, heading=_(self.commands_heading)) note = self.get_ending_note() if note: diff --git a/src/urpy/localization.py b/src/urpy/localization.py index 424fdab..2954672 100644 --- a/src/urpy/localization.py +++ b/src/urpy/localization.py @@ -1,10 +1,11 @@ import gettext -#UR_Bot © 2020 by "Association Union des Rôlistes & co" is licensed under Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA) -#To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ -#Ask a derogation at Contact.unionrolistes@gmail.com -def lcl(s: str, domain=None): +# UR_Bot © 2020 by "Association Union des Rôlistes & co" is licensed under Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA) +# To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ +# Ask a derogation at Contact.unionrolistes@gmail.com + +def lcl(s: str): """ Marks a string for localization. """ return s @@ -21,7 +22,7 @@ def __init__(self, localedir='', default_language='fr', default_domain='bot_base # TODO add handling of several domains (context manager) # TODO autoload - def add_translation(self, domain, codes, *aliases): # TODO make aliases + def add_translation(self, domain, codes): # TODO make aliases et ajouter ici le paramètre '*aliases' language = gettext.translation(domain, self._localedir, codes, fallback=True) if codes[0] not in self.languages: self.languages[codes[0]] = {} diff --git a/src/urpy/my_commands.py b/src/urpy/my_commands.py index 586f9be..74c8ceb 100644 --- a/src/urpy/my_commands.py +++ b/src/urpy/my_commands.py @@ -1,14 +1,12 @@ -from functools import partial -from types import MethodType +from abc import ABC, ABCMeta, abstractmethod -import discord from discord.ext import commands -from importlib import resources -from abc import ABC, abstractmethod, ABCMeta -#UR_Bot © 2020 by "Association Union des Rôlistes & co" is licensed under Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA) -#To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ -#Ask a derogation at Contact.unionrolistes@gmail.com + +# UR_Bot © 2020 by "Association Union des Rôlistes & co" is licensed under Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA) +# To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ +# Ask a derogation at Contact.unionrolistes@gmail.com + class VersionNCreditInterface(ABC): @staticmethod @@ -35,11 +33,13 @@ class MyBot(commands.Bot, VersionNCreditInterface): def __init__(self, command_prefix='$', *args, **kwargs): super(MyBot, self).__init__(command_prefix=command_prefix, *args, **kwargs) -class MyCogMeta(type(commands.Cog), VersionNCreditInterface, ABCMeta): + +class MyCogMeta(type(commands.Cog), VersionNCreditInterface, ABCMeta, ABC): pass + # TODO: shaky code, needs to be cleaned -class MyCog(commands.Cog, VersionNCreditInterface, metaclass=MyCogMeta): +class MyCog(commands.Cog, VersionNCreditInterface, ABC, metaclass=MyCogMeta): def __init__(self, bot, domain): self.bot = bot self.domain = domain @@ -48,8 +48,9 @@ def __init__(self, bot, domain): async def on_ready(self): print(f"\t| {self.qualified_name} started.") + # TODO: à améliorer -from functools import wraps +# from functools import wraps # def add_to_comm(name): @@ -62,7 +63,7 @@ async def on_ready(self): # return decorator - # args[0].bot.add_to_command(name, partial(func, args[0])) +# args[0].bot.add_to_command(name, partial(func, args[0])) # # class Test: # def __init__(self, name): diff --git a/src/urpy/utils.py b/src/urpy/utils.py index 5ddc76d..b8735fd 100644 --- a/src/urpy/utils.py +++ b/src/urpy/utils.py @@ -1,13 +1,14 @@ import re import sys -import requests -import discord from importlib import resources -from urpy import _ -#UR_Bot © 2020 by "Association Union des Rôlistes & co" is licensed under Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA) -#To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ -#Ask a derogation at Contact.unionrolistes@gmail.com +import discord +import requests + + +# UR_Bot © 2020 by "Association Union des Rôlistes & co" is licensed under Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA) +# To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ +# Ask a derogation at Contact.unionrolistes@gmail.com def error_log(*msg, name="BOT"): print(f"{name}:ERROR|", *msg, file=sys.stderr) @@ -29,6 +30,7 @@ async def get_public_ip() -> str: """ return requests.get('https://api.ipify.org').text + async def get_informations(msg: discord.Message): """ Extract information from an announcement message. @@ -51,7 +53,7 @@ async def get_informations(msg: discord.Message): infos = {} # Matches strings of the form : ' ** {name} ** {value} ' ending on ':', '**' or '\n' - for match in re.finditer("\*\*(.*)\*\* *(?:\n| )(.*)\n(?::|\*\*|\n)", msg.embeds[0].description): + for match in re.finditer(" \\*\\*(.*)\\*\\* *(?:\n|)(.*)\n(?::|\\*\\*|\n)", msg.embeds[0].description): infos[match.group(1).strip().lower()] = match.group(2) return infos @@ -61,7 +63,7 @@ def code_block(s): def edit_fmt(s): - return f"\|~ {s} ~|" + return f"\\|~ {s} ~|" def code_line(s: str): diff --git a/src/urpy/xml.py b/src/urpy/xml.py index 10d0e6d..ecd834f 100644 --- a/src/urpy/xml.py +++ b/src/urpy/xml.py @@ -1,59 +1,60 @@ -import pickle -from pathlib import Path -import discord -from discord import Webhook, AsyncWebhookAdapter -from aiohttp import ClientSession -from lxml import etree as et import cgi -from urpy.utils import error_log, log -from urpy.localization import lcl, Localization +from pickle import load import re from datetime import datetime -#UR_Bot © 2020 by "Association Union des Rôlistes & co" is licensed under Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA) -#To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ -#Ask a derogation at Contact.unionrolistes@gmail.com - - -game_tag = 'partie' -title_tag = 'titre' -max_players_tag = 'capacite' -min_players_tag = 'minimum' -nb_joined_tag = 'inscrits' -date_tag = 'date' -time_tag = 'heure' -length_tag = 'duree' -type_tag = 'type' -mj_tag = 'mj' -system_tag = 'systeme' -minors_allowed_tag = 'pjMineur' -platforms_tag = 'plateformes' -details_tag = 'details' -link_tag = 'lien' - -tags = [title_tag, max_players_tag, min_players_tag, nb_joined_tag, - date_tag, time_tag, length_tag, type_tag, mj_tag, system_tag, - minors_allowed_tag, platforms_tag, details_tag, link_tag] - -tags_to_form = { - title_tag: 'jdr_title', - max_players_tag: 'maxJoueurs', - min_players_tag: 'minJoueurs', - #date_tag: 'jdr_date', car on ne veut pas que celui là s'écrive automatiquement. On veut d'abord séparer la date et l'heure et faire le mettre sous la forme Y-M-D - length_tag: 'jdr_length', - type_tag: 'jdr_type', - system_tag: 'jdr_system', - minors_allowed_tag: 'jdr_pj', - details_tag: 'jdr_details', +from aiohttp import ClientSession +from discord import AllowedMentions, Embed, Webhook # , AsyncWebhookAdapter +from lxml import etree as et + +from Bot_Base.src.urpy.localization import lcl, Localization +from Bot_Base.src.urpy.utils import error_log, log + + +# UR_Bot © 2020 by "Association Union des Rôlistes & co" is licensed under Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA) +# To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ +# Ask a derogation at Contact.unionrolistes@gmail.com + +GAME_TAG = 'partie' +TITLE_TAG = 'titre' +MAX_PLAYER_TAG = 'capacite' +MIN_PLAYER_TAG = 'minimum' +NB_JOINED_TAG = 'inscrits' +DATE_TAG = 'date' +TIME_TAG = 'heure' +LENGTH_TAG = 'duree' +TYPE_TAG = 'type' +MJ_TAG = 'mj' +SYSTEM_TAG = 'systeme' +MINORS_ALLOWED_TAG = 'pjMineur' +PLATFORMS_TAG = 'plateformes' +DETAILS_TAG = 'details' +LINK_TAG = 'lien' + +TAGS = [TITLE_TAG, MAX_PLAYER_TAG, MIN_PLAYER_TAG, NB_JOINED_TAG, + DATE_TAG, TIME_TAG, LENGTH_TAG, TYPE_TAG, MJ_TAG, SYSTEM_TAG, + MINORS_ALLOWED_TAG, PLATFORMS_TAG, DETAILS_TAG, LINK_TAG] + +TAGS_TO_FORM = { + TITLE_TAG: 'jdr_title', + MAX_PLAYER_TAG: 'maxJoueurs', + MIN_PLAYER_TAG: 'minJoueurs', + # date_tag: 'jdr_date', car on ne veut pas que celui-là s'écrive automatiquement. ↓ + # On veut d'abord séparer la date et l'heure et faire le mettre sous la forme Y-M-D + LENGTH_TAG: 'jdr_length', + TYPE_TAG: 'jdr_type', + SYSTEM_TAG: 'jdr_system', + MINORS_ALLOWED_TAG: 'jdr_pj', + DETAILS_TAG: 'jdr_details', } -tags_to_lambda = { - nb_joined_tag: lambda f: '0', - #time_tag: lambda f: '15h00', - mj_tag: lambda f: f"<@{f.getvalue('user_id')}> [{f.getvalue('pseudo')}]", - platforms_tag: lambda f: " ".join(f.getlist('platform')), - link_tag: lambda f: 'https://discord.com/channels/TODO' +TAGS_TO_LAMBDA = { + NB_JOINED_TAG: lambda f: '0', + # time_tag: lambda f: '15h00', + MJ_TAG: lambda f: f"<@{f.getvalue('user_id')}> [{f.getvalue('pseudo')}]", + PLATFORMS_TAG: lambda f: " ".join(f.getlist('platform')), + LINK_TAG: lambda f: 'https://discord.com/channels/TODO' } _ = lcl @@ -61,7 +62,7 @@ class Calendar: """ - This class allows updating an xml file that contains the data of an event calendar. + This class allows updating a xml file that contains the data of an event calendar. """ creators_to_webhook = {} @@ -72,11 +73,11 @@ def __init__(self, fp: str, localization: Localization = None): # TODO localiza @fp path to xml file """ self.fp = fp - import os - from urpy import utils - #utils.html_header_content_type() - #print(os.listdir("/usr/share/urbot/")) - #print(open("/usr/share")) + # import os + # from Bot_Base.src.urpy import utils + # utils.html_header_content_type() + # print(os.listdir("/usr/share/urbot/")) + # print(open("/usr/share")) self.tree: et.ElementTree = et.parse(self.fp, et.XMLParser(remove_blank_text=True)) if localization is not None: global _ @@ -94,63 +95,71 @@ def get_last_id(self) -> int: # TODO change name and more def find_last_id(self) -> int: return max(map(lambda e: int(e.attrib['id']), self.tree.getroot())) + # TODO check Publish - async def add_event(self, form: cgi.FieldStorage, embed: discord.Embed): + async def add_event(self, form: cgi.FieldStorage, embed: Embed): """ Add an event to the calendar. """ print('Debug: Chargement du webhook...') - with open(f'/usr/local/src/URbot/wh', 'rb') as f: # TODO clean up - d = pickle.load(f) + with open('/usr/local/src/URbot/wh', 'rb') as f: # TODO clean up + d = load(f) print('Debug: Webhook chargé !') - + wh_url, guild_id, channel_id = d[int(form.getvalue('user_id'))] - async with ClientSession() as client: - webhook: Webhook = Webhook.from_url(wh_url, adapter=AsyncWebhookAdapter(client)) + async with ClientSession(): + webhook: Webhook = Webhook.from_url(wh_url) # , adapter=AsyncWebhookAdapter(client)) + + msg = await webhook.send("", wait=True, embed=embed, allowed_mentions=AllowedMentions(users=True)) - msg = await webhook.send("", wait=True, embed=embed, allowed_mentions=discord.AllowedMentions(users=True)) - try: root = self.tree.getroot() root.set('last_id', str(int(root.get('last_id')) + 1)) - parent = et.SubElement(self.tree.getroot(), game_tag, id=root.get('last_id')) + parent = et.SubElement(self.tree.getroot(), GAME_TAG, id=root.get('last_id')) - for tag in tags: + for tag in TAGS: new_elmnt = et.SubElement(parent, tag) - if tag in tags_to_form: - new_elmnt.text = form.getvalue(tags_to_form[tag], 'NotFound') - elif tag == link_tag: + if tag in TAGS_TO_FORM: + new_elmnt.text = form.getvalue(TAGS_TO_FORM[tag], 'NotFound') + elif tag == LINK_TAG: new_elmnt.text = f"https://discord.com/channels/{guild_id}/{channel_id}/{msg.id}" - elif tag == date_tag: + elif tag == DATE_TAG: - date_string = form.getvalue(tags_to_form[date_tag]) #On récupére la date en string (actuellement sous la forme 10/08/2021 11:00) - date = datetime.strptime(date_string, "%d/%m/%Y %H:%M") #On transforme ce string en objet (Doit avoir la même mise en forme / / / : que le string cité ci-dessus) - date = date.strftime("%Y-%m-%d")#On récupère uniquement la date sous la forme 2021-08-10, pour la compatibilité dans le calendrier web - #Ces changements de format ne concernent pas le message Discord, déjà posté, mais l'écriture dans le xml. Le calendrier php a besoin d'une date et heure sous ce format pour fonctionner + date_string = form.getvalue(TAGS_TO_FORM[ + DATE_TAG]) # On récupère la date en string (actuellement sous la forme 10/08/2021 11:00) + date = datetime.strptime(date_string, + "%d/%m/%Y %H:%M") # On transforme ce string en objet (Doit avoir la même mise en forme / / / : que le string cité ci-dessus) + date = date.strftime( + "%Y-%m-%d") # On récupère uniquement la date sous la forme 2021-08-10, pour la compatibilité dans le calendrier web + # Ces changements de format ne concernent pas le message Discord, déjà posté, mais l'écriture dans le xml. Le calendrier php a besoin d'une date et heure sous ce format pour fonctionner new_elmnt.text = date - elif tag == time_tag: + elif tag == TIME_TAG: - date_string = form.getvalue(tags_to_form[date_tag]) #On récupére la date en string (actuellement sous la forme 10/08/2021 11:00) - date2 = datetime.strptime(date_string, "%d/%m/%Y %H:%M") #On transforme ce string en objet (Doit avoir la même mise en forme / / / : que le string cité ci-dessus) - heure = date2.strftime("%Hh%M") #On récupère uniquement l'heure, sous la forme 12h00 + date_string = form.getvalue(TAGS_TO_FORM[ + DATE_TAG]) # On récupère la date en string (actuellement sous la forme 10/08/2021 11:00) + date2 = datetime.strptime(date_string, + "%d/%m/%Y %H:%M") # On transforme ce string en objet (Doit avoir la même mise en forme / / / : que le string cité ci-dessus) + heure = date2.strftime("%Hh%M") # On récupère uniquement l'heure, sous la forme 12h00 new_elmnt.text = heure else: - new_elmnt.text = tags_to_lambda[tag](form) + new_elmnt.text = TAGS_TO_LAMBDA[tag](form) except Exception as e: - print("Problème lors de l'écriture dans le XML") #Si l'écriture dans le xml ne marche pas, il ne faut pas que cela empêche de poster le message sur Discord + print( + f"Problème lors de l'écriture dans le XML. Erreur : {e}") + # Si l'écriture dans le xml ne marche pas, il ne faut pas que cela empêche de poster le message sur Discord - def remove_event(self, id, show_errors=True): # TODO better name for show_errors + def remove_event(self, ref, show_errors=True): # TODO better name for show_errors root = self.tree.getroot() try: - root.remove(next(e for e in root if e.get('id') == str(id))) + root.remove(next(e for e in root if e.get('id') == str(ref))) except StopIteration: if show_errors: - error_log(f"Attempt to remove an event that doesn't exist. Event id : {id}") + error_log(f"Attempt to remove an event that doesn't exist. Event id : {ref}") else: - log(f"Event with id {id} successfully removed.") + log(f"Event with id {ref} successfully removed.") def remove_events(self, ids: str): """ @@ -164,10 +173,10 @@ def remove_events(self, ids: str): if group.isnumeric(): self.remove_event(group) elif re.match("^[0-9]*-[0-9]*$", group): - start, end = (int(id) for id in group.split('-')) + start, end = (int(single_id) for single_id in group.split('-')) assert start <= end - for id in range(start, end): - self.remove_event(id, show_errors=False) + for single_id in range(start, end): + self.remove_event(single_id, show_errors=False) else: raise ValueError("Incorrect format of the ids' string.")