mirror of https://github.com/stijndcl/didier
				
				
				
			Merge pull request #72 from stijndcl/flask-backend
Create basics for backend server, + small fixespull/74/head
						commit
						a310d1696c
					
				|  | @ -1,14 +1,11 @@ | |||
| files/status.txt | ||||
| files/readyMessage.txt | ||||
| files/client.txt | ||||
| files/lastTasks.json | ||||
| files/c4.json | ||||
| files/hangman.json | ||||
| files/stats.json | ||||
| files/lost.json | ||||
| files/locked.json | ||||
| files/database.json | ||||
| files/ufora_notifications.json | ||||
| .idea/ | ||||
| __pycache__ | ||||
| .env | ||||
| .env | ||||
| /venv/ | ||||
|  |  | |||
|  | @ -0,0 +1,82 @@ | |||
| from discord.ext import ipc | ||||
| from functions.database import custom_commands | ||||
| import json | ||||
| from quart import Quart, jsonify, request | ||||
| from quart_cors import cors | ||||
| from time import time | ||||
| 
 | ||||
| 
 | ||||
| app = Quart(__name__) | ||||
| # TODO allow_origin=re.compile(r"http://localhost:.*") | ||||
| #      needs higher Python & Quart version | ||||
| app = cors(app, allow_origin="*") | ||||
| app.config.from_object(__name__) | ||||
| 
 | ||||
| 
 | ||||
| ipc_client = ipc.Client(secret_key="SOME_SECRET_KEY") | ||||
| 
 | ||||
| 
 | ||||
| @app.route("/ping", methods=["GET"]) | ||||
| async def ping(): | ||||
|     """ | ||||
|     Send a ping request, monitors bot latency, endpoint time, and PSQL latency | ||||
|     """ | ||||
|     latency = await ipc_client.request("get_bot_latency") | ||||
| 
 | ||||
|     return jsonify({"bot_latency": latency, "response_sent": time()}) | ||||
| 
 | ||||
| 
 | ||||
| @app.route("/dm", methods=["POST"]) | ||||
| async def send_dm(): | ||||
|     """ | ||||
|     Send a DM to the given user | ||||
|     """ | ||||
|     data = json.loads((await request.body).decode('UTF-8')) | ||||
| 
 | ||||
|     dm = await ipc_client.request( | ||||
|         "send_dm", | ||||
|         user=int(data["userid"]), | ||||
|         message=data.get("message") | ||||
|     ) | ||||
| 
 | ||||
|     return jsonify({"response": dm}) | ||||
| 
 | ||||
| 
 | ||||
| @app.route("/custom", methods=["GET"]) | ||||
| async def get_all_custom_commands(): | ||||
|     """ | ||||
|     Return a list of all custom commands in the bot | ||||
|     """ | ||||
|     commands = custom_commands.get_all() | ||||
| 
 | ||||
|     return jsonify(commands) | ||||
| 
 | ||||
| 
 | ||||
| @app.route("/custom/<command_id>") | ||||
| async def get_custom_command(command_id): | ||||
|     try: | ||||
|         command_id = int(command_id) | ||||
|     except ValueError: | ||||
|         # Id is not an int | ||||
|         return unprocessable_entity("Parameter id was not a valid integer.") | ||||
| 
 | ||||
|     command = custom_commands.get_by_id(command_id) | ||||
| 
 | ||||
|     if command is None: | ||||
|         return page_not_found("") | ||||
| 
 | ||||
|     return jsonify(command) | ||||
| 
 | ||||
| 
 | ||||
| @app.errorhandler(404) | ||||
| def page_not_found(e): | ||||
|     return jsonify({"error": "No resource could be found matching the given URL."}), 404 | ||||
| 
 | ||||
| 
 | ||||
| @app.errorhandler(422) | ||||
| def unprocessable_entity(e): | ||||
|     return jsonify({"error": e}), 422 | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     app.run() | ||||
|  | @ -5,6 +5,7 @@ from discord.ext import commands | |||
| from functions import checks, easterEggResponses | ||||
| from functions.database import stats, muttn, custom_commands, commands as command_stats | ||||
| import pytz | ||||
| from settings import READY_MESSAGE, SANDBOX, STATUS_MESSAGE | ||||
| import time | ||||
| import traceback | ||||
| 
 | ||||
