:spakles: eval keeps locals

This commit is contained in:
ddorn 2020-05-19 17:11:35 +02:00
parent 6d1560ceaa
commit 924abea6c8
4 changed files with 101 additions and 34 deletions

View File

@ -1,8 +1,11 @@
import asyncio
import re
import traceback
from contextlib import redirect_stdout
from io import StringIO
from pprint import pprint
from textwrap import indent
from typing import Union
import discord
from discord import TextChannel, PermissionOverwrite, Message, ChannelType
@ -35,13 +38,14 @@ COGS_SHORTCUTS = {
}
RE_QUERY = re.compile(
r"^! ?e(val)? (`{1,3}py(thon)?\n)?(?P<query>.*?)\n?(`{1,3})?\n?$", re.DOTALL
r"^! ?e(val)?[ \n]+(`{1,3}(py(thon)?\n)?)?(?P<query>.*?)\n?(`{1,3})?\n?$", re.DOTALL
)
class DevCog(Cog, name="Dev tools"):
def __init__(self, bot: CustomBot):
self.bot = bot
self.eval_locals = {}
@command(name="interrupt")
@has_role(Role.DEV)
@ -225,57 +229,99 @@ class DevCog(Cog, name="Dev tools"):
await channel.delete_messages(to_delete)
await ctx.message.delete()
def eval(self, msg: Message) -> discord.Embed:
async def eval(self, msg: Message) -> discord.Embed:
guild: discord.Guild = msg.guild
roles = guild.roles
members = guild.members
hugs_cog = self.bot.get_cog("Divers")
hugs = hugs_cog.hugs
channel: TextChannel = msg.channel
send = lambda text: asyncio.create_task(channel.send(text))
query = re.match(RE_QUERY, msg.content).group("query")
if not query:
raise TfjmError("No query found.")
if "\n" in query:
if any(word in query for word in ("=", "return", "await", ":", "\n")):
lines = query.splitlines()
if "return" not in lines[-1] and "=" not in lines[-1]:
if (
"return" not in lines[-1]
and "=" not in lines[-1]
and not lines[-1].startswith(" ")
):
lines[-1] = f"return {lines[-1]}"
query = "\n ".join(lines)
query = f"def q():\n {query}\nresp = q()"
query = "\n".join(lines)
full_query = f"""async def query():
try:
{indent(query, " " * 8)}
finally:
self.eval_locals.update(locals())
"""
else:
full_query = query
globs = {**globals(), **locals(), **self.eval_locals}
stdout = StringIO()
try:
if "\n" in query:
q = compile(query, filename="query.py", mode="exec")
globs = {**globals(), **locals()}
locs = {}
exec(query, globs, locs)
resp = locs["resp"]
else:
resp = eval(query, globals(), locals())
with redirect_stdout(stdout):
if "\n" in full_query:
locs = {}
exec(full_query, globs, locs)
resp = await locs["query"]()
else:
resp = eval(query, globs)
except Exception as e:
tb = StringIO()
traceback.print_tb(e.__traceback__, file=tb)
tb.seek(0)
embed = discord.Embed(title=str(e), color=discord.Colour.red())
embed.add_field(name="Query", value=f"```py\n{query}\n```", inline=False)
embed.add_field(
name="Traceback", value=f"```py\n{tb.read()}```", inline=False
name="Query", value=f"```py\n{full_query}\n```", inline=False
)
embed.add_field(
name="Traceback", value=self.to_field_value(tb), inline=False
)
else:
out = StringIO()
pprint(resp, out)
out.seek(0)
embed = discord.Embed(title="Result", color=discord.Colour.green())
embed.add_field(name="Query", value=f"```py\n{query}```", inline=False)
embed.add_field(name="Value", value=f"```py\n{out.read()}```", inline=False)
embed.add_field(name="Query", value=f"```py\n{full_query}```", inline=False)
value = self.to_field_value(out)
if resp is not None and value:
embed.add_field(name="Value", value=value, inline=False)
stdout = self.to_field_value(stdout)
if stdout:
embed.add_field(name="Standard output", value=stdout, inline=False)
embed.set_footer(text="You may edit your message.")
return embed
def to_field_value(self, string: Union[str, StringIO]):
if isinstance(string, StringIO):
string.seek(0)
string = string.read()
if not string:
return
if len(string) > 1000:
string = string[:500] + "\n...\n" + string[-500:]
return f"```py\n{string}```"
@command(name="eval", aliases=["e"])
@is_owner()
async def eval_cmd(self, ctx: Context):
""""""
embed = self.eval(ctx.message)
"""(dev) Evalue l'entrée."""
self.eval_locals["ctx"] = ctx
embed = await self.eval(ctx.message)
resp = await ctx.send(embed=embed)
def check(before, after):
@ -289,7 +335,7 @@ class DevCog(Cog, name="Dev tools"):
except asyncio.TimeoutError:
break
embed = self.eval(after)
embed = await self.eval(after)
await resp.edit(embed=embed)
# Remove the "You may edit your message"

