Will 0979231435
[V3 Sentry] Modify error logging cases (#1193)
* Fix duplicate on_

* Make better use of sentry error handler
2017-12-17 21:21:41 -05:00

227 lines
7.9 KiB
Python

import sys
import codecs
import datetime
import logging
import pkg_resources
import traceback
from pkg_resources import DistributionNotFound
import discord
from discord.ext import commands
from . import __version__
from .data_manager import storage_type
from .utils.chat_formatting import inline, bordered
from .rpc import initialize
from colorama import Fore, Style, init
log = logging.getLogger("red")
sentry_log = logging.getLogger("red.sentry")
init()
INTRO = """
______ _ ______ _ _ ______ _
| ___ \ | | | _ (_) | | | ___ \ | |
| |_/ /___ __| | ______ | | | |_ ___ ___ ___ _ __ __| | | |_/ / ___ | |_
| // _ \/ _` | |______| | | | | / __|/ __/ _ \| '__/ _` | | ___ \/ _ \| __|
| |\ \ __/ (_| | | |/ /| \__ \ (_| (_) | | | (_| | | |_/ / (_) | |_
\_| \_\___|\__,_| |___/ |_|___/\___\___/|_| \__,_| \____/ \___/ \__|
"""
def init_events(bot, cli_flags):
@bot.event
async def on_connect():
if bot.uptime is None:
print("Connected to Discord. Getting ready...")
@bot.event
async def on_ready():
if bot.uptime is not None:
return
bot.uptime = datetime.datetime.utcnow()
if cli_flags.no_cogs is False:
print("Loading packages...")
failed = []
packages = await bot.db.packages()
for package in packages:
try:
spec = await bot.cog_mgr.find_cog(package)
bot.load_extension(spec)
except Exception as e:
log.exception("Failed to load package {}".format(package),
exc_info=e)
await bot.remove_loaded_package(package)
if packages:
print("Loaded packages: " + ", ".join(packages))
guilds = len(bot.guilds)
users = len(set([m for m in bot.get_all_members()]))
try:
data = await bot.application_info()
invite_url = discord.utils.oauth_url(data.id)
except:
if bot.user.bot:
invite_url = "Could not fetch invite url"
else:
invite_url = None
prefixes = await bot.db.prefix()
lang = await bot.db.locale()
red_version = __version__
red_pkg = pkg_resources.get_distribution("Red-DiscordBot")
dpy_version = discord.__version__
INFO = [str(bot.user), "Prefixes: {}".format(', '.join(prefixes)),
'Language: {}'.format(lang),
"Red Bot Version: {}".format(red_version),
"Discord.py Version: {}".format(dpy_version),
"Shards: {}".format(bot.shard_count)]
if guilds:
INFO.extend(("Servers: {}".format(guilds), "Users: {}".format(users)))
else:
print("Ready. I'm not in any server yet!")
INFO.append('{} cogs with {} commands'.format(len(bot.cogs), len(bot.commands)))
INFO2 = []
sentry = await bot.db.enable_sentry()
mongo_enabled = storage_type() != "JSON"
reqs_installed = {
"voice": None,
"docs": None,
"test": None
}
for key in reqs_installed.keys():
reqs = [x.name for x in red_pkg._dep_map[key]]
try:
pkg_resources.require(reqs)
except DistributionNotFound:
reqs_installed[key] = False
else:
reqs_installed[key] = True
options = (
("Error Reporting", sentry),
("MongoDB", mongo_enabled),
("Voice", reqs_installed["voice"]),
("Docs", reqs_installed["docs"]),
("Tests", reqs_installed["test"])
)
on_symbol, off_symbol = _get_settings_symbols()
for option, enabled in options:
enabled = on_symbol if enabled else off_symbol
INFO2.append("{} {}".format(enabled, option))
print(Fore.RED + INTRO)
print(Style.RESET_ALL)
print(bordered(INFO, INFO2))
if invite_url:
print("\nInvite URL: {}\n".format(invite_url))
if bot.rpc_enabled:
await initialize(bot)
@bot.event
async def on_error(event_method, *args, **kwargs):
sentry_log.exception("Exception in {}".format(event_method))
@bot.event
async def on_command_error(ctx, error):
if isinstance(error, commands.MissingRequiredArgument):
await ctx.send_help()
elif isinstance(error, commands.BadArgument):
await ctx.send_help()
elif isinstance(error, commands.DisabledCommand):
await ctx.send("That command is disabled.")
elif isinstance(error, commands.CommandInvokeError):
# Need to test if the following still works
"""
no_dms = "Cannot send messages to this user"
is_help_cmd = ctx.command.qualified_name == "help"
is_forbidden = isinstance(error.original, discord.Forbidden)
if is_help_cmd and is_forbidden and error.original.text == no_dms:
msg = ("I couldn't send the help message to you in DM. Either"
" you blocked me or you disabled DMs in this server.")
await ctx.send(msg)
return
"""
log.exception("Exception in command '{}'"
"".format(ctx.command.qualified_name),
exc_info=error.original)
sentry_log.exception("Exception in command '{}'"
"".format(ctx.command.qualified_name),
exc_info=error.original)
message = ("Error in command '{}'. Check your console or "
"logs for details."
"".format(ctx.command.qualified_name))
exception_log = ("Exception in command '{}'\n"
"".format(ctx.command.qualified_name))
exception_log += "".join(traceback.format_exception(type(error),
error, error.__traceback__))
bot._last_exception = exception_log
await ctx.send(inline(message))
elif isinstance(error, commands.CommandNotFound):
pass
elif isinstance(error, commands.CheckFailure):
await ctx.send("⛔ You are not authorized to issue that command.")
elif isinstance(error, commands.NoPrivateMessage):
await ctx.send("That command is not available in DMs.")
elif isinstance(error, commands.CommandOnCooldown):
await ctx.send("This command is on cooldown. "
"Try again in {:.2f}s"
"".format(error.retry_after))
else:
log.exception(type(error).__name__, exc_info=error)
try:
sentry_error = error.original
except AttributeError:
sentry_error = error
sentry_log.exception("Unhandled command error.",
exc_info=sentry_error)
@bot.event
async def on_message(message):
bot.counter["messages_read"] += 1
await bot.process_commands(message)
@bot.event
async def on_resumed():
bot.counter["sessions_resumed"] += 1
@bot.event
async def on_command(command):
bot.counter["processed_commands"] += 1
def _get_settings_symbols():
"""Get symbols for displaying settings on stdout.
This is so we don't get encoding errors when trying to print unicode
emojis to stdout (particularly with Windows Command Prompt).
"""
encoder = codecs.getencoder(sys.stdout.encoding)
check_mark = "\N{SQUARE ROOT}"
try:
encoder(check_mark)
except UnicodeEncodeError:
on_symbol = "[X]"
off_symbol = "[ ]"
else:
on_symbol = check_mark
off_symbol = "X"
return on_symbol, off_symbol