import asyncio import logging import traceback import threading from enum import Enum import discord from discord.ext import commands from django.conf import settings from django.shortcuts import reverse from minecraft_manager.models import Application, Ticket from minecraft_manager.utils import url_path logger = logging.getLogger(__name__) description = ''' A Discord bot connected to an MCM instance. ''' class Discord(commands.Bot): discord_game = 'MCM' prefix = getattr(settings, 'DISCORD_BOT_PREFIX', '!') auth_roles = getattr(settings, 'DISCORD_BOT_ROLES', []) superuser_roles = getattr(settings, 'DISCORD_SUPERUSER_ROLES', []) error_users = getattr(settings, 'DISCORD_ERROR_USERS', []) def __init__(self, token): super().__init__(command_prefix=self.prefix, description=description, case_insensitive=True, help_command=None, activity=discord.Game(name=self.discord_game)) self.token = token self.load_extension("minecraft_manager.bot.commands") async def on_ready(self): print('Logged in as') print(self.user.name) print(self.user.id) print(discord.__version__) print('Voice Loaded: {0}'.format(discord.opus.is_loaded())) print('OAuth URL: https://discordapp.com/api/oauth2/authorize?client_id={0}&permissions=0&scope=bot'.format(self.user.id)) print('------') print('Logged in as {0} ({1}) with discord.py v{2}'.format(self.user.name, self.user.id, discord.__version__)) channel_id = getattr(settings, 'DISCORD_MCM_CHANNEL', None) if channel_id: channel = self.get_channel(channel_id) embed = discord.Embed(color=8311585) content = "" unanswered_applications = Application.objects.filter(accepted=None) if len(unanswered_applications) > 0: link = url_path(settings.MCM_BASE_LINK, 'dashboard/application') content += "[Unanswered Applications: {}]({})".format(len(unanswered_applications), link) unclaimed_tickets = Ticket.objects.filter(staff=None, resolved=False) if len(unclaimed_tickets) > 0: link = url_path(settings.MCM_BASE_LINK, 'dashboard/ticket') if content: content += "\n\n" content += "[Unclaimed Tickets: {}]({})".format(len(unclaimed_tickets), link) if content: embed.title = "MCM Reminder" embed.description = content await self.discord_message(channel, embed) async def discord_message(self, dest, message): if isinstance(message, discord.Embed): for idx, field in enumerate(message.fields): if not field.value: message.set_field_at(idx, name=field.name, value="N/A") await dest.send(embed=message) else: await dest.send(message) async def on_command_error(self, context, exception): if not isinstance(exception, commands.CommandInvokeError): return if hasattr(exception, "original"): error = ''.join(traceback.format_tb(exception.original.__traceback__)) else: error = exception logger.error(error) for user_id in self.error_users: user = self.get_user(user_id) if user: await self.discord_message(user, '```python\n{}```'.format(error)) def run_bot(self): loop = asyncio.get_event_loop() try: loop.run_until_complete(self.start(self.token)) except KeyboardInterrupt: logger.info("Bot received keyboard interrupt") except Exception as e: print(e) logger.info('Bot encountered the following unhandled exception %s', e) finally: loop.run_until_complete(self.logout()) logger.info("Bot shutting down...")