2020-06-03 19:41:30 +02:00
|
|
|
import argparse
|
|
|
|
import asyncio
|
2020-06-04 00:14:50 +02:00
|
|
|
import getpass
|
|
|
|
import json
|
2020-06-03 19:41:30 +02:00
|
|
|
import logging
|
2020-06-04 00:14:50 +02:00
|
|
|
import platform
|
2020-06-03 19:41:30 +02:00
|
|
|
import signal
|
|
|
|
import sys
|
2020-06-04 00:46:53 +02:00
|
|
|
from argparse import Namespace
|
2020-06-04 19:16:51 +02:00
|
|
|
from typing import NoReturn
|
2020-06-03 19:41:30 +02:00
|
|
|
|
|
|
|
import discord
|
2020-06-04 00:14:50 +02:00
|
|
|
import pip
|
2020-06-03 19:41:30 +02:00
|
|
|
from colorama import Fore, init, Style
|
2020-06-04 00:14:50 +02:00
|
|
|
from pip._vendor import distro
|
2020-06-03 19:41:30 +02:00
|
|
|
|
|
|
|
import tuxbot.logging
|
|
|
|
from tuxbot.core import data_manager
|
2020-06-04 00:14:50 +02:00
|
|
|
from tuxbot.core.bot import Tux
|
|
|
|
from tuxbot.core.utils.functions.cli import bordered
|
|
|
|
from . import __version__
|
2020-06-03 19:41:30 +02:00
|
|
|
|
|
|
|
log = logging.getLogger("tuxbot.main")
|
|
|
|
init()
|
|
|
|
|
|
|
|
|
2020-06-04 00:14:50 +02:00
|
|
|
def list_instances() -> NoReturn:
|
2020-06-04 00:46:53 +02:00
|
|
|
"""List all available instances
|
|
|
|
|
|
|
|
"""
|
2020-06-04 00:14:50 +02:00
|
|
|
with data_manager.config_file.open() as fs:
|
|
|
|
datas = json.load(fs)
|
|
|
|
|
|
|
|
instances = list(datas.keys())
|
|
|
|
|
|
|
|
info = {
|
|
|
|
'title': "Instances",
|
|
|
|
'rows': []
|
|
|
|
}
|
|
|
|
|
|
|
|
for instance in instances:
|
|
|
|
info['rows'].append(f"-> {instance}")
|
|
|
|
|
|
|
|
print(bordered(info))
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
|
|
|
|
|
def debug_info() -> NoReturn:
|
2020-06-04 00:46:53 +02:00
|
|
|
"""Show debug infos relatives to the bot
|
|
|
|
|
|
|
|
"""
|
2020-06-04 00:14:50 +02: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 = {
|
|
|
|
'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}",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
print(bordered(info))
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
|
|
2020-06-04 00:46:53 +02: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 19:41:30 +02:00
|
|
|
parser = argparse.ArgumentParser(
|
|
|
|
description="Tuxbot - OpenSource bot",
|
|
|
|
usage="tuxbot <instance_name> [arguments]"
|
|
|
|
)
|
2020-06-04 00:14:50 +02:00
|
|
|
parser.add_argument(
|
|
|
|
"--version", "-V",
|
|
|
|
action="store_true",
|
|
|
|
help="Show tuxbot's used version"
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"--debug",
|
|
|
|
action="store_true",
|
|
|
|
help="Show debug information."
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"--list-instances", "-L",
|
|
|
|
action="store_true",
|
|
|
|
help="List all instance names"
|
|
|
|
)
|
2020-06-04 19:16:51 +02:00
|
|
|
parser.add_argument(
|
|
|
|
"--token", "-T",
|
|
|
|
type=str,
|
|
|
|
help="Run Tuxbot with passed token"
|
|
|
|
)
|
2020-06-03 19:41:30 +02:00
|
|
|
parser.add_argument(
|
|
|
|
"instance_name", nargs="?",
|
2020-06-04 00:14:50 +02:00
|
|
|
help="Name of the bot instance created during `tuxbot-setup`."
|
|
|
|
)
|
2020-06-03 19:41:30 +02:00
|
|
|
|
|
|
|
args = parser.parse_args(args)
|
|
|
|
|
|
|
|
return args
|
|
|
|
|
|
|
|
|
2020-06-04 00:46:53 +02: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 19:16:51 +02:00
|
|
|
signal_type:int, None
|
2020-06-04 00:46:53 +02:00
|
|
|
Exiting signal code.
|
|
|
|
exit_code:None|int
|
|
|
|
Code to show when exiting.
|
|
|
|
"""
|
2020-06-03 19:41:30 +02:00
|
|
|
if signal_type:
|
|
|
|
log.info("%s received. Quitting...", signal_type)
|
|
|
|
sys.exit(0)
|
|
|
|
elif exit_code is None:
|
|
|
|
log.info("Shutting down from unhandled exception")
|
2020-06-04 00:14:50 +02:00
|
|
|
tux.shutdown_code = 1
|
2020-06-03 19:41:30 +02:00
|
|
|
|
|
|
|
if exit_code is not None:
|
2020-06-04 00:14:50 +02:00
|
|
|
tux.shutdown_code = exit_code
|
2020-06-03 19:41:30 +02:00
|
|
|
|
|
|
|
try:
|
|
|
|
await tux.logout()
|
|
|
|
finally:
|
|
|
|
pending = [
|
|
|
|
t for t in asyncio.all_tasks() if t is not asyncio.current_task()
|
|
|
|
]
|
|
|
|
|
|
|
|
for task in pending:
|
|
|
|
task.cancel()
|
|
|
|
|
|
|
|
await asyncio.gather(*pending, return_exceptions=True)
|
|
|
|
|
|
|
|
|
2020-06-04 00:46:53 +02:00
|
|
|
async def run_bot(tux: Tux, cli_flags: Namespace) -> None:
|
|
|
|
"""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.
|
|
|
|
"""
|
2020-06-04 16:36:22 +02:00
|
|
|
data_path = data_manager.data_path(tux.instance_name)
|
2020-06-03 19:41:30 +02:00
|
|
|
|
|
|
|
tuxbot.logging.init_logging(
|
2020-06-04 19:16:51 +02:00
|
|
|
10,
|
2020-06-03 19:41:30 +02:00
|
|
|
location=data_path / "logs"
|
|
|
|
)
|
|
|
|
|
|
|
|
log.debug("====Basic Config====")
|
|
|
|
log.debug("Data Path: %s", data_path)
|
|
|
|
|
|
|
|
if cli_flags.token:
|
|
|
|
token = cli_flags.token
|
|
|
|
else:
|
2020-06-04 19:16:51 +02:00
|
|
|
token = tux.config('core').get('token')
|
2020-06-03 19:41:30 +02:00
|
|
|
|
|
|
|
if not token:
|
|
|
|
log.critical("Token must be set if you want to login.")
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
try:
|
2020-06-04 19:16:51 +02:00
|
|
|
await tux.start(token, bot=True)
|
2020-06-03 19:41:30 +02:00
|
|
|
except discord.LoginFailure:
|
|
|
|
log.critical("This token appears to be valid.")
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
2020-06-04 00:46:53 +02:00
|
|
|
def main() -> NoReturn:
|
|
|
|
"""Main function
|
|
|
|
|
|
|
|
"""
|
2020-06-03 19:41:30 +02:00
|
|
|
tux = None
|
|
|
|
cli_flags = parse_cli_flags(sys.argv[1:])
|
2020-06-04 00:14:50 +02:00
|
|
|
|
|
|
|
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 19:41:30 +02:00
|
|
|
loop = asyncio.new_event_loop()
|
|
|
|
asyncio.set_event_loop(loop)
|
|
|
|
|
|
|
|
try:
|
2020-06-04 00:14:50 +02:00
|
|
|
if not cli_flags.instance_name:
|
2020-06-03 19:41:30 +02:00
|
|
|
print(Fore.RED
|
|
|
|
+ "No instance provided ! "
|
|
|
|
"You can use 'tuxbot -L' to list all available instances"
|
|
|
|
+ Style.RESET_ALL)
|
|
|
|
sys.exit(1)
|
|
|
|
|
2020-06-04 00:14:50 +02:00
|
|
|
tux = Tux(
|
2020-06-03 19:41:30 +02:00
|
|
|
cli_flags=cli_flags,
|
|
|
|
description="Tuxbot, made from and for OpenSource",
|
|
|
|
dm_help=None
|
|
|
|
)
|
|
|
|
|
|
|
|
loop.run_until_complete(run_bot(tux, cli_flags))
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
log.warning("Please use <prefix>quit instead of Ctrl+C to Shutdown!")
|
|
|
|
log.error("Received KeyboardInterrupt")
|
2020-06-04 00:14:50 +02:00
|
|
|
if tux is not None:
|
2020-06-03 19:41:30 +02:00
|
|
|
loop.run_until_complete(shutdown_handler(tux, signal.SIGINT))
|
|
|
|
except SystemExit as exc:
|
|
|
|
log.info("Shutting down with exit code: %s", exc.code)
|
2020-06-04 00:14:50 +02:00
|
|
|
if tux is not None:
|
2020-06-03 19:41:30 +02: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)
|
2020-06-04 00:14:50 +02:00
|
|
|
if tux is not None:
|
2020-06-03 19:41:30 +02: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")
|
2020-06-04 00:14:50 +02:00
|
|
|
loop.run_until_complete(asyncio.sleep(1))
|
2020-06-03 19:41:30 +02:00
|
|
|
asyncio.set_event_loop(None)
|
|
|
|
loop.stop()
|
|
|
|
loop.close()
|
2020-06-04 00:14:50 +02:00
|
|
|
exit_code = 1 if tux is None else tux.shutdown_code
|
2020-06-03 19:41:30 +02:00
|
|
|
sys.exit(exit_code)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|