如何在discord python bot中制作画布个人资料卡?

Res*_*ker 2 python canvas discord discord.py

我需要有关 discord.py 的帮助。我一直在用 python 创建一个机器人,所以我想用这个机器人制作一个画布个人资料卡。问题是,我在 google 中没有找到任何关于它的信息,只有 node.js。我不想重写我的机器人,我想制作一张个人资料卡,例如:juniperbot、mee6。请帮帮我吧!

fur*_*ras 6

我不知道jupiterbotmee6但如果canvas意味着在文档中使用 Canvas进行图像操作,discord.js那么它仅用于生成图像,并且只是send()作为普通文件.png.jpg.

Python 通常使用模块枕头来生成或修改图像。图像, ImageDraw , ImageFont


from discord.ext import commands
from discord import File

from PIL import Image, ImageDraw, ImageFont
import io


TOKEN = 'MY-TOKEN'


bot = commands.Bot(command_prefix='!')


@bot.command(name='canvas')
async def canvas(ctx, text=None):

    IMAGE_WIDTH = 600
    IMAGE_HEIGHT = 300

    # create empty image 600x300 
    image = Image.new('RGB', (IMAGE_WIDTH, IMAGE_HEIGHT)) # RGB, RGBA (with alpha), L (grayscale), 1 (black & white)

    # or load existing image
    #image = Image.open('/home/furas/images/lenna.png')

    # create object for drawing
    draw = ImageDraw.Draw(image)

    # draw red rectangle with green outline from point (50,50) to point (550,250) #(600-50, 300-50)
    draw.rectangle([50, 50, IMAGE_WIDTH-50, IMAGE_HEIGHT-50], fill=(255,0,0), outline=(0,255,0))

    # draw text in center
    text = f'Hello {ctx.author.name}'

    font = ImageFont.truetype('Arial.ttf', 30)

    text_width, text_height = draw.textsize(text, font=font)
    x = (IMAGE_WIDTH - text_width)//2
    y = (IMAGE_HEIGHT - text_height)//2

    draw.text( (x, y), text, fill=(0,0,255), font=font)

    # create buffer
    buffer = io.BytesIO()

    # save PNG in buffer
    image.save(buffer, format='PNG')    

    # move to beginning of buffer so `send()` it will read from beginning
    buffer.seek(0) 

    # send image
    await ctx.send(file=File(buffer, 'myimage.png'))


if __name__ == '__main__':
    bot.run(TOKEN)
Run Code Online (Sandbox Code Playgroud)

结果:

在此处输入图片说明


编辑:添加用户头像的版本。

我还展示了如何从 url 读取图像并将其用作背景。但它只能读取一次——在开始时。

@bot.command(name='canvas')
async def canvas(ctx, text=None):
    #print('\n'.join(dir(ctx)))
    #print('\n'.join(dir(ctx.author)))

    # --- create empty image ---

    #IMAGE_WIDTH = 600
    #IMAGE_HEIGHT = 300

    # create empty image 600x300 
    #image = Image.new('RGB', (IMAGE_WIDTH, IMAGE_HEIGHT)) # RGB, RGBA (with alpha), L (grayscale), 1 (black & white)

    # --- load image from local file ---

    # or load existing image
    #image = Image.open('/home/furas/Obrazy/images/lenna.png')

    # --- load image from url ---

    import urllib.request    

    url = 'https://upload.wikimedia.org/wikipedia/en/7/7d/Lenna_%28test_image%29.png?download'

    response = urllib.request.urlopen(url)
    image = Image.open(response)  # it doesn't need `io.Bytes` because it `response` has method `read()`
    print('size:', image.size)

    #IMAGE_WIDTH, IMAGE_HEIGHT = image.size
    IMAGE_WIDTH = image.size[0] 

    # --- draw on image ---

    # create object for drawing
    draw = ImageDraw.Draw(image)

    # draw red rectangle with green outline from point (50,50) to point (550,250) #(600-50, 300-50)
    draw.rectangle([50, 50, IMAGE_WIDTH-50, IMAGE_HEIGHT-50], fill=(255,0,0, 128), outline=(0,255,0))

    # draw text in center
    text = f'Hello {ctx.author.name}'

    font = ImageFont.truetype('Arial.ttf', 30)

    text_width, text_height = draw.textsize(text, font=font)
    x = (IMAGE_WIDTH - text_width)//2
    y = (IMAGE_HEIGHT - text_height)//2

    draw.text( (x, y), text, fill=(0,0,255), font=font)

    # --- avatar ---

    #print('avatar:', ctx.author.avatar_url)
    #print('avatar:', ctx.author.avatar_url_as(format='jpg'))
    #print('avatar:', ctx.author.avatar_url_as(format='png'))

    AVATAR_SIZE = 128

    # get URL to avatar
    # sometimes `size=` doesn't gives me image in expected size so later I use `resize()`
    avatar_asset = ctx.author.avatar_url_as(format='jpg', size=AVATAR_SIZE)

    # read JPG from server to buffer (file-like object)
    buffer_avatar = io.BytesIO()
    await avatar_asset.save(buffer_avatar)
    buffer_avatar.seek(0)

    # read JPG from buffer to Image 
    avatar_image = Image.open(buffer_avatar)

    # resize it 
    avatar_image = avatar_image.resize((AVATAR_SIZE, AVATAR_SIZE)) # 

    x = 50 + 5
    y = (IMAGE_HEIGHT-AVATAR_SIZE)//2  # center vertically
    image.paste(avatar_image, (x, y))

    # --- sending image ---

    # create buffer
    buffer_output = io.BytesIO()

    # save PNG in buffer
    image.save(buffer_output, format='PNG')    

    # move to beginning of buffer so `send()` it will read from beginning
    buffer_output.seek(0) 

    # send image
    await ctx.send(file=File(buffer_output, 'myimage.png'))
