import discord from io import StringIO from discord.ext import commands from django.contrib.auth.models import User from django.db import close_old_connections from minecraft_manager.api import api from minecraft_manager.bot.utils import get_application from minecraft_manager.models import Application, Player from minecraft_manager.utils import build_application, full_static class Commands(commands.Cog): def __init__(self, bot): self.bot = bot async def cog_before_invoke(self, ctx): # FIX STALE DB CONNECTIONS close_old_connections() async def cog_check(self, ctx): # No DMs if ctx.guild is None: return False # Check roles if not hasattr(ctx.author, "roles"): return False for role in ctx.author.roles: for auth_role in self.bot.auth_roles: if role.id == auth_role: return True return False def is_superuser(self, member: discord.Member): for role in member.roles: for auth_role in self.bot.superuser_roles: if role.id == auth_role: return True return False @commands.command() async def help(self, ctx): embed = discord.Embed(colour=discord.Colour(0x417505)) embed.set_thumbnail( url=full_static('favicon.png')) embed.title = "Minecraft Manager Help" embed.add_field(name="{}app search ".format(self.bot.prefix), value="Search for applications by partial or exact username.") embed.add_field(name="{}app info ".format(self.bot.prefix), value="Get detailed information about a specific application.") embed.add_field(name="{}app accept|deny ".format(self.bot.prefix), value="Take action on an application.") embed.add_field(name="{}demote ".format(self.bot.prefix), value="Demote a player to the role given to accepted applications.") embed.add_field(name="{}compare".format(self.bot.prefix), value="Compare Discord users to the Whitelist.") embed.add_field(name="{}sync".format(self.bot.prefix), value="Sync Discord users with Player records.") await self.bot.discord_message(ctx.message.channel, embed) @commands.group("app", aliases=["application"]) async def app(self, ctx): if ctx.invoked_subcommand is None: await self.bot.discord_message(ctx.message.channel, "No sub-command supplied. Info, Search, Accept, or Deny.") @commands.command() async def info(self, ctx, *args): await self._info(ctx, *args) @app.command("info") async def app_info(self, ctx, *args): await self._info(ctx, *args) async def _info(self, ctx, *args): if len(args) == 0: await self.bot.discord_message(ctx.message.channel, "Info requires an application ID or username.") key = args[0] is_id = True try: int(key) except: is_id = False application = None if is_id: application = get_application(key) if not application: await self.bot.discord_message(ctx.message.channel, "An Application with that ID doesn't exist.") return else: found = False applications = Application.objects.filter(username__icontains=key) if len(applications) == 0: applications = Application.objects.filter(player__username__icontains=key) if len(applications) == 1: await self.bot.discord_message(ctx.message.channel, "**No applications matched, however there is a player match**") application = applications[0] found = True elif len(applications) == 1: application = applications[0] found = True if not found: await self.bot.discord_message(ctx.message.channel, "An exact Application could not be found. Try search instead.") return await self.bot.discord_message(ctx.message.channel, build_application(application)) @commands.command() async def accept(self, ctx, app_id: int): await self._accept(ctx, app_id) @app.command("accept") async def app_accept(self, ctx, app_id: int): await self._accept(ctx, app_id) async def _accept(self, ctx, app_id: int): application = get_application(app_id) if not application: await self.bot.discord_message(ctx.message.channel, "An Application with that ID doesn't exist.") return if not application.accepted: application.accepted = True application.save() if Player.objects.filter(username__iexact=application.username).exists(): player = Player.objects.get(username__iexact=application.username) player.application_id = application.id player.save() await self.bot.discord_message(ctx.message.channel, "App ID **{0}** was successfully accepted.".format(app_id)) if not api.plugin(api.PLUGIN_ACCEPT, application.username): await self.bot.discord_message(ctx.message.channel, "Could not accept in-game, is the server running?") @commands.command() async def deny(self, ctx, app_id: int): await self._deny(ctx, app_id) @app.command("deny") async def app_deny(self, ctx, app_id: int): await self._deny(ctx, app_id) async def _deny(self, ctx, app_id: int): application = get_application(app_id) if not application: await self.bot.discord_message(ctx.message.channel, "An Application with that ID doesn't exist.") return if not application.accepted: application.accepted = False application.save() if Player.objects.filter(username__iexact=application.username).exists(): player = Player.objects.get(username__iexact=application.username) player.application_id = application.id player.save() await self.bot.discord_message(ctx.message.channel, "App ID **{0}** was successfully denied.".format(app_id)) if not api.plugin(api.PLUGIN_DENY, application.username): await self.bot.discord_message(ctx.message.channel, "Could not deny in-game, is the server running?") @commands.command() async def clear(self, ctx, app_id: int): await self._clear(ctx, app_id) @app.command("clear") async def app_clear(self, ctx, app_id: int): await self._clear(ctx, app_id) async def _clear(self, ctx, app_id: int): application = get_application(app_id) if not application: await self.bot.discord_message(ctx.message.channel, "An Application with that ID doesn't exist.") return if not application.accepted: application.accepted = None application.save() if Player.objects.filter(username__iexact=application.username).exists(): player = Player.objects.get(username__iexact=application.username) player.application_id = application.id player.save() await self.bot.discord_message(ctx.message.channel, "App ID **{0}** was successfully cleared.".format(app_id)) @commands.command() async def search(self, ctx, search: str): await self._search(ctx, search) @app.command("search") async def app_search(self, ctx, search: str): await self._search(ctx, search) async def _search(self, ctx, search: str): applications = Application.objects.filter(username__icontains=search)[:10] count = Application.objects.filter(username__icontains=search).count() if count > 0: if count == 1: info = build_application(applications[0]) else: info = "**Found the following applications**" for app in applications: info += "\n{0} - {1} ({2})".format(app.id, app.username.replace("_", "\\_"), app.status) if count > 10: info += "\n**This is only 10 applications out of {0} found. Please narrow your search if possible.**".format(len(applications)) else: players = Player.objects.filter(username__icontains=search, application__isnull=False)[:10] count = Player.objects.filter(username__icontains=search, application__isnull=False).count() if count > 0: if count == 1: await self.bot.discord_message(ctx.message.channel, "**No applications matched, however there is a player match**") info = build_application(players[0].application) else: info = "**No applications matched, however there are player matches**" for player in players: app = player.application info += "\n{0} - {1} AKA {2} ({3})".format(app.id, app.username.replace("_", "\\_"), player.username.replace("_", "\\_"), app.status) if count > 10: info += "\n**This is only 10 players out of {0} found. Please narrow your search if possible.**".format(len(players)) else: info = "No applications matched that search." await self.bot.discord_message(ctx.message.channel, info) @commands.command() async def demote(self, ctx, username: str): if not self.is_superuser(ctx.author): return await ctx.message.delete() if api.plugin(api.PLUGIN_DEMOTE, username): await self.bot.discord_message(ctx.message.channel, "{} has been demoted in-game.".format(username)) else: await self.bot.discord_message(ctx.message.channel, "{} could not be demoted in-game, is the server running?".format(username)) if User.objects.filter(username__iexact=username).exists(): user = User.objects.get(username__iexact=username) user.is_active = False user.save() await self.bot.discord_message(ctx.message.channel, "{} has been de-activated in MCM.".format(username)) else: await self.bot.discord_message(ctx.message.channel, "{} could not be found in MCM, is their account up-to-date?".format(username)) @commands.command() async def compare(self, ctx): await ctx.message.delete() await ctx.message.channel.trigger_typing() no_player = [] no_application = [] for member in ctx.message.guild.members: if member.bot: continue name = member.nick if member.nick else member.name try: Player.objects.get(username__iexact=name) except: no_player.append(name) try: Application.objects.get(username__iexact=name) except: no_player = no_player[:-1] no_application.append(name) if no_player: header = "**The following users have an application match, but no player match on the whitelist:**\n" await self.bot.discord_message(ctx.author, "{}```{}```".format(header, "\n".join(no_player))) if no_application: header = "**The following users do not have an application or player match on the whitelist:**\n" await self.bot.discord_message(ctx.author, "{}```{}```".format(header, "\n".join(no_application))) @commands.command() async def sync(self, ctx): def sync_player(player): for member in ctx.guild.members: name = member.nick if member.nick else member.name if player.username.lower() == name.lower(): player.discord_id = member.id player.save() return True return False # Attempt to sync users need_sync = Player.objects.filter(discord_id__isnull=True) need_sync_count = need_sync.count() synced = [] not_synced = [] if need_sync_count > 0: for ns in need_sync: if sync_player(ns): synced.append(ns.username) else: not_synced.append(ns.username) txt = "Synced\n\t{}\n\nNot Synced\n\t{}".format('\n\t'.join(synced), '\n\t'.join(not_synced)) attach = discord.File(fp=StringIO(txt), filename="sync.txt") await ctx.channel.send(content="Successfully synced {}/{} players.".format(len(synced), need_sync_count), file=attach) def setup(bot): bot.add_cog(Commands(bot))