import logging, os import discord from discord.ext import commands import asyncpg, asyncio def sql_db_does_not_exist(): logging.error("Database does not exist. Doublecheck if it has been created, and the user has access.") quit() def sql_authentication_error(): logging.error("Database authentication failed. Doublecheck username & password, and if the user has been created.") quit() def hint_quit(): # Hint how to edit the settings and quit logging.info("") logging.info(" edit local_settings.py") logging.info("") quit() def missing_config(): # Copy or create settings file if missing logging.basicConfig(level=logging.DEBUG) if not os.path.exists("local_settings.py"): logging.error("Settings file not found.") logging.info("Copying local_settings_example.py to local_settings.py") try: os.system("cp local_settings_example.py local_settings.py") except FileNotFoundError: logging.info("local_settings_example.py not found, creating local_settings.py") with open("local_settings.py", "w") as settings_file: settings_file.writelines( [ "import logging", "LOG_LEVEL = logging.INFO # Options: CRITICAL, ERROR, WARNING, INFO, and DEBUG", "", "DATABASE_NAME = \"\"", "DATABASE_USER = \"\"", "DATABASE_HOST = \"\"", "DATABASE_PASSWORD = \"\"", "", "WEB_HOST = \"\"", "WEB_SCHEME = \"\"", "", "DISCORD_TOKEN = \"\"", "COMMAND_PREFIX = \"\"", ] ) logging.error("Settings undefined.") logging.info("Configure the settings:") hint_quit() def correct_setting(setting): # Hint to correct specific setting and quit logging.info("Correct the %s in local_settings.py", setting) hint_quit() # Attempt to import the local settings and quit gracefully on failure try: import local_settings as settings # Environment dependant settings stored in local_settings.py, untracked by .gitinore except ModuleNotFoundError: # Local settings module import failure missing_config() # Prepare for configuration and inform operator async def main(): # Set loglevel try: logging.basicConfig(level=settings.LOG_LEVEL) except AttributeError: missing_config() # Define robot intents = discord.Intents.default() intents.message_content = True intents.invites = True try: bot = commands.Bot( command_prefix="/", description="Charlie's Angels bot", intents=intents, case_insensitive=True, ) except AttributeError: missing_config() # Load extensions default_extensions = [ "commands.admin", "commands.games", "commands.general", "commands.angels", "events.general", "events.angels", ] for ext in default_extensions: logging.info(f"Loading extension: {ext}") await bot.load_extension(ext) async def create_db_pool(): # Connect to database try: bot.pg = await asyncpg.create_pool( database=settings.DATABASE_NAME, user=settings.DATABASE_USER, host=settings.DATABASE_HOST, password=settings.DATABASE_PASSWORD, ) except AttributeError: missing_config() except asyncpg.exceptions.InvalidPasswordError: sql_authentication_error() except asyncpg.exceptions.InvalidCatalogNameError: sql_db_does_not_exist() # Create database pool await create_db_pool() # Create database tables if they do not exist from query.initialise_database import init_db, check_db try: await check_db(bot.pg) except asyncpg.exceptions.UndefinedTableError: logging.info("User table does not exists, assuming empty database. Populating database...") await init_db(bot.pg) await bot.start(settings.DISCORD_TOKEN) # Run robot try: asyncio.run(main()) except AttributeError: missing_config() except discord.errors.LoginFailure: logging.error("Invalid discord token.") correct_setting("DISCORD_TOKEN") except KeyboardInterrupt: logging.info("Received keyboard interrupt, exiting...") quit()