Nonebot2插件:逝世表情包(2)

前言已经说倦了.jpg

总之本文是对商店插件的拙劣模仿,不要在意复杂的语法使用,有问题可以直接说教我

广告好像nb2热度下来了,总之招租(

0.water之言

这篇文章依旧是实现简单的表情包,大概是下面这三个

日向麻麻是404群的光

群聊之光,萌新之友

喜报:账号被风控

喜报

草图

lostbeauty

需要准备的依旧是PIL库和requests库

1.实现

i.喜报

这里是板子

无水印

首先设置指令格式:"/喜报 需要输入的文字"
经常放代码是不是有水博客的嫌疑
import nonebot
from nonebot.rule import *
from nonebot import on_command
from nonebot import *
from nonebot.adapters.onebot.v11 import Bot, Event, MessageEvent, GroupMessageEvent
from nonebot.adapters.onebot.v11.message import Message
import PIL
from PIL import Image, ImageDraw, ImageFont


async def handle_rule(bot: Bot, event: Event) -> bool:
    with open("src/plugins/群名单.txt", encoding='utf-8') as file:
        white_block = []
        for line in file:
            line = line.strip()
            line = re.split("[ |#]", line)
            white_block.append(line[0])
    try:
        whatever, group_id, user_id = event.get_session_id().split('_')  # 获取当前群聊id,发起人id,返回的格式为group_groupid_userid
    except:  # 如果上面报错了,意味着发起的是私聊,返回格式为userid
        group_id = None
        user_id = event.get_session_id()
    if group_id in white_block:
        return True
    else:
        return False


xi_bao = on_command("喜报", rule=handle_rule, priority=50)


@xi_bao.handle()
async def xi_bao_handle(bot: Bot, event: Event):
    receive_text = str(event.get_message()).replace('/喜报', '').split()[0]  # 获取需要生成的文字
接着对图片进行处理(模板表情包都单独建个文件夹存模板和生成图像最好)
为了生成看起来像样的喜报,我们考虑红色字体加个橙底阴影,实现方式为在预计生成字体的位置上偏移1到3个像素生成同样大小的橙色文字,最后再生成红色文字覆盖再上面,这样就做出了阴影效果
接着是考虑文字过长的换行效果,为了满足喜报该有的大字报效果,设计为判断输入字节长度,并按照字节长度生成适配的大小,当最小的字体也不能适配时返回提示
大概需要了解就这些吧,其他代码里再注释
import PIL 
from PIL import Image,ImageDraw,ImageFont

# 生成阴影文字函数
def text_border(draw, x, y, font, shadowcolor, fillcolor, text):
    # draw thicker
    for ax in range(1, 5):
        for ay in range(1, 5):
            draw.text(((x + ax - 2), (y + ay - 2)), text, font=font,
                      fill=shadowcolor)  # 在不同方向上生成同样大小的橙色文字(shadowcolor),可以按自己的需求改
    # now draw the text over it
    draw.text((x, y), text, font=font, fill=fillcolor)  # 最后覆盖

receive_text = str(event.get_message()).replace('/喜报', '').split()[0]  # 获取需要生成的文字
user_pic = Image.open(f"模板地址")
# 添加文字,根据长度自行适配三种大小
draw = ImageDraw.Draw(user_pic)
limit_len = 0
# utf-8下的汉字3字节,英文1字节,这个范围是按照文字不会超过图片边界和最好的显示算的(懒得适配更多了,字体也不大了),如果有其他的想法自行计算
if len(receive_text.encode('utf-8')) < 57:
    font = ImageFont.truetype("simhei", 80)
    limit_len = 18
elif len(receive_text.encode('utf-8')) < 73:
    limit_len = 24
    font = ImageFont.truetype("simhei", 70)
elif len(receive_text.encode('utf-8')) < 82:
    limit_len = 27
    font = ImageFont.truetype("simhei", 60)
else:
    await xi_bao.finish(Message(f"字数超过限制"))
word = receive_text
w, h = font.getsize(word)
if len(receive_text.encode('utf-8')) > limit_len: # 换行处理,每行的字节数按照前面的分类定
    # 参数:位置、文本、填充、字体
    n = 0
    i = 0
    len_str = 0
    text_list = ['']
    while n < len(receive_text):
        if len_str + len(receive_text[n].encode('utf-8')) > limit_len:
            text_list.append('')
            i += 1
            len_str = 0
        else:
            len_str += len(receive_text[n].encode('utf-8'))
            text_list[i] += receive_text[n]
            n += 1
    i += 1
    for k in range(i):
        text_border(draw, (600 - font.getsize(text_list[k])[0]) / 2,
                    int(200 / i) / 2 + (80 + int(200 / i) * k) + 20 * (k), font, 'orange', 'red', text_list[k])
    # 这里的位置也是自己算的,感觉比较好看
else: # 只用显示一行
    text_border(draw, (600 - w) / 2, (450 - h) / 2, font, 'orange', 'red', word)
user_pic.save(f"你打算保存的地址")
await xi_bao.finish(
    Message(
        f"[CQ:image,file=file:///地址,id=40000]"))

样例:头图

ii.没有美貌

同样的这里是板子

我是板子

这个表情包不知道怎么命名好
总之实现方式和上面大差不差,为了尽可能像字幕也是用了一样的白底黑阴影的实现方式,对于过长的字体换行也是差不多的设计
懒得分段了,实现方式大差不差了
import nonebot
from nonebot.rule import *
from nonebot import on_command
from nonebot import *
from nonebot.adapters.onebot.v11 import Bot, Event, MessageEvent, GroupMessageEvent
from nonebot.adapters.onebot.v11.message import Message
import PIL
from PIL import Image, ImageDraw, ImageFont


async def handle_rule(bot: Bot, event: Event) -> bool:
    with open("src/plugins/群名单.txt", encoding='utf-8') as file:
        white_block = []
        for line in file:
            line = line.strip()
            line = re.split("[ |#]", line)
            white_block.append(line[0])
    try:
        whatever, group_id, user_id = event.get_session_id().split('_')  # 获取当前群聊id,发起人id,返回的格式为group_groupid_userid
    except:  # 如果上面报错了,意味着发起的是私聊,返回格式为userid
        group_id = None
        user_id = event.get_session_id()
    if group_id in white_block or group_id == None:
        return True
    else:
        return False


lmtb = on_command("如果", rule=handle_rule, priority=50)


def text_border(draw, x, y, font, shadowcolor, fillcolor, text):
    # draw thicker border
    for ax in range(1, 5):
        for ay in range(1, 5):
            draw.text(((x + ax - 2), (y + ay - 2)), text, font=font, fill=shadowcolor)
    # now draw the text over it
    draw.text((x, y), text, font=font, fill=fillcolor)


@lmtb.handle()
async def lmtb_handle(bot: Bot, event: Event):
    try:
        whatever, group_id, user_id = event.get_session_id().split('_')  # 获取当前群聊id,发起人id,返回的格式为group_groupid_userid
        data = await bot.call_api('get_group_member_list', **{
            'group_id': int(group_id)
        })
    except:  # 如果上面报错了,意味着发起的是私聊,返回格式为userid
        group_id = None
        user_id = event.get_session_id()
    rtext = str(event.get_message()).replace(' ', '').split('/如果')
    receive_text = "如果" + rtext[1] + "的话" # 这个表情包原版就是如果xxx的话,嗯,忘记了
    if receive_text == "":
        await lmtb.finish(Message(f"似乎什么都没有输入呢"))
    if len(receive_text.encode('utf-8')) > 147: # 这个表情包最好就两行
        await lmtb.finish(Message(f"字数超过限制了"))
    user_pic = Image.open(f"你模板的地址")
    # 添加文字
    draw = ImageDraw.Draw(user_pic)
    font = ImageFont.truetype("simhei", 25) # 实际上显示出来的效果还是不能很靠近原版,因为懒全用的自带字库,可以考虑自己调换
    word = receive_text
    # 参数:位置、文本、填充、字体
    if len(receive_text.encode('utf-8')) > 49: # 一样的换行处理
        n = 0
        i = 0
        len_str = 0
        text_list = ['']
        while n < len(receive_text):
            if len_str + len(receive_text[n].encode('utf-8')) > 49:
                text_list.append('')
                i += 1
                len_str = 0
            else:
                len_str += len(receive_text[n].encode('utf-8'))
                text_list[i] += receive_text[n]
                n += 1
        for k in range(i + 1):
            text_border(draw, 23, 257 - (i - k) * 32, font, 'black', 'white', text_list[k])
            # 这个位置的x最好不改吧,比较符合。y是大概移动得出来的,总之多试试?
    else:
        text_border(draw, 23, 257, font, 'black', 'white', word)
    user_pic.save(f"生成的表情包存放地址")
    await lmtb.finish(
        Message(f"[CQ:image,file=file:///表情包地址,id=40000]"))

示例:头图

iii.为了你,继续打工

还是板子

en

这个表情包的思路来自nb2官方商店表情包插件petpet,有很多好玩表情包的(而我只会面向cv编程)
实现思路大概为获取用户头像,新建一块和模板一样大小的白色画布,依次把用户头像和模板粘贴上去就形成了表情包
需要的注意的点为用户头像需要改变大小并旋转最后放到指定位置,同时模板对于放头像的位置设置了透明,粘贴时需要把alpha通道作为mask传入
没啥好分段的
import nonebot
import requests
from nonebot.rule import *
from nonebot import *
from nonebot.plugin import on_keyword
from nonebot.adapters.onebot.v11 import Bot, Event, MessageEvent, GroupMessageEvent
from nonebot.adapters.onebot.v11.message import Message
import re
import PIL
from PIL import Image, ImageDraw, ImageFont


async def handle_rule(bot: Bot, event: Event) -> bool:
    with open("src/plugins/群名单.txt", encoding='utf-8') as file:
        white_block = []
        for line in file:
            line = line.strip()
            line = re.split("[ |#]", line)
            white_block.append(line[0])
    try:
        whatever, group_id, user_id = event.get_session_id().split('_')  # 仔细想想这里用isinstance(event,GroupMessageEvent)会好点,但需要接受群聊id干脆摆烂了
    except:  # 如果上面报错了,意味着发起的是私聊,返回格式为userid
        group_id = None
        user_id = event.get_session_id()
    if group_id in white_block or group_id == None:
        return True
    else:
        return False

bk_work = on_command("回去工作", rule=handle_rule, priority=50)

@bk_work.handle()
async def bk_work_handle(bot:Bot,event:Event):
    msg = event.get_message()
    user_id = ''
    for i in msg:
        if i.type=='at':
            user_id = i.data['qq']
    msg = event.get_plaintext()
    msg = msg.replace('/回去工作','').split()
    if user_id == '' :
        if msg[0].isnumeric():
            user_id = msg[0]
        else:
            await bk_work.finish(Message(f"指令输入错误,请输入QQ号或@号主"))
    # 上面是获取用户的@或者输入QQ号        
    url1 =f"http://q1.qlogo.cn/g?b=qq&nk={user_id}&s=640"  # QQ高清头像api
    pic_file = requests.get(url1)
    open(f"用户头像保存地址", "wb").write(pic_file.content)
    original_img = Image.open("板子地址")
    user_img = Image.open("用户头像地址")
    user_img = user_img.resize((220, 310)) # 这个是抄的petpet里面的,按着来吧
    user_img = user_img.rotate(25,expand=True) # 同上,expand用于适配(拉伸)旋转后的图像
    meme = Image.new("RGBA", size=original_img.size, color='white')
    meme.paste(user_img, (56, 32)) # petpet里的位置,也是固定实现效果最好的位置
    meme.paste(original_img, mask=original_img) # 把alpha通道作为mask
    meme.save("表情包保存地址")
    await bk_work.finish(Message(f"[CQ:image,file=file:///表情包保存地址,id=40000]"))

示例:头图

看petpet源码的时候发现是Mq佬自己写了个PIL的工具包,实现出来比较美观。自己直接硬套实现效果也差不多但比较粗糙,还是学不懂

2.问题及其他

  1. 字体的适配可能不是很好,最好自己改成需要的
  2. 换行的实现比较粗暴,不知道有没有更好地实现方式
  3. 写挺丑
  4. 请多多star Mq佬的petpet和memes插件
posted @ 2022-05-23 21:37  FPICZEIT  阅读(353)  评论(0编辑  收藏  举报