Skip to content
This repository has been archived by the owner on Jun 27, 2019. It is now read-only.

Commit

Permalink
Minor improvemets to /debug command
Browse files Browse the repository at this point in the history
  • Loading branch information
Galarzaa90 committed Jun 19, 2018
1 parent 76936d0 commit e91d1d6
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 1,336 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# Changelog
## Version 1.2.2 (Unrealeased)
## Version 1.2.2 (2018-06-19)
- `/unregistered` no longer displays discord bots.
- Fixed display bug in `/settings askchannel`.
- Improved `/event make`, no longer aborts on failure, lets the user retry and cleans up messages after.
- Improved `/event` subcommands in general, they leave less messages behind.
- Fixed checks for `/watched` subcommands.
- Removed orphaned `utils/emoji.py`
- Minor improvements to `/debug` (now handles multiple lines), added `/eval`as alias.
- Documentation improvements.

## Version 1.2.1 (2018-06-14)
Expand Down
114 changes: 69 additions & 45 deletions cogs/owner.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import inspect
import platform
import textwrap
import traceback
from contextlib import redirect_stdout
from distutils.version import StrictVersion

import pkg_resources
from discord.ext import commands

# Everything is imported to put it in /debug scope
# Exposing for /debug command
from nabbot import NabBot
from utils import checks
from utils.config import *
from utils.config import config
from utils.database import *
from utils.discord import *
from utils.general import *
Expand All @@ -26,6 +27,7 @@ class Owner:
"""Commands exclusive to bot owners"""
def __init__(self, bot: NabBot):
self.bot = bot
self._last_result = None
self.sessions = set()

@staticmethod
Expand Down Expand Up @@ -75,40 +77,62 @@ def check(m):
pass
await ctx.send("Message sent to "+join_list(["@"+a.name for a in guild_admins], ", ", " and "))

@commands.command()
@commands.command(aliases=["eval"])
@checks.is_owner()
async def debug(self, ctx, *, code: str):
async def debug(self, ctx, *, body: str):
"""Evaluates Python code.
This command can be used to run python statements and get the response as a reply.
This commands lets you evaluate python code. If no errors are returned, the bot will react to the command call.
To show the result, you have to use `print()`.
Asynchronous functions must be waited for using `await`.
To show the results of the last command, use `_`.
"""
if "os." in code:
if "os." in body:
await ctx.send("I won't run that.")
return
code = code.strip('` ')
python = '```py\n{}\n```'

env = {
'bot': self.bot,
'ctx': ctx,
'message': ctx.message,
'guild': ctx.guild,
'server': ctx.guild,
'channel': ctx.channel,
'author': ctx.author
"bot": self.bot,
"ctx": ctx,
"channel": ctx.channel,
"author": ctx.author,
"server": ctx.guild,
"guild": ctx.guild,
"message": ctx.message,
"_": self._last_result
}

env.update(globals())

body = self.cleanup_code(body)
stdout = io.StringIO()

to_compile = f"async def func():\n{textwrap.indent(body, ' ')}"

try:
result = eval(code, env)
if asyncio.iscoroutine(result):
result = await result
exec(to_compile, env)
except Exception as e:
return await ctx.send(f'```py\n{e.__class__.__name__}: {e}\n```')

func = env["func"]
try:
with redirect_stdout(stdout):
ret = await func()
except Exception:
await ctx.send(python.format(traceback.format_exc()))
return
value = stdout.getvalue()
await ctx.send(f'```py\n{value}{traceback.format_exc()}\n```')
else:
value = stdout.getvalue()
try:
await ctx.message.add_reaction('\u2705')
except discord.HTTPException:
pass

await ctx.send(python.format(result))
if ret is None:
if value:
await ctx.send(f'```py\n{value}\n```')
else:
self._last_result = ret
await ctx.send(f'```py\n{value}{ret}\n```')

@commands.command()
@checks.is_owner()
Expand Down Expand Up @@ -425,43 +449,43 @@ async def purge(self, ctx):
async def repl(self, ctx):
"""Starts a REPL session in the current channel.
Similar to `/debug`, except this keep an open session where variables are stored.
To exit, type ``exit()``.
Similar to `eval`, but this keeps a running sesion where variables and results are stored.```.
"""
msg = ctx.message

