diff options
| -rw-r--r-- | bot/__main__.py | 14 | ||||
| -rw-r--r-- | bot/cogs/__init__.py | 3 | ||||
| -rw-r--r-- | bot/cogs/bot.py | 62 | ||||
| -rw-r--r-- | bot/cogs/eval.py | 77 | ||||
| -rw-r--r-- | bot/cogs/logging.py | 32 | ||||
| -rw-r--r-- | bot/interpreter.py | 42 |
6 files changed, 221 insertions, 9 deletions
diff --git a/bot/__main__.py b/bot/__main__.py index cb4157707..1579b6bd3 100644 --- a/bot/__main__.py +++ b/bot/__main__.py @@ -1,15 +1,11 @@ -#!/usr/bin/env python3 - -import discord -from discord.ext import commands +# coding=utf-8 import os -bot = commands.Bot(command_prefix=commands.when_mentioned_or( - os.environ.get("BOT_PREFIX"))) +from discord.ext.commands import AutoShardedBot, when_mentioned_or +bot = AutoShardedBot(command_prefix=when_mentioned_or(">>>", ">>> ")) -async def ping(ctx): - await ctx.send("Pong.") +bot.load_extension("bot.cogs.logging") +bot.load_extension("bot.cogs.bot") bot.run(os.environ.get("BOT_TOKEN")) diff --git a/bot/cogs/__init__.py b/bot/cogs/__init__.py new file mode 100644 index 000000000..ba286add3 --- /dev/null +++ b/bot/cogs/__init__.py @@ -0,0 +1,3 @@ +# coding=utf-8 + +__author__ = "Gareth Coles" diff --git a/bot/cogs/bot.py b/bot/cogs/bot.py new file mode 100644 index 000000000..435051fdf --- /dev/null +++ b/bot/cogs/bot.py @@ -0,0 +1,62 @@ +# coding=utf-8 +from discord import Embed +from discord.ext.commands import AutoShardedBot, group, Context + +__author__ = "Gareth Coles" + + +class Bot: + """ + Bot information commands + """ + + def __init__(self, bot: AutoShardedBot): + self.bot = bot + + @group(invoke_without_command=True, name="bot") + async def bot_group(self, ctx: Context): + """ + Bot information commands + """ + + await ctx.invoke(self.bot.get_command("help"), "bot") + + @bot_group.command(aliases=["about"]) + async def info(self, ctx: Context): + """ + Get information about the current bot + """ + + if not hasattr(self.bot, "shard_id") or self.bot.shard_id is None: + embed = Embed( + description="A utility bot designed just for the Python server!. Try `>>> help` for more info.\n\n" + "**Currently __not sharded__.**", + url="https://github.com/discord-python/bot" + ) + else: + embed = Embed( + description="A utility bot designed just for the Python server! Try `>>> help` for more info.", + url="https://github.com/discord-python/bot" + ) + embed.add_field( + name="Total Shards", value=self.bot.shard_count + ) + embed.add_field( + name="Current Shard", value=self.bot.shard_id + ) + + embed.add_field(name="Visible Guilds", value=str(len(self.bot.guilds))) + embed.add_field(name="Visible Users", value=str(len(self.bot.users))) + + embed.set_author( + name="Python Bot", + url="https://github.com/discord-python/bot", + icon_url="https://avatars3.githubusercontent.com/u/36101493?s=200&v=4" + ) + + await ctx.send(embed=embed) + + +def setup(bot): + bot.add_cog(Bot(bot)) + print("Cog loaded: Bot") diff --git a/bot/cogs/eval.py b/bot/cogs/eval.py new file mode 100644 index 000000000..82d55bbc3 --- /dev/null +++ b/bot/cogs/eval.py @@ -0,0 +1,77 @@ +# coding=utf-8 +from discord.ext.commands import AutoShardedBot, Context, is_owner, command +from io import StringIO + +from bot.interpreter import Interpreter + +__author__ = "Gareth Coles" + + +class Eval: + """ + Bot owner only: Evaluate Python code + """ + + def __init__(self, bot: AutoShardedBot): + self.bot = bot + self.interpreter = Interpreter(bot) + + @command() + @is_owner() + async def eval(self, ctx: Context, *, string: str): + """ + Bot owner only: Evaluate Python code + + Your code may be surrounded in a code fence, but it's not required. + Scope will be preserved - variables set will be present later on. + """ + + code = string.strip() + + if code.startswith("```") and code.endswith("```"): + if code.startswith("```python"): + code = code[9:-3] + elif code.startswith("```py"): + code = code[5:-3] + else: + code = code[3:-3] + elif code.startswith("`") and code.endswith("`"): + code = code[1:-1] + + code = code.strip().strip("\n") + io = StringIO() + + try: + rvalue = await self.interpreter.run(code, ctx, io) + except Exception as e: + await ctx.send( + f"{ctx.author.mention} **Code**\n" + f"```py\n{code}```\n\n" + f"**Error**\n```{e}```" + ) + else: + out_message = ( + f"{ctx.author.mention} **Code**\n" + f"```py\n{code}\n```" + ) + + output = io.getvalue() + + if output: + out_message = ( + f"{out_message}\n\n" + f"**Output**\n```{output}```" + ) + + if rvalue is not None: + out_message = ( + f"{out_message}\n\n" + f"**Returned**\n```py\n{repr(rvalue)}\n```" + ) + + await ctx.send(out_message) + + +def setup(bot): + bot.add_cog(Eval(bot)) + print("Cog loaded: Eval") diff --git a/bot/cogs/logging.py b/bot/cogs/logging.py new file mode 100644 index 000000000..150cd7c14 --- /dev/null +++ b/bot/cogs/logging.py @@ -0,0 +1,32 @@ +# coding=utf-8 +from discord import Message +from discord.ext.commands import AutoShardedBot + +__author__ = "Gareth Coles" + + +class Logging: + """ + Debug logging module + """ + + def __init__(self, bot: AutoShardedBot): + self.bot = bot + + async def on_ready(self): + print("Ready!") + + async def on_message(self, message: Message): + if not message.guild: # It's a DM + print(f"DM: @{message.author.name}#{message.author.discriminator} -> {message.clean_content}") + else: + print( + f"{message.guild.name} | #{message.channel.name} | " + f"@{message.author.name}#{message.author.discriminator} -> " + f"{message.clean_content}" + ) + + +def setup(bot): + bot.add_cog(Logging(bot)) + print("Cog loaded: Logging") diff --git a/bot/interpreter.py b/bot/interpreter.py new file mode 100644 index 000000000..3e7b822d9 --- /dev/null +++ b/bot/interpreter.py @@ -0,0 +1,42 @@ +# coding=utf-8 +from code import InteractiveInterpreter +from io import StringIO + +__author__ = 'Gareth Coles' + +CODE_TEMPLATE = """ +async def _func(): +{} +""" + + +class Interpreter(InteractiveInterpreter): + write_callable = None + + def __init__(self, bot): + _locals = {"bot": bot} + super().__init__(_locals) + + async def run(self, code, ctx, io, *args, **kwargs): + self.locals["_rvalue"] = [] + self.locals["ctx"] = ctx + self.locals["print"] = lambda x: io.write(f"{x}\n") + + code_io = StringIO() + + for line in code.split("\n"): + code_io.write(f" {line}\n") + + code = CODE_TEMPLATE.format(code_io.getvalue()) + del code_io + + self.runsource(code, *args, **kwargs) + self.runsource("_rvalue = _func()", *args, **kwargs) + + rvalue = await self.locals["_rvalue"] + + del self.locals["_rvalue"] + del self.locals["ctx"] + del self.locals["print"] + + return rvalue |