Run Code Online (Sandbox Code Playgroud)

结果:

在此处输入图片说明


编辑:使用新图像绘制透明矩形的示例和Image.alpha_composite()

Pillow doc:示例:绘制部分不透明文本

from discord.ext import commands
from discord import File

from PIL import Image, ImageDraw, ImageFont
import io

import urllib.request


TOKEN = 'MY-TOKEN'


bot = commands.Bot(command_prefix='!')


# read background image only once
url = 'https://upload.wikimedia.org/wikipedia/en/7/7d/Lenna_%28test_image%29.png?download'
response = urllib.request.urlopen(url)
background_image = Image.open(response)  # it doesn't need `io.Bytes` because it `response` has method `read()`
background_image = background_image.convert('RGBA') # add channel ALPHA to draw transparent rectangle


@bot.command(name='canvas')
async def canvas(ctx, text=None):

    AVATAR_SIZE = 128

    # --- duplicate image ----

    image = background_image.copy()

    image_width, image_height = image.size

    # --- draw on image ---

    # create object for drawing

    #draw = ImageDraw.Draw(image)

    # draw red rectangle with alpha channel on new image (with the same size as original image)

    rect_x0 = 20  # left marign
    rect_y0 = 20  # top marign

    rect_x1 = image_width - 20  # right margin
    rect_y1 = 20 + AVATAR_SIZE - 1  # top margin + size of avatar

    rect_width  = rect_x1 - rect_x0
    rect_height = rect_y1 - rect_y0

    rectangle_image = Image.new('RGBA', (image_width, image_height))
    rectangle_draw = ImageDraw.Draw(rectangle_image)

    rectangle_draw.rectangle((rect_x0, rect_y0, rect_x1, rect_y1), fill=(255,0,0, 128))

    # put rectangle on original image

    image = Image.alpha_composite(image, rectangle_image)

    # create object for drawing

    draw = ImageDraw.Draw(image) # create new object for drawing after changing original `image`

    # draw text in center

    text = f'Hello {ctx.author.name}'

    font = ImageFont.truetype('Arial.ttf', 30)


    text_width, text_height = draw.textsize(text, font=font)
    x = (rect_width - text_width - AVATAR_SIZE)//2     # skip avatar when center text
    y = (rect_height - text_height)//2

    x += rect_x0 + AVATAR_SIZE     # skip avatar when center text
    y += rect_y0

    draw.text((x, y), text, fill=(0,0,255,255), font=font)

    # --- avatar ---

    # get URL to avatar
    # sometimes `size=` doesn't gives me image in expected size so later I use `resize()`
    avatar_asset = ctx.author.avatar_url_as(format='jpg', size=AVATAR_SIZE)

    # read JPG from server to buffer (file-like object)
    buffer_avatar = io.BytesIO()
    await avatar_asset.save(buffer_avatar)
    buffer_avatar.seek(0)

    # read JPG from buffer to Image
    avatar_image = Image.open(buffer_avatar)

    # resize it
    avatar_image = avatar_image.resize((AVATAR_SIZE, AVATAR_SIZE)) #

    image.paste(avatar_image, (rect_x0, rect_y0))

    # --- sending image ---

    # create buffer
    buffer_output = io.BytesIO()

    # save PNG in buffer
    image.save(buffer_output, format='PNG')

    # move to beginning of buffer so `send()` it will read from beginning
    buffer_output.seek(0)

    # send image
    await ctx.send(file=File(buffer_output, 'myimage.png'))


if __name__ == '__main__':
    print('Running ... https://discord.com/channels/709507681441808385/709507681441808388')
    bot.run(TOKEN)
Run Code Online (Sandbox Code Playgroud)

结果:

在此处输入图片说明


BTW:示例如何使用 alpha 通道创建圆形图像(创建圆形头像):

从图像中裁剪圆形缩略图的最简单方法是什么?


编辑:使用遮罩显示圆形头像的版本

    # --- avatar ---

    # get URL to avatar
    # sometimes `size=` doesn't gives me image in expected size so later I use `resize()`
    avatar_asset = ctx.author.avatar_url_as(format='jpg', size=AVATAR_SIZE)

    # read JPG from server to buffer (file-like object)
    buffer_avatar = io.BytesIO(await avatar_asset.read())

#    buffer_avatar = io.BytesIO()
#    await avatar_asset.save(buffer_avatar)
#    buffer_avatar.seek(0)

    # read JPG from buffer to Image
    avatar_image = Image.open(buffer_avatar)

    # resize it
    avatar_image = avatar_image.resize((AVATAR_SIZE, AVATAR_SIZE)) #

    circle_image = Image.new('L', (AVATAR_SIZE, AVATAR_SIZE))
    circle_draw = ImageDraw.Draw(circle_image)
    circle_draw.ellipse((0, 0, AVATAR_SIZE, AVATAR_SIZE), fill=255)
    #avatar_image.putalpha(circle_image)
    #avatar_image.show()

    image.paste(avatar_image, (rect_x0, rect_y0), circle_image)
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明