跳到主要内容

杂项

一些杂项功能的说明和配置,用户通常不需要涉及这些,以下内容主要供搭建者和开发者查阅。

货币系统

IdhagnBot 有一个简单的货币系统,目前只有 purchase

sign 插件使用这个系统,不同群聊之间的货币是独立的。具体请参考 API 文档

状态文件在 states/currency/群号.yaml,参考如下:

警告

你通常不应该修改状态文件,除非别无选择。

users:
987654321: 100 # 用户: 金币数

用户别名

在使用 /rua 等命令时,需要指定一个用户,除了可以直接输入 QQ 号或者 @ 对方,IdhagnBot 还会将群员昵称和群名片中的空格和特殊符号去掉,用于查找群员。

但当群员的昵称和群名片只包含特殊符号而不包含任何中文、英文、数字时,IdhagnBot 将不能通过名字找到他,此时你可以为他手动指定别名。

假如一个名字可以查找到两个或更多群员,为了防止不必要的误会,IdhagnBot 默认也会拒绝匹配到任何一个,除非手动指定别名。

别名还可以用于给群员“起外号”,如果给某人起了不太好听的外号而被他打了我不为此负责

使用 /match 名字 可以查看名字能搜索到哪些群员(如果只有一个群员,也会显示他的别名),使用 /match QQ号 可以查看群员的所有别名,包括自动生成的。

config/user_aliases.yaml

aliases:
- contexts: # 这些别名在哪些上下文生效,不指定时在所有上下文生效
- 123456789 # -1 指私聊,-2 指任意群聊
aliases:
- names:
- 名字1
- 名字2
users: # 部分命令可以接受指代多个群员的别名,但大部分命令只接受指代一个群员的别名
- 123456789
- 987654321

开发示例

通常你需要使用 util.user_aliases.match_uid,这个函数会在内部处理大部分细节,在出错时抛出 util.util.AggregateError,包含相对友好的错误信息,当一个名称既能精确匹配也能模糊匹配时,精确匹配优先。

如果需要更底层的方法,你可以使用 util.user_aliases.get_aliasesutil.user_aliases.match,参见 API 文档

日志

IdhagnBot 和 Nonebot2 一样,也使用 Loguru 作为日志库,但 IdhagnBot 覆盖了 Nonebot2 的默认格式。

config/log.yaml

sinks: # 如果不指定,默认值如下,可以指定多个 sink
- level: INFO # 日志等级,可以是字符串或数字,不区分大小写,参见 Loguru 的日志等级
fold_nonebot: true # 是否将 nonebot. 开头的模块都显示为 nonebot,就像 Nonebot2 默认的日志一样
format: "<g>{time:HH:mm:ss}</g>|<lvl>{level:8}</lvl>| <c>{name}</c> - {message}" # 日志格式,此处为默认格式
colorize: null # 是否使用彩色日志,false 强制关闭,true 强制开启,null 自动检测
backtrace: true # 异常的 traceback 是否显示完整栈(而非只显示到 try-except)
diagnose: false # 异常是否显示变量的值等(可能包含敏感内容,并且很长)
# type: stderr # stdout、stderr 或 syslog
# syslog 使用 Python 的 syslog 标准库,只在 POSIX 系统上可用(如 Linux、Mac 等)
# 如果你将机器人作为 Systemd 服务运行,推荐使用 syslog
file: 日志文件名 # 和 type 二选一,只有 file 能用下面的选项
rotation: 日志滚动
retention: 清理旧日志
compression: 压缩日志
encoding: utf8 # 编码,默认为 utf8
newline: null # 为 null 时使用系统换行,也可指定为 "\r"、"\n" 或者 "\r\n"
warnings_as_log: true # 将 Python 标准库 warnings 输出的警告作为 WARN 等级的日志输出,默认为 true
stdio_as_log: false # 将 stdout 和 stderr 分别作为 INFO 和 ERROR 等级的日志输出,默认为 false,不建议开启,可能导致奇怪的问题

关于 rotation、retention 和 compression 的写法,请参见 Loguru 的文档

开发示例也请参见 Loguru

杂项方法

util.colorutil

包含一些解析颜色字符串和处理颜色的方法,解析颜色字符串时支持 3 位或 6 位十六进制、CSS 颜色名、rgb(r, g, b)hsl(h, s, l)、Material Design 颜色名,不支持 Alpha 通道。

util.hook

包含 NoneBot 没有的额外钩子,目前只有 on_message_sent 一个。

util.importing

用于加载插件和子模块。

util.imutil

包含一些处理 Pillow 图片的实用方法。

util.misc

包含一些实用但琐碎的方法。这个命名也许应该改成 util.misc 的,我有点脑抽……之前叫util.util,现在已经改了

util.record

使用 SQLModel 记录收到的消息,用于词云等功能。

config/misc.yaml