variables = {
'ctx': ctx,
'bot': self.bot,
'message': msg,
'server': msg.guild,
'guild': msg.guild,
'channel': msg.channel,
'author': msg.author,
"ctx": ctx,
"bot": self.bot,
"message": ctx.message,
"server": ctx.guild,
"guild": ctx.guild,
"channel": ctx.channel,
"author": ctx.author,
"_": None
}

variables.update(globals())

if msg.channel.id in self.sessions:
if ctx.channel.id in self.sessions:
await ctx.send('Already running a REPL session in this channel. Exit it with `quit`.')
return

self.sessions.add(msg.channel.id)
self.sessions.add(ctx.channel.id)
await ctx.send('Enter code to execute or evaluate. `exit()` or `quit` to exit.')

while True:
def check(m):
return m.content.startswith('`') and m.author == ctx.author and m.channel == ctx.channel

try:
response = await self.bot.wait_for("message", check=check)
response = await self.bot.wait_for("message", check=check, timeout=10.0*60.0)
except asyncio.TimeoutError:
return
await ctx.send('Exiting REPL session.')
self.sessions.remove(ctx.channel.id)

cleaned = self.cleanup_code(response.content)

if cleaned in ('quit', 'exit', 'exit()'):
await ctx.send('Exiting.')
self.sessions.remove(msg.channel.id)
self.sessions.remove(ctx.channel.id)
return

executor = exec
Expand Down Expand Up @@ -491,27 +515,27 @@ def check(m):
result = executor(code, variables)
if inspect.isawaitable(result):
result = await result
except Exception as e:
except Exception:
value = stdout.getvalue()
fmt = '```py\n{}{}\n```'.format(value, traceback.format_exc())
fmt = f'```py\n{value}{traceback.format_exc()}\n```'
else:
value = stdout.getvalue()
if result is not None:
fmt = '```py\n{}{}\n```'.format(value, result)
fmt = f'```py\n{value}{result}\n```'
variables['_'] = result
elif value:
fmt = '```py\n{}\n```'.format(value)
fmt = f'```py\n{value}\n```'

try:
if fmt is not None:
if len(fmt) > 2000:
await msg.channel.send('Content too big to be printed.')
await ctx.send("Content too big to be printed.")
else:
await msg.channel.send(fmt)
await ctx.send(fmt)
except discord.Forbidden:
pass
except discord.HTTPException as e:
await msg.channel.send('Unexpected error: `{}`'.format(e))
await ctx.send(f'Unexpected error: `{e}`')

@commands.command(aliases=["reset"])
@checks.is_owner()
Expand Down
9 changes: 6 additions & 3 deletions cogs/tibia.py
Original file line number Diff line number Diff line change
Expand Up @@ -1405,7 +1405,7 @@ async def time(self, ctx):
await ctx.send(reply)

@commands.command(aliases=['check', 'char', 'character'])
async def whois(self, ctx, *, name):
async def whois(self, ctx: NabCtx, *, name):
"""Shows a character's or a discord user's information.
If the parameter matches a discord user, it displays a list of the characters linked to that user.
Expand All @@ -1417,8 +1417,7 @@ async def whois(self, ctx, *, name):
Additionally, if the character is in the highscores, their ranking will be shown.
"""
permissions = ctx.channel.permissions_for(ctx.me)
if not permissions.embed_links:
if not ctx.bot_permissions.embed_links:
await ctx.send("Sorry, I need `Embed Links` permission for this command.")
return

Expand All @@ -1439,13 +1438,17 @@ async def whois(self, ctx, *, name):
if name.lower() == ctx.me.display_name.lower():
await ctx.invoke(self.bot.all_commands.get('about'))
return

try:
char = await get_character(name)
except NetworkError:
await ctx.send("Sorry, I couldn't fetch the character's info, maybe you should try again...")
return
char_string = self.get_char_string(char)
user = self.bot.get_member(name, ctx.guild)
# If the user is a bot, then don't, just don't
if user.bot:
user = None
embed = self.get_user_embed(ctx, user)

# No user or char with that name
Expand Down
2 changes: 1 addition & 1 deletion nabbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def __init__(self):
# A list version is created from the dictionary
self.tracked_worlds = {}
self.tracked_worlds_list = []
self.__version__ = "1.2.2a"
self.__version__ = "1.2.2"

async def on_ready(self):
"""Called when the bot is ready."""
Expand Down
Loading

0 comments on commit e91d1d6

Please sign in to comment.