From 35c88084baba8c16815c11689f92ea4b0437c206 Mon Sep 17 00:00:00 2001 From: Twentysix Date: Sun, 4 Jun 2017 19:37:39 +0200 Subject: [PATCH] [Core] Ported [p]set commands, added [p]set coowners --- core/bot.py | 11 +- core/core_commands.py | 238 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 245 insertions(+), 4 deletions(-) diff --git a/core/bot.py b/core/bot.py index a8157ae52..b389b587d 100644 --- a/core/bot.py +++ b/core/bot.py @@ -15,6 +15,7 @@ class Red(commands.Bot): token=None, prefix=[], packages=[], + owner=None, coowners=[], whitelist=[], blacklist=[], @@ -42,14 +43,16 @@ class Red(commands.Bot): if "command_prefix" not in kwargs: kwargs["command_prefix"] = prefix_manager + if "owner_id" not in kwargs: + kwargs["owner_id"] = self.db.get("owner") + self.counter = Counter() self.uptime = None super().__init__(**kwargs) - async def is_owner(self, user, allow_coowners=True): - if allow_coowners: - if user.id in self.db.coowners(): - return True + async def is_owner(self, user): + if user.id in self.db.coowners(): + return True return await super().is_owner(user) async def send_cmd_help(self, ctx): diff --git a/core/core_commands.py b/core/core_commands.py index 442dcb1d8..ab82d99c1 100644 --- a/core/core_commands.py +++ b/core/core_commands.py @@ -1,12 +1,21 @@ from discord.ext import commands from core import checks +from string import ascii_letters, digits +from random import SystemRandom import logging import importlib import os import discord +import aiohttp +import asyncio log = logging.getLogger("red") +OWNER_DISCLAIMER = ("Setting as owner people who do not have access to " + "the system that is hosting Red is **extremely " + "dangerous**.\n**Owners and co owners are able to access " + "any data that is present on the host system.**") + class Core: """Commands related to core functions""" @@ -73,3 +82,232 @@ class Core: print("Reloading " + path) m = importlib.import_module(path) importlib.reload(m) + + @commands.group(name="set") + async def _set(self, ctx): + """Changes Red's settings""" + if ctx.invoked_subcommand is None: + await ctx.bot.send_cmd_help(ctx) + + @_set.command() + @checks.guildowner() + @commands.guild_only() + async def adminrole(self, ctx, *, role: discord.Role): + """Sets the admin role for this server""" + await ctx.bot.db.guild(ctx.guild).set("admin_role", role.id) + await ctx.send("The admin role for this server has been set.") + + @_set.command() + @checks.guildowner() + @commands.guild_only() + async def modrole(self, ctx, *, role: discord.Role): + """Sets the mod role for this server""" + await ctx.bot.db.guild(ctx.guild).set("mod_role", role.id) + await ctx.send("The mod role for this server has been set.") + + @_set.command() + @checks.is_owner() + async def avatar(self, ctx, url: str): + """Sets Red's avatar""" + session = aiohttp.ClientSession() + async with session.get(url) as r: + data = await r.read() + await session.close() + + try: + await self.bot.user.edit(avatar=data) + except discord.HTTPException: + await ctx.send("Failed. Remember that you can edit my avatar " + "up to two times a hour. The URL must be a " + "direct link to a JPG / PNG.") + except discord.InvalidArgument: + await ctx.send("JPG / PNG format only.") + else: + await ctx.send("Done.") + + @_set.command(name="game") + @checks.is_owner() + @commands.guild_only() + async def _game(self, ctx, *, game: str): + """Sets Red's playing status""" + status = ctx.me.status + game = discord.Game(name=game) + await ctx.bot.change_presence(status=status, game=game) + await ctx.send("Game set.") + + @_set.command() + @checks.is_owner() + @commands.guild_only() + async def status(self, ctx, *, status: str): + """Sets Red's status + Available statuses: + online + idle + dnd + invisible""" + + statuses = { + "online" : discord.Status.online, + "idle" : discord.Status.idle, + "dnd" : discord.Status.dnd, + "invisible" : discord.Status.invisible + } + + game = ctx.me.game + + try: + status = statuses[status.lower()] + except KeyError: + await ctx.bot.send_cmd_help(ctx) + else: + await ctx.bot.change_presence(status=status, + game=game) + await ctx.send("Status changed to %s." % status) + + @_set.command() + @checks.is_owner() + @commands.guild_only() + async def stream(self, ctx, streamer=None, *, stream_title=None): + """Sets Red's streaming status + Leaving both streamer and stream_title empty will clear it.""" + + status = ctx.me.status + + if stream_title: + stream_title = stream_title.strip() + if "twitch.tv/" not in streamer: + streamer = "https://www.twitch.tv/" + streamer + game = discord.Game(type=1, url=streamer, name=stream_title) + await ctx.bot.change_presence(game=game, status=status) + elif streamer is not None: + await ctx.bot.send_cmd_help(ctx) + return + else: + await ctx.bot.change_presence(game=None, status=status) + await ctx.send("Done.") + + @_set.command(name="username", aliases=["name"]) + @checks.is_owner() + async def _username(self, ctx, *, username: str): + """Sets Red's username""" + try: + await ctx.bot.user.edit(username=username) + except discord.HTTPException: + await ctx.send("Failed to change name. Remember that you can " + "only do it up to 2 times an hour. Use " + "nicknames if you need frequent changes. " + "`{}set nickname`".format(ctx.prefix)) + else: + await ctx.send("Done.") + + @_set.command(name="nickname") + @checks.admin() + @commands.guild_only() + async def _nickname(self, ctx, *, nickname: str): + """Sets Red's nickname""" + try: + await ctx.bot.user.edit(nick=nickname) + except discord.Forbidden: + await ctx.send("I lack the permissions to change my own " + "nickname.") + else: + await ctx.send("Done.") + + @_set.command(aliases=["prefixes"]) + @checks.is_owner() + async def prefix(self, ctx, *prefixes): + """Sets Red's global prefix(es)""" + if not prefixes: + await ctx.bot.send_cmd_help(ctx) + return + prefixes = sorted(prefixes, reverse=True) + await ctx.bot.db.set("prefix", prefixes) + await ctx.send("Prefix set.") + + @_set.command(aliases=["serverprefixes"]) + @checks.admin() + @commands.guild_only() + async def serverprefix(self, ctx, *prefixes): + """Sets Red's server prefix(es)""" + if not prefixes: + await ctx.bot.db.guild(ctx.guild).set("prefix", []) + await ctx.send("Server prefixes have been reset.") + return + prefixes = sorted(prefixes, reverse=True) + await ctx.bot.db.guild(ctx.guild).set("prefix", prefixes) + await ctx.send("Prefix set.") + + @_set.command() + @commands.cooldown(1, 60 * 10, commands.BucketType.default) + async def owner(self, ctx): + """Sets Red's main owner""" + def check(m): + return m.author == ctx.author and m.channel == ctx.channel + + # According to the Python docs this is suitable for cryptographic use + random = SystemRandom() + length = random.randint(25, 35) + chars = ascii_letters + digits + token = "" + + for i in range(length): + token += random.choice(chars) + log.info("{0} ({0.id}) requested to be set as owner.") + print("\nVerification token:") + print(token) + + await ctx.send("Remember:\n" + OWNER_DISCLAIMER) + await asyncio.sleep(5) + + await ctx.send("I have printed a one-time token in the console. " + "Copy and paste it here to confirm you are the owner.") + + try: + message = await ctx.bot.wait_for("message", check=check, + timeout=60) + except asyncio.TimeoutError: + self.owner.reset_cooldown(ctx) + await ctx.send("The set owner request has timed out.") + else: + if message.content.strip() == token: + self.owner.reset_cooldown(ctx) + await self.bot.db.set("owner", ctx.author.id) + ctx.bot.author_id = ctx.author.id + await ctx.send("You have been set as owner.") + else: + await ctx.send("Invalid token.") + + @_set.command(aliases=["coowners"]) + @checks.is_owner() + @commands.guild_only() + async def coowner(self, ctx, *coowners: discord.Member): + """Sets Red's coowner(s) + + Leave empty to reset""" + def check(m): + return m.author == ctx.author and m.channel == ctx.channel + + coowners = [m.id for m in coowners] + + if not coowners: + await ctx.bot.db.set("coowners", []) + await ctx.send("Coowners list cleared.") + return + + await ctx.send("Remember:\n" + OWNER_DISCLAIMER) + await asyncio.sleep(5) + + await ctx.send("Type `I understand` if you have read and understand " + "the above message.") + + try: + message = await ctx.bot.wait_for("message", check=check, + timeout=60) + except asyncio.TimeoutError: + await ctx.send("The set owner request has timed out.") + else: + if message.content.lower().strip() == "i understand": + await ctx.bot.db.set("coowners", coowners) + await ctx.send("{} coowner(s) set.".format(len(coowners))) + else: + await ctx.send("Set coowner request aborted.") \ No newline at end of file