feat(core|logs>sentry): feat sentry error handler
This commit is contained in:
parent
554c0b52d5
commit
647cc4bd64
11 changed files with 151 additions and 202 deletions
|
@ -1,3 +1,2 @@
|
||||||
youtrack
|
|
||||||
pylint>=2.6.0
|
pylint>=2.6.0
|
||||||
black>=20.8b1
|
black>=20.8b1
|
|
@ -26,6 +26,7 @@ install_requires =
|
||||||
psutil>=5.7.2
|
psutil>=5.7.2
|
||||||
requests>=2.25.1
|
requests>=2.25.1
|
||||||
rich>=6.0.0
|
rich>=6.0.0
|
||||||
|
sentry_sdk>=0.19.5
|
||||||
structured_config>=4.12
|
structured_config>=4.12
|
||||||
tortoise-orm>=0.16.17
|
tortoise-orm>=0.16.17
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
from rich.console import Console
|
|
||||||
from rich.traceback import install
|
|
||||||
from tuxbot import ExitCodes
|
from tuxbot import ExitCodes
|
||||||
|
from tuxbot.core.utils.console import console
|
||||||
console = Console()
|
|
||||||
install(console=console, show_locals=True)
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
|
|
|
@ -11,9 +11,7 @@ import discord
|
||||||
import humanize
|
import humanize
|
||||||
import pip
|
import pip
|
||||||
from rich.columns import Columns
|
from rich.columns import Columns
|
||||||
from rich.console import Console
|
|
||||||
from rich.panel import Panel
|
from rich.panel import Panel
|
||||||
from rich.traceback import install
|
|
||||||
from rich.table import Table, box
|
from rich.table import Table, box
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
from rich import print as rprint
|
from rich import print as rprint
|
||||||
|
@ -21,14 +19,12 @@ from rich import print as rprint
|
||||||
import tuxbot.logging
|
import tuxbot.logging
|
||||||
from tuxbot.core.bot import Tux
|
from tuxbot.core.bot import Tux
|
||||||
from tuxbot.core import config
|
from tuxbot.core import config
|
||||||
from .core.utils import data_manager
|
from tuxbot.core.utils import data_manager
|
||||||
|
from tuxbot.core.utils.console import console
|
||||||
from . import __version__, version_info, ExitCodes
|
from . import __version__, version_info, ExitCodes
|
||||||
|
|
||||||
log = logging.getLogger("tuxbot.main")
|
log = logging.getLogger("tuxbot.main")
|
||||||
|
|
||||||
console = Console()
|
|
||||||
install(console=console, show_locals=True)
|
|
||||||
|
|
||||||
BORDER_STYLE = "not dim"
|
BORDER_STYLE = "not dim"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,19 +4,12 @@ HAS_MODELS = False
|
||||||
|
|
||||||
|
|
||||||
class DevConfig(Structure):
|
class DevConfig(Structure):
|
||||||
url: str = StrField("")
|
sentryKey: str = StrField("")
|
||||||
login: str = StrField("")
|
|
||||||
password: str = StrField("")
|
|
||||||
|
|
||||||
|
|
||||||
extra = {
|
extra = {
|
||||||
"url": {
|
"sentryKey": {
|
||||||
"type": str,
|
"type": str,
|
||||||
"description": "URL of the YouTrack instance (without /youtrack/)",
|
"description": "Sentry KEY for error logging (https://sentry.io/)",
|
||||||
},
|
|
||||||
"login": {"type": str, "description": "Login for YouTrack instance"},
|
|
||||||
"password": {
|
|
||||||
"type": str,
|
|
||||||
"description": "Password for YouTrack instance",
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,46 +1,30 @@
|
||||||
import logging
|
import logging
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from youtrack.connection import Connection as YouTrack
|
|
||||||
from structured_config import ConfigFile
|
|
||||||
|
|
||||||
from tuxbot.core.bot import Tux
|
from tuxbot.core.bot import Tux
|
||||||
from tuxbot.core.i18n import (
|
from tuxbot.core.i18n import (
|
||||||
Translator,
|
Translator,
|
||||||
)
|
)
|
||||||
from tuxbot.core.utils.data_manager import cogs_data_path
|
from tuxbot.core.utils import checks
|
||||||
from .config import DevConfig
|
from tuxbot.core.utils.functions.extra import command_extra, ContextPlus
|
||||||
from ...core.utils import checks
|
|
||||||
from ...core.utils.functions.extra import group_extra, ContextPlus
|
|
||||||
|
|
||||||
log = logging.getLogger("tuxbot.cogs.Dev")
|
log = logging.getLogger("tuxbot.cogs.Dev")
|
||||||
_ = Translator("Dev", __file__)
|
_ = Translator("Dev", __file__)
|
||||||
|
|
||||||
|
|
||||||
class Dev(commands.Cog, name="Dev"):
|
class Dev(commands.Cog, name="Dev"):
|
||||||
yt: YouTrack # pylint: disable=invalid-name
|
|
||||||
|
|
||||||
def __init__(self, bot: Tux):
|
def __init__(self, bot: Tux):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
self.__config: DevConfig = ConfigFile(
|
|
||||||
str(cogs_data_path(self.bot.instance_name, "Dev") / "config.yaml"),
|
|
||||||
DevConfig,
|
|
||||||
).config
|
|
||||||
|
|
||||||
# pylint: disable=invalid-name
|
|
||||||
self.yt = YouTrack(
|
|
||||||
self.__config.url.rstrip("/") + "/youtrack/",
|
|
||||||
login=self.__config.login,
|
|
||||||
password=self.__config.password,
|
|
||||||
)
|
|
||||||
|
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
|
|
||||||
@group_extra(name="issue", aliases=["issues"], deletable=True)
|
@command_extra(name="crash", deletable=True)
|
||||||
@checks.is_owner()
|
@checks.is_owner()
|
||||||
async def _issue(self, ctx: ContextPlus):
|
async def _crash(self, ctx: ContextPlus, crash_type: str):
|
||||||
"""Manage bot issues."""
|
if crash_type == "ZeroDivisionError":
|
||||||
|
await ctx.send(str(5 / 0))
|
||||||
@_issue.command(name="list", aliases=["liste", "all", "view"])
|
elif crash_type == "TypeError":
|
||||||
async def _lang_list(self, ctx: ContextPlus):
|
await ctx.send(str(int([])))
|
||||||
pass
|
elif crash_type == "IndexError":
|
||||||
|
await ctx.send(str([0][5]))
|
||||||
|
|
|
@ -4,7 +4,7 @@ from collections import namedtuple
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
|
||||||
from tuxbot.core.bot import Tux
|
from tuxbot.core.bot import Tux
|
||||||
from .logs import Logs, on_error, GatewayHandler
|
from .logs import Logs, GatewayHandler
|
||||||
from .config import LogsConfig, HAS_MODELS
|
from .config import LogsConfig, HAS_MODELS
|
||||||
|
|
||||||
VersionInfo = namedtuple("VersionInfo", "major minor micro release_level")
|
VersionInfo = namedtuple("VersionInfo", "major minor micro release_level")
|
||||||
|
@ -24,4 +24,3 @@ def setup(bot: Tux):
|
||||||
|
|
||||||
handler = GatewayHandler(cog)
|
handler = GatewayHandler(cog)
|
||||||
logging.getLogger().addHandler(handler)
|
logging.getLogger().addHandler(handler)
|
||||||
commands.AutoShardedBot.on_error = on_error
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ class LogsConfig(Structure):
|
||||||
guilds: str = StrField("")
|
guilds: str = StrField("")
|
||||||
errors: str = StrField("")
|
errors: str = StrField("")
|
||||||
gateway: str = StrField("")
|
gateway: str = StrField("")
|
||||||
|
sentryKey: str = StrField("")
|
||||||
|
|
||||||
|
|
||||||
extra = {
|
extra = {
|
||||||
|
@ -35,4 +36,8 @@ extra = {
|
||||||
"type": str,
|
"type": str,
|
||||||
"description": "URL of the webhook used for send gateway information",
|
"description": "URL of the webhook used for send gateway information",
|
||||||
},
|
},
|
||||||
|
"sentryKey": {
|
||||||
|
"type": str,
|
||||||
|
"description": "Sentry KEY for error logging (https://sentry.io/)",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ from logging import LogRecord
|
||||||
import discord
|
import discord
|
||||||
import humanize
|
import humanize
|
||||||
import psutil
|
import psutil
|
||||||
|
import sentry_sdk
|
||||||
from discord.ext import commands, tasks
|
from discord.ext import commands, tasks
|
||||||
from structured_config import ConfigFile
|
from structured_config import ConfigFile
|
||||||
|
|
||||||
|
@ -64,7 +65,70 @@ class Logs(commands.Cog, name="Logs"):
|
||||||
self._resumes = []
|
self._resumes = []
|
||||||
self._identifies = defaultdict(list)
|
self._identifies = defaultdict(list)
|
||||||
|
|
||||||
def _clear_gateway_data(self):
|
self.old_on_error = bot.on_error
|
||||||
|
bot.on_error = self.on_error
|
||||||
|
|
||||||
|
sentry_sdk.init(
|
||||||
|
dsn=self.__config.sentryKey,
|
||||||
|
traces_sample_rate=1.0,
|
||||||
|
environment=self.bot.instance_name,
|
||||||
|
debug=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def cog_unload(self):
|
||||||
|
self.bot.on_error = self.old_on_error
|
||||||
|
|
||||||
|
async def on_error(self, event):
|
||||||
|
raise event
|
||||||
|
|
||||||
|
# =========================================================================
|
||||||
|
# =========================================================================
|
||||||
|
|
||||||
|
def webhook(self, log_type):
|
||||||
|
webhook = discord.Webhook.from_url(
|
||||||
|
getattr(self.__config, log_type),
|
||||||
|
adapter=discord.AsyncWebhookAdapter(self.bot.session),
|
||||||
|
)
|
||||||
|
return webhook
|
||||||
|
|
||||||
|
async def send_guild_stats(self, e, guild):
|
||||||
|
e.add_field(name="Name", value=guild.name)
|
||||||
|
e.add_field(name="ID", value=guild.id)
|
||||||
|
e.add_field(name="Shard ID", value=guild.shard_id or "N/A")
|
||||||
|
e.add_field(
|
||||||
|
name="Owner", value=f"{guild.owner} (ID: {guild.owner.id})"
|
||||||
|
)
|
||||||
|
|
||||||
|
bots = sum(member.bot for member in guild.members)
|
||||||
|
total = guild.member_count
|
||||||
|
online = sum(
|
||||||
|
member.status is discord.Status.online for member in guild.members
|
||||||
|
)
|
||||||
|
|
||||||
|
e.add_field(name="Members", value=str(total))
|
||||||
|
e.add_field(name="Bots", value=f"{bots} ({bots / total:.2%})")
|
||||||
|
e.add_field(name="Online", value=f"{online} ({online / total:.2%})")
|
||||||
|
|
||||||
|
if guild.icon:
|
||||||
|
e.set_thumbnail(url=guild.icon_url)
|
||||||
|
|
||||||
|
if guild.me:
|
||||||
|
e.timestamp = guild.me.joined_at
|
||||||
|
|
||||||
|
await self.webhook("guilds").send(embed=e)
|
||||||
|
|
||||||
|
def add_record(self, record: LogRecord):
|
||||||
|
self._gateway_queue.put_nowait(record)
|
||||||
|
|
||||||
|
async def notify_gateway_status(self, record: LogRecord):
|
||||||
|
types = {"INFO": ":information_source:", "WARNING": ":warning:"}
|
||||||
|
|
||||||
|
emoji = types.get(record.levelname, ":heavy_multiplication_x:")
|
||||||
|
dt = datetime.datetime.utcfromtimestamp(record.created)
|
||||||
|
msg = f"{emoji} `[{dt:%Y-%m-%d %H:%M:%S}] {record.message}`"
|
||||||
|
await self.webhook("gateway").send(msg)
|
||||||
|
|
||||||
|
def clear_gateway_data(self):
|
||||||
one_week_ago = datetime.datetime.utcnow() - datetime.timedelta(days=7)
|
one_week_ago = datetime.datetime.utcnow() - datetime.timedelta(days=7)
|
||||||
to_remove = [
|
to_remove = [
|
||||||
index
|
index
|
||||||
|
@ -81,11 +145,6 @@ class Logs(commands.Cog, name="Logs"):
|
||||||
for index in reversed(to_remove):
|
for index in reversed(to_remove):
|
||||||
del dates[index]
|
del dates[index]
|
||||||
|
|
||||||
@tasks.loop(seconds=0.0)
|
|
||||||
async def gateway_worker(self):
|
|
||||||
record = await self._gateway_queue.get()
|
|
||||||
await self.notify_gateway_status(record)
|
|
||||||
|
|
||||||
async def register_command(self, ctx: ContextPlus):
|
async def register_command(self, ctx: ContextPlus):
|
||||||
if ctx.command is None:
|
if ctx.command is None:
|
||||||
return
|
return
|
||||||
|
@ -120,6 +179,14 @@ class Logs(commands.Cog, name="Logs"):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# =========================================================================
|
||||||
|
# =========================================================================
|
||||||
|
|
||||||
|
@tasks.loop(seconds=0.0)
|
||||||
|
async def gateway_worker(self):
|
||||||
|
record = await self._gateway_queue.get()
|
||||||
|
await self.notify_gateway_status(record)
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
async def on_command_completion(self, ctx: ContextPlus):
|
async def on_command_completion(self, ctx: ContextPlus):
|
||||||
await self.register_command(ctx)
|
await self.register_command(ctx)
|
||||||
|
@ -128,57 +195,6 @@ class Logs(commands.Cog, name="Logs"):
|
||||||
async def on_socket_response(self, msg):
|
async def on_socket_response(self, msg):
|
||||||
self.bot.stats["socket"][msg.get("t")] += 1
|
self.bot.stats["socket"][msg.get("t")] += 1
|
||||||
|
|
||||||
def webhook(self, log_type):
|
|
||||||
webhook = discord.Webhook.from_url(
|
|
||||||
getattr(self.__config, log_type),
|
|
||||||
adapter=discord.AsyncWebhookAdapter(self.bot.session),
|
|
||||||
)
|
|
||||||
return webhook
|
|
||||||
|
|
||||||
async def log_error(self, *, ctx: ContextPlus = None, extra=None):
|
|
||||||
e = discord.Embed(title="Error", colour=0xDD5F53)
|
|
||||||
e.description = f"```py\n{traceback.format_exc()}\n```"
|
|
||||||
e.add_field(name="Extra", value=extra, inline=False)
|
|
||||||
e.timestamp = datetime.datetime.utcnow()
|
|
||||||
|
|
||||||
if ctx is not None:
|
|
||||||
fmt = "{0} (ID: {0.id})"
|
|
||||||
author = fmt.format(ctx.author)
|
|
||||||
channel = fmt.format(ctx.channel)
|
|
||||||
guild = "None" if ctx.guild is None else fmt.format(ctx.guild)
|
|
||||||
|
|
||||||
e.add_field(name="Author", value=author)
|
|
||||||
e.add_field(name="Channel", value=channel)
|
|
||||||
e.add_field(name="Guild", value=guild)
|
|
||||||
|
|
||||||
await self.webhook("errors").send(embed=e)
|
|
||||||
|
|
||||||
async def send_guild_stats(self, e, guild):
|
|
||||||
e.add_field(name="Name", value=guild.name)
|
|
||||||
e.add_field(name="ID", value=guild.id)
|
|
||||||
e.add_field(name="Shard ID", value=guild.shard_id or "N/A")
|
|
||||||
e.add_field(
|
|
||||||
name="Owner", value=f"{guild.owner} (ID: {guild.owner.id})"
|
|
||||||
)
|
|
||||||
|
|
||||||
bots = sum(member.bot for member in guild.members)
|
|
||||||
total = guild.member_count
|
|
||||||
online = sum(
|
|
||||||
member.status is discord.Status.online for member in guild.members
|
|
||||||
)
|
|
||||||
|
|
||||||
e.add_field(name="Members", value=str(total))
|
|
||||||
e.add_field(name="Bots", value=f"{bots} ({bots / total:.2%})")
|
|
||||||
e.add_field(name="Online", value=f"{online} ({online / total:.2%})")
|
|
||||||
|
|
||||||
if guild.icon:
|
|
||||||
e.set_thumbnail(url=guild.icon_url)
|
|
||||||
|
|
||||||
if guild.me:
|
|
||||||
e.timestamp = guild.me.joined_at
|
|
||||||
|
|
||||||
await self.webhook("guilds").send(embed=e)
|
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
async def on_guild_join(self, guild: discord.guild):
|
async def on_guild_join(self, guild: discord.guild):
|
||||||
e = discord.Embed(colour=0x53DDA4, title="New Guild") # green colour
|
e = discord.Embed(colour=0x53DDA4, title="New Guild") # green colour
|
||||||
|
@ -204,7 +220,9 @@ class Logs(commands.Cog, name="Logs"):
|
||||||
await self.webhook("dm").send(embed=e)
|
await self.webhook("dm").send(embed=e)
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
async def on_command_error(self, ctx: ContextPlus, error):
|
async def on_command_error(
|
||||||
|
self, ctx: ContextPlus, error: commands.CommandError
|
||||||
|
):
|
||||||
await self.register_command(ctx)
|
await self.register_command(ctx)
|
||||||
if not isinstance(
|
if not isinstance(
|
||||||
error, (commands.CommandInvokeError, commands.ConversionError)
|
error, (commands.CommandInvokeError, commands.ConversionError)
|
||||||
|
@ -215,6 +233,11 @@ class Logs(commands.Cog, name="Logs"):
|
||||||
if isinstance(error, (discord.Forbidden, discord.NotFound)):
|
if isinstance(error, (discord.Forbidden, discord.NotFound)):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
sentry_sdk.capture_exception(error)
|
||||||
|
self.bot.console.log(
|
||||||
|
"Command Error, check sentry or discord error channel"
|
||||||
|
)
|
||||||
|
|
||||||
e = discord.Embed(title="Command Error", colour=0xCC3366)
|
e = discord.Embed(title="Command Error", colour=0xCC3366)
|
||||||
e.add_field(name="Name", value=ctx.command.qualified_name)
|
e.add_field(name="Name", value=ctx.command.qualified_name)
|
||||||
e.add_field(name="Author", value=f"{ctx.author} (ID: {ctx.author.id})")
|
e.add_field(name="Author", value=f"{ctx.author} (ID: {ctx.author.id})")
|
||||||
|
@ -251,18 +274,10 @@ class Logs(commands.Cog, name="Logs"):
|
||||||
else:
|
else:
|
||||||
self._resumes.append(datetime.datetime.utcnow())
|
self._resumes.append(datetime.datetime.utcnow())
|
||||||
|
|
||||||
self._clear_gateway_data()
|
self.clear_gateway_data()
|
||||||
|
|
||||||
def add_record(self, record: LogRecord):
|
# =========================================================================
|
||||||
self._gateway_queue.put_nowait(record)
|
# =========================================================================
|
||||||
|
|
||||||
async def notify_gateway_status(self, record: LogRecord):
|
|
||||||
types = {"INFO": ":information_source:", "WARNING": ":warning:"}
|
|
||||||
|
|
||||||
emoji = types.get(record.levelname, ":heavy_multiplication_x:")
|
|
||||||
dt = datetime.datetime.utcfromtimestamp(record.created)
|
|
||||||
msg = f"{emoji} `[{dt:%Y-%m-%d %H:%M:%S}] {record.message}`"
|
|
||||||
await self.webhook("gateway").send(msg)
|
|
||||||
|
|
||||||
@command_extra(name="commandstats", hidden=True, deletable=True)
|
@command_extra(name="commandstats", hidden=True, deletable=True)
|
||||||
@commands.is_owner()
|
@commands.is_owner()
|
||||||
|
@ -318,27 +333,3 @@ class Logs(commands.Cog, name="Logs"):
|
||||||
datetime.datetime.now() - self.bot.uptime
|
datetime.datetime.now() - self.bot.uptime
|
||||||
)
|
)
|
||||||
await ctx.send(f"Uptime: **{uptime}**")
|
await ctx.send(f"Uptime: **{uptime}**")
|
||||||
|
|
||||||
|
|
||||||
async def on_error(self, event, *args):
|
|
||||||
e = discord.Embed(title="Event Error", colour=0xA32952)
|
|
||||||
e.add_field(name="Event", value=event)
|
|
||||||
e.description = f"```py\n{traceback.format_exc()}\n```"
|
|
||||||
e.timestamp = datetime.datetime.utcnow()
|
|
||||||
|
|
||||||
args_str = ["```py"]
|
|
||||||
for index, arg in enumerate(args):
|
|
||||||
args_str.append(f"[{index}]: {arg!r}")
|
|
||||||
args_str.append("```")
|
|
||||||
e.add_field(name="Args", value="\n".join(args_str), inline=False)
|
|
||||||
|
|
||||||
hook = self.get_cog("Logs").webhook("errors")
|
|
||||||
try:
|
|
||||||
await hook.send(embed=e)
|
|
||||||
except (
|
|
||||||
discord.HTTPException,
|
|
||||||
discord.NotFound,
|
|
||||||
discord.Forbidden,
|
|
||||||
discord.InvalidArgument,
|
|
||||||
):
|
|
||||||
pass
|
|
||||||
|
|
|
@ -10,9 +10,8 @@ import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from rich import box
|
from rich import box
|
||||||
from rich.columns import Columns
|
from rich.columns import Columns
|
||||||
from rich.console import Console
|
|
||||||
from rich.panel import Panel
|
from rich.panel import Panel
|
||||||
from rich.progress import Progress, TextColumn, BarColumn
|
from rich.progress import Progress
|
||||||
from rich.table import Table
|
from rich.table import Table
|
||||||
from tortoise import Tortoise
|
from tortoise import Tortoise
|
||||||
|
|
||||||
|
@ -22,7 +21,10 @@ from tuxbot.core.utils.data_manager import (
|
||||||
data_path,
|
data_path,
|
||||||
config_dir,
|
config_dir,
|
||||||
)
|
)
|
||||||
from .config import (
|
from tuxbot.core.utils.functions.extra import ContextPlus
|
||||||
|
from tuxbot.core.utils.functions.prefix import get_prefixes
|
||||||
|
from tuxbot.core.utils.console import console
|
||||||
|
from tuxbot.core.config import (
|
||||||
Config,
|
Config,
|
||||||
ConfigFile,
|
ConfigFile,
|
||||||
search_for,
|
search_for,
|
||||||
|
@ -31,17 +33,14 @@ from .config import (
|
||||||
)
|
)
|
||||||
from . import __version__, ExitCodes
|
from . import __version__, ExitCodes
|
||||||
from . import exceptions
|
from . import exceptions
|
||||||
from .utils.functions.extra import ContextPlus
|
|
||||||
from .utils.functions.prefix import get_prefixes
|
|
||||||
|
|
||||||
log = logging.getLogger("tuxbot")
|
log = logging.getLogger("tuxbot")
|
||||||
console = Console()
|
|
||||||
|
|
||||||
packages: List[str] = [
|
packages: List[str] = [
|
||||||
"jishaku",
|
"jishaku",
|
||||||
"tuxbot.cogs.Admin",
|
"tuxbot.cogs.Admin",
|
||||||
"tuxbot.cogs.Logs",
|
"tuxbot.cogs.Logs",
|
||||||
# "tuxbot.cogs.Dev",
|
"tuxbot.cogs.Dev",
|
||||||
"tuxbot.cogs.Utils",
|
"tuxbot.cogs.Utils",
|
||||||
"tuxbot.cogs.Polls",
|
"tuxbot.cogs.Polls",
|
||||||
"tuxbot.cogs.Custom",
|
"tuxbot.cogs.Custom",
|
||||||
|
@ -51,13 +50,7 @@ packages: List[str] = [
|
||||||
|
|
||||||
class Tux(commands.AutoShardedBot):
|
class Tux(commands.AutoShardedBot):
|
||||||
_loading: asyncio.Task
|
_loading: asyncio.Task
|
||||||
_progress = {
|
_progress = {"tasks": {}, "main": Progress()}
|
||||||
"main": Progress(
|
|
||||||
TextColumn("[bold blue]{task.fields[task_name]}", justify="right"),
|
|
||||||
BarColumn(),
|
|
||||||
),
|
|
||||||
"tasks": {},
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, *args, cli_flags=None, **kwargs):
|
def __init__(self, *args, cli_flags=None, **kwargs):
|
||||||
# by default, if the bot shutdown without any intervention,
|
# by default, if the bot shutdown without any intervention,
|
||||||
|
@ -162,20 +155,19 @@ class Tux(commands.AutoShardedBot):
|
||||||
last_run=datetime.datetime.timestamp(self.uptime),
|
last_run=datetime.datetime.timestamp(self.uptime),
|
||||||
)
|
)
|
||||||
|
|
||||||
self._progress["main"].stop_task(self._progress["tasks"]["connecting"])
|
with self._progress["main"] as progress:
|
||||||
self._progress["main"].remove_task(
|
progress.stop_task(self._progress["tasks"]["discord_connecting"])
|
||||||
self._progress["tasks"]["connecting"]
|
progress.remove_task(self._progress["tasks"]["discord_connecting"])
|
||||||
)
|
self._progress["tasks"].pop("discord_connecting")
|
||||||
self._progress["tasks"].pop("connecting")
|
self.console.clear()
|
||||||
console.clear()
|
|
||||||
|
|
||||||
console.print(
|
self.console.print(
|
||||||
Panel(f"[bold blue]Tuxbot V{version_info.major}", style="blue"),
|
Panel(f"[bold blue]Tuxbot V{version_info.major}", style="blue"),
|
||||||
justify="center",
|
justify="center",
|
||||||
)
|
)
|
||||||
console.print()
|
self.console.print()
|
||||||
|
|
||||||
columns = Columns(expand=True, align="center")
|
columns = Columns(align="center", expand=True)
|
||||||
|
|
||||||
table = Table(style="dim", border_style="not dim", box=box.HEAVY_HEAD)
|
table = Table(style="dim", border_style="not dim", box=box.HEAVY_HEAD)
|
||||||
table.add_column(
|
table.add_column(
|
||||||
|
@ -204,8 +196,8 @@ class Tux(commands.AutoShardedBot):
|
||||||
table.add_row(status)
|
table.add_row(status)
|
||||||
columns.add_renderable(table)
|
columns.add_renderable(table)
|
||||||
|
|
||||||
console.print(columns)
|
self.console.print(columns)
|
||||||
console.print()
|
self.console.print()
|
||||||
|
|
||||||
async def is_owner(
|
async def is_owner(
|
||||||
self, user: Union[discord.User, discord.Member]
|
self, user: Union[discord.User, discord.Member]
|
||||||
|
@ -278,29 +270,24 @@ class Tux(commands.AutoShardedBot):
|
||||||
await self.process_commands(message)
|
await self.process_commands(message)
|
||||||
|
|
||||||
async def start(self, token, bot): # pylint: disable=arguments-differ
|
async def start(self, token, bot): # pylint: disable=arguments-differ
|
||||||
"""Connect to Discord and start all connections.
|
"""Connect to Discord and start all connections."""
|
||||||
|
with Progress() as progress:
|
||||||
Todo: add postgresql connect here
|
task = progress.add_task(
|
||||||
"""
|
"Connecting to PostgreSQL...", total=len(self.extensions)
|
||||||
with self._progress.get("main") as progress:
|
|
||||||
task_id = self._progress.get("tasks")[
|
|
||||||
"connecting"
|
|
||||||
] = progress.add_task(
|
|
||||||
"connecting",
|
|
||||||
task_name="Connecting to PostgreSQL...",
|
|
||||||
start=False,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
models = []
|
models = []
|
||||||
|
|
||||||
for extension, _ in self.extensions.items():
|
for extension, _ in self.extensions.items():
|
||||||
if extension == "jishaku":
|
if extension == "jishaku":
|
||||||
|
progress.advance(task)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if importlib.import_module(extension).HAS_MODELS:
|
if importlib.import_module(extension).HAS_MODELS:
|
||||||
models.append(f"{extension}.models.__init__")
|
models.append(f"{extension}.models.__init__")
|
||||||
|
|
||||||
progress.update(task_id)
|
progress.advance(task)
|
||||||
|
|
||||||
await Tortoise.init(
|
await Tortoise.init(
|
||||||
db_url="postgres://{}:{}@{}:{}/{}".format(
|
db_url="postgres://{}:{}@{}:{}/{}".format(
|
||||||
self.config.Core.Database.username,
|
self.config.Core.Database.username,
|
||||||
|
@ -313,17 +300,13 @@ class Tux(commands.AutoShardedBot):
|
||||||
)
|
)
|
||||||
await Tortoise.generate_schemas()
|
await Tortoise.generate_schemas()
|
||||||
|
|
||||||
self._progress["main"].stop_task(self._progress["tasks"]["connecting"])
|
with self._progress["main"] as progress:
|
||||||
self._progress["main"].remove_task(
|
task_id = self._progress["tasks"][
|
||||||
self._progress["tasks"]["connecting"]
|
"discord_connecting"
|
||||||
)
|
|
||||||
self._progress["tasks"].pop("connecting")
|
|
||||||
|
|
||||||
with self._progress.get("main") as progress:
|
|
||||||
task_id = self._progress.get("tasks")[
|
|
||||||
"connecting"
|
|
||||||
] = progress.add_task(
|
] = progress.add_task(
|
||||||
"connecting", task_name="Connecting to Discord...", start=False
|
"discord_connecting",
|
||||||
|
task_name="Connecting to Discord...",
|
||||||
|
start=False,
|
||||||
)
|
)
|
||||||
progress.update(task_id)
|
progress.update(task_id)
|
||||||
await super().start(token, bot=bot)
|
await super().start(token, bot=bot)
|
||||||
|
@ -341,21 +324,22 @@ class Tux(commands.AutoShardedBot):
|
||||||
active=False,
|
active=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
for task in self._progress["tasks"]:
|
with self._progress["main"] as progress:
|
||||||
self._progress["main"].log("Shutting down", task)
|
for task in self._progress["tasks"]:
|
||||||
|
progress.log("Shutting down", task)
|
||||||
|
|
||||||
self._progress["main"].stop_task(self._progress["tasks"][task])
|
progress.stop_task(self._progress["tasks"][task])
|
||||||
self._progress["main"].remove_task(
|
progress.remove_task(self._progress["tasks"][task])
|
||||||
self._progress["tasks"]["connecting"]
|
progress.stop()
|
||||||
)
|
|
||||||
self._progress["main"].stop()
|
|
||||||
|
|
||||||
pending = [
|
pending = [
|
||||||
t for t in asyncio.all_tasks() if t is not asyncio.current_task()
|
t for t in asyncio.all_tasks() if t is not asyncio.current_task()
|
||||||
]
|
]
|
||||||
|
|
||||||
for task in pending:
|
for task in pending:
|
||||||
console.log("Canceling", task.get_name(), f"({task.get_coro()})")
|
self.console.log(
|
||||||
|
"Canceling", task.get_name(), f"({task.get_coro()})"
|
||||||
|
)
|
||||||
task.cancel()
|
task.cancel()
|
||||||
await asyncio.gather(*pending, return_exceptions=False)
|
await asyncio.gather(*pending, return_exceptions=False)
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,6 @@ import discord
|
||||||
from discord import Embed
|
from discord import Embed
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
|
||||||
from rich.console import Console
|
|
||||||
|
|
||||||
console = Console()
|
|
||||||
|
|
||||||
TOKEN_REPLACEMENT = "■" * random.randint(3, 15)
|
TOKEN_REPLACEMENT = "■" * random.randint(3, 15)
|
||||||
PASSWORD_REPLACEMENT = "■" * random.randint(3, 15)
|
PASSWORD_REPLACEMENT = "■" * random.randint(3, 15)
|
||||||
IP_REPLACEMENT = "■" * random.randint(3, 15)
|
IP_REPLACEMENT = "■" * random.randint(3, 15)
|
||||||
|
@ -130,6 +126,11 @@ class ContextPlus(commands.Context):
|
||||||
def session(self) -> aiohttp.ClientSession:
|
def session(self) -> aiohttp.ClientSession:
|
||||||
return self.bot.session
|
return self.bot.session
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
items = ("%s = %r" % (k, v) for k, v in self.__dict__.items())
|
||||||
|
|
||||||
|
return "<%s: {%s}>" % (self.__class__.__name__, ", ".join(items))
|
||||||
|
|
||||||
|
|
||||||
class CommandPLus(commands.Command):
|
class CommandPLus(commands.Command):
|
||||||
def __init__(self, function, **kwargs):
|
def __init__(self, function, **kwargs):
|
||||||
|
|
Loading…
Reference in a new issue