Compare commits
3 Commits
1f88499d44
...
331599eb38
Author | SHA1 | Date |
---|---|---|
Romain J | 331599eb38 | |
Romain J | 9a0786af7c | |
Romain J | c1e253689d |
|
@ -25,4 +25,4 @@ If applicable, add screenshots to help explain your problem.
|
|||
- Python Version [e.g. 3.7.4]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
<-- Add any other context about the problem here. -->
|
|
@ -1,16 +1,22 @@
|
|||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="romain">
|
||||
<words>
|
||||
<w>asctime</w>
|
||||
<w>commandstats</w>
|
||||
<w>francais</w>
|
||||
<w>ipinfo</w>
|
||||
<w>iplocalise</w>
|
||||
<w>jishaku</w>
|
||||
<w>levelname</w>
|
||||
<w>localiseip</w>
|
||||
<w>postgresql</w>
|
||||
<w>releaselevel</w>
|
||||
<w>socketstats</w>
|
||||
<w>splt</w>
|
||||
<w>systemd</w>
|
||||
<w>tutux</w>
|
||||
<w>tuxbot</w>
|
||||
<w>tuxbot's</w>
|
||||
<w>tuxvenv</w>
|
||||
<w>webhooks</w>
|
||||
</words>
|
||||
|
|
|
@ -39,12 +39,12 @@ Continue to [create the venv](#creating-the-virtual-environnement).
|
|||
|
||||
#### Windows
|
||||
|
||||
*go to hell*
|
||||
*not for now and not for the future*
|
||||
|
||||
|
||||
## Creating the Virtual Environnement
|
||||
## Creating the Virtual Environment
|
||||
|
||||
To set up the virtual environnement and install the bot, simply run this two commands:
|
||||
To set up the virtual environment and install the bot, simply run this two commands:
|
||||
|
||||
```shell script
|
||||
make
|
||||
|
|
|
@ -24,11 +24,11 @@ install_requires =
|
|||
certifi==2020.4.5.1
|
||||
chardet==3.0.4
|
||||
ciso8601==2.1.3
|
||||
click==7.1.2
|
||||
colorama==0.4.3
|
||||
discord-flags==2.1.1
|
||||
discord.py==1.3.3
|
||||
discord.py==1.3.4
|
||||
dnspython==1.16.0
|
||||
flatten-dict==0.3.0
|
||||
humanize==2.4.0
|
||||
idna==2.9
|
||||
import-expression==1.1.3
|
||||
|
@ -43,13 +43,13 @@ install_requires =
|
|||
pytz==2020.1
|
||||
regex==2020.6.7
|
||||
requests==2.23.0
|
||||
rich==6.0.0
|
||||
six==1.15.0
|
||||
toml>=0.9.4
|
||||
tortoise-orm==0.16.13
|
||||
typed-ast>=1.4.0
|
||||
typing-extensions==3.7.4.2
|
||||
urllib3==1.25.9
|
||||
websockets==8.1
|
||||
wheel==0.34.2
|
||||
yarl==1.4.2
|
||||
|
||||
|
|
4
setup.py
4
setup.py
|
@ -1,3 +1,5 @@
|
|||
from setuptools import setup
|
||||
|
||||
setup(python_requires=">=3.7")
|
||||
setup(
|
||||
python_requires=">=3.7",
|
||||
)
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
import subprocess
|
||||
import os
|
||||
from collections import namedtuple
|
||||
|
||||
build = subprocess.check_output(["git", "rev-parse", "--short", "HEAD"]).decode()
|
||||
build = os.popen('git rev-parse --short HEAD').read().strip()
|
||||
info = os.popen('git log -n 1 -s --format="%s"').read().strip()
|
||||
|
||||
VersionInfo = namedtuple("VersionInfo", "major minor micro releaselevel build")
|
||||
version_info = VersionInfo(major=3, minor=0, micro=0, releaselevel="alpha", build=build)
|
||||
VersionInfo = namedtuple(
|
||||
"VersionInfo", "major minor micro releaselevel build, info"
|
||||
)
|
||||
version_info = VersionInfo(
|
||||
major=3, minor=0, micro=0,
|
||||
releaselevel="alpha", build=build, info=info
|
||||
)
|
||||
|
||||
__version__ = "v{}.{}.{}-{}.{}".format(
|
||||
version_info.major,
|
||||
|
@ -13,3 +19,9 @@ __version__ = "v{}.{}.{}-{}.{}".format(
|
|||
version_info.releaselevel,
|
||||
version_info.build,
|
||||
).replace("\n", "")
|
||||
|
||||
|
||||
class ExitCodes:
|
||||
CRITICAL = 1
|
||||
SHUTDOWN = 0
|
||||
RESTART = 42
|
||||
|
|
|
@ -1,27 +1,34 @@
|
|||
import argparse
|
||||
import asyncio
|
||||
import getpass
|
||||
import json
|
||||
import logging
|
||||
import platform
|
||||
import signal
|
||||
import sys
|
||||
import os
|
||||
from argparse import Namespace
|
||||
from typing import NoReturn
|
||||
|
||||
import discord
|
||||
import pip
|
||||
from colorama import Fore, init, Style
|
||||
from pip._vendor import distro
|
||||
import tracemalloc
|
||||
from rich.columns import Columns
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
from rich.traceback import install
|
||||
from rich.table import Table, box
|
||||
from rich.text import Text
|
||||
from rich import print
|
||||
|
||||
import tuxbot.logging
|
||||
from tuxbot.core import data_manager
|
||||
from tuxbot.core.bot import Tux, ExitCodes
|
||||
from tuxbot.core.utils.functions.cli import bordered
|
||||
from . import __version__
|
||||
from tuxbot.core.bot import Tux
|
||||
from . import __version__, version_info, ExitCodes
|
||||
|
||||
log = logging.getLogger("tuxbot.main")
|
||||
init()
|
||||
|
||||
console = Console()
|
||||
install(console=console)
|
||||
tracemalloc.start()
|
||||
|
||||
|
||||
def list_instances() -> NoReturn:
|
||||
|
@ -29,21 +36,38 @@ def list_instances() -> NoReturn:
|
|||
|
||||
"""
|
||||
with data_manager.config_file.open() as fs:
|
||||
datas = json.load(fs)
|
||||
data = json.load(fs)
|
||||
|
||||
info = {"title": "Instances", "rows": []}
|
||||
console.print(
|
||||
Panel("[bold green]Instances", style="green"),
|
||||
justify="center"
|
||||
)
|
||||
console.print()
|
||||
|
||||
for instance, details in datas.items():
|
||||
info["rows"].append(
|
||||
f"-> {instance} " f"{'up' if details.get('IS_RUNNING') else 'down'}"
|
||||
columns = Columns(expand=True, padding=2, align="center")
|
||||
for instance, details in data.items():
|
||||
is_running = details.get('IS_RUNNING')
|
||||
|
||||
table = Table(
|
||||
style="dim", border_style="not dim",
|
||||
box=box.HEAVY_HEAD
|
||||
)
|
||||
table.add_column("Name")
|
||||
table.add_column(("Running" if is_running else "Down") + " since")
|
||||
table.add_row(instance, "42")
|
||||
table.title = Text(
|
||||
instance,
|
||||
style="green" if is_running else "red"
|
||||
)
|
||||
columns.add_renderable(table)
|
||||
console.print(columns)
|
||||
console.print()
|
||||
|
||||
print(bordered(info))
|
||||
sys.exit(0)
|
||||
sys.exit(os.EX_OK)
|
||||
|
||||
|
||||
def debug_info() -> NoReturn:
|
||||
"""Show debug infos relatives to the bot
|
||||
"""Show debug info relatives to the bot
|
||||
|
||||
"""
|
||||
python_version = sys.version.replace("\n", "")
|
||||
|
@ -51,29 +75,65 @@ def debug_info() -> NoReturn:
|
|||
tuxbot_version = __version__
|
||||
dpy_version = discord.__version__
|
||||
|
||||
os_info = distro.linux_distribution()
|
||||
os_info = f"{os_info[0]} {os_info[1]}"
|
||||
uptime = os.popen('uptime').read().strip().split()
|
||||
|
||||
runner = getpass.getuser()
|
||||
console.print(
|
||||
Panel("[bold blue]Debug Info", style="blue"),
|
||||
justify="center"
|
||||
)
|
||||
console.print()
|
||||
|
||||
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}",
|
||||
],
|
||||
}
|
||||
columns = Columns(expand=True, padding=2, align="center")
|
||||
|
||||
print(bordered(info))
|
||||
sys.exit(0)
|
||||
table = Table(
|
||||
style="dim", border_style="not dim",
|
||||
box=box.HEAVY_HEAD
|
||||
)
|
||||
table.add_column(
|
||||
"Bot Info",
|
||||
)
|
||||
table.add_row(f"[u]Tuxbot version:[/u] {tuxbot_version}")
|
||||
table.add_row(f"[u]Major:[/u] {version_info.major}")
|
||||
table.add_row(f"[u]Minor:[/u] {version_info.minor}")
|
||||
table.add_row(f"[u]Micro:[/u] {version_info.micro}")
|
||||
table.add_row(f"[u]Level:[/u] {version_info.releaselevel}")
|
||||
table.add_row(f"[u]Last change:[/u] {version_info.info}")
|
||||
columns.add_renderable(table)
|
||||
|
||||
table = Table(
|
||||
style="dim", border_style="not dim",
|
||||
box=box.HEAVY_HEAD
|
||||
)
|
||||
table.add_column(
|
||||
"Python Info",
|
||||
)
|
||||
table.add_row(f"[u]Python version:[/u] {python_version}")
|
||||
table.add_row(f"[u]Python executable path:[/u] {sys.executable}")
|
||||
table.add_row(f"[u]Pip version:[/u] {pip_version}")
|
||||
table.add_row(f"[u]Discord.py version:[/u] {dpy_version}")
|
||||
columns.add_renderable(table)
|
||||
|
||||
table = Table(
|
||||
style="dim", border_style="not dim",
|
||||
box=box.HEAVY_HEAD
|
||||
)
|
||||
table.add_column(
|
||||
"Server Info",
|
||||
)
|
||||
table.add_row(f"[u]System:[/u] {os.uname().sysname}")
|
||||
table.add_row(f"[u]System arch:[/u] {os.uname().machine}")
|
||||
table.add_row(f"[u]Kernel:[/u] {os.uname().release}")
|
||||
table.add_row(f"[u]User:[/u] {os.getlogin()}")
|
||||
table.add_row(f"[u]Uptime:[/u] {uptime[2]}")
|
||||
table.add_row(
|
||||
f"[u]Load Average:[/u] {' '.join(map(str, os.getloadavg()))}"
|
||||
)
|
||||
columns.add_renderable(table)
|
||||
|
||||
console.print(columns)
|
||||
console.print()
|
||||
|
||||
sys.exit(os.EX_OK)
|
||||
|
||||
|
||||
def parse_cli_flags(args: list) -> Namespace:
|
||||
|
@ -92,13 +152,19 @@ def parse_cli_flags(args: list) -> Namespace:
|
|||
usage="tuxbot <instance_name> [arguments]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--version", "-V", action="store_true", help="Show tuxbot's used version"
|
||||
"--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"
|
||||
"--debug", action="store_true",
|
||||
help="Show debug information."
|
||||
)
|
||||
parser.add_argument("--token", "-T", type=str, help="Run Tuxbot with passed token")
|
||||
parser.add_argument(
|
||||
"--list-instances", "-L", action="store_true",
|
||||
help="List all instance names"
|
||||
)
|
||||
parser.add_argument("--token", "-T", type=str,
|
||||
help="Run Tuxbot with passed token")
|
||||
parser.add_argument(
|
||||
"instance_name",
|
||||
nargs="?",
|
||||
|
@ -126,7 +192,6 @@ async def shutdown_handler(tux: Tux, signal_type, exit_code=None) -> NoReturn:
|
|||
"""
|
||||
if signal_type:
|
||||
log.info("%s received. Quitting...", signal_type)
|
||||
sys.exit(ExitCodes.SHUTDOWN)
|
||||
elif exit_code is None:
|
||||
log.info("Shutting down from unhandled exception")
|
||||
tux.shutdown_code = ExitCodes.CRITICAL
|
||||
|
@ -134,15 +199,7 @@ async def shutdown_handler(tux: Tux, signal_type, exit_code=None) -> NoReturn:
|
|||
if exit_code is not None:
|
||||
tux.shutdown_code = exit_code
|
||||
|
||||
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)
|
||||
await tux.shutdown()
|
||||
|
||||
|
||||
async def run_bot(tux: Tux, cli_flags: Namespace) -> None:
|
||||
|
@ -178,10 +235,17 @@ async def run_bot(tux: Tux, cli_flags: Namespace) -> None:
|
|||
|
||||
try:
|
||||
await tux.load_packages()
|
||||
await tux.start(token, bot=True)
|
||||
console.print()
|
||||
await tux.start(token=token, bot=True)
|
||||
except discord.LoginFailure:
|
||||
log.critical("This token appears to be valid.")
|
||||
console.print()
|
||||
console.print(
|
||||
"[prompt.invalid]This token appears to be valid. [i]exiting...[/i]"
|
||||
)
|
||||
sys.exit(ExitCodes.CRITICAL)
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
return None
|
||||
|
||||
|
@ -198,19 +262,19 @@ def main() -> NoReturn:
|
|||
elif cli_flags.debug:
|
||||
debug_info()
|
||||
elif cli_flags.version:
|
||||
print("Tuxbot V3")
|
||||
print(f"Tuxbot V{version_info.major}")
|
||||
print(f"Complete Version: {__version__}")
|
||||
sys.exit(0)
|
||||
|
||||
sys.exit(os.EX_OK)
|
||||
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
|
||||
try:
|
||||
if not cli_flags.instance_name:
|
||||
print(
|
||||
Fore.RED + "No instance provided ! "
|
||||
console.print(
|
||||
"[red]No instance provided ! "
|
||||
"You can use 'tuxbot -L' to list all available instances"
|
||||
+ Style.RESET_ALL
|
||||
)
|
||||
sys.exit(ExitCodes.CRITICAL)
|
||||
|
||||
|
@ -222,13 +286,12 @@ def main() -> NoReturn:
|
|||
|
||||
loop.run_until_complete(run_bot(tux, cli_flags))
|
||||
except KeyboardInterrupt:
|
||||
print(
|
||||
Fore.RED
|
||||
+ "Please use <prefix>quit instead of Ctrl+C to Shutdown!"
|
||||
+ Style.RESET_ALL
|
||||
console.print(
|
||||
" [red]Please use <prefix>quit instead of Ctrl+C to Shutdown!"
|
||||
)
|
||||
log.warning("Please use <prefix>quit instead of Ctrl+C to Shutdown!")
|
||||
log.error("Received KeyboardInterrupt")
|
||||
console.print("[i]Trying to shutdown...")
|
||||
if tux is not None:
|
||||
loop.run_until_complete(shutdown_handler(tux, signal.SIGINT))
|
||||
except SystemExit as exc:
|
||||
|
@ -236,6 +299,7 @@ def main() -> NoReturn:
|
|||
if tux is not None:
|
||||
loop.run_until_complete(shutdown_handler(tux, None, exc.code))
|
||||
except Exception as exc:
|
||||
console.print_exception()
|
||||
log.exception("Unexpected exception (%s): ", type(exc), exc_info=exc)
|
||||
if tux is not None:
|
||||
loop.run_until_complete(shutdown_handler(tux, None, 1))
|
||||
|
@ -247,8 +311,12 @@ def main() -> NoReturn:
|
|||
loop.stop()
|
||||
loop.close()
|
||||
exit_code = ExitCodes.CRITICAL if tux is None else tux.shutdown_code
|
||||
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
try:
|
||||
main()
|
||||
except Exception:
|
||||
console.print_exception()
|
||||
|
|
|
@ -3,14 +3,14 @@ from collections import namedtuple
|
|||
from .admin import Admin
|
||||
from ...core.bot import Tux
|
||||
|
||||
VersionInfo = namedtuple("VersionInfo", "major minor micro releaselevel")
|
||||
version_info = VersionInfo(major=2, minor=0, micro=0, releaselevel="alpha")
|
||||
VersionInfo = namedtuple("VersionInfo", "major minor micro release_level")
|
||||
version_info = VersionInfo(major=2, minor=0, micro=0, release_level="alpha")
|
||||
|
||||
__version__ = "v{}.{}.{}-{}".format(
|
||||
version_info.major,
|
||||
version_info.minor,
|
||||
version_info.micro,
|
||||
version_info.releaselevel,
|
||||
version_info.release_level,
|
||||
).replace("\n", "")
|
||||
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ class Admin(commands.Cog, name="Admin"):
|
|||
@checks.is_admin()
|
||||
async def _lang(self, ctx: ContextPlus):
|
||||
"""Manage lang settings."""
|
||||
pass
|
||||
|
||||
@_lang.command(name="set", aliases=["define", "choice"])
|
||||
async def _lang_set(self, ctx: ContextPlus, lang: str):
|
||||
|
|
|
@ -15,7 +15,7 @@ class Warnings(commands.Cog, name="Warnings"):
|
|||
@commands.guild_only()
|
||||
@checks.is_mod()
|
||||
async def _warn(self, ctx: commands.Context):
|
||||
pass
|
||||
division_by_zero = 1 / 0
|
||||
|
||||
@_warn.command(name="add")
|
||||
@commands.guild_only()
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
from colorama import init
|
||||
|
||||
from .. import __version__, version_info, VersionInfo
|
||||
from .. import __version__, version_info, VersionInfo, ExitCodes
|
||||
from .config import Config
|
||||
|
||||
__all__ = ["Config", "__version__", "version_info", "VersionInfo"]
|
||||
|
||||
init()
|
||||
__all__ = [
|
||||
"Config",
|
||||
"__version__",
|
||||
"version_info",
|
||||
"VersionInfo",
|
||||
"ExitCodes",
|
||||
]
|
||||
|
|
|
@ -5,18 +5,25 @@ import sys
|
|||
from typing import List, Union
|
||||
|
||||
import discord
|
||||
from colorama import Fore, Style, init
|
||||
from discord.ext import commands
|
||||
from rich import box
|
||||
from rich.columns import Columns
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
from rich.progress import Progress, TextColumn, BarColumn
|
||||
from rich.table import Table
|
||||
from rich.traceback import install
|
||||
from tuxbot import version_info
|
||||
|
||||
from . import Config
|
||||
from .data_manager import logs_data_path
|
||||
|
||||
from .utils.functions.cli import bordered
|
||||
|
||||
from . import __version__
|
||||
from . import __version__, ExitCodes
|
||||
from .utils.functions.extra import ContextPlus
|
||||
|
||||
log = logging.getLogger("tuxbot")
|
||||
init()
|
||||
console = Console()
|
||||
install(console=console)
|
||||
|
||||
NAME = r"""
|
||||
_____ _ _ _ _
|
||||
|
@ -31,6 +38,13 @@ packages: List[str] = ["jishaku", "tuxbot.cogs.warnings", "tuxbot.cogs.admin"]
|
|||
|
||||
class Tux(commands.AutoShardedBot):
|
||||
_loading: asyncio.Task
|
||||
_progress = {
|
||||
'main': Progress(
|
||||
TextColumn("[bold blue]{task.fields[task_name]}", justify="right"),
|
||||
BarColumn()
|
||||
),
|
||||
'tasks': {}
|
||||
}
|
||||
|
||||
def __init__(self, *args, cli_flags=None, **kwargs):
|
||||
# by default, if the bot shutdown without any intervention,
|
||||
|
@ -71,52 +85,86 @@ class Tux(commands.AutoShardedBot):
|
|||
|
||||
async def load_packages(self):
|
||||
if packages:
|
||||
print("Loading packages...")
|
||||
for package in packages:
|
||||
try:
|
||||
self.load_extension(package)
|
||||
except Exception as e:
|
||||
print(
|
||||
Fore.RED
|
||||
+ f"Failed to load package {package}"
|
||||
+ Style.RESET_ALL
|
||||
+ f" check "
|
||||
f"{str((self.logs / 'tuxbot.log').resolve())} "
|
||||
f"for more details"
|
||||
)
|
||||
with Progress() as progress:
|
||||
task = progress.add_task(
|
||||
"Loading packages...",
|
||||
total=len(packages)
|
||||
)
|
||||
|
||||
log.exception(f"Failed to load package {package}", exc_info=e)
|
||||
for package in packages:
|
||||
try:
|
||||
self.load_extension(package)
|
||||
progress.console.print(f"{package} loaded")
|
||||
except Exception as e:
|
||||
log.exception(
|
||||
f"Failed to load package {package}",
|
||||
exc_info=e
|
||||
)
|
||||
progress.console.print(
|
||||
f"[red]Failed to load package {package} "
|
||||
f"[i](see "
|
||||
f"{str((self.logs / 'tuxbot.log').resolve())} "
|
||||
f"for more details)[/i]"
|
||||
)
|
||||
|
||||
progress.advance(task)
|
||||
|
||||
async def on_ready(self):
|
||||
self.uptime = datetime.datetime.now()
|
||||
INFO = {
|
||||
"title": "INFO",
|
||||
"rows": [
|
||||
str(self.user),
|
||||
f"Prefixes: {', '.join(self.config('core').get('prefixes'))}",
|
||||
f"Language: {self.config('core').get('locale')}",
|
||||
f"Tuxbot Version: {__version__}",
|
||||
f"Discord.py Version: {discord.__version__}",
|
||||
"Python Version: " + sys.version.replace("\n", ""),
|
||||
f"Shards: {self.shard_count}",
|
||||
f"Servers: {len(self.guilds)}",
|
||||
f"Users: {len(self.users)}",
|
||||
],
|
||||
}
|
||||
self._progress.get("main").stop_task(
|
||||
self._progress.get("tasks")["connecting"]
|
||||
)
|
||||
self._progress.get("main").remove_task(
|
||||
self._progress.get("tasks")["connecting"]
|
||||
)
|
||||
console.clear()
|
||||
|
||||
COGS = {"title": "COGS", "rows": []}
|
||||
console.print(
|
||||
Panel(f"[bold blue]Tuxbot V{version_info.major}", style="blue"),
|
||||
justify="center"
|
||||
)
|
||||
console.print()
|
||||
|
||||
columns = Columns(expand=True, padding=2, align="center")
|
||||
|
||||
table = Table(
|
||||
style="dim", border_style="not dim",
|
||||
box=box.HEAVY_HEAD
|
||||
)
|
||||
table.add_column(
|
||||
"INFO",
|
||||
)
|
||||
table.add_row(str(self.user))
|
||||
table.add_row(f"Prefixes: {', '.join(self.config('core').get('prefixes'))}")
|
||||
table.add_row(f"Language: {self.config('core').get('locale')}")
|
||||
table.add_row(f"Tuxbot Version: {__version__}")
|
||||
table.add_row(f"Discord.py Version: {discord.__version__}")
|
||||
table.add_row(f"Shards: {self.shard_count}")
|
||||
table.add_row(f"Servers: {len(self.guilds)}")
|
||||
table.add_row(f"Users: {len(self.users)}")
|
||||
columns.add_renderable(table)
|
||||
|
||||
table = Table(
|
||||
style="dim", border_style="not dim",
|
||||
box=box.HEAVY_HEAD
|
||||
)
|
||||
table.add_column(
|
||||
"COGS",
|
||||
)
|
||||
for extension in packages:
|
||||
COGS["rows"].append(
|
||||
f"[{'X' if extension in self.extensions else ' '}] {extension}"
|
||||
)
|
||||
if extension in self.extensions:
|
||||
status = f"[green]:heavy_check_mark: {extension} "
|
||||
else:
|
||||
status = f"[red]:cross_mark: {extension} "
|
||||
|
||||
print(Fore.LIGHTBLUE_EX + NAME)
|
||||
print(Style.RESET_ALL)
|
||||
print(bordered(INFO, COGS))
|
||||
table.add_row(status)
|
||||
columns.add_renderable(table)
|
||||
|
||||
print(f"\n{'=' * 118}\n\n")
|
||||
console.print(columns)
|
||||
console.print()
|
||||
|
||||
async def is_owner(self, user: Union[discord.User, discord.Member]) -> bool:
|
||||
async def is_owner(self,
|
||||
user: Union[discord.User, discord.Member]) -> bool:
|
||||
"""Determines if the user is a bot owner.
|
||||
|
||||
Parameters
|
||||
|
@ -152,9 +200,9 @@ class Tux(commands.AutoShardedBot):
|
|||
return
|
||||
|
||||
if (
|
||||
message.guild.id in self.config.get_blacklist("guild")
|
||||
or message.channel.id in self.config.get_blacklist("channel")
|
||||
or message.author.id in self.config.get_blacklist("user")
|
||||
message.guild.id in self.config.get_blacklist("guild")
|
||||
or message.channel.id in self.config.get_blacklist("channel")
|
||||
or message.author.id in self.config.get_blacklist("user")
|
||||
):
|
||||
return
|
||||
|
||||
|
@ -168,11 +216,45 @@ class Tux(commands.AutoShardedBot):
|
|||
async def on_message(self, message: discord.Message):
|
||||
await self.process_commands(message)
|
||||
|
||||
async def start(self, token, bot):
|
||||
"""Connect to Discord and start all connections.
|
||||
|
||||
Todo: add postgresql connect here
|
||||
"""
|
||||
with self._progress.get("main") as pg:
|
||||
task_id = self._progress.get("tasks")["connecting"] = pg.add_task(
|
||||
"connecting",
|
||||
task_name="Connecting to Discord...", start=False
|
||||
)
|
||||
pg.update(task_id)
|
||||
await super().start(token, bot=bot)
|
||||
|
||||
async def logout(self):
|
||||
"""Disconnect from Discord and closes all actives connections.
|
||||
|
||||
Todo: add postgresql logout here
|
||||
"""
|
||||
for task in self._progress.get("tasks").keys():
|
||||
self._progress.get("main").log("Shutting down", task)
|
||||
|
||||
self._progress.get("main").stop_task(
|
||||
self._progress.get("tasks")[task]
|
||||
)
|
||||
self._progress.get("main").remove_task(
|
||||
self._progress.get("tasks")["connecting"]
|
||||
)
|
||||
self._progress.get("main").stop()
|
||||
|
||||
pending = [
|
||||
t for t in asyncio.all_tasks() if
|
||||
t is not asyncio.current_task()
|
||||
]
|
||||
|
||||
for task in pending:
|
||||
console.log("Canceling", task.get_name(), f"({task.get_coro()})")
|
||||
task.cancel()
|
||||
await asyncio.gather(*pending, return_exceptions=True)
|
||||
|
||||
await super().logout()
|
||||
|
||||
async def shutdown(self, *, restart: bool = False):
|
||||
|
@ -192,9 +274,3 @@ class Tux(commands.AutoShardedBot):
|
|||
|
||||
await self.logout()
|
||||
sys.exit(self.shutdown_code)
|
||||
|
||||
|
||||
class ExitCodes:
|
||||
CRITICAL = 1
|
||||
SHUTDOWN = 0
|
||||
RESTART = 42
|
||||
|
|
|
@ -33,10 +33,12 @@ def init_logging(level: int, location: pathlib.Path) -> None:
|
|||
)
|
||||
|
||||
dpy_handler = logging.handlers.RotatingFileHandler(
|
||||
str(dpy_logger_file.resolve()), maxBytes=MAX_BYTES, backupCount=MAX_OLD_LOGS
|
||||
str(dpy_logger_file.resolve()),
|
||||
maxBytes=MAX_BYTES, backupCount=MAX_OLD_LOGS
|
||||
)
|
||||
base_handler = logging.handlers.RotatingFileHandler(
|
||||
str(base_logger_file.resolve()), maxBytes=MAX_BYTES, backupCount=MAX_OLD_LOGS
|
||||
str(base_logger_file.resolve()),
|
||||
maxBytes=MAX_BYTES, backupCount=MAX_OLD_LOGS
|
||||
)
|
||||
|
||||
stdout_handler = logging.StreamHandler(sys.stdout)
|
||||
|
|
174
tuxbot/setup.py
174
tuxbot/setup.py
|
@ -3,14 +3,19 @@ import logging
|
|||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import NoReturn, Union, List, Set
|
||||
from typing import NoReturn, Union, List
|
||||
|
||||
import click
|
||||
from colorama import Fore, Style, init
|
||||
from rich.prompt import Prompt, IntPrompt
|
||||
from rich.console import Console
|
||||
from rich.rule import Rule
|
||||
from rich.traceback import install
|
||||
from rich import print
|
||||
|
||||
from tuxbot.core.data_manager import config_dir, app_dir
|
||||
|
||||
init()
|
||||
console = Console()
|
||||
console.clear()
|
||||
install(console=console)
|
||||
|
||||
try:
|
||||
config_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
@ -77,14 +82,15 @@ def get_name() -> str:
|
|||
"""
|
||||
name = ""
|
||||
while not name:
|
||||
print(
|
||||
name = Prompt.ask(
|
||||
"What name do you want to give this instance?\n"
|
||||
"(valid characters: A-Z, a-z, 0-9, _, -)"
|
||||
"[i](valid characters: A-Z, a-z, 0-9, _, -)[/i]\n",
|
||||
default="prod",
|
||||
console=console
|
||||
)
|
||||
name = input("> ")
|
||||
if re.fullmatch(r"[a-zA-Z0-9_\-]*", name) is None:
|
||||
print()
|
||||
print(Fore.RED + "ERROR: Invalid characters provided" + Style.RESET_ALL)
|
||||
print("[prompt.invalid]ERROR: Invalid characters provided")
|
||||
name = ""
|
||||
return name
|
||||
|
||||
|
@ -113,42 +119,35 @@ def get_data_dir(instance_name: str) -> Path:
|
|||
except OSError:
|
||||
print()
|
||||
print(
|
||||
Fore.RED + f"mkdir: cannot create directory '{path}':"
|
||||
f" Permission denied" + Style.RESET_ALL
|
||||
f"mkdir: cannot create directory '{path}': Permission denied"
|
||||
)
|
||||
path = ""
|
||||
|
||||
return path
|
||||
|
||||
while not data_path_input:
|
||||
print(
|
||||
"where do you want to save the configurations?\n"
|
||||
"Press [enter] to keep the default path"
|
||||
data_path_input = Path(
|
||||
Prompt.ask(
|
||||
"where do you want to save the configurations?",
|
||||
default=str(data_path),
|
||||
console=console
|
||||
)
|
||||
)
|
||||
print()
|
||||
print(f"Default: {data_path}")
|
||||
|
||||
data_path_input = input("> ")
|
||||
try:
|
||||
exists = data_path_input.exists()
|
||||
except OSError:
|
||||
print()
|
||||
print(
|
||||
"[prompt.invalid]"
|
||||
"Impossible to verify the validity of the path,"
|
||||
" make sure it does not contain any invalid characters."
|
||||
)
|
||||
data_path_input = ""
|
||||
exists = False
|
||||
|
||||
if data_path_input != "":
|
||||
data_path_input = Path(data_path_input)
|
||||
|
||||
try:
|
||||
exists = data_path_input.exists()
|
||||
except OSError:
|
||||
print()
|
||||
print(
|
||||
Fore.RED + "Impossible to verify the validity of the path, "
|
||||
"make sure it does not contain any invalid characters."
|
||||
+ Style.RESET_ALL
|
||||
)
|
||||
data_path_input = ""
|
||||
exists = False
|
||||
|
||||
if data_path_input and not exists:
|
||||
data_path_input = make_data_dir(data_path_input)
|
||||
else:
|
||||
data_path_input = make_data_dir(data_path)
|
||||
if data_path_input and not exists:
|
||||
data_path_input = make_data_dir(data_path_input)
|
||||
|
||||
print()
|
||||
print(
|
||||
|
@ -156,7 +155,11 @@ def get_data_dir(instance_name: str) -> Path:
|
|||
f"`{instance_name}` instance"
|
||||
)
|
||||
|
||||
if not click.confirm("Please confirm", default=True):
|
||||
if Prompt.ask(
|
||||
"Please confirm",
|
||||
choices=["y", "n"], default="y",
|
||||
console=console
|
||||
) != "y":
|
||||
print("Rerun the process to redo this configuration.")
|
||||
sys.exit(0)
|
||||
|
||||
|
@ -178,25 +181,23 @@ def get_token() -> str:
|
|||
token = ""
|
||||
|
||||
while not token:
|
||||
print(
|
||||
"Please enter the bot token\n"
|
||||
"(you can find it at https://discord.com/developers/applications)"
|
||||
token = Prompt.ask(
|
||||
"Please enter the bot token "
|
||||
"(you can find it at https://discord.com/developers/applications)",
|
||||
console=console
|
||||
)
|
||||
token = input("> ")
|
||||
if (
|
||||
re.fullmatch(
|
||||
r"([a-zA-Z0-9]{24}\.[a-zA-Z0-9_]{6}\.[a-zA-Z0-9_\-]{27}|mfa\.[a-zA-Z0-9_\-]{84})",
|
||||
token,
|
||||
)
|
||||
is None
|
||||
):
|
||||
print(Fore.RED + "ERROR: Invalid token provided" + Style.RESET_ALL)
|
||||
if re.fullmatch(
|
||||
r"([a-zA-Z0-9]{24}\.[a-zA-Z0-9_]{6}\.[a-zA-Z0-9_\-]{27}"
|
||||
r"|mfa\.[a-zA-Z0-9_\-]{84})",
|
||||
token) \
|
||||
is None:
|
||||
print("[prompt.invalid]ERROR: Invalid token provided")
|
||||
token = ""
|
||||
return token
|
||||
|
||||
|
||||
def get_multiple(
|
||||
question: str, confirmation: str, value_type: type
|
||||
question: str, confirmation: str, value_type: type
|
||||
) -> List[Union[str, int]]:
|
||||
"""Give possibility to user to fill multiple value.
|
||||
|
||||
|
@ -214,15 +215,29 @@ def get_multiple(
|
|||
List[Union[str, int]]
|
||||
List containing user filled values.
|
||||
"""
|
||||
print(question)
|
||||
user_input = input("> ")
|
||||
prompt = IntPrompt if value_type is int else Prompt
|
||||
|
||||
user_input = prompt.ask(question, console=console)
|
||||
|
||||
if not user_input:
|
||||
return []
|
||||
|
||||
values = [user_input]
|
||||
|
||||
while click.confirm(confirmation, default=False):
|
||||
values.append(value_type(input("> ")))
|
||||
while Prompt.ask(
|
||||
confirmation,
|
||||
choices=["y", "n"], default="y",
|
||||
console=console
|
||||
) != "n":
|
||||
new = prompt.ask("Other")
|
||||
|
||||
if new not in values:
|
||||
values.append(new)
|
||||
else:
|
||||
print(
|
||||
f"[prompt.invalid]"
|
||||
f"ERROR: `{new}` is already present, [i]ignored[/i]"
|
||||
)
|
||||
|
||||
return values
|
||||
|
||||
|
@ -236,24 +251,23 @@ def additional_config() -> dict:
|
|||
Dict with cog name as key and configs as value.
|
||||
"""
|
||||
p = Path(r"tuxbot/cogs").glob("**/additional_config.json")
|
||||
datas = {}
|
||||
data = {}
|
||||
|
||||
for file in p:
|
||||
print()
|
||||
print("\n" * 4)
|
||||
cog_name = str(file.parent).split("/")[-1]
|
||||
datas[cog_name] = {}
|
||||
data[cog_name] = {}
|
||||
|
||||
with file.open("r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
print(f"\n==Configuration for `{cog_name}` module==")
|
||||
print(Rule(f"\nConfiguration for `{cog_name}` module"))
|
||||
|
||||
for key, value in data.items():
|
||||
print()
|
||||
print(value["description"])
|
||||
datas[cog_name][key] = input("> ")
|
||||
data[cog_name][key] = Prompt.ask(value["description"])
|
||||
|
||||
return datas
|
||||
return data
|
||||
|
||||
|
||||
def finish_setup(data_dir: Path) -> NoReturn:
|
||||
|
@ -264,15 +278,29 @@ def finish_setup(data_dir: Path) -> NoReturn:
|
|||
data_dir:Path
|
||||
Where to save configs.
|
||||
"""
|
||||
print("Now, it's time to finish this setup by giving bot informations\n")
|
||||
print(
|
||||
Rule(
|
||||
"Now, it's time to finish this setup by giving bot information"
|
||||
)
|
||||
)
|
||||
print()
|
||||
|
||||
token = get_token()
|
||||
|
||||
print()
|
||||
prefixes = get_multiple(
|
||||
"Choice a (or multiple) prefix for the bot", "Add another prefix ?", str
|
||||
"Choice a (or multiple) prefix for the bot", "Add another prefix ?",
|
||||
str
|
||||
)
|
||||
mentionable = click.confirm("Does the bot answer if it's mentioned?", default=True)
|
||||
|
||||
print()
|
||||
mentionable = Prompt.ask(
|
||||
"Does the bot answer if it's mentioned?",
|
||||
choices=["y", "n"],
|
||||
default="y"
|
||||
) == "y"
|
||||
|
||||
print()
|
||||
owners_id = get_multiple(
|
||||
"Give the owner id of this bot", "Add another owner ?", int
|
||||
)
|
||||
|
@ -305,7 +333,12 @@ def basic_setup() -> NoReturn:
|
|||
"""Configs who refer to instances.
|
||||
|
||||
"""
|
||||
print("Hi ! it's time for you to give me informations about you instance")
|
||||
print(
|
||||
Rule(
|
||||
"Hi ! it's time for you to give me information about you instance"
|
||||
)
|
||||
)
|
||||
print()
|
||||
name = get_name()
|
||||
|
||||
data_dir = get_data_dir(name)
|
||||
|
@ -318,11 +351,14 @@ def basic_setup() -> NoReturn:
|
|||
|
||||
if name in instances_list:
|
||||
print()
|
||||
print(
|
||||
Fore.RED + f"WARNING: An instance named `{name}` already exists "
|
||||
f"Continuing will overwrite this instance configs." + Style.RESET_ALL
|
||||
console.print(
|
||||
f"WARNING: An instance named `{name}` already exists "
|
||||
f"Continuing will overwrite this instance configs.", style="red"
|
||||
)
|
||||
if not click.confirm("Are you sure you want to continue?", default=False):
|
||||
if Prompt.ask(
|
||||
"Are you sure you want to continue?",
|
||||
choices=["y", "n"], default="n"
|
||||
) == "n":
|
||||
print("Abandon...")
|
||||
sys.exit(0)
|
||||
|
||||
|
@ -357,6 +393,8 @@ def setup() -> NoReturn:
|
|||
basic_setup()
|
||||
except KeyboardInterrupt:
|
||||
print("Exiting...")
|
||||
except:
|
||||
console.print_exception()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
Loading…
Reference in New Issue