special_font:
pixel: Unifont # 用在 meme_word.nokia 中,未指定时 fallback 为 sans
osu: "" # 用在 meme_word.osu 中,未指定时 fallback 为 sans bold
font_substitute:
# PangoCairo 使用 FontConfig,因此 IdhagnBot 会跟随系统设置,但你也可以手动覆盖
# 目前 IdhagnBot 使用了这些通用名
serif: Noto Serif CJK SC
serif bold: Noto Serif CJK SC Bold
sans: Noto Sans CJK SC
sans medium: Noto Sans CJK SC Medium
sans bold: Noto Sans CJK SC Bold
sans heavy: Noto Sans CJK SC Heavy
monospace: Iosevka
# 其实上面这些字体并没有什么特殊含义,只是我喜欢而已(逃)
chromium: "" # Pyppeteer 使用的 Chromium 路径,默认为空,即让 Pyppeteer 自行下载
# 以下两项默认都是 bicubic
resample: bicubic # Pillow 除缩放外的图片插值方式,可用:nearest、bilinear、bicubic
scale_resample: bicubic # Pillow 缩放图片插值方式,除上面三个还可用:box、hamming、lanczos
# 以下四项请参考 PyCairo 的文档:https://pycairo.readthedocs.io/en/latest/reference/enums.html
# default 为使用系统默认值
text_antialias: gray # 字体抗锯齿方式,默认:gray,可用:default、none、fast、good、best、gray、subpixel
# best 抗拒齿在部分平台上可能无法正常渲染
# 不建议使用次像素抗拒齿,会导致字体边缘发绿等问题
text_subpixel: default # 次像素排列方式,默认:default,可用:default、rgb、bgr、vrgb、vbgr
# 这个选项只在使用次像素抗锯齿时有效
text_hint_metrics: true # 是否微调字体度量,默认:true,可用:default、true、false
text_hint_style: slight # 是否微调字体轮廓,默认:slight,可用:default、none、slight、medium、full
libimagequant: false
# 默认为 false,要启用 libimagequant,将此处改为 true 并参考下文自行编译安装 Pillow
# 因为 libimagequant 为 GPL 协议,PyPI 上的 Pillow 二进制包没有编译 libimagequant 支持
# 如果为 true 但 libimagequant 不可用,会显示一个警告并使用 quantize 指定的量化方法
# 另外,libimagequant 包含抖动仿色,因此启用之后下面的 dither 将不生效
quantize: fastoctree
# 默认为 fastoctree,也可为 mediancut 或者 maxcoverage
# 注意对于 RGBA 图片无论设置为什么都会使用 fastoctree,因为其余两个不支持 RGBA
dither: true
# 是否启用抖动仿色,默认为 true

字体渲染

IdhagnBot 使用 PangoCairo 来渲染文字,以提供 Emoji 等特殊符号的 fallback,也支持其他一些高级排版特性。

配置文件见上

开发示例

util.textutil 主要提供 layout、render 和 paste 三个函数,paste 会将文字粘贴到目标图像上(并返回渲染的图片),是最便捷的函数。如果需要更多的控制,render 返回 PIL.Image.Image,layout 返回 Pango.Layout。具体请参考 API 文档

浏览器渲染

IdhagnBot 由于作者的精神洁癖,偏向尽可能使用 Pillow、PyCairo、NumPy、OpenCV 等库生成图片而非调用浏览器,但以下功能由于极其复杂只能使用浏览器渲染:

  • wikipedia

配置文件见上

开发示例

util.misc.browser 会使用配置文件中的 Chromium 路径创建 Pyppeteer 的浏览器实例,并且自动关闭浏览器,防止后台残留。

from util import misc

async with misc.browser() as browser:
pass # 使用获取的 browser

图片采样

IdhagnBot 默认使用双立方插值来缩放图片或进行其他变换,通常不应该造成性能影响。但如果确实有,请参考 Pillow 的文档 适当降低采样质量。

配置文件见上

开发示例

在使用 Image.resize 等方法时,请使用 util.imutil.resampleutil.imutil.scale_resample,而非硬编码采样。

from PIL import Image
from util import imutil

im = Image.new("RGBA", (100, 100))
# scale_resample 可能被指定为 Box、Hamming 或 Lanczos,不要用在 resize 以外的方法中
im.resize((50, 50), imutil.scale_resample())
# resample 一定只会被指定为 Nearest、Bilinear 或 Bicubic
im.rotate(45, imutil.resample())

抖动仿色

IdhagnBot 在保存 GIF 时会自动转换为色板图片并应用抖动仿色,默认使用 Quantize.MEDIANCUT(对于 RGBA 的图片强制使用 Quantize.FASTOCTREE,因为只有它支持 RGBA,除非启用 libimagequant)。

因为 libimagequant 为 GPL 协议,PyPI 上的 Pillow 二进制包没有编译 libimagequant 支持,要启用 libimagequant,需要自行安装 libimagequant,再从源码编译安装 Pillow,并更改配置文件

sudo pacman -S libimagequant
# 方式1: 适用于 venv 和 PEP582
# (PDM 目前不支持单独重装某一个包,这行命令会重装所有包)
PDM_NO_BINARY='Pillow' pdm sync -r
# 方式2: 仅适用于 venv,如果找不到 pip 可以运行 pdm run python -m ensurepip
pdm run python -m pip install Pillow --no-cache-dir --force-reinstall --no-binary Pillow
# 检查: 下面这行应该输出 True
pdm run python -c 'from PIL import features; print(features.check("libimagequant"))'

开发示例

util.imutil.to_segment 在保存 GIF 时会自动转换为色板图片并应用抖动仿色,如果需要手动转换,使用 util.imutil.quantize

from util import imutil

# fmt 为保存静态图的格式,默认为 png。
# afmt 为保存动图时的格式,默认为 gif,也可以是 png(APNG)和 webp。
# 在使用 gif 时会自动应用抖动仿色
# frames 为包含动图帧的数组,如果只有一帧将不会保存为动图
# frametime 为每一帧的时间,可以是单个毫秒数,即所有帧时间相同
# 也可以是与 frames 等长的毫秒数数组或者与 frames 等长的动图
segment = imutil.to_segment(frames, frametime, fmt="png", afmt="gif")
# 返回的是 nonebot 的 MessageSegment
await matcher.send(segment)

im = imutil.quantize(im) # 如果需要手动转换