View File

@ -8,6 +8,7 @@ import urllib
from collections import defaultdict, Counter
from dataclasses import dataclass, field
from functools import partial
from itertools import groupby
from math import log
from operator import attrgetter, itemgetter
from time import time
@ -177,7 +178,7 @@ class MiscCog(Cog, name="Divers"):
@command(name="fan", aliases=["join", "adhere"], hidden=True)
async def fan_club_cmd(self, ctx: Context, who: Member):
"""Permet de rejoindre le fan-club d'Ananas ou Citron Vert."""
"""Permet de rejoindre un fan club existant."""
role_id = FAN_CLUBS.get(who.id, None)
role = get(ctx.guild.roles, id=role_id)
@ -362,10 +363,6 @@ class MiscCog(Cog, name="Divers"):
diffs[m.id].add(h.hugger)
for m, d in diffs.items():
if m == self.bot.user.id:
print(everyone_diff)
print(d)
print(everyone_diff.union(d))
stats[m] += len(everyone_diff.union(d)) * 42 + everyone_hugs
top = sorted(list(stats.items()), key=itemgetter(1), reverse=True)
@ -392,10 +389,7 @@ class MiscCog(Cog, name="Divers"):
await ctx.send(embed=embed)
async def send_hugs_stats_for(self, ctx: Context, who: Member):
embed = discord.Embed(
title=f"Câlins de {who.display_name}", color=discord.Colour.magenta()
)
async def send_hugs_stats_for(self, ctx: Context, who: discord.Member):
given = self.hugs_given(ctx, who.id)
received = self.hugs_received(ctx, who.id)
@ -410,6 +404,26 @@ class MiscCog(Cog, name="Divers"):
"Morceaux": (len(cut), 30),
}
most_given = Counter(h.hugged for h in given).most_common(1)
most_received = Counter(h.hugger for h in received).most_common(1)
most_given = most_given[0] if most_given else (0, 0)
most_received = most_received[0] if most_received else (0, 0)
embed = discord.Embed(
title=f"Câlins de {who.display_name}",
color=discord.Colour.magenta(),
description=(
f"On peut dire que {who.mention} est très câlin·e, avec un score de "
f"{self.score_for(ctx, who.id)}. Iel a beaucoup câliné "
f"{self.name_for(ctx, most_given[0])} "
f"*({most_given[1]} :heart:)* et "
f"s'est beaucoup fait câliner par {self.name_for(ctx, most_received[0])} "
f"*({most_received[1]} :heart:)* !"
),
)
user: discord.User = self.bot.get_user(who.id)
embed.set_thumbnail(url=user.avatar_url)
for f, (v, h_factor) in infos.items():
heart = self.heart_for_stat(v * h_factor)
if f == "Morceaux":
@ -454,7 +468,13 @@ class MiscCog(Cog, name="Divers"):
if memb is not None:
name = memb.mention
else:
name = ctx.guild.get_role(member_or_role_id).mention
role = ctx.guild.get_role(member_or_role_id)
if role is None:
name = getattr(
self.bot.get_user(member_or_role_id), "mention", "Personne"
)
else:
name = role.name
return name

View File

@ -510,7 +510,7 @@ class TirageCog(Cog, name="Tirages"):
@commands.command(name="dice-all", aliases=["da"])
@commands.has_role(Role.DEV)
async def dice_all_cmd(self, ctx, *teams):
"""(dev) Lance un dé pour chaque equipe afin de tester les tirages."""
"""(dev) Lance un dé pour chaque equipe en entrée."""
channel = ctx.channel.id
if channel in self.tirages:
for t in teams:

View File

@ -12,6 +12,7 @@ __all__ = [
"TEAMS_CHANNEL_CATEGORY",
"DIEGO",
"ANANAS",
"YOHANN",
"FAN_CLUBS",
"BOT",
"TOURNOIS",