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

View File

@ -8,6 +8,7 @@ import urllib
from collections import defaultdict, Counter from collections import defaultdict, Counter
from dataclasses import dataclass, field from dataclasses import dataclass, field
from functools import partial from functools import partial
from itertools import groupby
from math import log from math import log
from operator import attrgetter, itemgetter from operator import attrgetter, itemgetter
from time import time from time import time
@ -177,7 +178,7 @@ class MiscCog(Cog, name="Divers"):
@command(name="fan", aliases=["join", "adhere"], hidden=True) @command(name="fan", aliases=["join", "adhere"], hidden=True)
async def fan_club_cmd(self, ctx: Context, who: Member): 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_id = FAN_CLUBS.get(who.id, None)
role = get(ctx.guild.roles, id=role_id) role = get(ctx.guild.roles, id=role_id)
@ -362,10 +363,6 @@ class MiscCog(Cog, name="Divers"):
diffs[m.id].add(h.hugger) diffs[m.id].add(h.hugger)
for m, d in diffs.items(): 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 stats[m] += len(everyone_diff.union(d)) * 42 + everyone_hugs
top = sorted(list(stats.items()), key=itemgetter(1), reverse=True) top = sorted(list(stats.items()), key=itemgetter(1), reverse=True)
@ -392,10 +389,7 @@ class MiscCog(Cog, name="Divers"):
await ctx.send(embed=embed) await ctx.send(embed=embed)
async def send_hugs_stats_for(self, ctx: Context, who: Member): async def send_hugs_stats_for(self, ctx: Context, who: discord.Member):
embed = discord.Embed(
title=f"Câlins de {who.display_name}", color=discord.Colour.magenta()
)
given = self.hugs_given(ctx, who.id) given = self.hugs_given(ctx, who.id)
received = self.hugs_received(ctx, who.id) received = self.hugs_received(ctx, who.id)
@ -410,6 +404,26 @@ class MiscCog(Cog, name="Divers"):
"Morceaux": (len(cut), 30), "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(): for f, (v, h_factor) in infos.items():
heart = self.heart_for_stat(v * h_factor) heart = self.heart_for_stat(v * h_factor)
if f == "Morceaux": if f == "Morceaux":
@ -454,7 +468,13 @@ class MiscCog(Cog, name="Divers"):
if memb is not None: if memb is not None:
name = memb.mention name = memb.mention
else: 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 return name

View File

@ -510,7 +510,7 @@ class TirageCog(Cog, name="Tirages"):
@commands.command(name="dice-all", aliases=["da"]) @commands.command(name="dice-all", aliases=["da"])
@commands.has_role(Role.DEV) @commands.has_role(Role.DEV)
async def dice_all_cmd(self, ctx, *teams): 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 channel = ctx.channel.id
if channel in self.tirages: if channel in self.tirages:
for t in teams: for t in teams:

View File

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