|  | @ -30,20 +31,13 @@ class Events(commands.Cog): | |||
|         """ | ||||
|         Function called when the bot is ready & done leading. | ||||
|         """ | ||||
|         # Change status | ||||
|         with open("files/status.txt", "r") as statusFile: | ||||
|             status = statusFile.readline() | ||||
|         # Set status | ||||
|         await self.client.change_presence(status=discord.Status.online, activity=discord.Game(STATUS_MESSAGE)) | ||||
| 
 | ||||
|         await self.client.change_presence(status=discord.Status.online, activity=discord.Game(str(status))) | ||||
| 
 | ||||
|         # Print a message in the terminal to show that he's ready | ||||
|         with open("files/readyMessage.txt", "r") as readyFile: | ||||
|             readyMessage = readyFile.readline() | ||||
| 
 | ||||
|         print(readyMessage) | ||||
|         print(READY_MESSAGE) | ||||
| 
 | ||||
|         # Add constants to the client as a botvar | ||||
|         self.client.constants = constants.Live if "zandbak" not in readyMessage else constants.Zandbak | ||||
|         self.client.constants = constants.Live if SANDBOX else constants.Zandbak | ||||
| 
 | ||||
|     @commands.Cog.listener() | ||||
|     async def on_message(self, message): | ||||
|  |  | |||
|  | @ -0,0 +1,26 @@ | |||
| from discord.ext import commands, ipc | ||||
| 
 | ||||
| 
 | ||||
| class IPC(commands.Cog): | ||||
|     def __init__(self, client): | ||||
|         self.client = client | ||||
| 
 | ||||
|     @ipc.server.route() | ||||
|     async def send_dm(self, data): | ||||
|         print("got here") | ||||
|         user = self.client.get_user(data.user) | ||||
|         await user.send(data.message) | ||||
|         print("sent") | ||||
|         return True | ||||
| 
 | ||||
|     @ipc.server.route() | ||||
|     async def get_bot_latency(self, data): | ||||
|         """ | ||||
|         Get Didier's latency | ||||
|         """ | ||||
| 
 | ||||
|         return self.client.latency * 1000 | ||||
| 
 | ||||
| 
 | ||||
