nonebot-plugin-kawaii-logos 0.0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nonebot_plugin_kawaii_logos/__init__.py +76 -0
- nonebot_plugin_kawaii_logos/command.py +43 -0
- nonebot_plugin_kawaii_logos/config.py +37 -0
- nonebot_plugin_kawaii_logos/draw.py +155 -0
- nonebot_plugin_kawaii_logos/tools.py +535 -0
- nonebot_plugin_kawaii_logos-0.0.0.1.dist-info/LICENSE +21 -0
- nonebot_plugin_kawaii_logos-0.0.0.1.dist-info/METADATA +134 -0
- nonebot_plugin_kawaii_logos-0.0.0.1.dist-info/RECORD +9 -0
- nonebot_plugin_kawaii_logos-0.0.0.1.dist-info/WHEEL +4 -0
@@ -0,0 +1,76 @@
|
|
1
|
+
# coding=utf-8
|
2
|
+
import html
|
3
|
+
from PIL import Image
|
4
|
+
from nonebot import logger, require, on_command
|
5
|
+
from nonebot.plugin import PluginMetadata, inherit_supported_adapters
|
6
|
+
from nonebot.adapters import Event
|
7
|
+
|
8
|
+
from .tools import save_image
|
9
|
+
from .config import Config, menu_data
|
10
|
+
from .command import command_kawaii_logos
|
11
|
+
|
12
|
+
require("nonebot_plugin_saa")
|
13
|
+
from nonebot_plugin_saa import Image as saaImage, MessageFactory
|
14
|
+
from nonebot_plugin_saa import Text as saaText
|
15
|
+
|
16
|
+
__plugin_meta__ = PluginMetadata(
|
17
|
+
name="kawaii_logos",
|
18
|
+
description="logo生成器,可以生成一些logo",
|
19
|
+
usage="/kwlogo",
|
20
|
+
type="application",
|
21
|
+
# 发布必填,当前有效类型有:`library`(为其他插件编写提供功能),`application`(向机器人用户提供功能)。
|
22
|
+
homepage="https://github.com/SuperGuGuGu/nonebot_plugin_kawaii_logos",
|
23
|
+
# 发布必填。
|
24
|
+
config=Config,
|
25
|
+
# 插件配置项类,如无需配置可不填写。
|
26
|
+
supported_adapters=inherit_supported_adapters(
|
27
|
+
"nonebot_plugin_saa",
|
28
|
+
),
|
29
|
+
# 支持的适配器集合,其中 `~` 在此处代表前缀 `nonebot.adapters.`,其余适配器亦按此格式填写。
|
30
|
+
# 若插件可以保证兼容所有适配器(即仅使用基本适配器功能)可不填写,否则应该列出插件支持的适配器。
|
31
|
+
extra={'menu_data': menu_data},
|
32
|
+
)
|
33
|
+
|
34
|
+
|
35
|
+
kawaii_logos_cmd = on_command("kwlogo", priority=10, block=False)
|
36
|
+
|
37
|
+
|
38
|
+
@kawaii_logos_cmd.handle()
|
39
|
+
async def download_msg(event: Event):
|
40
|
+
if not event.get_type().startswith("message"):
|
41
|
+
await kawaii_logos_cmd.finish()
|
42
|
+
msg: str = str(event.get_message().copy())
|
43
|
+
if msg == "":
|
44
|
+
await kawaii_logos_cmd.finish()
|
45
|
+
|
46
|
+
command_prefix = f"{msg.split('kwlogo')[0]}kwlogo"
|
47
|
+
args = msg.removeprefix(command_prefix).removeprefix(" ")
|
48
|
+
args = html.unescape(args) # 反转义文字
|
49
|
+
|
50
|
+
msg = await command_kawaii_logos(args=args)
|
51
|
+
|
52
|
+
await send(msg)
|
53
|
+
await kawaii_logos_cmd.finish()
|
54
|
+
|
55
|
+
|
56
|
+
async def send(msg):
|
57
|
+
if msg is None:
|
58
|
+
return
|
59
|
+
|
60
|
+
if type(msg) is not list:
|
61
|
+
msg = [msg]
|
62
|
+
|
63
|
+
saa_msg = []
|
64
|
+
for m in msg:
|
65
|
+
if type(m) is Image.Image:
|
66
|
+
saa_msg.append(saaImage(save_image(m, to_bytes=True)))
|
67
|
+
elif type(m) is bytes:
|
68
|
+
saa_msg.append(saaImage(m))
|
69
|
+
else:
|
70
|
+
saa_msg.append(saaText(m))
|
71
|
+
|
72
|
+
if not saa_msg:
|
73
|
+
return
|
74
|
+
|
75
|
+
msg_builder = MessageFactory(saa_msg)
|
76
|
+
await msg_builder.send()
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# coding=utf-8
|
2
|
+
from nonebot import logger
|
3
|
+
from .draw import template_1
|
4
|
+
|
5
|
+
|
6
|
+
async def command_kawaii_logos(args: str):
|
7
|
+
# 解析
|
8
|
+
args = args.split(" ")
|
9
|
+
if len(args) == 0:
|
10
|
+
return "请添加要生成logo的文字"
|
11
|
+
elif len(args) == 1:
|
12
|
+
logo_data: dict = {"标题": args[0], "副标题": None, "下": None,"上": None}
|
13
|
+
elif len(args) == 2:
|
14
|
+
logo_data: dict = {"标题": args[0], "副标题": args[1], "下": None,"上": None}
|
15
|
+
elif len(args) == 3:
|
16
|
+
logo_data: dict = {"标题": args[0], "副标题": args[1], "下": args[2],"上": None}
|
17
|
+
elif len(args) == 4:
|
18
|
+
logo_data: dict = {"标题": args[0], "副标题": args[1], "下": args[2],"上": args[3]}
|
19
|
+
else:
|
20
|
+
return "文字参数过多,最多包含4段文字"
|
21
|
+
|
22
|
+
# 检查内容长度
|
23
|
+
if not 3 <= len(logo_data["标题"]):
|
24
|
+
return "第1段文字过短"
|
25
|
+
if not len(logo_data["标题"]) <= 10:
|
26
|
+
return "第1段文字过长"
|
27
|
+
if not 3 <= len(logo_data["副标题"]):
|
28
|
+
return "第2段文字过短"
|
29
|
+
if not len(logo_data["副标题"]) <= 10:
|
30
|
+
return "第2段文字过长"
|
31
|
+
if not 3 <= len(logo_data["下"]):
|
32
|
+
return "第3段文字过短"
|
33
|
+
if not len(logo_data["下"]) <= 15:
|
34
|
+
return "第3段文字过长"
|
35
|
+
if not 3 <= len(logo_data["上"]):
|
36
|
+
return "第4段文字过短"
|
37
|
+
if not len(logo_data["上"]) <= 15:
|
38
|
+
return "第4段文字过长"
|
39
|
+
|
40
|
+
image = await template_1(logo_data=logo_data)
|
41
|
+
|
42
|
+
return image
|
43
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# coding=utf-8
|
2
|
+
from nonebot import get_plugin_config, logger
|
3
|
+
from pydantic import BaseModel
|
4
|
+
from pathlib import Path
|
5
|
+
from nonebot import require
|
6
|
+
|
7
|
+
try:
|
8
|
+
require("nonebot_plugin_localstore")
|
9
|
+
import nonebot_plugin_localstore as store
|
10
|
+
|
11
|
+
plugin_cache_dir: Path = store.get_plugin_cache_dir()
|
12
|
+
# plugin_config_dir: Path = store.get_plugin_config_dir()
|
13
|
+
plugin_data_dir: Path = store.get_plugin_data_dir()
|
14
|
+
except Exception as e:
|
15
|
+
plugin_cache_dir: Path = Path("./nonebot_plugin_kawaii_logos/cache")
|
16
|
+
plugin_data_dir: Path = Path("./nonebot_plugin_kawaii_logos/data")
|
17
|
+
|
18
|
+
|
19
|
+
class Config(BaseModel):
|
20
|
+
...
|
21
|
+
|
22
|
+
|
23
|
+
try:
|
24
|
+
plugin_config = get_plugin_config(Config)
|
25
|
+
# qb_url = plugin_config.qbm_url
|
26
|
+
except Exception as e:
|
27
|
+
pass
|
28
|
+
|
29
|
+
|
30
|
+
menu_data = [
|
31
|
+
{
|
32
|
+
"trigger_method": "qb帮助",
|
33
|
+
"func": "列出命令列表",
|
34
|
+
"trigger_condition": " ",
|
35
|
+
"brief_des": "qb帮助",
|
36
|
+
}
|
37
|
+
]
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# coding=utf-8
|
2
|
+
import os
|
3
|
+
import random
|
4
|
+
import cv2
|
5
|
+
import numpy as np
|
6
|
+
from PIL import Image
|
7
|
+
from nonebot import logger
|
8
|
+
|
9
|
+
from .config import plugin_cache_dir
|
10
|
+
from .tools import circle_corner, text_to_b64, load_image, draw_text, save_image
|
11
|
+
|
12
|
+
|
13
|
+
text_full = (
|
14
|
+
"あアいイうウえエおオかカきキくクけケこコさサしシすスせセそソたタちチつツてテとトなナにニぬヌねネのノはハひヒふフへヘほホまマみミむムめメもモや"
|
15
|
+
"ヤゆユよヨらラりリるルれレろロわワをヲんンがガぎギぐグげゲごゴざザじジずズぜゼぞゾだダぢヂづヅでデどドばバびビぶブべベぼボぱパぴピぷプぺペぽポ"
|
16
|
+
)
|
17
|
+
text_half = "ゃャゅュょョ"
|
18
|
+
|
19
|
+
|
20
|
+
async def template_1(logo_data: dict[str, str]) -> Image.Image:
|
21
|
+
"""
|
22
|
+
模板1
|
23
|
+
:param logo_data:
|
24
|
+
:return:
|
25
|
+
"""
|
26
|
+
image_size = (1000, 500)
|
27
|
+
image = Image.new("RGBA", image_size, "#FFFFFF00")
|
28
|
+
|
29
|
+
size_list = [random.randint(110, 190) for _ in range(len(logo_data["标题"]))]
|
30
|
+
rotate_list = [random.randint(-20, 20) for _ in range(len(logo_data["标题"]))]
|
31
|
+
y_list = [random.randint(-20, 20) for _ in range(len(logo_data["标题"]))]
|
32
|
+
|
33
|
+
image_size_list = [2, 1, 0, -1]
|
34
|
+
image_color_list = ["#7c71ec", "#fff", "#7c71ec", "#fff"]
|
35
|
+
for i, image_size in enumerate(image_size_list):
|
36
|
+
color = image_color_list[i]
|
37
|
+
x, y = 0, 100
|
38
|
+
for i_t, text in enumerate(logo_data["标题"]):
|
39
|
+
rotate = rotate_list[i_t]
|
40
|
+
size = size_list[i_t]
|
41
|
+
|
42
|
+
paste_alpha = await kawaii_text_to_image(text, image_size)
|
43
|
+
paste_alpha = paste_alpha.resize((size, size))
|
44
|
+
paste_alpha = paste_alpha.rotate(rotate)
|
45
|
+
image_color = Image.new("RGBA", paste_alpha.size, color)
|
46
|
+
image.paste(image_color, (x, y + y_list[i_t]), mask=paste_alpha)
|
47
|
+
|
48
|
+
x += int(size * 0.7)
|
49
|
+
|
50
|
+
return image
|
51
|
+
|
52
|
+
|
53
|
+
async def kawaii_text_to_image(text: str, size=0) -> Image.Image:
|
54
|
+
"""
|
55
|
+
获取字符图片
|
56
|
+
高光仅适用于日文
|
57
|
+
:param text:要获取的文字
|
58
|
+
:param size: -1:高光, 0:本体, 1:描边一, 2:描边二
|
59
|
+
:return:PLI.Image.Image
|
60
|
+
"""
|
61
|
+
text_image_path = plugin_cache_dir / "text"
|
62
|
+
text_image_path.mkdir(exist_ok=True)
|
63
|
+
text_image_path = text_image_path / f"{text_to_b64(text, replace=True)}_{size}.png"
|
64
|
+
if os.path.exists(text_image_path):
|
65
|
+
image = await load_image(text_image_path)
|
66
|
+
return image
|
67
|
+
|
68
|
+
logger.debug(f"未绘制‘{text}’,进行绘制")
|
69
|
+
if size == -1:
|
70
|
+
if text not in text_full + text_half:
|
71
|
+
return Image.new("RGBA", (200, 200), (0, 0, 0, 0))
|
72
|
+
for i, t in enumerate(text_full + text_half):
|
73
|
+
if t == text:
|
74
|
+
text = i + 1
|
75
|
+
url = f"https://cdn.kanon.ink/api/image?imageid=knapi-kawaii_logos-{text}_{size}.png"
|
76
|
+
try:
|
77
|
+
image = await load_image(url, cache_image=False)
|
78
|
+
save_image(image, text_image_path.parent, text_image_path.name, mode="png")
|
79
|
+
except Exception as e:
|
80
|
+
logger.error("请求高光图片失败")
|
81
|
+
logger.error(e)
|
82
|
+
image = Image.new("RGBA", (200, 200), (0, 0, 0, 0))
|
83
|
+
return image
|
84
|
+
|
85
|
+
image_0_path = text_image_path.parent / f"{text_to_b64(text, replace=True)}_0.png"
|
86
|
+
if os.path.exists(image_0_path):
|
87
|
+
image_0 = await load_image(image_0_path)
|
88
|
+
else:
|
89
|
+
paste_image = await draw_text(
|
90
|
+
text,
|
91
|
+
size=170,
|
92
|
+
textlen=50,
|
93
|
+
fontfile="胖胖猪肉体_猫啃网.otf",
|
94
|
+
text_color="#fff",
|
95
|
+
calculate=False
|
96
|
+
)
|
97
|
+
image_0 = Image.new("RGBA", (200, 200), (0, 0, 0, 0))
|
98
|
+
image_0.alpha_composite(paste_image, (
|
99
|
+
int((image_0.size[0] - paste_image.size[0]) / 2),
|
100
|
+
int((image_0.size[1] - paste_image.size[1]) / 2)
|
101
|
+
))
|
102
|
+
save_image(image_0, image_0_path.parent, f"{text_to_b64(text, replace=True)}_0.png", mode="png")
|
103
|
+
if size == 0:
|
104
|
+
return image_0
|
105
|
+
|
106
|
+
if size == 1:
|
107
|
+
image_1 = draw_out_line(image_0, 7, (255, 255, 255, 255))
|
108
|
+
image_1 = image_1.convert("RGBA")
|
109
|
+
save_image(image_1, text_image_path.parent, text_image_path.name, mode="png")
|
110
|
+
return image_1
|
111
|
+
|
112
|
+
if size == 2:
|
113
|
+
image_2 = draw_out_line(image_0, 11, (255, 255, 255, 255))
|
114
|
+
image_2 = image_2.convert("RGBA")
|
115
|
+
save_image(image_2, text_image_path.parent, text_image_path.name, mode="png")
|
116
|
+
return image_2
|
117
|
+
|
118
|
+
|
119
|
+
def draw_out_line(image, size=5, color=None):
|
120
|
+
if color is None:
|
121
|
+
logger.warning("color is None")
|
122
|
+
color = [100, 100, 100, 255]
|
123
|
+
image_np = np.array(image)
|
124
|
+
image_gray = cv2.cvtColor(image_np, cv2.COLOR_RGB2GRAY)
|
125
|
+
|
126
|
+
# 使用形态学操作生成描边
|
127
|
+
kernel = np.ones((3, 3), np.uint8)
|
128
|
+
dilated = cv2.dilate(image_gray, kernel, iterations=size)
|
129
|
+
|
130
|
+
edges = dilated - image_gray
|
131
|
+
|
132
|
+
# 将描边区域叠加到原图上
|
133
|
+
image_np[edges > 0] = color # 描边颜色
|
134
|
+
|
135
|
+
# 将结果转换回 PIL 图像
|
136
|
+
result_image = Image.fromarray(image_np)
|
137
|
+
|
138
|
+
image = Image.new("RGBA", result_image.size, (0, 0, 0, 0))
|
139
|
+
image.alpha_composite(result_image)
|
140
|
+
|
141
|
+
pixels = image.load()
|
142
|
+
for i in range(image.width):
|
143
|
+
for j in range(image.height):
|
144
|
+
if pixels[i, j] == (0, 0, 0, 255):
|
145
|
+
pass
|
146
|
+
# pixels[i, j] = (0, 0, 0, 0)
|
147
|
+
elif pixels[i, j] == (0, 0, 0, 0):
|
148
|
+
pass
|
149
|
+
else:
|
150
|
+
pixels[i, j] = (color[0], color[1], color[2], 255)
|
151
|
+
|
152
|
+
return image
|
153
|
+
|
154
|
+
|
155
|
+
|
@@ -0,0 +1,535 @@
|
|
1
|
+
# coding=utf-8
|
2
|
+
import base64
|
3
|
+
import io
|
4
|
+
import json
|
5
|
+
import re
|
6
|
+
import shutil
|
7
|
+
import httpx
|
8
|
+
from PIL.Image import Image as PIL_Image
|
9
|
+
from PIL import Image, ImageDraw, ImageFont
|
10
|
+
import random
|
11
|
+
import os
|
12
|
+
import time
|
13
|
+
import matplotlib.font_manager as fm
|
14
|
+
from nonebot import logger
|
15
|
+
from pathlib import Path
|
16
|
+
from .config import plugin_cache_dir
|
17
|
+
|
18
|
+
kwlogo_cache = {
|
19
|
+
"font_path": {}
|
20
|
+
}
|
21
|
+
system_font_list = fm.findSystemFonts(fontpaths=None, fontext='ttf')
|
22
|
+
for font_path in system_font_list:
|
23
|
+
font_path = font_path.replace("\\", "/")
|
24
|
+
kwlogo_cache["font_path"][font_path.split("/")[-1]] = font_path
|
25
|
+
|
26
|
+
|
27
|
+
def save_image(
|
28
|
+
image,
|
29
|
+
image_path: str | Path = None,
|
30
|
+
image_name: int | str = None,
|
31
|
+
to_bytes: bool = False,
|
32
|
+
mode: str = "jpg"):
|
33
|
+
"""
|
34
|
+
保存图片文件到缓存文件夹
|
35
|
+
:param image:要保存的图片
|
36
|
+
:param image_path: 指定的图片所在文件夹路径,默认为缓存
|
37
|
+
:param image_name:图片名称,不填为随机数字
|
38
|
+
:param to_bytes: 是否转为bytes
|
39
|
+
:param mode: 保存的图片格式
|
40
|
+
:return:保存的路径
|
41
|
+
"""
|
42
|
+
if mode == "jpg":
|
43
|
+
image = image.convert("RGB")
|
44
|
+
if to_bytes is True and type(image) is PIL_Image:
|
45
|
+
# 将Pillow图像数据保存到内存中
|
46
|
+
image_stream = io.BytesIO()
|
47
|
+
image.save(image_stream, format='JPEG')
|
48
|
+
image_stream.seek(0)
|
49
|
+
return image_stream.read()
|
50
|
+
|
51
|
+
d_y, d_m, d_d = time.strftime("%Y/%m/%d", time.localtime()).split("/")
|
52
|
+
time_now = int(time.time())
|
53
|
+
|
54
|
+
if image_path is None:
|
55
|
+
image_path = plugin_cache_dir / "cache" / d_y / d_m / d_d
|
56
|
+
os.makedirs(image_path, exist_ok=True)
|
57
|
+
|
58
|
+
if image_name is None:
|
59
|
+
image_name = f"{time_now}_{random.randint(1000, 9999)}"
|
60
|
+
num = 50
|
61
|
+
while True and num > 0:
|
62
|
+
num -= 1
|
63
|
+
random_num = str(random.randint(1000, 9999))
|
64
|
+
if os.path.exists(image_path / f"{image_name}_{random_num}.{mode}"):
|
65
|
+
continue
|
66
|
+
image_name = f"{image_name}_{random_num}.{mode}"
|
67
|
+
break
|
68
|
+
|
69
|
+
logger.debug(f"保存图片文件:{image_path}/{image_name}")
|
70
|
+
image.save(image_path / image_name)
|
71
|
+
|
72
|
+
if to_bytes is True:
|
73
|
+
image_file = open(image_path / image_name, "rb")
|
74
|
+
image = image_file.read()
|
75
|
+
image_file.close()
|
76
|
+
return image
|
77
|
+
return image_path / image_name
|
78
|
+
|
79
|
+
|
80
|
+
def circle_corner(img, radii: int):
|
81
|
+
"""
|
82
|
+
圆角处理
|
83
|
+
:param img: 源图象。
|
84
|
+
:param radii: 半径,如:30。
|
85
|
+
:return: 返回一个圆角处理后的图象。
|
86
|
+
"""
|
87
|
+
|
88
|
+
# 画圆(用于分离4个角)
|
89
|
+
circle = Image.new('L', (radii * 2, radii * 2), 0) # 创建一个黑色背景的画布
|
90
|
+
draw = ImageDraw.Draw(circle)
|
91
|
+
draw.ellipse((0, 0, radii * 2, radii * 2), fill=255) # 画白色圆形
|
92
|
+
|
93
|
+
# 原图
|
94
|
+
img = img.convert("RGBA")
|
95
|
+
w, h = img.size
|
96
|
+
|
97
|
+
# 画4个角(将整圆分离为4个部分)
|
98
|
+
alpha = Image.new('L', img.size, 255)
|
99
|
+
alpha.paste(circle.crop((0, 0, radii, radii)), (0, 0)) # 左上角
|
100
|
+
alpha.paste(circle.crop((radii, 0, radii * 2, radii)), (w - radii, 0)) # 右上角
|
101
|
+
alpha.paste(circle.crop((radii, radii, radii * 2, radii * 2)), (w - radii, h - radii)) # 右下角
|
102
|
+
alpha.paste(circle.crop((0, radii, radii, radii * 2)), (0, h - radii)) # 左下角
|
103
|
+
# alpha.show()
|
104
|
+
|
105
|
+
img.putalpha(alpha) # 白色区域透明可见,黑色区域不可见
|
106
|
+
return img
|
107
|
+
|
108
|
+
|
109
|
+
def image_resize2(image, size: [int, int], overturn=False):
|
110
|
+
"""
|
111
|
+
重缩放图像
|
112
|
+
:param image: 要缩放的图像
|
113
|
+
:param size: 缩放后的大小
|
114
|
+
:param overturn: 是否放大到全屏
|
115
|
+
:return: 缩放后的图像
|
116
|
+
"""
|
117
|
+
x, y = image.size
|
118
|
+
if size[0] is None:
|
119
|
+
size = (int(size[1] * x / y), size[1])
|
120
|
+
if size[1] is None:
|
121
|
+
size = (size[0], int(size[0] * y / x))
|
122
|
+
|
123
|
+
image_background = Image.new("RGBA", size=size, color=(0, 0, 0, 0))
|
124
|
+
image = image.convert("RGBA")
|
125
|
+
w, h = image_background.size
|
126
|
+
x, y = image.size
|
127
|
+
if overturn:
|
128
|
+
if w / h >= x / y:
|
129
|
+
rex = w
|
130
|
+
rey = int(rex * y / x)
|
131
|
+
paste_image = image.resize((rex, rey))
|
132
|
+
image_background.alpha_composite(paste_image, (0, 0))
|
133
|
+
else:
|
134
|
+
rey = h
|
135
|
+
rex = int(rey * x / y)
|
136
|
+
paste_image = image.resize((rex, rey))
|
137
|
+
x = int((w - rex) / 2)
|
138
|
+
image_background.alpha_composite(paste_image, (x, 0))
|
139
|
+
else:
|
140
|
+
if w / h >= x / y:
|
141
|
+
rey = h
|
142
|
+
rex = int(rey * x / y)
|
143
|
+
paste_image = image.resize((rex, rey))
|
144
|
+
x = int((w - rex) / 2)
|
145
|
+
y = 0
|
146
|
+
image_background.alpha_composite(paste_image, (x, y))
|
147
|
+
else:
|
148
|
+
rex = w
|
149
|
+
rey = int(rex * y / x)
|
150
|
+
paste_image = image.resize((rex, rey))
|
151
|
+
x = 0
|
152
|
+
y = int((h - rey) / 2)
|
153
|
+
image_background.alpha_composite(paste_image, (x, y))
|
154
|
+
|
155
|
+
return image_background
|
156
|
+
|
157
|
+
|
158
|
+
async def draw_text(
|
159
|
+
texts: str,
|
160
|
+
size: int,
|
161
|
+
textlen: int = 20,
|
162
|
+
fontfile: str = "",
|
163
|
+
text_color=None,
|
164
|
+
calculate=False
|
165
|
+
):
|
166
|
+
"""
|
167
|
+
- 文字转图片
|
168
|
+
:param texts: 输入的字符串
|
169
|
+
:param size: 文字尺寸
|
170
|
+
:param textlen: 一行的文字数量
|
171
|
+
:param fontfile: 字体文字
|
172
|
+
:param text_color: 字体颜色,例:"#FFFFFF"、(10, 10, 10)
|
173
|
+
:param calculate: 计算长度。True时只返回空白图,不用粘贴文字,加快速度。
|
174
|
+
|
175
|
+
:return: 图片文件(RGBA)
|
176
|
+
"""
|
177
|
+
if texts is None:
|
178
|
+
texts = "None"
|
179
|
+
if text_color is None:
|
180
|
+
text_color = "#000000"
|
181
|
+
|
182
|
+
def get_font_render_w(text):
|
183
|
+
if text == " ":
|
184
|
+
return 20
|
185
|
+
none = ["\n", ""]
|
186
|
+
if text in none:
|
187
|
+
return 1
|
188
|
+
canvas = Image.new('RGB', (500, 500))
|
189
|
+
draw = ImageDraw.Draw(canvas)
|
190
|
+
draw.text((0, 0), text, font=font, fill=(255, 255, 255))
|
191
|
+
bbox = canvas.getbbox()
|
192
|
+
# 宽高
|
193
|
+
# size = (bbox[2] - bbox[0], bbox[3] - bbox[1])
|
194
|
+
if bbox is None:
|
195
|
+
return 0
|
196
|
+
return bbox[2]
|
197
|
+
|
198
|
+
def sequence_generator(sequence):
|
199
|
+
for value in sequence:
|
200
|
+
yield value
|
201
|
+
|
202
|
+
default_font = ["msyh.ttc", "DejaVuSans.ttf", "msjh.ttc", "msjhl.ttc", "msjhb.ttc", "YuGothR.ttc"]
|
203
|
+
if fontfile is None or fontfile == "":
|
204
|
+
fontfile = "msyh.ttc"
|
205
|
+
if not fontfile.startswith("/") or ":/" in fontfile:
|
206
|
+
# 获取字体绝对路径
|
207
|
+
|
208
|
+
font_list = [fontfile] + default_font + ["no_font"]
|
209
|
+
for font in font_list:
|
210
|
+
if font == "no_font":
|
211
|
+
logger.warning(f"{fontfile}字体加载失败,将使用默认字体")
|
212
|
+
# raise f"字体加载失败,请安装字体"
|
213
|
+
fontfile = font_list[0]
|
214
|
+
break
|
215
|
+
|
216
|
+
if font in kwlogo_cache["font_path"].keys():
|
217
|
+
fontfile = kwlogo_cache["font_path"][font]
|
218
|
+
break
|
219
|
+
|
220
|
+
if os.path.exists(fontfile):
|
221
|
+
break
|
222
|
+
|
223
|
+
font = ImageFont.truetype(font=fontfile, size=size)
|
224
|
+
|
225
|
+
# 计算图片尺寸
|
226
|
+
print_x = 0
|
227
|
+
print_y = 0
|
228
|
+
jump_num = 0
|
229
|
+
text_num = -1
|
230
|
+
max_text_y = 0
|
231
|
+
max_text_y_list = []
|
232
|
+
texts_len = len(texts)
|
233
|
+
for text in texts:
|
234
|
+
text_num += 1
|
235
|
+
if jump_num > 0:
|
236
|
+
jump_num -= 1
|
237
|
+
else:
|
238
|
+
if (textlen * size) < print_x or text == "\n":
|
239
|
+
print_x = 0
|
240
|
+
print_y += 1.3 * max_text_y
|
241
|
+
max_text_y_list.append(max_text_y)
|
242
|
+
max_text_y = 0
|
243
|
+
if text == "\n":
|
244
|
+
continue
|
245
|
+
if text == " ":
|
246
|
+
print_x += get_font_render_w(text) + 2
|
247
|
+
if size > max_text_y:
|
248
|
+
max_text_y = size
|
249
|
+
continue
|
250
|
+
if text == "<":
|
251
|
+
while text_num + jump_num < texts_len and texts[text_num + jump_num] != ">":
|
252
|
+
jump_num += 1
|
253
|
+
jump_num += 0
|
254
|
+
|
255
|
+
text = texts[text_num:text_num + jump_num]
|
256
|
+
pattern = r'src="([^"]+)"'
|
257
|
+
urls = re.findall(pattern, text)
|
258
|
+
if urls:
|
259
|
+
pattern = r'width="(\d+)"'
|
260
|
+
image_size_x = re.findall(pattern, text)
|
261
|
+
|
262
|
+
paste_image = await load_image(urls[0])
|
263
|
+
if image_size_x:
|
264
|
+
paste_image = image_resize2(paste_image, (int(image_size_x[0]), None))
|
265
|
+
print_x += paste_image.size[0] + 2
|
266
|
+
if paste_image.size[1] > max_text_y:
|
267
|
+
max_text_y = paste_image.size[1]
|
268
|
+
continue
|
269
|
+
print_x += get_font_render_w(text) + 2
|
270
|
+
if size > max_text_y:
|
271
|
+
max_text_y = size
|
272
|
+
max_text_y_list.append(max_text_y)
|
273
|
+
text_y_list = sequence_generator(max_text_y_list)
|
274
|
+
|
275
|
+
x = int((textlen + 1.5) * size)
|
276
|
+
y = int(print_y + 1.2 * size)
|
277
|
+
|
278
|
+
image = Image.new("RGBA", size=(x, y), color=(0, 0, 0, 0)) # 生成透明图片
|
279
|
+
draw_image = ImageDraw.Draw(image)
|
280
|
+
|
281
|
+
# 绘制文字
|
282
|
+
if calculate is False:
|
283
|
+
print_x = 0
|
284
|
+
print_y = 0
|
285
|
+
jump_num = 0
|
286
|
+
text_num = -1
|
287
|
+
draw_max_text_y = next(text_y_list)
|
288
|
+
for text in texts:
|
289
|
+
text_num += 1
|
290
|
+
if jump_num > 0:
|
291
|
+
jump_num -= 1
|
292
|
+
else:
|
293
|
+
if (textlen * size) < print_x or text == "\n":
|
294
|
+
print_x = 0
|
295
|
+
print_y += draw_max_text_y
|
296
|
+
draw_max_text_y = next(text_y_list, None)
|
297
|
+
if text == "\n":
|
298
|
+
continue
|
299
|
+
if text in ["\n", " "]:
|
300
|
+
if text == " ":
|
301
|
+
print_x += get_font_render_w(text) + 2
|
302
|
+
continue
|
303
|
+
if text == "<":
|
304
|
+
while text_num + jump_num < texts_len and texts[text_num + jump_num] != ">":
|
305
|
+
jump_num += 1
|
306
|
+
jump_num += 0
|
307
|
+
|
308
|
+
text = texts[text_num:text_num + jump_num]
|
309
|
+
pattern = r'src="([^"]+)"'
|
310
|
+
urls = re.findall(pattern, text)
|
311
|
+
if urls:
|
312
|
+
pattern = r'width="(\d+)"'
|
313
|
+
image_size_x = re.findall(pattern, text)
|
314
|
+
|
315
|
+
paste_image = await load_image(urls[0])
|
316
|
+
if image_size_x:
|
317
|
+
paste_image = image_resize2(paste_image, (int(image_size_x[0]), None))
|
318
|
+
image.alpha_composite(paste_image, (int(print_x), int(print_y)))
|
319
|
+
print_x += paste_image.size[0] + 2
|
320
|
+
continue
|
321
|
+
|
322
|
+
draw_image.text(xy=(int(print_x), int(print_y)),
|
323
|
+
text=text,
|
324
|
+
fill=text_color,
|
325
|
+
font=font)
|
326
|
+
print_x += get_font_render_w(text) + 2
|
327
|
+
# 把输出的图片裁剪为只有内容的部分
|
328
|
+
bbox = image.getbbox()
|
329
|
+
if bbox is None:
|
330
|
+
box_image = Image.new("RGBA", (2, size), (0, 0, 0, 0))
|
331
|
+
else:
|
332
|
+
box_image = Image.new("RGBA", (bbox[2] - bbox[0], bbox[3] - bbox[1]), (0, 0, 0, 0))
|
333
|
+
box_image.paste(image, (0 - int(bbox[0]), 0 - int(bbox[1])), mask=image)
|
334
|
+
image = box_image
|
335
|
+
return image
|
336
|
+
|
337
|
+
|
338
|
+
async def load_image(path: str | Image.Image | Path, size=None, mode=None, cache_image=True):
|
339
|
+
"""
|
340
|
+
读取图片或请求网络图片
|
341
|
+
:param path: 图片路径/图片url
|
342
|
+
:param size: 出错时候返回的图片尺寸
|
343
|
+
:param mode: 图片读取模式
|
344
|
+
:param cache_image: 焕缓存获取的图片
|
345
|
+
:return:image
|
346
|
+
"""
|
347
|
+
if type(path) is Image.Image:
|
348
|
+
return path
|
349
|
+
if mode is None:
|
350
|
+
mode = "r"
|
351
|
+
try:
|
352
|
+
if type(path) is str and path.startswith("http"):
|
353
|
+
if cache_image is False:
|
354
|
+
image = await connect_api("image", path)
|
355
|
+
else:
|
356
|
+
cache_path = Path(path.removeprefix("http://").removeprefix("https://").split("?")[0])
|
357
|
+
|
358
|
+
if os.path.exists(plugin_cache_dir / "web_cache" / cache_path):
|
359
|
+
return Image.open(plugin_cache_dir / "web_cache" / cache_path)
|
360
|
+
file_name = cache_path.name
|
361
|
+
file_path = plugin_cache_dir / "web_cache" / cache_path.parent
|
362
|
+
os.makedirs(file_path, exist_ok=True)
|
363
|
+
image = await connect_api("image", path)
|
364
|
+
image.save(file_path / file_name)
|
365
|
+
|
366
|
+
return image
|
367
|
+
else:
|
368
|
+
if type(path) is str and path.startswith("{cache_dir}"):
|
369
|
+
image_path = plugin_cache_dir / Path(path.removeprefix("{cache_dir}"))
|
370
|
+
if not os.path.exists(image_path):
|
371
|
+
raise "图片不存在"
|
372
|
+
image = Image.open(image_path, mode)
|
373
|
+
if mode == "rb":
|
374
|
+
return save_image(image, to_bytes=True)
|
375
|
+
return image
|
376
|
+
return Image.open(path, mode)
|
377
|
+
except Exception as e:
|
378
|
+
logger.error(f"读取图片错误:{path}")
|
379
|
+
logger.error(e)
|
380
|
+
if size is not None:
|
381
|
+
return Image.new("RGBA", size, (0, 0, 0, 0))
|
382
|
+
raise "图片读取错误"
|
383
|
+
|
384
|
+
|
385
|
+
def draw_gradient_color(
|
386
|
+
color_a: tuple | str,
|
387
|
+
color_b: tuple | str,
|
388
|
+
size: tuple[int, int] | list[int, int],
|
389
|
+
):
|
390
|
+
"""
|
391
|
+
绘制一张从左到右的渐变
|
392
|
+
:param size: 图片的尺寸
|
393
|
+
:param color_a: 图片读取模式
|
394
|
+
:param color_b: 图片读取模式
|
395
|
+
:return:image
|
396
|
+
"""
|
397
|
+
|
398
|
+
def covert_color(c: tuple | str) -> tuple:
|
399
|
+
"""
|
400
|
+
转换str颜色到tuple颜色
|
401
|
+
"""
|
402
|
+
if type(c) is str:
|
403
|
+
c = (
|
404
|
+
int(c[1:3], 16),
|
405
|
+
int(c[3:5], 16),
|
406
|
+
int(c[5:7], 16),
|
407
|
+
255 if len(c) == 7 else int(c[7:9], 16)
|
408
|
+
)
|
409
|
+
return c
|
410
|
+
|
411
|
+
color_a = covert_color(color_a)
|
412
|
+
color_b = covert_color(color_b)
|
413
|
+
|
414
|
+
image = Image.new("RGBA", (size[0], 1), (0, 0, 0, 0))
|
415
|
+
img_array = image.load()
|
416
|
+
for i in range(size[0]):
|
417
|
+
color = (
|
418
|
+
int(color_a + ((color_b[0] - color_a[0]) / size[0] * i)),
|
419
|
+
int(color_a + ((color_b[1] - color_a[1]) / size[0] * i)),
|
420
|
+
int(color_a + ((color_b[2] - color_a[2]) / size[0] * i)),
|
421
|
+
int(color_a + ((color_b[3] - color_a[3]) / size[0] * i)),
|
422
|
+
)
|
423
|
+
img_array[i, 0] = color
|
424
|
+
image = image.resize(size)
|
425
|
+
return image
|
426
|
+
|
427
|
+
|
428
|
+
async def connect_api(
|
429
|
+
connect_type: str,
|
430
|
+
url: str,
|
431
|
+
post_json=None,
|
432
|
+
file_path: str = None,
|
433
|
+
timeout: int = 10
|
434
|
+
):
|
435
|
+
"""
|
436
|
+
请求网络资源
|
437
|
+
:param connect_type: json, image, file
|
438
|
+
:param url: url
|
439
|
+
:param post_json: 要post的内容
|
440
|
+
:param file_path: 文件的保存路径
|
441
|
+
:param timeout: 超时时间
|
442
|
+
:return:
|
443
|
+
"""
|
444
|
+
logger.debug(f"connect_api请求URL:{url}")
|
445
|
+
h = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
|
446
|
+
"Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.76"}
|
447
|
+
if connect_type == "json":
|
448
|
+
if post_json is None:
|
449
|
+
async with httpx.AsyncClient() as client:
|
450
|
+
data = await client.get(url, headers=h, timeout=timeout)
|
451
|
+
return json.loads(data.text)
|
452
|
+
else:
|
453
|
+
async with httpx.AsyncClient() as client:
|
454
|
+
data = await client.post(url, json=post_json, headers=h, timeout=timeout)
|
455
|
+
return json.loads(data.text)
|
456
|
+
elif connect_type == "image":
|
457
|
+
if url is None or url in ["none", "None", "", " "]:
|
458
|
+
image = await draw_text("获取图片出错", 50, 10)
|
459
|
+
else:
|
460
|
+
try:
|
461
|
+
async with httpx.AsyncClient() as client:
|
462
|
+
data = await client.get(url, timeout=timeout)
|
463
|
+
image = Image.open(io.BytesIO(data.content))
|
464
|
+
except Exception as e:
|
465
|
+
logger.error(e)
|
466
|
+
logger.error(url)
|
467
|
+
raise "获取图片出错"
|
468
|
+
return image
|
469
|
+
elif connect_type == "file":
|
470
|
+
cache_file_path = file_path + "cache"
|
471
|
+
f = open(cache_file_path, "wb")
|
472
|
+
try:
|
473
|
+
res = httpx.get(url, headers=h, timeout=timeout).content
|
474
|
+
f.write(res)
|
475
|
+
logger.debug(f"下载完成-{file_path}")
|
476
|
+
except Exception as e:
|
477
|
+
logger.error(e)
|
478
|
+
raise Exception
|
479
|
+
finally:
|
480
|
+
f.close()
|
481
|
+
shutil.copyfile(cache_file_path, file_path)
|
482
|
+
os.remove(cache_file_path)
|
483
|
+
return
|
484
|
+
|
485
|
+
|
486
|
+
async def mix_image(image_1, image_2, mix_type=1):
|
487
|
+
"""
|
488
|
+
将两张图合并为1张
|
489
|
+
:param image_1: 要合并的图像1
|
490
|
+
:param image_2: 要合并的图像2
|
491
|
+
:param mix_type: 合成方式。1:竖向
|
492
|
+
:return:
|
493
|
+
"""
|
494
|
+
if type(image_1) is str:
|
495
|
+
image_1 = await load_image(image_1)
|
496
|
+
if type(image_2) is str:
|
497
|
+
image_2 = await load_image(image_2)
|
498
|
+
if mix_type == 1:
|
499
|
+
x1, y1 = image_1.size
|
500
|
+
x2, y2 = image_2.size
|
501
|
+
if image_1.mode == "RGB":
|
502
|
+
image_1 = image_1.convert("RGBA")
|
503
|
+
if image_2.mode == "RGB":
|
504
|
+
image_2 = image_2.convert("RGBA")
|
505
|
+
|
506
|
+
if x1 > x2:
|
507
|
+
x2_m = x1
|
508
|
+
y2_m = int(x2_m / x2 * y2)
|
509
|
+
images = Image.new("RGBA", (x2_m, y2_m + y1), (0, 0, 0, 0))
|
510
|
+
image_2_m = image_2.resize((x2_m, y2_m))
|
511
|
+
images.alpha_composite(image_1, (0, 0))
|
512
|
+
images.alpha_composite(image_2_m, (0, y1))
|
513
|
+
return images
|
514
|
+
else: # x1 < x2
|
515
|
+
x1_m = x2
|
516
|
+
y1_m = int(x1_m / x1 * y1)
|
517
|
+
images = Image.new("RGBA", (x1_m, y1_m + y2), (0, 0, 0, 0))
|
518
|
+
image_1_m = image_1.resize((x1_m, y1_m))
|
519
|
+
images.alpha_composite(image_1_m, (0, 0))
|
520
|
+
images.alpha_composite(image_2, (0, y1_m))
|
521
|
+
return images
|
522
|
+
raise "未知的合并图像方式"
|
523
|
+
|
524
|
+
|
525
|
+
def text_to_b64(text: str, replace=False) -> str:
|
526
|
+
b64 = str(base64.b64encode(text.encode('UTF-8')), encoding='UTF-8')
|
527
|
+
if replace is True:
|
528
|
+
return b64.replace("+", "-").replace("/", "_")
|
529
|
+
return b64
|
530
|
+
|
531
|
+
|
532
|
+
def b64_to_text(b64_text: str, replace=False) -> str:
|
533
|
+
if replace is True:
|
534
|
+
b64_text = b64_text.replace("-", "+").replace("_", "/")
|
535
|
+
return base64.b64decode(b64_text).decode('UTF-8')
|
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2024 SuperGuGuGu
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
@@ -0,0 +1,134 @@
|
|
1
|
+
Metadata-Version: 2.3
|
2
|
+
Name: nonebot-plugin-kawaii-logos
|
3
|
+
Version: 0.0.0.1
|
4
|
+
Summary: logo生成器
|
5
|
+
License: MIT
|
6
|
+
Keywords: nonebot2,qbittorrent
|
7
|
+
Author: SuperGuGuGu
|
8
|
+
Author-email: from89917812@163.com
|
9
|
+
Requires-Python: >=3.10,<4.0
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
16
|
+
Requires-Dist: httpx (>=0.23)
|
17
|
+
Requires-Dist: matplotlib (>=3.9.0)
|
18
|
+
Requires-Dist: nonebot-plugin-localstore (>=0.6.0)
|
19
|
+
Requires-Dist: nonebot-plugin-send-anything-anywhere (>=0.7.0)
|
20
|
+
Requires-Dist: nonebot2 (>=2.2.0)
|
21
|
+
Requires-Dist: opencv-python (>=4.9.0)
|
22
|
+
Requires-Dist: pathlib (>=1.0)
|
23
|
+
Requires-Dist: pillow (>=11.0.0)
|
24
|
+
Project-URL: Repository, https://github.com/SuperGuGuGu/nonebot-plugin-kawaii-logos
|
25
|
+
Description-Content-Type: text/markdown
|
26
|
+
|
27
|
+
<div align="center">
|
28
|
+
<a href="https://v2.nonebot.dev/store"><img src="https://github.com/A-kirami/nonebot-plugin-template/blob/resources/nbp_logo.png" width="180" height="180" alt="NoneBotPluginLogo"></a>
|
29
|
+
<br>
|
30
|
+
<p><img src="https://github.com/A-kirami/nonebot-plugin-template/blob/resources/NoneBotPlugin.svg" width="240" alt="NoneBotPluginText"></p>
|
31
|
+
</div>
|
32
|
+
|
33
|
+
<div align="center">
|
34
|
+
|
35
|
+
# nonebot-plugin-kawaii-logos
|
36
|
+
|
37
|
+
_✨ kawaii_logo生成器 ✨_
|
38
|
+
|
39
|
+
|
40
|
+
<a href="./LICENSE">
|
41
|
+
<img src="https://img.shields.io/github/license/SuperGuGuGu/nonebot_plugin_kawaii_logos.svg" alt="license">
|
42
|
+
</a>
|
43
|
+
<a href="https://pypi.python.org/pypi/nonebot-plugin-kawaii-logos">
|
44
|
+
<img src="https://img.shields.io/pypi/v/nonebot-plugin-kawaii-logos.svg" alt="pypi">
|
45
|
+
</a>
|
46
|
+
<img src="https://img.shields.io/badge/python-3.10+-blue.svg" alt="python">
|
47
|
+
|
48
|
+
</div>
|
49
|
+
|
50
|
+
## 📖 介绍
|
51
|
+
|
52
|
+
logo生成器,可以生成一些logo
|
53
|
+
|
54
|
+
跨平台
|
55
|
+
|
56
|
+
## 💿 安装
|
57
|
+
|
58
|
+
<details open>
|
59
|
+
<summary>使用 nb-cli 安装</summary>
|
60
|
+
在 nonebot2 项目的根目录下打开命令行, 输入以下指令即可安装
|
61
|
+
|
62
|
+
nb plugin install nonebot-plugin-kawaii-logos
|
63
|
+
|
64
|
+
</details>
|
65
|
+
|
66
|
+
<details>
|
67
|
+
<summary>使用包管理器安装</summary>
|
68
|
+
在 nonebot2 项目的插件目录下, 打开命令行, 根据你使用的包管理器, 输入相应的安装命令
|
69
|
+
|
70
|
+
<details>
|
71
|
+
<summary>pip</summary>
|
72
|
+
|
73
|
+
pip install nonebot-plugin-kawaii-logos
|
74
|
+
|
75
|
+
</details>
|
76
|
+
<details>
|
77
|
+
<summary>pdm</summary>
|
78
|
+
|
79
|
+
pdm add nonebot-plugin-kawaii-logos
|
80
|
+
|
81
|
+
</details>
|
82
|
+
<details>
|
83
|
+
<summary>poetry</summary>
|
84
|
+
|
85
|
+
poetry add nonebot-plugin-kawaii-logos
|
86
|
+
|
87
|
+
</details>
|
88
|
+
<details>
|
89
|
+
<summary>conda</summary>
|
90
|
+
|
91
|
+
conda install nonebot-plugin-kawaii-logos
|
92
|
+
|
93
|
+
</details>
|
94
|
+
|
95
|
+
打开 nonebot2 项目根目录下的 `pyproject.toml` 文件, 在 `[tool.nonebot]` 部分追加写入
|
96
|
+
|
97
|
+
plugins = ["nonebot_plugin_kawaii_logos"]
|
98
|
+
|
99
|
+
</details>
|
100
|
+
|
101
|
+
## ⚙️ 配置
|
102
|
+
|
103
|
+
在 nonebot2 项目的`.env`文件中添加下表中的必填配置
|
104
|
+
|
105
|
+
| 配置项 | 必填 | 默认值 | 说明 | 示例 |
|
106
|
+
|:-------------:|:--:|:---:|:--:|:--:|
|
107
|
+
| config_config | 否 | 示例 | 示例 | 示例 |
|
108
|
+
|
109
|
+
本插件使用了nonebot-plugin-localstore存储文件。
|
110
|
+
|
111
|
+
如有需要修改存储位置,请参考 [localstore文档](https://github.com/nonebot/plugin-localstore)
|
112
|
+
|
113
|
+
## 🎉 使用
|
114
|
+
|
115
|
+
### 指令表
|
116
|
+
|
117
|
+
- ✅: 支持
|
118
|
+
- 🚧: 部分支持或正在完善
|
119
|
+
- 🗓️️: 计划中
|
120
|
+
- ✖️: 不支持/无计划
|
121
|
+
|
122
|
+
| 指令 | 说明 | 需要at | 功能实现 | 图形界面 |
|
123
|
+
|:------:|:--:|:----:|:----:|:----:|
|
124
|
+
| kwlogo | 示例 | 否 | 🗓️ | 🗓️ |
|
125
|
+
|
126
|
+
### 效果图
|
127
|
+
|
128
|
+
[假装有图片.jpg]
|
129
|
+
|
130
|
+
## ⭐
|
131
|
+
|
132
|
+
<p><img src="https://api.star-history.com/svg?repos=SuperGuGuGu/nonebot_plugin_kawaii_logos&type=Date" width="480" alt="NoneBotPluginText"></p>
|
133
|
+
|
134
|
+
|
@@ -0,0 +1,9 @@
|
|
1
|
+
nonebot_plugin_kawaii_logos/__init__.py,sha256=ko_KKoxZ_OGRokYPYJU0_-lbpytXg7AHmmXCqSXFpCY,2428
|
2
|
+
nonebot_plugin_kawaii_logos/command.py,sha256=Xs0wrj6THHtIgY2qdKYi1gaYyfBsRYZkRg9U4dESWXs,1503
|
3
|
+
nonebot_plugin_kawaii_logos/config.py,sha256=eJEWSIKW6CXYCPrg2hkow-SaxExbA2qfE8niSanWWtw,912
|
4
|
+
nonebot_plugin_kawaii_logos/draw.py,sha256=Sst9kU300GMDwCx_cj70da5j8qzIzeyEnZbH5KXdfZo,5558
|
5
|
+
nonebot_plugin_kawaii_logos/tools.py,sha256=NkXf8xJbh0nkk5dHl2GkvXRHDl2M0t_Zg2hcziL_OOY,18379
|
6
|
+
nonebot_plugin_kawaii_logos-0.0.0.1.dist-info/LICENSE,sha256=4QwosCUBWhxPzvBqBKkegYlm-APuqbMaNBTOAYjrt7E,1068
|
7
|
+
nonebot_plugin_kawaii_logos-0.0.0.1.dist-info/METADATA,sha256=HpmgfLYyfBPALwy_kvMVxVtAtnX6zbPjtgX44vW8tow,3679
|
8
|
+
nonebot_plugin_kawaii_logos-0.0.0.1.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
9
|
+
nonebot_plugin_kawaii_logos-0.0.0.1.dist-info/RECORD,,
|