tuxbot-bot/tuxbot/__main__.py

255 lines
6.6 KiB
Python
Raw Normal View History

2020-06-03 17:41:30 +00:00
import argparse
import asyncio
import getpass
import json
2020-06-03 17:41:30 +00:00
import logging
import platform
2020-06-03 17:41:30 +00:00
import signal
import sys
2020-06-03 22:46:53 +00:00
from argparse import Namespace
2020-06-04 17:16:51 +00:00
from typing import NoReturn
2020-06-03 17:41:30 +00:00
import discord
import pip
2020-06-03 17:41:30 +00:00
from colorama import Fore, init, Style
from pip._vendor import distro
2020-06-03 17:41:30 +00:00
import tuxbot.logging
from tuxbot.core import data_manager
2020-06-04 22:29:14 +00:00
from tuxbot.core.bot import Tux, ExitCodes
from tuxbot.core.utils.functions.cli import bordered
from . import __version__
2020-06-03 17:41:30 +00:00
log = logging.getLogger("tuxbot.main")
init()
def list_instances() -> NoReturn:
2020-06-03 22:46:53 +00:00
"""List all available instances
"""
with data_manager.config_file.open() as fs:
datas = json.load(fs)
2020-06-06 16:51:47 +00:00
info = {"title": "Instances", "rows": []}
for instance, details in datas.items():
info["rows"].append(
f"-> {instance} " f"{'up' if details.get('IS_RUNNING') else 'down'}"
)
print(bordered(info))
sys.exit(0)
def debug_info() -> NoReturn:
2020-06-03 22:46:53 +00:00
"""Show debug infos relatives to the bot
"""
2020-06-06 16:51:47 +00:00
python_version = sys.version.replace("\n", "")
pip_version = pip.__version__
tuxbot_version = __version__
dpy_version = discord.__version__
os_info = distro.linux_distribution()
os_info = f"{os_info[0]} {os_info[1]}"
runner = getpass.getuser()
info = {
2020-06-06 16:51:47 +00:00
"title": "Debug Info",
"rows": [
f"Tuxbot version: {tuxbot_version}",
"",
f"Python version: {python_version}",
f"Python executable path: {sys.executable}",
f"Pip version: {pip_version}",
f"Discord.py version: {dpy_version}",
"",
f"OS info: {os_info}",
f"System arch: {platform.machine()}",
f"User: {runner}",
2020-06-06 16:51:47 +00:00
],
}
print(bordered(info))
sys.exit(0)
2020-06-03 22:46:53 +00:00
def parse_cli_flags(args: list) -> Namespace:
"""Parser for cli values.
Parameters
----------
args:list
Is a list of all passed values.
Returns
-------
Namespace
"""
2020-06-03 17:41:30 +00:00
parser = argparse.ArgumentParser(
description="Tuxbot - OpenSource bot",
2020-06-06 16:51:47 +00:00
usage="tuxbot <instance_name> [arguments]",
)
parser.add_argument(
2020-06-06 16:51:47 +00:00
"--version", "-V", action="store_true", help="Show tuxbot's used version"
)
2020-06-06 16:51:47 +00:00
parser.add_argument("--debug", action="store_true", help="Show debug information.")
parser.add_argument(
2020-06-06 16:51:47 +00:00
"--list-instances", "-L", action="store_true", help="List all instance names"
)
2020-06-06 16:51:47 +00:00
parser.add_argument("--token", "-T", type=str, help="Run Tuxbot with passed token")
2020-06-04 17:16:51 +00:00
parser.add_argument(
2020-06-06 16:51:47 +00:00
"instance_name",
nargs="?",
help="Name of the bot instance created during `tuxbot-setup`.",
)
2020-06-03 17:41:30 +00:00
args = parser.parse_args(args)
return args
2020-06-03 22:46:53 +00:00
async def shutdown_handler(tux: Tux, signal_type, exit_code=None) -> NoReturn:
"""Handler when the bot shutdown
It cancels all running task.
Parameters
----------
tux:Tux
Object for the bot.
2020-06-04 17:16:51 +00:00
signal_type:int, None
2020-06-03 22:46:53 +00:00
Exiting signal code.
exit_code:None|int
Code to show when exiting.
"""
2020-06-03 17:41:30 +00:00
if signal_type:
log.info("%s received. Quitting...", signal_type)
2020-06-04 22:29:14 +00:00
sys.exit(ExitCodes.SHUTDOWN)
2020-06-03 17:41:30 +00:00
elif exit_code is None:
log.info("Shutting down from unhandled exception")
2020-06-04 22:29:14 +00:00
tux.shutdown_code = ExitCodes.CRITICAL
2020-06-03 17:41:30 +00:00
if exit_code is not None:
tux.shutdown_code = exit_code
2020-06-03 17:41:30 +00:00
try:
await tux.logout()
finally:
2020-06-06 16:51:47 +00:00
pending = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
2020-06-03 17:41:30 +00:00
for task in pending:
task.cancel()
await asyncio.gather(*pending, return_exceptions=True)
async def run_bot(tux: Tux, cli_flags: Namespace) -> None:
2020-06-03 22:46:53 +00:00
"""This run the bot.
Parameters
----------
tux:Tux
Object for the bot.
cli_flags:Namespace
All different flags passed in the console.
Returns
-------
None
When exiting, this function return None.
"""
data_path = data_manager.data_path(tux.instance_name)
2020-06-03 17:41:30 +00:00
2020-06-06 16:51:47 +00:00
tuxbot.logging.init_logging(10, location=data_path / "logs")
2020-06-03 17:41:30 +00:00
log.debug("====Basic Config====")
log.debug("Data Path: %s", data_path)
if cli_flags.token:
token = cli_flags.token
else:
2020-06-06 16:51:47 +00:00
token = tux.config("core").get("token")
2020-06-03 17:41:30 +00:00
if not token:
log.critical("Token must be set if you want to login.")
2020-06-04 22:29:14 +00:00
sys.exit(ExitCodes.CRITICAL)
2020-06-03 17:41:30 +00:00
try:
2020-06-04 22:29:14 +00:00
await tux.load_packages()
2020-06-04 17:16:51 +00:00
await tux.start(token, bot=True)
2020-06-03 17:41:30 +00:00
except discord.LoginFailure:
log.critical("This token appears to be valid.")
2020-06-04 22:29:14 +00:00
sys.exit(ExitCodes.CRITICAL)
2020-06-03 17:41:30 +00:00
return None
2020-06-03 22:46:53 +00:00
def main() -> NoReturn:
"""Main function
"""
2020-06-03 17:41:30 +00:00
tux = None
cli_flags = parse_cli_flags(sys.argv[1:])
if cli_flags.list_instances:
list_instances()
elif cli_flags.debug:
debug_info()
elif cli_flags.version:
print("Tuxbot V3")
print(f"Complete Version: {__version__}")
sys.exit(0)
2020-06-03 17:41:30 +00:00
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
if not cli_flags.instance_name:
2020-06-06 16:51:47 +00:00
print(
Fore.RED + "No instance provided ! "
"You can use 'tuxbot -L' to list all available instances"
+ Style.RESET_ALL
)
2020-06-04 22:29:14 +00:00
sys.exit(ExitCodes.CRITICAL)
2020-06-03 17:41:30 +00:00
tux = Tux(
2020-06-03 17:41:30 +00:00
cli_flags=cli_flags,
description="Tuxbot, made from and for OpenSource",
2020-06-06 16:51:47 +00:00
dm_help=None,
2020-06-03 17:41:30 +00:00
)
loop.run_until_complete(run_bot(tux, cli_flags))
2020-06-03 17:41:30 +00:00
except KeyboardInterrupt:
2020-06-06 16:51:47 +00:00
print(
Fore.RED
+ "Please use <prefix>quit instead of Ctrl+C to Shutdown!"
+ Style.RESET_ALL
)
2020-06-03 17:41:30 +00:00
log.warning("Please use <prefix>quit instead of Ctrl+C to Shutdown!")
log.error("Received KeyboardInterrupt")
if tux is not None:
2020-06-03 17:41:30 +00:00
loop.run_until_complete(shutdown_handler(tux, signal.SIGINT))
except SystemExit as exc:
log.info("Shutting down with exit code: %s", exc.code)
if tux is not None:
2020-06-03 17:41:30 +00:00
loop.run_until_complete(shutdown_handler(tux, None, exc.code))
except Exception as exc:
log.exception("Unexpected exception (%s): ", type(exc), exc_info=exc)
if tux is not None:
2020-06-03 17:41:30 +00:00
loop.run_until_complete(shutdown_handler(tux, None, 1))
finally:
loop.run_until_complete(loop.shutdown_asyncgens())
log.info("Please wait, cleaning up a bit more")
loop.run_until_complete(asyncio.sleep(1))
2020-06-03 17:41:30 +00:00
asyncio.set_event_loop(None)
loop.stop()
loop.close()
2020-06-04 22:29:14 +00:00
exit_code = ExitCodes.CRITICAL if tux is None else tux.shutdown_code
2020-06-03 17:41:30 +00:00
sys.exit(exit_code)
if __name__ == "__main__":
main()