| def setup(client): | ||||
|     client.add_cog(IPC(client)) | ||||
|  | @ -246,7 +246,7 @@ class Tasks(commands.Cog): | |||
|         Task that checks for new Ufora announcements every few minutes | ||||
|         """ | ||||
|         # Don't run this when testing | ||||
|         if self.client.user.id == int(constants.coolerDidierId): | ||||
|         if self.client.user.id != int(constants.didierId): | ||||
|             return | ||||
| 
 | ||||
|         # Get new notifications | ||||
|  |  | |||
|  | @ -15,8 +15,8 @@ class Translate(commands.Cog): | |||
|     def cog_check(self, ctx): | ||||
|         return not self.client.locked | ||||
| 
 | ||||
|     # @commands.command(name="Translate", aliases=["Tl", "Trans"], usage="[Tekst] [Van]* [Naar]*") | ||||
|     # @help.Category(Category.Words) | ||||
|     @commands.command(name="Translate", aliases=["Tl", "Trans"], usage="[Tekst] [Van]* [Naar]*") | ||||
|     @help.Category(Category.Words) | ||||
|     async def translate(self, ctx, query=None, to="nl", fr="auto"): | ||||
|         if query is None: | ||||
|             return await ctx.send("Controleer je argumenten.") | ||||
|  | @ -39,9 +39,11 @@ class Translate(commands.Cog): | |||
|             embed.set_author(name="Didier Translate") | ||||
| 
 | ||||
|             if fr == "auto": | ||||
|                 language = translation.extra_data["original-language"] | ||||
|                 language = translation.src | ||||
|                 embed.add_field(name="Gedetecteerde taal", value=tc(LANGUAGES[language])) | ||||
|                 embed.add_field(name="Zekerheid", value="{}%".format(translation.extra_data["confidence"] * 100)) | ||||
| 
 | ||||
|                 if translation.extra_data["confidence"] is not None: | ||||
|                     embed.add_field(name="Zekerheid", value="{}%".format(translation.extra_data["confidence"] * 100)) | ||||
| 
 | ||||
|             embed.add_field(name="Origineel ({})".format(translation.src.upper()), value=query, inline=False) | ||||
|             embed.add_field(name="Vertaling ({})".format(to.upper()), value=translation.text) | ||||
|  |  | |||
							
								
								
									
										36
									
								
								didier.py
								
								
								
								
							
							
						
						
									
										36
									
								
								didier.py
								
								
								
								
							|  | @ -1,33 +1,19 @@ | |||
| import discord | ||||
| from discord.ext import commands | ||||
| from dotenv import load_dotenv | ||||
| from functions.prefixes import get_prefix | ||||
| import os | ||||
| from settings import TOKEN | ||||
| from startup.didier import Didier | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     load_dotenv(verbose=True) | ||||
| 
 | ||||
| load_dotenv(verbose=True) | ||||
|     # Configure intents (1.5.0) | ||||
|     intents = discord.Intents.default() | ||||
|     intents.members = True | ||||
| 
 | ||||
|     client = Didier(command_prefix=get_prefix, case_insensitive=True, intents=intents) | ||||
| 
 | ||||
| # Configure intents (1.5.0) | ||||
| intents = discord.Intents.default() | ||||
| intents.members = True | ||||
|     if client.ipc is not None: | ||||
|         client.ipc.start() | ||||
| 
 | ||||
| client = commands.Bot(command_prefix=get_prefix, case_insensitive=True, intents=intents) | ||||
| 
 | ||||
| # Remove default help because it sucks & I made my own | ||||
| client.remove_command("help") | ||||
| 
 | ||||
| # Load utils first so it can be used in other places & it's not None | ||||
| client.load_extension("cogs.utils") | ||||
| client.load_extension("cogs.failedchecks") | ||||
| client.load_extension("cogs.events") | ||||
| 
 | ||||
| # Load all remaining cogs | ||||
| for file in os.listdir("./cogs"): | ||||
|     if file.endswith(".py") and not (file.startswith(("utils", "failedchecks", "events"),)): | ||||
|         client.load_extension("cogs.{}".format(file[:-3])) | ||||
| 
 | ||||
| # Get the token out of the file & run the bot | ||||
| with open("files/client.txt", "r") as fp: | ||||
|     token = fp.readline() | ||||
| client.run(token) | ||||
|     client.run(TOKEN) | ||||
|  |  | |||
							
								
								
									
										15
									
								
								faq.md
								
								
								
								
							
							
						
						
									
										15
									
								
								faq.md
								
								
								
								
							|  | @ -1,5 +1,14 @@ | |||
| # FAQ | ||||
| Answers to Frequently Asked Questions. | ||||
| Answers to Frequently Asked Questions and solutions to issues that may arise. | ||||
| 
 | ||||
| ### Table of Contents | ||||
| A list of all questions (in order) so you can easily find what you're looking for. | ||||
| ## Issues installing dotenv | ||||
| 
 | ||||
| The name of this package is `python-dotenv`, as listed in `requirements.txt`. The _name_ of the package, however, is just `dotenv`. This confuses PyCharm, which will tell you that you don't have `dotenv` installed as it can't link those two together. You can just click `ignore this requirement` if you don't like the warning. | ||||
| 
 | ||||
| ## Issues installing psycopg2 | ||||
| 
 | ||||
| The `psychopg2` and `psychopg2-binary` packages might cause you some headaches when trying to install them, especially when using PyCharm to install dependencies. The reason for this is because these are `PSQL` packages, and as a result they require you to have `PSQL` installed on your system to function properly. | ||||
| 
 | ||||
| ## Package is not listed in project requirements | ||||
| 
 | ||||
| This is the exact same case as [Issues installing dotenv](#issues-installing-dotenv), and occurs for packages such as `Quart`. The names of the modules differ from the name used to install it from `pip`. Once again, you can ignore this message. | ||||
|  | @ -0,0 +1,5 @@ | |||
| { | ||||
|   "guessed": [], | ||||
|   "guesses": 0, | ||||
|   "word": "" | ||||
| } | ||||
|  | @ -0,0 +1,9 @@ | |||
| { | ||||
|   "interest": 0, | ||||
|   "lost": 0, | ||||
|   "poke": 0, | ||||
|   "prison": 0, | ||||
|   "birthdays": 0, | ||||
|   "channels": 0, | ||||
|   "remind": 0 | ||||
| } | ||||
|  | @ -0,0 +1,4 @@ | |||
| { | ||||
|   "locked": false, | ||||
|   "until": -1 | ||||
| } | ||||
|  | @ -0,0 +1,4 @@ | |||
| { | ||||
|   "lost": 0, | ||||
|   "today": 0 | ||||
| } | ||||
|  | @ -0,0 +1,19 @@ | |||
| { | ||||
|   "cf": { | ||||
|     "h": 0, | ||||
|     "t": 0 | ||||
|   }, | ||||
|   "dice": { | ||||
|     "2": 0, | ||||
|     "5": 0, | ||||
|     "3": 0, | ||||
|     "6": 0, | ||||
|     "1": 0, | ||||
|     "4": 0 | ||||
|   }, | ||||
|   "rob": { | ||||
|     "robs_success": 0, | ||||
|     "robs_failed": 0, | ||||
|     "bail_paid": 0.0 | ||||
|   } | ||||
| } | ||||
|  | @ -0,0 +1,12 @@ | |||
| { | ||||
|   "Algoritmen en Datastructuren 2": [], | ||||
|   "Communicatienetwerken": [], | ||||
|   "Computerarchitectuur": [], | ||||
|   "Functioneel Programmeren": [], | ||||
|   "Multimedia": [], | ||||
|   "Software Engineering Lab 1": [], | ||||
|   "Statistiek en Probabiliteit": [], | ||||
|   "Systeemprogrammeren": [], | ||||
|   "Webdevelopment": [], | ||||
|   "Wetenschappelijk Rekenen": [] | ||||
| } | ||||
|  | @ -127,3 +127,55 @@ def add_alias(command: str, alias: str): | |||
| 
 | ||||
|     cursor.execute("INSERT INTO custom_command_aliases(command, alias) VALUES(%s, %s)", (command_id, alias,)) | ||||
|     connection.commit() | ||||
| 
 | ||||
| 
 | ||||
| def get_all(): | ||||
|     """ | ||||
|     Return a list of all registered custom commands | ||||
|     """ | ||||
|     connection = utils.connect() | ||||
|     cursor = connection.cursor() | ||||
| 
 | ||||
|     cursor.execute("SELECT * FROM custom_commands") | ||||
|     commands = cursor.fetchall() | ||||
|     ret = [] | ||||
| 
 | ||||
|     # Create a list of all entries | ||||
|     for command in commands: | ||||
|         dic = {"id": command[0], "name": command[1], "response": command[2]} | ||||
| 
 | ||||
|         # Find and add aliases | ||||
|         cursor.execute("SELECT id, alias FROM custom_command_aliases WHERE command = %s", (command[0],)) | ||||
|         aliases = cursor.fetchall() | ||||
| 
 | ||||
|         if aliases: | ||||
|             dic["aliases"] = list(map(lambda x: {"id": x[0], "alias": x[1]}, aliases)) | ||||
| 
 | ||||
|         ret.append(dic) | ||||
| 
 | ||||
|     return ret | ||||
| 
 | ||||
| 
 | ||||
| def get_by_id(command_id: int): | ||||
|     """ | ||||
|     Return a command that matches a given id | ||||
|     """ | ||||
|     connection = utils.connect() | ||||
|     cursor = connection.cursor() | ||||
| 
 | ||||
|     cursor.execute("SELECT * FROM custom_commands WHERE id = %s", (command_id,)) | ||||
|     command = cursor.fetchone() | ||||
| 
 | ||||
|     # Nothing found | ||||
|     if not command: | ||||
|         return None | ||||
| 
 | ||||
|     dic = {"id": command[0], "name": command[1], "response": command[2]} | ||||
| 
 | ||||
|     cursor.execute("SELECT id, alias FROM custom_command_aliases WHERE command = %s", (command_id,)) | ||||
|     aliases = cursor.fetchall() | ||||
| 
 | ||||
|     if aliases: | ||||
|         dic["aliases"] = list(map(lambda x: {"id": x[0], "alias": x[1]}, aliases)) | ||||
| 
 | ||||
|     return dic | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| import psycopg2 | ||||
| import json | ||||
| import os | ||||
| from settings import DB_HOST, DB_NAME, DB_USERNAME, DB_PASSWORD | ||||
| 
 | ||||
| 
 | ||||
| connection = None | ||||
|  | @ -17,15 +16,11 @@ def connect(): | |||
| def create_connection(): | ||||
|     global connection | ||||
| 
 | ||||
|     dir_path = os.path.dirname(os.path.realpath(__file__)) | ||||
|     with open(os.path.join(dir_path, "../../files/database.json"), "r") as fp: | ||||
|         db = json.load(fp) | ||||
| 
 | ||||
|     connection = psycopg2.connect( | ||||
|         host=db["host"], | ||||
|         database=db["database"], | ||||
|         user=db["username"], | ||||
|         password=db["password"] | ||||
|         host=DB_HOST, | ||||
|         database=DB_NAME, | ||||
|         user=DB_USERNAME, | ||||
|         password=DB_PASSWORD | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										37
									
								
								ignored.md
								
								
								
								
							
							
						
						
									
										37
									
								
								ignored.md
								
								
								
								
							|  | @ -2,28 +2,7 @@ | |||
| 
 | ||||
| A list of all ignored files with copy-pastable templates. Useful for when you want to work on commands that use these, for obvious reasons. Every file has a copy-pastable template to make it easy for you to use. | ||||
| 
 | ||||
| These are usually files which would be overkill to make a PSQL table for. Other possibilities are files that are never edited, but should be different on every machine (Discord token, status message, ...). | ||||
| 
 | ||||
| ### files/client.txt | ||||
| 
 | ||||
| Contains the application's token to connect to Discord. You can create your own bot & put it's token in this file to run & test Didier code. | ||||
| 
 | ||||
|     token_goes_here | ||||
| 
 | ||||
| ### files/database.json | ||||
| 
 | ||||
| Contains the credentials needed to connect to the PSQL database. This is ignored so that I don't have to leak my IP address, but also so that you can set up a local database to mess around without affecting the Live one or having to change any code. | ||||
| 
 | ||||
| ```json | ||||
| { | ||||
|   "username": "username", | ||||
|   "password": "password", | ||||
|   "host": "host_address", | ||||
|   "database": "database_name" | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| When connecting to a local PSQL database, `host` should be `"localhost"`. | ||||
| These are usually files which would be overkill to make a PSQL table for. Other possibilities are files that are never edited, but should be different on every machine. | ||||
| 
 | ||||
| ### files/hangman.json | ||||
| 
 | ||||
|  | @ -64,14 +43,6 @@ Contains a boolean indicating whether or not the server is currently locked, and | |||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### files/readyMessage.txt | ||||
| 
 | ||||
| Contains the message printed in your terminal when Didier is ready. | ||||
| 
 | ||||
|     I'M READY I'M READY I'M READY I'M READY | ||||
|      | ||||
| In case you were wondering: yes, this is a Spongebob reference. | ||||
| 
 | ||||
| ### files/stats.json | ||||
| 
 | ||||
| Contains the stats to track for gambling games. Weren't made as a PSQL table because they would be too long (and every game is different). | ||||
|  | @ -98,12 +69,6 @@ Contains the stats to track for gambling games. Weren't made as a PSQL table bec | |||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### files/status.txt | ||||
| 
 | ||||
| Contains Didier's status message for when he logs in. Keep in mind that his activity is set to `Playing `. This was first used in Didier V1 to show whether or not he was in sandbox mode. | ||||
| 
 | ||||
|     with your Didier Dinks. | ||||
|      | ||||
| ### files/ufora_notifications.json | ||||
| 
 | ||||
| Stores ID's of all received Ufora notifications. | ||||
|  |  | |||
|  | @ -51,13 +51,16 @@ When creating a new Didier command, you can add it to a `Category` by adding a d | |||
| 
 | ||||
| ```python | ||||
| from decorators import help | ||||
| from discord.ext import commands | ||||
| from enums.help_categories import Category | ||||
| from functions import checks | ||||
| 
 | ||||
| @commands.command(name="Command Name", aliases=["Cn"]) | ||||
| @commands.check(checks.allowedChannels) | ||||
| @help.Category(Category.Currency) | ||||
| async def command_name(self, ctx): | ||||
|     # Command code | ||||
|     await ctx.send("Command response") | ||||
| ``` | ||||
| 
 | ||||
| This allows commands across multiple Cogs to be classified under the same category in the help page. | ||||
|  |  | |||
|  | @ -1,6 +1,8 @@ | |||
| python-dotenv==0.14.0 | ||||
| beautifulsoup4==4.9.1 | ||||
| discord.py==1.7.0 | ||||
| discord.py==1.7.3 | ||||
| git+https://github.com/Rapptz/discord-ext-menus@master | ||||
| discord-ext-ipc==2.0.0 | ||||
| psycopg2==2.8.5 | ||||
| psycopg2-binary==2.8.5 | ||||
| python-dateutil==2.6.1 | ||||
|  | @ -10,4 +12,6 @@ requests-unixsocket==0.1.5 | |||
| tabulate==0.8.7 | ||||
| yarl==1.4.2 | ||||
| feedparser==6.0.2 | ||||
| googletrans==3.0.0 | ||||
| googletrans==4.0.0rc1 | ||||
| quart==0.6.15 | ||||
| Quart-CORS==0.1.3 | ||||
|  | @ -0,0 +1,33 @@ | |||
| from dotenv import load_dotenv | ||||
| import os | ||||
| 
 | ||||
| 
 | ||||
| load_dotenv() | ||||
| 
 | ||||
| 
 | ||||
| def _to_bool(value: str) -> bool: | ||||
|     """ | ||||
|     Env variables are strings so this converts them to booleans | ||||
|     """ | ||||
|     return value.lower() in ["true", "1", "y", "yes"] | ||||
| 
 | ||||
| 
 | ||||
| # Sandbox or live | ||||
| SANDBOX = _to_bool(os.getenv("SANDBOX", "true")) | ||||
| 
 | ||||
| # Tokens & API keys | ||||
| URBANDICTIONARY = os.getenv("URBANDICTIONARY", "") | ||||
| IMGFLIP_NAME = os.getenv("IMGFLIPNAME", "") | ||||
| IMGFLIP_PASSWORD = os.getenv("IMGFLIPPASSWORD", "") | ||||
| 
 | ||||
| # Database credentials | ||||
| DB_USERNAME = os.getenv("DBUSERNAME", "") | ||||
| DB_PASSWORD = os.getenv("DBPASSWORD", "") | ||||
| DB_HOST = os.getenv("DBHOST", "") | ||||
| DB_NAME = os.getenv("DBNAME", "") | ||||
| 
 | ||||
| # Discord-related | ||||
| TOKEN = os.getenv("TOKEN", "") | ||||
| HOST_IPC = _to_bool(os.getenv("HOSTIPC", "false")) | ||||
| READY_MESSAGE = os.getenv("READYMESSAGE", "I'M READY I'M READY I'M READY I'M READY")  # Yes, this is a Spongebob reference | ||||
| STATUS_MESSAGE = os.getenv("STATUSMESSAGE", "with your Didier Dinks.") | ||||
|  | @ -0,0 +1,46 @@ | |||
| from discord.ext import commands, ipc | ||||
| from settings import HOST_IPC | ||||
| from startup.init_files import check_all | ||||
| import os | ||||
| 
 | ||||
| 
 | ||||
| class Didier(commands.Bot): | ||||
|     """ | ||||
|     Main Bot class for Didier | ||||
|     """ | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         super().__init__(*args, **kwargs) | ||||
| 
 | ||||
|         self._host_ipc = HOST_IPC | ||||
| 
 | ||||
|         # IPC Server | ||||
|         # TODO secret key | ||||
|         self.ipc = ipc.Server(self, secret_key="SOME_SECRET_KEY") if self._host_ipc else None | ||||
| 
 | ||||
|         # Cogs that should be loaded before the others | ||||
|         self._preload = ("ipc", "utils", "failedchecks", "events",) | ||||
| 
 | ||||
|         # Remove default help command | ||||
|         self.remove_command("help") | ||||
| 
 | ||||
|         # Load all extensions | ||||
|         self.init_extensions() | ||||
| 
 | ||||
|         # Check missing files | ||||
|         check_all() | ||||
| 
 | ||||
|     def init_extensions(self): | ||||
|         # Load initial extensions | ||||
|         for ext in self._preload: | ||||
|             self.load_extension(f"cogs.{ext}") | ||||
| 
 | ||||
|         # Load all remaining cogs | ||||
|         for file in os.listdir("./cogs"): | ||||
|             if file.endswith(".py") and not (file.startswith(self._preload)): | ||||
|                 self.load_extension("cogs.{}".format(file[:-3])) | ||||
| 
 | ||||
|     async def on_ipc_ready(self): | ||||
|         print("IPC server is ready.") | ||||
| 
 | ||||
|     async def on_ipc_error(self, endpoint, error): | ||||
|         print(endpoint, "raised", error) | ||||
|  | @ -0,0 +1,13 @@ | |||
| import json | ||||
| from os import path | ||||
| 
 | ||||
| 
 | ||||
| def check_all(): | ||||
|     files = ["hangman", "lastTasks", "locked", "lost", "stats", "ufora_notifications"] | ||||
| 
 | ||||
|     for f in files: | ||||
|         if not path.isfile(path.join(f"files/{f}.json")): | ||||
|             with open(f"files/{f}.json", "w+") as new_file, open(f"files/default/{f}.json", "r") as default: | ||||
|                 content = json.load(default) | ||||
|                 json.dump(content, new_file) | ||||
|                 print(f"Created missing file: files/{f}.json") | ||||
		Loading…
	
		Reference in New Issue