def screenshot(self, ctx):
"""Take a screenshot.
Usage: screenshot"""
echeck_perms(ctx, ('bot_owner',))
if have_pil and sys.platform not in ['linux', 'linux2']:
grabber = ImageGrab
else:
grabber = pyscreenshot
image = grabber.grab()
img_bytes = io.BytesIO()
image.save(img_bytes, format='PNG')
img_bytes.seek(0)
await ctx.send('This is *probably* what my screen looks like right now.', file=discord.File(img_bytes, 'screenshot.png'))
python类File()的实例源码
def render(self, ctx, *, webpage: str):
"""Render a webpage to image.
Usage: render [url]"""
echeck_perms(ctx, ('bot_owner',))
await ctx.send(':warning: Not working. '
'Type `yes` quickly if you\'re fine with this crashing.')
if not (await self.bot.wait_for('message', timeout=6.0,
check=lambda m: m.content.lower().startswith('yes') and m.author == ctx.author and m.channel == ctx.channel)):
return
self.web_render = scr.Screenshot()
image = self.web_render.capture(webpage)
await ctx.send(file=discord.File(io.BytesIO(image), 'webpage.png'))
def dog(cmd, message, args):
doggie_url = 'http://www.randomdoggiegenerator.com/randomdoggie.php'
async with aiohttp.ClientSession() as session:
async with session.get(doggie_url) as data:
doggie_image = await data.read()
with Image.open(BytesIO(doggie_image)) as img:
img.save(f'cache/pupper_{message.id}.png')
await message.channel.send(file=discord.File(f'cache/pupper_{message.id}.png'))
os.remove(f'cache/pupper_{message.id}.png')
def color(cmd, message, args):
if not args:
await message.channel.send(cmd.help())
else:
if len(args) == 3:
for arg in args:
if int(arg) > 255:
await cmd.bot.send_message(message.channel,
'Error processing inputted variables.\nNo number can be greater than 255 when defining RGB.')
return
try:
clr = (int(args[0]), int(args[1]), int(args[2]))
except:
await message.channel.send('Error processing inputted variables.')
return
else:
try:
clr = str(args[0]).replace('#', '')
part1 = clr[:2]
part1 = int(part1, 16)
part2 = clr[2:-2]
part2 = int(part2, 16)
part3 = clr[4:]
part3 = int(part3, 16)
clr = (part1, part2, part3)
except:
await message.channel.send('Error processing inputted variables.')
return
img = Image.new('RGB', (50, 50), clr)
img.save(f'cache/{message.author.id}.png')
await message.channel.send(file=discord.File(f'cache/{message.author.id}.png'))
os.remove(f'cache/{message.author.id}.png')
def _default_flip(self, ctx):
"""Flip called with no arguments"""
side = random.choices(SIDES, WEIGHTS)[0]
file = discord.File(f'data/images/coins/{side}.png', 'coin.png')
embed = (discord.Embed(colour=ctx.bot.colour, description=f'...flipped **{side}**')
.set_author(name=ctx.author.display_name, icon_url=ctx.author.avatar_url)
.set_image(url='attachment://coin.png')
)
await ctx.send(file=file, embed=embed)
def _flip_image(self, num_sides):
images = {
Side.heads: self._heads_image,
Side.tails: self._tails_image,
}
stats = collections.Counter()
root = num_sides ** 0.5
height, width = round(root), int(math.ceil(root))
sides = (random.choices(SIDES, WEIGHTS)[0] for _ in range(num_sides))
size = self.image_size
image = Image.new('RGBA', (width * size, height * size))
for i, side in enumerate(sides):
y, x = divmod(i, width)
image.paste(images[side], (x * size, y * size))
stats[side] += 1
message = ' and '.join(pluralize(**{str(side)[:-1]: n}) for side, n in stats.items())
f = io.BytesIO()
image.save(f, 'png')
f.seek(0)
return message, discord.File(f, filename='flipcoins.png')
def _create_ship_image(self, score, avatar1, avatar2):
ava_im1 = Image.open(avatar1).convert('RGBA')
ava_im2 = Image.open(avatar2).convert('RGBA')
# Assume the two images are square
size = min(ava_im1.size, ava_im2.size)
offset = round(_scale(0, 100, size[0], 0, score))
ava_im1.thumbnail(size)
ava_im2.thumbnail(size)
# paste img1 on top of img2
newimg1 = Image.new('RGBA', size=size, color=(0, 0, 0, 0))
newimg1.paste(ava_im2, (-offset, 0))
newimg1.paste(ava_im1, (offset, 0))
# paste img2 on top of img1
newimg2 = Image.new('RGBA', size=size, color=(0, 0, 0, 0))
newimg2.paste(ava_im1, (offset, 0))
newimg2.paste(ava_im2, (-offset, 0))
# blend with alpha=0.5
im = Image.blend(newimg1, newimg2, alpha=0.6)
mask = Image.open(self._mask).convert('L')
mask = mask.resize(ava_im1.size, resample=Image.BILINEAR)
im.putalpha(mask)
f = io.BytesIO()
im.save(f, 'png')
f.seek(0)
return discord.File(f, filename='test.png')
def sql(self, ctx, *, query: str):
"""Run some SQL."""
# the imports are here because I imagine some people would want to use
# this cog as a base for their other cog, and since this one is kinda
# odd and unnecessary for most people, I will make it easy to remove
# for those people.
from .utils.formats import pluralize
import time
query = self.cleanup_code(query)
is_multistatement = query.count(';') > 1
async with ctx.db.get_session() as session:
try:
start = time.perf_counter()
results = [dict(r) async for r in await session.cursor(query)]
dt = (time.perf_counter() - start) * 1000.0
except Exception:
return await ctx.send(f'```py\n{traceback.format_exc()}\n```')
if is_multistatement or not results:
return await ctx.send(f'`{dt:.2f}ms: {results}`')
print(results)
num_rows = len(results)
headers = list(results[0])
rendered = _tabulate((list(r.values()) for r in results), headers)
fmt = f'```\n{rendered}\n```\n*Returned {pluralize(row=num_rows)} in {dt:.2f}ms*'
if len(fmt) > 2000:
fp = io.BytesIO(fmt.encode('utf-8'))
await ctx.send('Too many results...', file=discord.File(fp, 'results.txt'))
else:
await ctx.send(fmt)
def chill(self, ctx):
"""
Channel 1 and chill?
"""
await ctx.send(
content=f"<@&172426880543227904> <@&262334316611239937> <@&172427010310799361>, chill? - {ctx.author}",
file=discord.File("bot/resources/chill.jpg"))
await ctx.message.delete()
def gg(self, ctx):
"""
GG EZ
"""
await ctx.send(
file=discord.File("bot/resources/ggez.jpg"))
await ctx.message.delete()
def ohno(self, ctx, *words):
"""Generates your very own SCP-3078 cognitohazardous shitpost.
e.g. "when you inhale the devil's mary jane smoke"
"""
# Damn I tried to imitate the SCP-3078 instances but they don't follow
# the same layout in different imitations, so the first one is followed
# the best here.
font = ImageFont.truetype(join(folder, "Calibri.ttf"), 19)
append = None
width = 256
x_start = 22
y_cur = 13
y_pad = 3
image = Image.open(join(folder, "SCP-3078.png"))
draw = ImageDraw.Draw(image)
for word in words:
if append is None:
append = word
else:
prev = append
append = " ".join([append, word])
w, h = draw.textsize(append, font=font)
if w > width:
append = word
draw.text((x_start, y_cur), prev, fill=(0, 0, 0),
font=font)
y_cur += h + y_pad
if append is not None:
draw.text((x_start, y_cur), append, fill=(0, 0, 0), font=font)
image_bytes = BytesIO()
image.save(image_bytes, format="png")
image_bytes.seek(0)
await ctx.send(file=File(image_bytes, filename="SCP-3078.png"))
image_bytes.close()
def dog(cmd, message, args):
doggie_url = 'http://www.randomdoggiegenerator.com/randomdoggie.php'
async with aiohttp.ClientSession() as session:
async with session.get(doggie_url) as data:
doggie_image = await data.read()
with open(f'cache/pupper_{message.id}.png', 'wb') as pupper_img:
pupper_img.write(doggie_image)
await message.channel.send(file=discord.File(f'cache/pupper_{message.id}.png'))
os.remove(f'cache/pupper_{message.id}.png')
def color(cmd, message, args):
if args:
if len(args) == 1:
color_input = args[0]
while color_input.startswith('#'):
color_input = color_input[1:]
if len(color_input) == 6:
try:
color_tupple = (int(color_input[:2], 16), int(color_input[2:-2], 16), int(color_input[4:], 16))
response = None
image = Image.new('RGB', (128, 128), color_tupple)
image.save(f'cache/{message.id}.png')
except ValueError:
response = discord.Embed(color=0xBE1931, title='? Something here is not a number.')
else:
response = discord.Embed(color=0xBE1931, title='? Invalid HEX color code.')
elif len(args) == 3:
try:
color_tupple = (int(args[0]), int(args[1]), int(args[2]))
response = None
image = Image.new('RGB', (128, 128), color_tupple)
image.save(f'cache/{message.id}.png')
except ValueError:
response = discord.Embed(color=0xBE1931, title='? Something here is not a number.')
else:
response = discord.Embed(color=0xBE1931, title='? Invalid input, HEX or RGB sequence, please.')
else:
response = discord.Embed(color=0xBE1931, title='? Nothing inputted.')
if response:
await message.channel.send(embed=response)
else:
img_file = discord.File(f'cache/{message.id}.png')
await message.channel.send(file=img_file)
os.remove(f'cache/{message.id}.png')
def match(self, ctx, match_id : int):
"""Gets a summary of the dota match with the given id"""
await ctx.channel.trigger_typing()
match = await get_match(match_id)
duration = get_pretty_duration(match['duration'], postfix=False)
game_mode = self.dota_game_strings[f"game_mode_{match['game_mode']}"]
lobby_type = self.dota_game_strings[f"lobby_type_{match['lobby_type']}"] + " "
if lobby_type == "Normal ":
lobby_type = ""
description = (f"This {lobby_type}**{game_mode}** match ended in {duration} \n"
f"More info at [DotaBuff](https://www.dotabuff.com/matches/{match_id}), "
f"[OpenDota](https://www.opendota.com/matches/{match_id}), or "
f"[STRATZ](https://www.stratz.com/match/{match_id})")
embed = discord.Embed(description=description,
timestamp=datetime.datetime.utcfromtimestamp(match['start_time']), color=self.embed_color)
embed.set_author(name="Match {}".format(match_id), url="https://www.opendota.com/matches/{}".format(match_id))
embed.add_field(name="Game Mode", value=self.dota_game_strings[f"game_mode_{match['game_mode']}"])
embed.add_field(name="Lobby Type", value=self.dota_game_strings[f"lobby_type_{match['lobby_type']}"])
match_image = discord.File(await drawdota.create_match_image(match), filename="matchimage.png")
embed.set_image(url=f"attachment://{match_image.filename}")
embed.set_footer(text="Started")
await ctx.send(embed=embed, file=match_image)
def matches(self, ctx, matchcount=10, player=None):
"""Gets a list of your recent matches
Specifiy a `matchcount` to get a specific number of matches. Default is 10
You can also mention a user to get their recent matches
**Example:**
`{cmdpfx}matches 5 @PlayerPerson`"""
steam32 = await get_check_steamid(player, ctx)
await ctx.channel.trigger_typing()
matches = await opendota_query(f"/players/{steam32}/recentmatches")
if matchcount < 1:
raise UserError("Gotta have a matchcount of 1 or more")
if matchcount > 20:
raise UserError("Sorries, 20 is the maximum number of matches for this command")
if not matches:
raise UserError("Looks like this player hasn't played any matches")
matches = matches[:matchcount]
embed = discord.Embed()
embed.title = "Recent Matches"
embed.url = f"https://www.opendota.com/players/{steam32}"
matches_image = await drawdota.draw_matches_table(matches, self.dota_game_strings)
matches_image = discord.File(matches_image, "matches.png")
embed.set_image(url=f"attachment://{matches_image.filename}")
await ctx.send(embed=embed, file=matches_image)
def dotagif(self, ctx, match_id : int, start, end, ms_per_second : int = 100):
"""Creates a gif of a specific part of a dota match
The part of the match that you specify must be less than 10 minutes long
`ms_per_second` is how many miliseconds between frames of the gif (each frame is 1 dota second)
**Example:**
`{cmdpfx}dotagif 3370877768 28:37 30:30`"""
await ctx.channel.trigger_typing()
match = await get_match(match_id)
if not is_parsed(match):
raise MatchNotParsedError(match_id, "get laning info")
stratz_match = await get_stratz_match(match_id)
if not is_stratz_parsed(stratz_match):
raise UserError(f"It looks like match `{match_id}` hasn't been parsed by STRATZ")
start = int(get_time(start))
end = int(get_time(end))
if end - start > 600:
raise UserError("The length of this clip must be less than 10 minutes")
if ms_per_second < 1 or ms_per_second > 655350:
raise UserError("That is outside the bounds of the `ms_per_second` value")
async with ctx.channel.typing():
await thinker.think(ctx.message)
try:
image = discord.File(await drawdota.create_dota_gif(match, stratz_match, start, end, ms_per_second), "map.gif")
await ctx.send(file=image)
finally:
await thinker.stop_thinking(ctx.message)
def lasagna(self, ctx):
"""A baked Italian dish
Contains wide strips of pasta cooked and layered with meat or vegetables, cheese, and tomato sauce."""
await ctx.send(file=discord.File(settings.resource("images/lasagna.jpg")))
def restget(self, ctx, url):
"""Gets a json response from a rest api and returns it"""
await ctx.channel.trigger_typing()
data = await httpgetter.get(url)
filename = settings.resource("temp/response.json")
write_json(filename, data)
await ctx.send(file=discord.File(filename))
os.remove(filename)
def talents(self, ctx, *, hero : str):
"""Gets the talents of a specific hero
You can give this command almost any variant of the hero's name, or the hero's id, in the same format as `{cmdpfx}hero`
**Examples:**
`{cmdpfx}talents shadow fiend`"""
hero = self.lookup_hero(hero)
if not hero:
raise UserError("That doesn't look like a hero")
image = await drawdota.draw_hero_talents(hero)
image = discord.File(image, f"{hero.name}_talents.png")
await ctx.send(file=image)
def midi(self, ctx: commands.Context,
tempo: int=120, *, data: str=None):
"""
Convert text to MIDI. Multiple channels can be used by splitting text with |.
Letters are converted to their pitch values using a mapping.
Full documentation about it is not provided, read the code at
https://github.com/lnmds/jose/blob/master/ext/midi.py
To give longer input than a discord message allows you may upload a .txt file of up to 20 KiB.
"""
if data is None:
try:
data = await self.download_data(ctx.message)
except Exception as err:
log.exception('error downloading file at midi')
raise self.SayException('We had an error while downloading '
'the file, are you sure it is text?')
before = time.monotonic()
midi_file = await self.make_midi(tempo, data)
duration = (time.monotonic() - before) * 1000
if midi_file is None:
return await ctx.send('Failed to generate a MIDI file!')
file = io.BytesIO()
await self.loop.run_in_executor(None, midi_file.writeFile, file)
file.seek(0)
wrapped = discord.File(file, filename='boop.midi')
await ctx.send(f'Took {duration:.3f}ms!', file=wrapped)