How to make a canvas profile card in discord python bot?

有些话、适合烂在心里 提交于 2021-01-24 09:05:14

问题


I need help with discord.py. I've been creating a bot on python, so I wanted to make a canvas profile card with this bot. The problem is, I did not find anything in google about it, only node.js. I do not want to rewrite my bot and I'd like to make a profile card like example: juniperbot, mee6. Help me with it please!


回答1:


I don't know jupiterbot nor mee6 but if canvas means Image manipulation with Canvas in documentation for discord.js then it is used only to generate image and simply send() as normal file .png or .jpg.

Python usually uses module pillow to generate or modify image. Image, 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)

Result:


EDIT: Version which add user's avatar.

I also shows how to read image from url and use it as background. But it could read it only once - at start.

@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'))

Result:


EDIT: Example which draws transparent rectangle using new Image and Image.alpha_composite()

Pillow doc: Example: Draw Partial Opacity Text

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)

Result:


BTW: Example how to use alpha channel to create circle image (to create circle avatar):

What's the most simple way to crop a circle thumbnail from an image?


EDIT: Version which use mask to display circle avatar

    # --- 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)



来源:https://stackoverflow.com/questions/61724373/how-to-make-a-canvas-profile-card-in-discord-python-bot

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!