Red-DiscordBot/main.py
Will 3d76f3a787 [Core V3] Make the bot data path configurable (#879)
* Initial commit

* Fix sentry

* Make cog manager install path work relative to the bot's dir

* Fix downloader to save data relative to the defined data folder

* Fix sentry test

* Fix downloader tests

* Change logfile location

* Add another line to codeowners

* Basic tests

* Fix versioning

* Add in FutureWarning for config file changes

* Add reference to issue
2017-08-20 15:49:51 -04:00

186 lines
5.9 KiB
Python

# Discord Version check
import discord
import sys
if discord.version_info.major < 1:
print("You are not running the rewritten version of discord.py.\n\n"
"In order to use Red v3 you MUST be running d.py version"
" >= 1.0.0.")
sys.exit(1)
from core.bot import Red, ExitCodes
from core.cog_manager import CogManagerUI
from core.data_manager import load_basic_configuration
from core.global_checks import init_global_checks
from core.events import init_events
from core.sentry_setup import init_sentry_logging
from core.cli import interactive_config, confirm, parse_cli_flags, ask_sentry
from core.core_commands import Core
from core.dev_commands import Dev
import asyncio
import logging.handlers
import logging
import os
from pathlib import Path
from warnings import warn
#
# Red - Discord Bot v3
#
# Made by Twentysix, improved by many
#
def init_loggers(cli_flags):
# d.py stuff
dpy_logger = logging.getLogger("discord")
dpy_logger.setLevel(logging.WARNING)
console = logging.StreamHandler()
console.setLevel(logging.WARNING)
dpy_logger.addHandler(console)
# Red stuff
logger = logging.getLogger("red")
red_format = logging.Formatter(
'%(asctime)s %(levelname)s %(module)s %(funcName)s %(lineno)d: '
'%(message)s',
datefmt="[%d/%m/%Y %H:%M]")
stdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.setFormatter(red_format)
if cli_flags.debug:
os.environ['PYTHONASYNCIODEBUG'] = '1'
logger.setLevel(logging.DEBUG)
else:
logger.setLevel(logging.WARNING)
from core.data_manager import core_data_path
logfile_path = core_data_path() / 'red.log'
fhandler = logging.handlers.RotatingFileHandler(
filename=str(logfile_path), encoding='utf-8', mode='a',
maxBytes=10**7, backupCount=5)
fhandler.setFormatter(red_format)
logger.addHandler(fhandler)
logger.addHandler(stdout_handler)
# Sentry stuff
sentry_logger = logging.getLogger("red.sentry")
sentry_logger.setLevel(logging.WARNING)
return logger, sentry_logger
def determine_main_folder() -> Path:
return Path(os.path.dirname(__file__)).resolve()
async def _get_prefix_and_token(red, indict):
"""
Again, please blame <@269933075037814786> for this.
:param indict:
:return:
"""
indict['token'] = await red.db.token()
indict['prefix'] = await red.db.prefix()
indict['enable_sentry'] = await red.db.enable_sentry()
if __name__ == '__main__':
cli_flags = parse_cli_flags()
if cli_flags.config:
load_basic_configuration(Path(cli_flags.config).resolve())
else:
warn("Soon you will need to change the way you load the bot."
" The new method of loading has yet to be decided upon but"
" will be made clear in announcements from the support server"
" and from documentation. Please see issue #938 for further"
" discussion on this topic.",
category=FutureWarning)
import core.data_manager
defaults = core.data_manager.basic_config_default.copy()
defaults['DATA_PATH'] = str(determine_main_folder())
defaults['CORE_PATH_APPEND'] = 'core/.data'
defaults['COG_PATH_APPEND'] = 'cogs/.data'
core.data_manager.basic_config = defaults
log, sentry_log = init_loggers(cli_flags)
description = "Red v3 - Alpha"
bot_dir = determine_main_folder()
red = Red(cli_flags, description=description, pm_help=None,
bot_dir=bot_dir)
init_global_checks(red)
init_events(red, cli_flags)
red.add_cog(Core())
red.add_cog(CogManagerUI())
if cli_flags.dev:
red.add_cog(Dev())
loop = asyncio.get_event_loop()
tmp_data = {}
loop.run_until_complete(_get_prefix_and_token(red, tmp_data))
token = os.environ.get("RED_TOKEN", tmp_data['token'])
prefix = cli_flags.prefix or tmp_data['prefix']
enable_sentry = tmp_data['enable_sentry']
if token is None or not prefix:
if cli_flags.no_prompt is False:
new_token = interactive_config(red, token_set=bool(token),
prefix_set=bool(prefix))
if new_token:
token = new_token
else:
log.critical("Token and prefix must be set in order to login.")
sys.exit(1)
if enable_sentry is None:
ask_sentry(red)
loop.run_until_complete(_get_prefix_and_token(red, tmp_data))
if tmp_data['enable_sentry']:
init_sentry_logging(red, sentry_log)
cleanup_tasks = True
try:
loop.run_until_complete(red.start(token, bot=not cli_flags.not_bot))
except discord.LoginFailure:
cleanup_tasks = False # No login happened, no need for this
log.critical("This token doesn't seem to be valid. If it belongs to "
"a user account, remember that the --not-bot flag "
"must be used. For self-bot functionalities instead, "
"--self-bot")
db_token = red.db.token()
if db_token and not cli_flags.no_prompt:
print("\nDo you want to reset the token? (y/n)")
if confirm("> "):
loop.run_until_complete(red.db.token.set(""))
print("Token has been reset.")
except KeyboardInterrupt:
log.info("Keyboard interrupt detected. Quitting...")
loop.run_until_complete(red.logout())
red._shutdown_mode = ExitCodes.SHUTDOWN
except Exception as e:
log.critical("Fatal exception", exc_info=e)
sentry_log.critical("Fatal Exception", exc_info=e)
loop.run_until_complete(red.logout())
finally:
if cleanup_tasks:
pending = asyncio.Task.all_tasks(loop=red.loop)
gathered = asyncio.gather(*pending, loop=red.loop)
gathered.cancel()
red.loop.run_until_complete(gathered)
gathered.exception()
sys.exit(red._shutdown_mode.value)