1. 程式人生 > 其它 >Nonebot2外掛:逝世表情包(2)

Nonebot2外掛:逝世表情包(2)

前言已經說倦了.jpg

總之本文是對商店外掛的拙劣模仿,不要在意複雜的語法使用,有問題可以直接說教我

廣告好像nb2熱度下來了,總之招租(

0.water之言

這篇文章依舊是實現簡單的表情包,大概是下面這三個

日向麻麻是404群的光
喜報:賬號被風控
草圖
需要準備的依舊是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.為了你,繼續打工

還是板子
這個表情包的思路來自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外掛