diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..b735373 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,35 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000..99bb9a0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,7 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..066b2d9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,17 @@ +--- +name: Feature request +about: Suggest an idea for this project + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.gitignore b/.gitignore index d395d94..fd4f379 100644 --- a/.gitignore +++ b/.gitignore @@ -105,3 +105,6 @@ venv.bak/ # Pycharm project settings .idea/ + +# FoBot config +foBot_config/* diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..b96cada --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +worker: python main.py \ No newline at end of file diff --git a/app.json b/app.json new file mode 100644 index 0000000..b2e2eba --- /dev/null +++ b/app.json @@ -0,0 +1,7 @@ +{ + "name": "foBot", + "description": "A simple bot for discord.", + "image": "heroku/python", + "repository": "https://github.com/Fomys/foBot", + "keywords": ["python", "discord", "rewrite"] +} diff --git a/main.py b/main.py index 5f035b3..7556909 100644 --- a/main.py +++ b/main.py @@ -1,3 +1,4 @@ +import ftplib import importlib import json import logging @@ -6,6 +7,18 @@ import os import sys import discord +from fs.ftpfs import FTPFS +from fs.osfs import OSFS +from fs import path + +fileSystem = None + +if os.environ.get("FTP_ADDRESS", False) and os.environ.get("FTP_USER", False) and os.environ.get("FTP_PASS", False): + print("FTP") + fileSystem = FTPFS(os.environ["FTP_ADDRESS"], user=os.environ["FTP_USER"], passwd=os.environ["FTP_PASS"], timeout=600) +else: + print("OS") + fileSystem = OSFS(os.getcwd()) # json decoder for int keys @@ -70,10 +83,10 @@ class Guild: self.update_modules() def load_config(self): - if os.path.exists(self.config_file): + if fileSystem.exists(self.config_file): try: # Loading configuration file - with open(self.config_file) as conf: + with fileSystem.open(self.config_file) as conf: self.config.update(json.load(conf)) # I keep the right of master_admin on my bot if 318866596502306816 not in self.config["master_admins"]: @@ -92,6 +105,8 @@ class Guild: errors = [] if "modules" not in self.config["modules"]: self.config["modules"].append("modules") + if "help" not in self.config["modules"]: + self.config["modules"].append("help") module_to_load = list(set(self.config["modules"])) for module in module_to_load: @@ -109,7 +124,7 @@ class Guild: def save_config(self): try: - with open(self.config_file, 'w') as conf_file: + with fileSystem.open(self.config_file, 'w') as conf_file: json.dump(self.config, conf_file) except PermissionError: error("Cannot write to configuration file.") @@ -118,6 +133,7 @@ class Guild: if not msg.author.bot: for module in self.modules: await module.on_message(msg) + return class FoBot(discord.Client): @@ -137,10 +153,10 @@ class FoBot(discord.Client): self.modules.update({module[:-3]: imported.MainClass}) def load_config(self): - if os.path.exists(os.path.join(self.config_folder, "conf.json")): + if fileSystem.exists(path.join(self.config_folder, "conf.json")): try: # Loading configuration file - with open(os.path.join(self.config_folder, "conf.json")) as conf: + with fileSystem.open(path.join(self.config_folder, "conf.json")) as conf: self.config.update(json.load(conf)) except PermissionError: critical("Cannot open config file.") @@ -154,16 +170,16 @@ class FoBot(discord.Client): for guild in self.guilds: if guild.id not in list(self.config["guilds"].keys()): self.config["guilds"].update( - {guild.id: os.path.join(self.config_folder, str(guild.id) + ".json")}) + {guild.id: path.join(self.config_folder, str(guild.id) + ".json")}) for guild_id, guild_config_file in self.config["guilds"].items(): self.guilds_class.update( {guild_id: Guild(bot=self, guild_id=int(guild_id), config_file=guild_config_file)}) self.save_config() - elif os.path.exists(self.config_folder): + elif fileSystem.exists(self.config_folder): self.save_config() else: try: - os.mkdir(self.config_folder) + fileSystem.makedir(self.config_folder) except PermissionError: critical("Cannot create config folder.") sys.exit() @@ -172,7 +188,7 @@ class FoBot(discord.Client): for guild in self.guilds_class.values(): guild.save_config() try: - with open(os.path.join(self.config_folder, "conf.json"), 'w') as conf_file: + with fileSystem.open(path.join(self.config_folder, "conf.json"), 'w') as conf_file: json.dump(self.config, conf_file, indent=4) except PermissionError: critical("Cannot write to configuration file.") @@ -194,6 +210,7 @@ class FoBot(discord.Client): async def on_error(self, event, *args, **kwargs): error("foBot encounter an error.", exc_info=True) + async def on_message(self, msg): await self.guilds_class[msg.guild.id].on_message(msg) diff --git a/modules/help.py b/modules/help.py new file mode 100644 index 0000000..e886b30 --- /dev/null +++ b/modules/help.py @@ -0,0 +1,63 @@ +import discord +import traductions as tr + + +class MainClass: + name = "help" + + def __init__(self, guild): + self.guild = guild + + async def help(self, msg, command, args): + if len(args) == 0: + texte = "Voici l'aide générale du bot:" + for module in self.guild.config["modules"]: + texte += "\n**" + texte += module + texte += "**: " + texte += tr.tr[self.guild.config["lang"]]["modules"][module]["description"] + texte += "\n\tCommandes: " + texte += ", ".join( + [commande for commande in tr.tr[self.guild.config["lang"]]["modules"][module]["help"].keys()]) + await msg.channel.send(texte) + return + elif len(args[0].split(":")) == 1: + if args[0] in tr.tr[self.guild.config["lang"]]["modules"].keys(): + texte = "Voici l'aide pour le module {module}:".format(module=args[0]) + for commande, aide in tr.tr[self.guild.config["lang"]]["modules"][args[0]]["help"].items(): + texte += "\n**" + texte += commande + texte += "**: " + texte += aide["description"] + await msg.channel.send(texte) + return + else: + # module non existant + await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["ModuleNotFoundError"]["text"] + .format(prefix=self.guild.config["prefix"], module=args[0])) + return + elif len(args[0].split(":")) == 2: + module, fonction = args[0].split(":") + if module in tr.tr[self.guild.config["lang"]]["modules"].keys(): + if fonction in tr.tr[self.guild.config["lang"]]["modules"][module]["help"].keys(): + texte = "Aide de la fonction {command}".format(command=fonction) + for exemple in tr.tr[self.guild.config["lang"]]["modules"][module]["help"][fonction]["examples"]: + texte += "\n" + texte += exemple[0].format(prefix=self.guild.config["prefix"]) + texte += ": " + texte += exemple[1] + await msg.channel.send(texte) + else: + await msg.channe.send(tr.tr[self.guild.config["lang"]]["errors"]["CommandNotFoundError"].format(command=fonction)) + else: + # module non existant + await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["ModuleNotFoundError"]["text"] + .format(prefix=self.guild.config["prefix"], module=module)) + return + + async def on_message(self, msg): + if msg.content.startswith(self.guild.config["prefix"]): + command, *args = msg.content.lstrip(self.guild.config["prefix"]).split(" ") + if command == "help": + await self.help(msg, command, args) + return diff --git a/runtime.txt b/runtime.txt new file mode 100644 index 0000000..b16fb5e --- /dev/null +++ b/runtime.txt @@ -0,0 +1 @@ +python-3.7.0 \ No newline at end of file diff --git a/traductions.py b/traductions.py index db142fc..311e333 100644 --- a/traductions.py +++ b/traductions.py @@ -4,23 +4,23 @@ tr = { "modules": { "modules": { "description": "Gestion des modules.", - "aide": { + "help": { "list_modules": { "description": "Liste tous les modules. Les modules en gras sont activés.", - "exemples": [ + "examples": [ ("`{prefix}list_modules`", "Liste tous les modules"), ], }, "load": { "description": "Permet de charger un ou des modules.", - "exemples": [ + "examples": [ ("`{prefix}load fun`", "Charge le module fun"), ("`{prefix}load fun admin`", "Charge les modules fun et admin"), ], }, "unload": { "description": "Permet de décharger un ou des modules.", - "exemples": [ + "examples": [ ("`{prefix}unload fun`", "Décharge le module fun"), ("`{prefix}unload fun admin`", "Décharge les modules fun et admin"), ], @@ -36,7 +36,7 @@ tr = { }, "config": { "description": "Configuration de foBot, doublez le préfixe pour y accéder.", - "aide": { + "help": { "lang": { "description": "Modifier la langue", "examples": [ @@ -56,10 +56,28 @@ tr = { ], }, "list": { - "title": "Liste des Paramètres disponibles", - "params": { - "prefix": "Le préfixe utilisé sur le serveur", - }, + "description": "Liste des paramètres disponibles", + "examples": [ + ("`{prefix}{prefix}list`", "Liste des paramètres modifiables") + ], + }, + "add_master_admin": { + "description": "Ajoute un administrateur du bot", + "examples": [ + ("`{prefix}{prefix}add_master_admin <@unemention>`", + "Ajoute <@unemention> aux administrateurs du bot"), + ("`{prefix}{prefix}add_master_admin <@unemention>, <@unemention2>`", + "Ajoute <@unemention> et <@unemention1> aux administrateurs du bot"), + ], + }, + "del_master_admin": { + "description": "Supprime un administrateur du bot", + "examples": [ + ("`{prefix}{prefix}del_master_admin <@unemention>`", + "Supprime <@unemention> des administrateurs du bot"), + ("`{prefix}{prefix}add_master_admin <@unemention>, <@unemention2>`", + "Supprime <@unemention> et <@unemention1> des administrateurs du bot"), + ], }, }, "lang": "La langue {lang} est maintenant utilisée.", @@ -77,15 +95,30 @@ tr = { }, "deeptown": { "description": "Commandes relatives au jeu deeptown.", - "aide":{ + "aide": { }, - "best_place_mine":"Voici les meilleurs emplacements pour le minerais {ore}\n```\n", - "to_make":"Pour faire {quantity} {item} il faudra {time}. Il vous faudra {needed}. La valeur totale de la production est {value}.", - "recursive_to_make":{ - "header":"Pour faire {quantity} {item} il vous faudra:\n```", - "line":"{item:20} | {quantity} | {time}" - } + "best_place_mine": "Voici les meilleurs emplacements pour le minerais {ore}\n```\n", + "to_make": "Pour faire {quantity} {item} il faudra {time}. Il vous faudra {needed}. La valeur totale " + "de la production est {value}.", + "recursive_to_make": { + "header": "Pour faire {quantity} {item} il vous faudra:\n```", + "line": "{item:20} | {quantity} | {time}" + }, + }, + "help": { + "description": "Active la commande d'aide", + "help": { + "help": { + "description": "Permat d'aficher de l'aide", + "examples": [ + ("`{prefix}help`", "Affiche l'aide générale"), + ("`{prefix}help config`", "Liste les commandes disponibles dans le module config"), + ("`{prefix}help config:lang`", + "Affiche l'aide avancé de la commande lang u module config"), + ], + }, + }, }, }, "errors": { @@ -95,7 +128,8 @@ tr = { "ModuleNotFoundOrDeactivatedError": { "title": "Erreur", "name": "Erreur lors de la désactivation du module {module}:", - "value": "Celui-ci n'existe pas ou n'es pas activé. Tapez {prefix}list_modules pour voir la liste des modules disponibles.", + "value": "Celui-ci n'existe pas ou n'es pas activé. Tapez {prefix}list_modules pour voir la liste des " + "modules disponibles.", }, "ModuleNotFoundError": { @@ -103,6 +137,8 @@ tr = { "name": "Erreur lors de l'activation du module {module}:", "value": "Celui-ci n'existe pas. Tapez {prefix}list_modules pour voir la liste des modules " "disponibles.", + "text": "Le module {module} n'existe pas, tapez {prefix}list_modules pour voir la liste des modules " + "disponibles.", }, "ForbiddenConfigError": "Ce paramètre ne peut pas être modifié directement.", "UnknownConfigError": "Le paramètre demandé n'existe pas. Utilisez {prefix}list pour lister les paramètres " @@ -111,8 +147,9 @@ tr = { "NoMentionsError": "Vous devez mentioner un utilisateur pour le rajouter à la liste des administrateurs " "du bot.", "OreNotFoundError": "{ore} n'est pas un minerais valide.", - "NotIntError":"{number} n'est pas un nombre entier valide.", - "ItemNotFoundError":"{item} n'extiste pas dans deeptown", + "NotIntError": "{number} n'est pas un nombre entier valide.", + "ItemNotFoundError": "{item} n'extiste pas dans deeptown", + "CommandNotFoundError": "La commande {command} n'existe pas." }, }, }