mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2025-12-15 06:12:53 +08:00
feat✨: 添加词条word_bank
This commit is contained in:
parent
cf208e2f64
commit
137870b698
@ -125,7 +125,7 @@ async def build_help() -> BuildImage:
|
||||
)
|
||||
if task_list := await TaskInfo.all():
|
||||
task_str = "\n".join([task.name for task in task_list])
|
||||
task_str = "通过 开启/关闭 来控制群被动\n----------\n" + task_str
|
||||
task_str = "通过 开启/关闭群被动 来控制群被动\n----------\n" + task_str
|
||||
task_image = await text2image(task_str, padding=5, color=(255, 255, 255))
|
||||
await task_image.circle_corner(10)
|
||||
A = BuildImage(task_image.width + 50, task_image.height + 85, "#EAEDF2")
|
||||
|
||||
@ -121,7 +121,7 @@ async def build_help() -> BuildImage:
|
||||
)
|
||||
if task_list := await TaskInfo.all():
|
||||
task_str = "\n".join([task.name for task in task_list])
|
||||
task_str = "通过 开启/关闭 来控制群被动\n----------\n" + task_str
|
||||
task_str = "通过 开启/关闭群被动 来控制群被动\n----------\n" + task_str
|
||||
task_image = await text2image(task_str, padding=5, color=(255, 255, 255))
|
||||
await task_image.circle_corner(10)
|
||||
A = BuildImage(task_image.width + 50, task_image.height + 85, "#EAEDF2")
|
||||
|
||||
18
zhenxun/plugins/word_bank/__init__.py
Normal file
18
zhenxun/plugins/word_bank/__init__.py
Normal file
@ -0,0 +1,18 @@
|
||||
from pathlib import Path
|
||||
|
||||
import nonebot
|
||||
|
||||
from zhenxun.configs.config import Config
|
||||
|
||||
Config.add_plugin_config(
|
||||
"word_bank",
|
||||
"WORD_BANK_LEVEL",
|
||||
5,
|
||||
help="设置增删词库的权限等级",
|
||||
default_value=5,
|
||||
type=int,
|
||||
)
|
||||
Config.set_name("word_bank", "词库问答")
|
||||
|
||||
|
||||
nonebot.load_plugins(str(Path(__file__).parent.resolve()))
|
||||
24
zhenxun/plugins/word_bank/_config.py
Normal file
24
zhenxun/plugins/word_bank/_config.py
Normal file
@ -0,0 +1,24 @@
|
||||
from zhenxun.configs.path_config import DATA_PATH
|
||||
|
||||
data_dir = DATA_PATH / "word_bank"
|
||||
data_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
scope2int = {
|
||||
"全局": 0,
|
||||
"群聊": 1,
|
||||
"私聊": 2,
|
||||
}
|
||||
|
||||
type2int = {
|
||||
"精准": 0,
|
||||
"模糊": 1,
|
||||
"正则": 2,
|
||||
"图片": 3,
|
||||
}
|
||||
|
||||
int2type = {
|
||||
0: "精准",
|
||||
1: "模糊",
|
||||
2: "正则",
|
||||
3: "图片",
|
||||
}
|
||||
288
zhenxun/plugins/word_bank/_data_source.py
Normal file
288
zhenxun/plugins/word_bank/_data_source.py
Normal file
@ -0,0 +1,288 @@
|
||||
import re
|
||||
|
||||
from nonebot.adapters.onebot.v11 import unescape
|
||||
from nonebot_plugin_alconna import At as alcAt
|
||||
from nonebot_plugin_alconna import Image as alcImage
|
||||
from nonebot_plugin_alconna import Text as alcText
|
||||
from nonebot_plugin_alconna import UniMessage, UniMsg
|
||||
from nonebot_plugin_saa import Image, Mention, MessageFactory, Text
|
||||
|
||||
from zhenxun.utils.image_utils import ImageTemplate
|
||||
|
||||
from ._model import WordBank
|
||||
|
||||
|
||||
def get_img_and_at_list(message: UniMsg) -> tuple[list[str], list[str]]:
|
||||
"""获取图片和at数据
|
||||
|
||||
参数:
|
||||
message: UniMsg
|
||||
|
||||
返回:
|
||||
tuple[list[str], list[str]]: 图片列表,at列表
|
||||
"""
|
||||
img_list, at_list = [], []
|
||||
for msg in message:
|
||||
if isinstance(msg, alcImage):
|
||||
img_list.append(msg.url)
|
||||
elif isinstance(msg, alcAt):
|
||||
at_list.append(msg.target)
|
||||
return img_list, at_list
|
||||
|
||||
|
||||
def get_problem(message: UniMsg) -> str:
|
||||
"""获取问题内容
|
||||
|
||||
参数:
|
||||
message: UniMsg
|
||||
|
||||
返回:
|
||||
str: 问题文本
|
||||
"""
|
||||
problem = ""
|
||||
a, b = True, True
|
||||
for msg in message:
|
||||
if isinstance(msg, alcText) or isinstance(msg, str):
|
||||
msg = str(msg)
|
||||
if "问" in str(msg) and a:
|
||||
a = False
|
||||
split_text = msg.split("问")
|
||||
if len(split_text) > 1:
|
||||
problem += "问".join(split_text[1:])
|
||||
if b:
|
||||
if "答" in problem:
|
||||
b = False
|
||||
problem = problem.split("答")[0]
|
||||
elif "答" in msg and b:
|
||||
b = False
|
||||
# problem += "答".join(msg.split("答")[:-1])
|
||||
problem += msg.split("答")[0]
|
||||
if not a and not b:
|
||||
break
|
||||
if isinstance(msg, alcAt):
|
||||
problem += f"[at:{msg.target}]"
|
||||
return problem
|
||||
|
||||
|
||||
def get_answer(message: UniMsg) -> UniMessage | None:
|
||||
"""获取at时回答
|
||||
|
||||
参数:
|
||||
message: UniMsg
|
||||
|
||||
返回:
|
||||
str: 回答内容
|
||||
"""
|
||||
temp_message = None
|
||||
answer = ""
|
||||
index = 0
|
||||
for msg in message:
|
||||
index += 1
|
||||
if isinstance(msg, alcText) or isinstance(msg, str):
|
||||
msg = str(msg)
|
||||
if "答" in msg:
|
||||
answer += "答".join(msg.split("答")[1:])
|
||||
break
|
||||
if answer:
|
||||
temp_message = message[index:]
|
||||
temp_message.insert(0, alcText(answer))
|
||||
return temp_message
|
||||
|
||||
|
||||
class WordBankManage:
|
||||
|
||||
@classmethod
|
||||
async def update_word(
|
||||
cls,
|
||||
replace: str,
|
||||
problem: str = "",
|
||||
index: int | None = None,
|
||||
group_id: str | None = None,
|
||||
word_scope: int = 1,
|
||||
) -> tuple[str, str]:
|
||||
"""修改群词条
|
||||
|
||||
参数:
|
||||
params: 参数
|
||||
group_id: 群号
|
||||
word_scope: 词条范围
|
||||
|
||||
返回:
|
||||
tuple[str, str]: 处理消息,替换的旧词条
|
||||
"""
|
||||
return await cls.__word_handle(
|
||||
problem, group_id, "update", index, None, word_scope, replace
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def delete_word(
|
||||
cls,
|
||||
problem: str,
|
||||
index: int | None = None,
|
||||
aid: int | None = None,
|
||||
group_id: str | None = None,
|
||||
word_scope: int = 1,
|
||||
) -> tuple[str, str]:
|
||||
"""删除群词条
|
||||
|
||||
参数:
|
||||
params: 参数
|
||||
index: 指定下标
|
||||
aid: 指定回答下标
|
||||
group_id: 群号
|
||||
word_scope: 词条范围
|
||||
|
||||
返回:
|
||||
tuple[str, str]: 处理消息,空
|
||||
"""
|
||||
return await cls.__word_handle(
|
||||
problem, group_id, "delete", index, aid, word_scope
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def __word_handle(
|
||||
cls,
|
||||
problem: str,
|
||||
group_id: str | None,
|
||||
handle_type: str,
|
||||
index: int | None = None,
|
||||
aid: int | None = None,
|
||||
word_scope: int = 0,
|
||||
replace_problem: str = "",
|
||||
) -> tuple[str, str]:
|
||||
"""词条操作
|
||||
|
||||
参数:
|
||||
problem: 参数
|
||||
group_id: 群号
|
||||
handle_type: 类型
|
||||
index: 指定回答下标
|
||||
aid: 指定回答下标
|
||||
word_scope: 词条范围
|
||||
replace_problem: 替换问题内容
|
||||
|
||||
返回:
|
||||
tuple[str, str]: 处理消息,替换的旧词条
|
||||
"""
|
||||
if index is not None:
|
||||
problem, code = await cls.__get_problem_str(index, group_id, word_scope)
|
||||
if code != 200:
|
||||
return problem, ""
|
||||
if handle_type == "delete":
|
||||
if index:
|
||||
problem, _problem_list = await WordBank.get_problem_all_answer(
|
||||
problem, None, group_id, word_scope
|
||||
)
|
||||
if not _problem_list:
|
||||
return problem, ""
|
||||
if await WordBank.delete_group_problem(problem, group_id, aid, word_scope): # type: ignore
|
||||
return "删除词条成功!", ""
|
||||
return "词条不存在", ""
|
||||
if handle_type == "update":
|
||||
old_problem = await WordBank.update_group_problem(
|
||||
problem, replace_problem, group_id, word_scope=word_scope
|
||||
)
|
||||
return f"修改词条成功!\n{old_problem} -> {replace_problem}", old_problem
|
||||
return "类型错误", ""
|
||||
|
||||
@classmethod
|
||||
async def __get_problem_str(
|
||||
cls, idx: int, group_id: str | None = None, word_scope: int = 1
|
||||
) -> tuple[str, int]:
|
||||
"""通过id获取问题字符串
|
||||
|
||||
参数:
|
||||
idx: 下标
|
||||
group_id: 群号
|
||||
word_scope: 获取类型
|
||||
"""
|
||||
if word_scope in [0, 2]:
|
||||
all_problem = await WordBank.get_problem_by_scope(word_scope)
|
||||
elif group_id:
|
||||
all_problem = await WordBank.get_group_all_problem(group_id)
|
||||
else:
|
||||
raise Exception("词条类型与群组id不能为空")
|
||||
if idx < 0 or idx >= len(all_problem):
|
||||
return "问题下标id必须在范围内", 999
|
||||
return all_problem[idx][0], 200
|
||||
|
||||
@classmethod
|
||||
async def show_word(
|
||||
cls,
|
||||
problem: str | None,
|
||||
index: int | None = None,
|
||||
group_id: str | None = None,
|
||||
word_scope: int | None = 1,
|
||||
) -> Text | MessageFactory | Image:
|
||||
"""获取群词条
|
||||
|
||||
参数:
|
||||
problem: 问题
|
||||
group_id: 群组id
|
||||
word_scope: 词条范围
|
||||
index: 指定回答下标
|
||||
"""
|
||||
if problem or index != None:
|
||||
msg_list = []
|
||||
problem, _problem_list = await WordBank.get_problem_all_answer(
|
||||
problem, # type: ignore
|
||||
index,
|
||||
group_id if group_id is None else None,
|
||||
word_scope,
|
||||
)
|
||||
if not _problem_list:
|
||||
return Text(problem)
|
||||
for msg in _problem_list:
|
||||
_text = str(msg)
|
||||
if isinstance(msg, Mention):
|
||||
_text = f"[at:{msg.data}]"
|
||||
elif isinstance(msg, Image):
|
||||
_text = msg.data
|
||||
elif isinstance(msg, list):
|
||||
_text = []
|
||||
for m in msg:
|
||||
__text = str(m)
|
||||
if isinstance(m, Mention):
|
||||
__text = f"[at:{m.data['user_id']}]"
|
||||
elif isinstance(m, Image):
|
||||
# TODO: 显示词条回答图片
|
||||
# __text = (m.data["image"], 30, 30)
|
||||
__text = "[图片]"
|
||||
_text.append(__text)
|
||||
msg_list.append("".join(_text))
|
||||
column_name = ["序号", "回答内容"]
|
||||
data_list = []
|
||||
for index, msg in enumerate(msg_list):
|
||||
data_list.append([index, msg])
|
||||
template_image = await ImageTemplate.table_page(
|
||||
f"词条 {problem} 的回答", None, column_name, data_list
|
||||
)
|
||||
return Image(template_image.pic2bytes())
|
||||
else:
|
||||
result = []
|
||||
if group_id:
|
||||
_problem_list = await WordBank.get_group_all_problem(group_id)
|
||||
elif word_scope is not None:
|
||||
_problem_list = await WordBank.get_problem_by_scope(word_scope)
|
||||
else:
|
||||
raise Exception("群组id和词条范围不能都为空")
|
||||
global_problem_list = await WordBank.get_problem_by_scope(0)
|
||||
if not _problem_list and not global_problem_list:
|
||||
return Text("未收录任何词条...")
|
||||
column_name = ["序号", "关键词", "匹配类型", "收录用户"]
|
||||
data_list = [list(s) for s in _problem_list]
|
||||
for i in range(len(data_list)):
|
||||
data_list[i].insert(0, i)
|
||||
group_image = await ImageTemplate.table_page(
|
||||
"群组内词条" if group_id else "私聊词条", None, column_name, data_list
|
||||
)
|
||||
result.append(Image(group_image.pic2bytes()))
|
||||
if global_problem_list:
|
||||
data_list = [list(s) for s in global_problem_list]
|
||||
for i in range(len(data_list)):
|
||||
data_list[i].insert(0, i)
|
||||
global_image = await ImageTemplate.table_page(
|
||||
"全局词条", None, column_name, data_list
|
||||
)
|
||||
result.append(Image(global_image.pic2bytes()))
|
||||
return MessageFactory(result)
|
||||
566
zhenxun/plugins/word_bank/_model.py
Normal file
566
zhenxun/plugins/word_bank/_model.py
Normal file
@ -0,0 +1,566 @@
|
||||
import random
|
||||
import re
|
||||
import time
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from nonebot_plugin_alconna import At as alcAt
|
||||
from nonebot_plugin_alconna import Image as alcImage
|
||||
from nonebot_plugin_alconna import Text as alcText
|
||||
from nonebot_plugin_saa import Image, Mention, MessageFactory, Text
|
||||
from tortoise import Tortoise, fields
|
||||
from tortoise.expressions import Q
|
||||
from typing_extensions import Self
|
||||
|
||||
from zhenxun.configs.path_config import DATA_PATH
|
||||
from zhenxun.services.db_context import Model
|
||||
from zhenxun.utils.http_utils import AsyncHttpx
|
||||
from zhenxun.utils.image_utils import get_img_hash
|
||||
|
||||
from ._config import int2type
|
||||
|
||||
path = DATA_PATH / "word_bank"
|
||||
|
||||
|
||||
class WordBank(Model):
|
||||
|
||||
id = fields.IntField(pk=True, generated=True, auto_increment=True)
|
||||
"""自增id"""
|
||||
user_id = fields.CharField(255)
|
||||
"""用户id"""
|
||||
group_id = fields.CharField(255, null=True)
|
||||
"""群聊id"""
|
||||
word_scope = fields.IntField(default=0)
|
||||
"""生效范围 0: 全局 1: 群聊 2: 私聊"""
|
||||
word_type = fields.IntField(default=0)
|
||||
"""词条类型 0: 完全匹配 1: 模糊 2: 正则 3: 图片"""
|
||||
status = fields.BooleanField()
|
||||
"""词条状态"""
|
||||
problem = fields.TextField()
|
||||
"""问题,为图片时使用图片hash"""
|
||||
answer = fields.TextField()
|
||||
"""回答"""
|
||||
placeholder = fields.TextField(null=True)
|
||||
"""占位符"""
|
||||
image_path = fields.TextField(null=True)
|
||||
"""使用图片作为问题时图片存储的路径"""
|
||||
to_me = fields.CharField(255, null=True)
|
||||
"""昵称开头时存储的昵称"""
|
||||
create_time = fields.DatetimeField(auto_now=True)
|
||||
"""创建时间"""
|
||||
update_time = fields.DatetimeField(auto_now_add=True)
|
||||
"""更新时间"""
|
||||
platform = fields.CharField(255, default="qq")
|
||||
"""平台"""
|
||||
author = fields.CharField(255, null=True, default="")
|
||||
"""收录人"""
|
||||
|
||||
class Meta:
|
||||
table = "word_bank2"
|
||||
table_description = "词条数据库"
|
||||
|
||||
@classmethod
|
||||
async def exists(
|
||||
cls,
|
||||
user_id: str | None,
|
||||
group_id: str | None,
|
||||
problem: str,
|
||||
answer: str | None,
|
||||
word_scope: int | None = None,
|
||||
word_type: int | None = None,
|
||||
) -> bool:
|
||||
"""检测问题是否存在
|
||||
|
||||
参数:
|
||||
user_id: 用户id
|
||||
group_id: 群号
|
||||
problem: 问题
|
||||
answer: 回答
|
||||
word_scope: 词条范围
|
||||
word_type: 词条类型
|
||||
"""
|
||||
query = cls.filter(problem=problem)
|
||||
if user_id:
|
||||
query = query.filter(user_id=user_id)
|
||||
if group_id:
|
||||
query = query.filter(group_id=group_id)
|
||||
if answer:
|
||||
query = query.filter(answer=answer)
|
||||
if word_type is not None:
|
||||
query = query.filter(word_type=word_type)
|
||||
if word_scope is not None:
|
||||
query = query.filter(word_scope=word_scope)
|
||||
return await query.exists()
|
||||
|
||||
@classmethod
|
||||
async def add_problem_answer(
|
||||
cls,
|
||||
user_id: str,
|
||||
group_id: str | None,
|
||||
word_scope: int,
|
||||
word_type: int,
|
||||
problem: str,
|
||||
answer: list[str | alcText | alcAt | alcImage],
|
||||
to_me_nickname: str | None = None,
|
||||
platform: str = "",
|
||||
author: str = "",
|
||||
):
|
||||
"""添加或新增一个问答
|
||||
|
||||
参数:
|
||||
user_id: 用户id
|
||||
group_id: 群号
|
||||
word_scope: 词条范围,
|
||||
word_type: 词条类型,
|
||||
problem: 问题, 为图片时是URl
|
||||
answer: 回答
|
||||
to_me_nickname: at真寻名称
|
||||
platform: 所属平台
|
||||
author: 收录人id
|
||||
"""
|
||||
# 对图片做额外处理
|
||||
image_path = None
|
||||
if word_type == 3:
|
||||
_file = (
|
||||
path / "problem" / f"{group_id}" / f"{user_id}_{int(time.time())}.jpg"
|
||||
)
|
||||
_file.parent.mkdir(exist_ok=True, parents=True)
|
||||
await AsyncHttpx.download_file(problem, _file)
|
||||
problem = get_img_hash(_file)
|
||||
image_path = f"problem/{group_id}/{user_id}_{int(time.time())}.jpg"
|
||||
new_answer, placeholder_list = await cls._answer2format(
|
||||
answer, user_id, group_id
|
||||
)
|
||||
if not await cls.exists(
|
||||
user_id, group_id, problem, new_answer, word_scope, word_type
|
||||
):
|
||||
await cls.create(
|
||||
user_id=user_id,
|
||||
group_id=group_id,
|
||||
word_scope=word_scope,
|
||||
word_type=word_type,
|
||||
status=True,
|
||||
problem=str(problem).strip(),
|
||||
answer=new_answer,
|
||||
image_path=image_path,
|
||||
placeholder=",".join(placeholder_list),
|
||||
create_time=datetime.now().replace(microsecond=0),
|
||||
update_time=datetime.now().replace(microsecond=0),
|
||||
to_me=to_me_nickname,
|
||||
platform=platform,
|
||||
author=author,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def _answer2format(
|
||||
cls,
|
||||
answer: list[str | alcText | alcAt | alcImage],
|
||||
user_id: str,
|
||||
group_id: str | None,
|
||||
) -> tuple[str, list[Any]]:
|
||||
"""将特殊字段转化为占位符,图片,at等
|
||||
|
||||
参数:
|
||||
answer: 回答内容
|
||||
user_id: 用户id
|
||||
group_id: 群号
|
||||
|
||||
返回:
|
||||
tuple[str, list[Any]]: 替换后的文本回答内容,占位符
|
||||
"""
|
||||
placeholder_list = []
|
||||
text = ""
|
||||
index = 0
|
||||
for seg in answer:
|
||||
placeholder = uuid.uuid1()
|
||||
if isinstance(seg, str):
|
||||
text += seg
|
||||
elif isinstance(seg, alcText):
|
||||
text += seg.text
|
||||
elif seg.type == "face": # TODO: face貌似无用...
|
||||
text += f"[face:placeholder_{placeholder}]"
|
||||
placeholder_list.append(seg.data["id"])
|
||||
elif isinstance(seg, alcAt):
|
||||
text += f"[at:placeholder_{placeholder}]"
|
||||
placeholder_list.append(seg.target)
|
||||
elif isinstance(seg, alcImage) and seg.url:
|
||||
text += f"[image:placeholder_{placeholder}]"
|
||||
index += 1
|
||||
_file = (
|
||||
path
|
||||
/ "answer"
|
||||
/ f"{group_id or user_id}"
|
||||
/ f"{user_id}_{placeholder}.jpg"
|
||||
)
|
||||
_file.parent.mkdir(exist_ok=True, parents=True)
|
||||
await AsyncHttpx.download_file(seg.url, _file)
|
||||
placeholder_list.append(
|
||||
f"answer/{group_id or user_id}/{user_id}_{placeholder}.jpg"
|
||||
)
|
||||
return text, placeholder_list
|
||||
|
||||
@classmethod
|
||||
async def _format2answer(
|
||||
cls,
|
||||
problem: str,
|
||||
answer: str,
|
||||
user_id: int,
|
||||
group_id: int,
|
||||
query: Self | None = None,
|
||||
) -> MessageFactory | Text:
|
||||
"""将占位符转换为实际内容
|
||||
|
||||
参数:
|
||||
problem: 问题内容
|
||||
answer: 回答内容
|
||||
user_id: 用户id
|
||||
group_id: 群组id
|
||||
"""
|
||||
result_list = []
|
||||
if not query:
|
||||
query = await cls.get_or_none(
|
||||
problem=problem,
|
||||
user_id=user_id,
|
||||
group_id=group_id,
|
||||
answer=answer,
|
||||
)
|
||||
if not answer:
|
||||
answer = str(query.answer) # type: ignore
|
||||
if query and query.placeholder:
|
||||
type_list = re.findall(rf"\[(.*?):placeholder_.*?]", answer)
|
||||
answer_split = re.split(rf"\[.*:placeholder_.*?]", answer)
|
||||
placeholder_split = query.placeholder.split(",")
|
||||
for index, ans in enumerate(answer_split):
|
||||
result_list.append(Text(ans))
|
||||
if index < len(type_list):
|
||||
t = type_list[index]
|
||||
p = placeholder_split[index]
|
||||
if t == "image":
|
||||
result_list.append(Image(path / p))
|
||||
elif t == "at":
|
||||
result_list.append(Mention(p))
|
||||
return MessageFactory(result_list)
|
||||
return Text(answer)
|
||||
|
||||
@classmethod
|
||||
async def check_problem(
|
||||
cls,
|
||||
group_id: str | None,
|
||||
problem: str,
|
||||
word_scope: int | None = None,
|
||||
word_type: int | None = None,
|
||||
) -> Any:
|
||||
"""检测是否包含该问题并获取所有回答
|
||||
|
||||
参数:
|
||||
group_id: 群组id
|
||||
problem: 问题内容
|
||||
word_scope: 词条范围
|
||||
word_type: 词条类型
|
||||
"""
|
||||
query = cls
|
||||
if group_id:
|
||||
if word_scope:
|
||||
query = query.filter(word_scope=word_scope)
|
||||
else:
|
||||
query = query.filter(Q(group_id=group_id) | Q(word_scope=0))
|
||||
else:
|
||||
query = query.filter(Q(word_scope=2) | Q(word_scope=0))
|
||||
if word_type:
|
||||
query = query.filter(word_scope=word_type)
|
||||
# 完全匹配
|
||||
if data_list := await query.filter(
|
||||
Q(Q(word_type=0) | Q(word_type=3)), Q(problem=problem)
|
||||
).all():
|
||||
return data_list
|
||||
db = Tortoise.get_connection("default")
|
||||
# 模糊匹配
|
||||
sql = query.filter(word_type=1).sql() + " and POSITION(problem in $1) > 0"
|
||||
data_list = await db.execute_query_dict(sql, [problem])
|
||||
if data_list:
|
||||
return [cls(**data) for data in data_list]
|
||||
# 正则
|
||||
sql = (
|
||||
query.filter(word_type=2, word_scope__not=999).sql() + " and $1 ~ problem;"
|
||||
)
|
||||
data_list = await db.execute_query_dict(sql, [problem])
|
||||
if data_list:
|
||||
return [cls(**data) for data in data_list]
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
async def get_answer(
|
||||
cls,
|
||||
group_id: str | None,
|
||||
problem: str,
|
||||
word_scope: int | None = None,
|
||||
word_type: int | None = None,
|
||||
) -> Text | MessageFactory | None:
|
||||
"""根据问题内容获取随机回答
|
||||
|
||||
参数:
|
||||
user_id: 用户id
|
||||
group_id: 群组id
|
||||
problem: 问题内容
|
||||
word_scope: 词条范围
|
||||
word_type: 词条类型
|
||||
"""
|
||||
data_list = await cls.check_problem(group_id, problem, word_scope, word_type)
|
||||
if data_list:
|
||||
random_answer = random.choice(data_list)
|
||||
if random_answer.word_type == 2:
|
||||
r = re.search(random_answer.problem, problem)
|
||||
has_placeholder = re.search(rf"\$(\d)", random_answer.answer)
|
||||
if r and r.groups() and has_placeholder:
|
||||
pats = re.sub(r"\$(\d)", r"\\\1", random_answer.answer)
|
||||
random_answer.answer = re.sub(random_answer.problem, pats, problem)
|
||||
return (
|
||||
await cls._format2answer(
|
||||
random_answer.problem,
|
||||
random_answer.answer,
|
||||
random_answer.user_id,
|
||||
random_answer.group_id,
|
||||
random_answer,
|
||||
)
|
||||
if random_answer.placeholder
|
||||
else Text(random_answer.answer)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_problem_all_answer(
|
||||
cls,
|
||||
problem: str,
|
||||
index: int | None = None,
|
||||
group_id: str | None = None,
|
||||
word_scope: int | None = 0,
|
||||
) -> tuple[str, list[Text | MessageFactory]]:
|
||||
"""获取指定问题所有回答
|
||||
|
||||
参数:
|
||||
problem: 问题
|
||||
index: 下标
|
||||
group_id: 群号
|
||||
word_scope: 词条范围
|
||||
|
||||
返回:
|
||||
tuple[str, list[Text | MessageFactory]]: 问题和所有回答
|
||||
"""
|
||||
if index is not None:
|
||||
# TODO: group_by和order_by不能同时使用
|
||||
if group_id:
|
||||
_problem = (
|
||||
await cls.filter(group_id=group_id).order_by("create_time")
|
||||
# .group_by("problem")
|
||||
.values_list("problem", flat=True)
|
||||
)
|
||||
else:
|
||||
_problem = (
|
||||
await cls.filter(word_scope=(word_scope or 0)).order_by(
|
||||
"create_time"
|
||||
)
|
||||
# .group_by("problem")
|
||||
.values_list("problem", flat=True)
|
||||
)
|
||||
# if index is None and problem not in _problem:
|
||||
# return "词条不存在...", []
|
||||
sort_problem = []
|
||||
for p in _problem:
|
||||
if p not in sort_problem:
|
||||
sort_problem.append(p)
|
||||
if index > len(sort_problem) - 1:
|
||||
return "下标错误,必须小于问题数量...", []
|
||||
problem = sort_problem[index] # type: ignore
|
||||
f = cls.filter(problem=problem, word_scope=(word_scope or 0))
|
||||
if group_id:
|
||||
f = f.filter(group_id=group_id)
|
||||
answer_list = await f.all()
|
||||
if not answer_list:
|
||||
return "词条不存在...", []
|
||||
return problem, [await cls._format2answer("", "", 0, 0, a) for a in answer_list]
|
||||
|
||||
@classmethod
|
||||
async def delete_group_problem(
|
||||
cls,
|
||||
problem: str,
|
||||
group_id: str | None,
|
||||
index: int | None = None,
|
||||
word_scope: int = 1,
|
||||
):
|
||||
"""删除指定问题全部或指定回答
|
||||
|
||||
参数:
|
||||
problem: 问题文本
|
||||
group_id: 群号
|
||||
index: 回答下标
|
||||
word_scope: 词条范围
|
||||
"""
|
||||
if await cls.exists(None, group_id, problem, None, word_scope):
|
||||
if index is not None:
|
||||
if group_id:
|
||||
query = await cls.filter(
|
||||
group_id=group_id, problem=problem, word_scope=word_scope
|
||||
).all()
|
||||
else:
|
||||
query = await cls.filter(
|
||||
word_scope=word_scope, problem=problem
|
||||
).all()
|
||||
await query[index].delete()
|
||||
else:
|
||||
if group_id:
|
||||
await WordBank.filter(
|
||||
group_id=group_id, problem=problem, word_scope=word_scope
|
||||
).delete()
|
||||
else:
|
||||
await WordBank.filter(
|
||||
word_scope=word_scope, problem=problem
|
||||
).delete()
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def update_group_problem(
|
||||
cls,
|
||||
problem: str,
|
||||
replace_str: str,
|
||||
group_id: str | None,
|
||||
index: int | None = None,
|
||||
word_scope: int = 1,
|
||||
) -> str:
|
||||
"""修改词条问题
|
||||
|
||||
参数:
|
||||
problem: 问题
|
||||
replace_str: 替换问题
|
||||
group_id: 群号
|
||||
index: 问题下标
|
||||
word_scope: 词条范围
|
||||
|
||||
返回:
|
||||
str: 修改前的问题
|
||||
"""
|
||||
if index is not None:
|
||||
if group_id:
|
||||
query = await cls.filter(group_id=group_id, problem=problem).all()
|
||||
else:
|
||||
query = await cls.filter(word_scope=word_scope, problem=problem).all()
|
||||
tmp = query[index].problem
|
||||
query[index].problem = replace_str
|
||||
await query[index].save(update_fields=["problem"])
|
||||
return tmp
|
||||
else:
|
||||
if group_id:
|
||||
await cls.filter(group_id=group_id, problem=problem).update(
|
||||
problem=replace_str
|
||||
)
|
||||
else:
|
||||
await cls.filter(word_scope=word_scope, problem=problem).update(
|
||||
problem=replace_str
|
||||
)
|
||||
return problem
|
||||
|
||||
@classmethod
|
||||
async def get_group_all_problem(cls, group_id: str) -> list[tuple[Any | str]]:
|
||||
"""获取群聊所有词条
|
||||
|
||||
参数:
|
||||
group_id: 群号
|
||||
"""
|
||||
return cls._handle_problem(
|
||||
await cls.filter(group_id=group_id).order_by("create_time").all() # type: ignore
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_problem_by_scope(cls, word_scope: int):
|
||||
"""通过词条范围获取词条
|
||||
|
||||
参数:
|
||||
word_scope: 词条范围
|
||||
"""
|
||||
return cls._handle_problem(
|
||||
await cls.filter(word_scope=word_scope).order_by("create_time").all() # type: ignore
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_problem_by_type(cls, word_type: int):
|
||||
"""通过词条类型获取词条
|
||||
|
||||
参数:
|
||||
word_type: 词条类型
|
||||
"""
|
||||
return cls._handle_problem(
|
||||
await cls.filter(word_type=word_type).order_by("create_time").all() # type: ignore
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _handle_problem(cls, problem_list: list["WordBank"]):
|
||||
"""格式化处理问题
|
||||
|
||||
参数:
|
||||
msg_list: 消息列表
|
||||
"""
|
||||
_tmp = []
|
||||
result_list = []
|
||||
for q in problem_list:
|
||||
if q.problem not in _tmp:
|
||||
# TODO: 获取收录人名称
|
||||
problem = (
|
||||
(path / q.image_path, 30, 30) if q.image_path else q.problem,
|
||||
int2type[q.word_type],
|
||||
# q.author,
|
||||
"-",
|
||||
)
|
||||
result_list.append(problem)
|
||||
_tmp.append(q.problem)
|
||||
return result_list
|
||||
|
||||
@classmethod
|
||||
async def _move(
|
||||
cls,
|
||||
user_id: str,
|
||||
group_id: str | None,
|
||||
problem: str,
|
||||
answer: str,
|
||||
placeholder: str,
|
||||
):
|
||||
"""旧词条图片移动方法
|
||||
|
||||
参数:
|
||||
user_id: 用户id
|
||||
group_id: 群号
|
||||
problem: 问题
|
||||
answer: 回答
|
||||
placeholder: 占位符
|
||||
"""
|
||||
word_scope = 0
|
||||
word_type = 0
|
||||
# 对图片做额外处理
|
||||
if not await cls.exists(
|
||||
user_id, group_id, problem, answer, word_scope, word_type
|
||||
):
|
||||
await cls.create(
|
||||
user_id=user_id,
|
||||
group_id=group_id,
|
||||
word_scope=word_scope,
|
||||
word_type=word_type,
|
||||
status=True,
|
||||
problem=problem,
|
||||
answer=answer,
|
||||
image_path=None,
|
||||
placeholder=placeholder,
|
||||
create_time=datetime.now().replace(microsecond=0),
|
||||
update_time=datetime.now().replace(microsecond=0),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def _run_script(cls):
|
||||
return [
|
||||
"ALTER TABLE word_bank2 ADD to_me varchar(255);", # 添加 to_me 字段
|
||||
"ALTER TABLE word_bank2 ALTER COLUMN create_time TYPE timestamp with time zone USING create_time::timestamp with time zone;",
|
||||
"ALTER TABLE word_bank2 ALTER COLUMN update_time TYPE timestamp with time zone USING update_time::timestamp with time zone;",
|
||||
"ALTER TABLE word_bank2 RENAME COLUMN user_qq TO user_id;", # 将user_qq改为user_id
|
||||
"ALTER TABLE word_bank2 ALTER COLUMN user_id TYPE character varying(255);",
|
||||
"ALTER TABLE word_bank2 ALTER COLUMN group_id TYPE character varying(255);",
|
||||
"ALTER TABLE word_bank2 ADD platform varchar(255) DEFAULT 'qq';",
|
||||
"ALTER TABLE word_bank2 ADD author varchar(255) DEFAULT '';",
|
||||
]
|
||||
59
zhenxun/plugins/word_bank/_rule.py
Normal file
59
zhenxun/plugins/word_bank/_rule.py
Normal file
@ -0,0 +1,59 @@
|
||||
from io import BytesIO
|
||||
|
||||
import imagehash
|
||||
from nonebot.adapters import Bot, Event
|
||||
from nonebot.typing import T_State
|
||||
from nonebot_plugin_alconna import At as alcAt
|
||||
from nonebot_plugin_alconna import Text as alcText
|
||||
from nonebot_plugin_alconna import UniMsg
|
||||
from nonebot_plugin_session import EventSession
|
||||
from PIL import Image
|
||||
from requests import session
|
||||
|
||||
from zhenxun.services.log import logger
|
||||
from zhenxun.utils.http_utils import AsyncHttpx
|
||||
|
||||
from ._data_source import get_img_and_at_list
|
||||
from ._model import WordBank
|
||||
|
||||
|
||||
async def check(
|
||||
bot: Bot,
|
||||
event: Event,
|
||||
message: UniMsg,
|
||||
session: EventSession,
|
||||
state: T_State,
|
||||
) -> bool:
|
||||
text = message.extract_plain_text().strip()
|
||||
img_list, at_list = get_img_and_at_list(message)
|
||||
problem = text
|
||||
if not text and len(img_list) == 1:
|
||||
try:
|
||||
r = await AsyncHttpx.get(img_list[0])
|
||||
problem = str(imagehash.average_hash(Image.open(BytesIO(r.content))))
|
||||
except Exception as e:
|
||||
logger.warning(f"获取图片失败", "词条检测", session=session, e=e)
|
||||
if at_list:
|
||||
temp = ""
|
||||
# TODO: 支持更多消息类型
|
||||
for msg in message:
|
||||
if isinstance(msg, alcAt):
|
||||
temp += f"[at:{msg.target}]"
|
||||
elif isinstance(msg, alcText):
|
||||
temp += msg.text
|
||||
problem = temp
|
||||
if event.is_tome() and bot.config.nickname:
|
||||
if isinstance(message[0], alcAt) and message[0].target == bot.self_id:
|
||||
problem = f"[at:{bot.self_id}]" + problem
|
||||
else:
|
||||
if problem and bot.config.nickname:
|
||||
nickname = [
|
||||
nk for nk in bot.config.nickname if str(message).startswith(nk)
|
||||
]
|
||||
problem = nickname[0] + problem if nickname else problem
|
||||
if problem and (
|
||||
await WordBank.check_problem(session.id3 or session.id2, problem) is not None
|
||||
):
|
||||
state["problem"] = problem # type: ignore
|
||||
return True
|
||||
return False
|
||||
54
zhenxun/plugins/word_bank/command.py
Normal file
54
zhenxun/plugins/word_bank/command.py
Normal file
@ -0,0 +1,54 @@
|
||||
from nonebot import on_regex
|
||||
from nonebot_plugin_alconna import (
|
||||
Alconna,
|
||||
Args,
|
||||
Option,
|
||||
Subcommand,
|
||||
on_alconna,
|
||||
store_true,
|
||||
)
|
||||
|
||||
from zhenxun.utils.rules import admin_check, ensure_group
|
||||
|
||||
_add_matcher = on_regex(
|
||||
r"^(全局|私聊)?添加词条\s*?(模糊|正则|图片)?问\s*?(\S*\s?\S*)\s*?答\s?(\S*)",
|
||||
priority=5,
|
||||
block=True,
|
||||
rule=admin_check("word_bank", "WORD_BANK_LEVEL"),
|
||||
)
|
||||
|
||||
|
||||
_del_matcher = on_alconna(
|
||||
Alconna(
|
||||
"删除词条",
|
||||
Args["problem?", str],
|
||||
Option("--all", action=store_true, help_text="所有词条"),
|
||||
Option("--id", Args["index", int], help_text="下标id"),
|
||||
Option("--aid", Args["answer_id", int], help_text="回答下标id"),
|
||||
),
|
||||
priority=5,
|
||||
block=True,
|
||||
)
|
||||
|
||||
|
||||
_update_matcher = on_alconna(
|
||||
Alconna(
|
||||
"修改词条",
|
||||
Args["replace", str]["problem?", str],
|
||||
Option("--id", Args["index", int], help_text="词条id"),
|
||||
Option("--all", action=store_true, help_text="全局词条"),
|
||||
)
|
||||
)
|
||||
|
||||
_show_matcher = on_alconna(
|
||||
Alconna(
|
||||
"显示词条",
|
||||
Args["problem?", str],
|
||||
Option("-g|--group", Args["gid", str], help_text="群组id"),
|
||||
Option("--id", Args["index", int], help_text="词条id"),
|
||||
Option("--all", action=store_true, help_text="全局词条"),
|
||||
),
|
||||
aliases={"查看词条"},
|
||||
priority=5,
|
||||
block=True,
|
||||
)
|
||||
31
zhenxun/plugins/word_bank/message_handle.py
Normal file
31
zhenxun/plugins/word_bank/message_handle.py
Normal file
@ -0,0 +1,31 @@
|
||||
from nonebot import on_message
|
||||
from nonebot.plugin import PluginMetadata
|
||||
from nonebot.typing import T_State
|
||||
from nonebot_plugin_session import EventSession
|
||||
|
||||
from zhenxun.configs.utils import PluginExtraData
|
||||
from zhenxun.services import logger
|
||||
from zhenxun.utils.enum import PluginType
|
||||
|
||||
from ._model import WordBank
|
||||
from ._rule import check
|
||||
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="词库问答回复操作",
|
||||
description="",
|
||||
usage="""""",
|
||||
extra=PluginExtraData(
|
||||
author="HibiKier", version="0.1", plugin_type=PluginType.HIDDEN
|
||||
).dict(),
|
||||
)
|
||||
|
||||
_matcher = on_message(priority=6, block=True, rule=check)
|
||||
|
||||
|
||||
@_matcher.handle()
|
||||
async def _(session: EventSession, state: T_State):
|
||||
if problem := state.get("problem"):
|
||||
gid = session.id3 or session.id2
|
||||
if result := await WordBank.get_answer(gid, problem):
|
||||
await result.send()
|
||||
logger.info(f" 触发词条 {problem}", "词条检测", session=session)
|
||||
314
zhenxun/plugins/word_bank/word_handle.py
Normal file
314
zhenxun/plugins/word_bank/word_handle.py
Normal file
@ -0,0 +1,314 @@
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
from nonebot.adapters import Bot, Message
|
||||
from nonebot.adapters.onebot.v11 import unescape
|
||||
from nonebot.exception import FinishedException
|
||||
from nonebot.internal.params import Arg, ArgStr
|
||||
from nonebot.params import RegexGroup
|
||||
from nonebot.plugin import PluginMetadata
|
||||
from nonebot.typing import T_State
|
||||
from nonebot_plugin_alconna import AlconnaQuery, Arparma
|
||||
from nonebot_plugin_alconna import Image as alcImage
|
||||
from nonebot_plugin_alconna import Match, Query
|
||||
from nonebot_plugin_alconna import Text as alcText
|
||||
from nonebot_plugin_alconna import UniMsg
|
||||
from nonebot_plugin_saa import Image, MessageFactory, Text
|
||||
from nonebot_plugin_session import EventSession
|
||||
|
||||
from zhenxun.configs.config import Config
|
||||
from zhenxun.configs.utils import PluginExtraData
|
||||
from zhenxun.services.log import logger
|
||||
|
||||
from ._config import scope2int, type2int
|
||||
from ._data_source import WordBankManage, get_answer, get_img_and_at_list, get_problem
|
||||
from ._model import WordBank
|
||||
from .command import _add_matcher, _del_matcher, _show_matcher, _update_matcher
|
||||
|
||||
base_config = Config.get("word_bank")
|
||||
|
||||
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="词库问答",
|
||||
description="自定义词条内容随机回复",
|
||||
usage="""
|
||||
usage:
|
||||
对指定问题的随机回答,对相同问题可以设置多个不同回答
|
||||
删除词条后每个词条的id可能会变化,请查看后再删除
|
||||
更推荐使用id方式删除
|
||||
问题回答支持的类型:at, image
|
||||
查看词条命令:群聊时为 群词条+全局词条,私聊时为 私聊词条+全局词条
|
||||
添加词条正则:添加词条(模糊|正则|图片)?问\s*?(\S*\s?\S*)\s*?答\s?(\S*)
|
||||
正则问可以通过$1类推()捕获的组
|
||||
指令:
|
||||
添加词条 ?[模糊|正则|图片]问...答...:添加问答词条,可重复添加相同问题的不同回答
|
||||
删除词条 [问题/下标] ?[下标]:删除指定词条指定或全部回答
|
||||
修改词条 [问题/下标] [新问题]:修改词条问题
|
||||
查看词条 ?[问题/下标]:查看全部词条或对应词条回答
|
||||
示例:添加图片词条问答嗨嗨嗨
|
||||
[图片]...
|
||||
示例:添加词条@萝莉 我来啦
|
||||
示例:添加词条问谁是萝莉答是我
|
||||
示例:添加词条正则问那个(.+)是萝莉答没错$1是萝莉
|
||||
示例:删除词条 谁是萝莉
|
||||
示例:删除词条 谁是萝莉 0
|
||||
示例:删除词条 id:0 1
|
||||
示例:修改词条 谁是萝莉 是你
|
||||
示例:修改词条 id:0 是你
|
||||
示例:查看词条
|
||||
示例:查看词条 谁是萝莉
|
||||
示例:查看词条 id:0 (群/私聊词条)
|
||||
示例:查看词条 gid:0 (全局词条)
|
||||
""".strip(),
|
||||
extra=PluginExtraData(
|
||||
author="HibiKier & yajiwa",
|
||||
version="0.1",
|
||||
superuser_help="""
|
||||
在私聊中超级用户额外设置
|
||||
指令:
|
||||
(全局|私聊)?添加词条\s*?(模糊|正则|图片)?问\s*?(\S*\s?\S*)\s*?答\s?(\S*):添加问答词条,可重复添加相同问题的不同回答
|
||||
全局添加词条
|
||||
私聊添加词条
|
||||
(私聊情况下)删除词条: 删除私聊词条
|
||||
(私聊情况下)删除全局词条
|
||||
(私聊情况下)修改词条: 修改私聊词条
|
||||
(私聊情况下)修改全局词条
|
||||
用法与普通用法相同
|
||||
""",
|
||||
admin_level=base_config.get("WORD_BANK_LEVEL"),
|
||||
).dict(),
|
||||
)
|
||||
|
||||
|
||||
@_add_matcher.handle()
|
||||
async def _(
|
||||
bot: Bot,
|
||||
session: EventSession,
|
||||
state: T_State,
|
||||
message: UniMsg,
|
||||
reg_group: tuple[Any, ...] = RegexGroup(),
|
||||
):
|
||||
img_list, at_list = get_img_and_at_list(message)
|
||||
user_id = session.id1
|
||||
group_id = session.id3 or session.id2
|
||||
if not group_id and user_id not in bot.config.superusers:
|
||||
await Text("权限不足捏...").finish(reply=True)
|
||||
word_scope, word_type, problem, answer = reg_group
|
||||
if not word_scope and not group_id:
|
||||
word_scope = "私聊"
|
||||
if (
|
||||
word_scope
|
||||
and word_scope in ["全局", "私聊"]
|
||||
and user_id not in bot.config.superusers
|
||||
):
|
||||
await Text("权限不足,无法添加该范围词条...").finish(reply=True)
|
||||
if (not problem or not problem.strip()) and word_type != "图片":
|
||||
await Text("词条问题不能为空!").finish(reply=True)
|
||||
if (not answer or not answer.strip()) and not len(img_list) and not len(at_list):
|
||||
await Text("词条回答不能为空!").finish(reply=True)
|
||||
if word_type != "图片":
|
||||
state["problem_image"] = "YES"
|
||||
temp_problem = message.copy()
|
||||
# answer = message.copy()
|
||||
# 对at问题对额外处理
|
||||
# if at_list:
|
||||
answer = get_answer(message.copy())
|
||||
# text = str(message.pop(0)).split("答", maxsplit=1)[-1].strip()
|
||||
# temp_problem.insert(0, alcText(text))
|
||||
state["word_scope"] = word_scope
|
||||
state["word_type"] = word_type
|
||||
state["problem"] = get_problem(temp_problem)
|
||||
state["answer"] = answer
|
||||
logger.info(
|
||||
f"添加词条 范围: {word_scope} 类型: {word_type} 问题: {problem} 回答: {answer}",
|
||||
"添加词条",
|
||||
session=session,
|
||||
)
|
||||
|
||||
|
||||
@_add_matcher.got("problem_image", prompt="请发送该回答设置的问题图片")
|
||||
async def _(
|
||||
bot: Bot,
|
||||
session: EventSession,
|
||||
message: UniMsg,
|
||||
word_scope: str | None = ArgStr("word_scope"),
|
||||
word_type: str | None = ArgStr("word_type"),
|
||||
problem: str | None = ArgStr("problem"),
|
||||
answer: Any = Arg("answer"),
|
||||
):
|
||||
if not session.id1:
|
||||
await Text("用户id不存在...").finish()
|
||||
user_id = session.id1
|
||||
group_id = session.id3 or session.id2
|
||||
try:
|
||||
if word_type == "图片":
|
||||
problem = [m for m in message if isinstance(m, alcImage)][0].url
|
||||
elif word_type == "正则" and problem:
|
||||
problem = unescape(problem)
|
||||
try:
|
||||
re.compile(problem)
|
||||
except re.error:
|
||||
await Text(f"添加词条失败,正则表达式 {problem} 非法!").finish(
|
||||
reply=True
|
||||
)
|
||||
# if str(event.user_id) in bot.config.superusers and isinstance(event, PrivateMessageEvent):
|
||||
# word_scope = "私聊"
|
||||
nickname = None
|
||||
if problem and bot.config.nickname:
|
||||
nickname = [nk for nk in bot.config.nickname if problem.startswith(nk)]
|
||||
if not problem:
|
||||
await Text("获取问题失败...").finish(reply=True)
|
||||
await WordBank.add_problem_answer(
|
||||
user_id,
|
||||
(
|
||||
group_id
|
||||
if group_id and (not word_scope or word_scope == "私聊")
|
||||
else "0"
|
||||
),
|
||||
scope2int[word_scope] if word_scope else 1,
|
||||
type2int[word_type] if word_type else 0,
|
||||
problem,
|
||||
answer,
|
||||
nickname[0] if nickname else None,
|
||||
session.platform,
|
||||
session.id1,
|
||||
)
|
||||
except Exception as e:
|
||||
if isinstance(e, FinishedException):
|
||||
await _add_matcher.finish()
|
||||
logger.error(
|
||||
f"添加词条 {problem} 错误...",
|
||||
"添加词条",
|
||||
session=session,
|
||||
e=e,
|
||||
)
|
||||
await Text(
|
||||
f"添加词条 {problem if word_type != '图片' else '图片'} 发生错误!"
|
||||
).finish(reply=True)
|
||||
if word_type == "图片":
|
||||
result = MessageFactory([Text("添加词条 "), Image(problem), Text(" 成功!")])
|
||||
else:
|
||||
result = Text(f"添加词条 {problem} 成功!")
|
||||
await result.send()
|
||||
logger.info(
|
||||
f"添加词条 {problem} 成功!",
|
||||
"添加词条",
|
||||
session=session,
|
||||
)
|
||||
|
||||
|
||||
@_del_matcher.handle()
|
||||
async def _(
|
||||
bot: Bot,
|
||||
session: EventSession,
|
||||
problem: Match[str],
|
||||
index: Match[int],
|
||||
answer_id: Match[int],
|
||||
arparma: Arparma,
|
||||
all: Query[bool] = AlconnaQuery("all.value", False),
|
||||
):
|
||||
if not problem.available and not index.available:
|
||||
await Text("此命令之后需要跟随指定词条或id,通过“显示词条“查看").finish(
|
||||
reply=True
|
||||
)
|
||||
word_scope = 1 if session.id3 or session.id2 else 2
|
||||
if all.result:
|
||||
word_scope = 0
|
||||
if gid := session.id3 or session.id2:
|
||||
result, _ = await WordBankManage.delete_word(
|
||||
problem.result,
|
||||
index.result if index.available else None,
|
||||
answer_id.result if answer_id.available else None,
|
||||
gid,
|
||||
word_scope,
|
||||
)
|
||||
else:
|
||||
if session.id1 not in bot.config.superusers:
|
||||
await Text("权限不足捏...").finish(reply=True)
|
||||
result, _ = await WordBankManage.delete_word(
|
||||
problem.result,
|
||||
index.result if index.available else None,
|
||||
answer_id.result if answer_id.available else None,
|
||||
None,
|
||||
word_scope,
|
||||
)
|
||||
await Text(result).send(reply=True)
|
||||
logger.info(f"删除词条: {problem.result}", arparma.header_result, session=session)
|
||||
|
||||
|
||||
@_update_matcher.handle()
|
||||
async def _(
|
||||
bot: Bot,
|
||||
session: EventSession,
|
||||
replace: str,
|
||||
problem: Match[str],
|
||||
index: Match[int],
|
||||
arparma: Arparma,
|
||||
all: Query[bool] = AlconnaQuery("all.value", False),
|
||||
):
|
||||
if not problem.available and not index.available:
|
||||
await Text("此命令之后需要跟随指定词条或id,通过“显示词条“查看").finish(
|
||||
reply=True
|
||||
)
|
||||
word_scope = 1 if session.id3 or session.id2 else 2
|
||||
if all.result:
|
||||
word_scope = 0
|
||||
if gid := session.id3 or session.id2:
|
||||
result, old_problem = await WordBankManage.update_word(
|
||||
replace,
|
||||
problem.result if problem.available else "",
|
||||
index.result if index.available else None,
|
||||
gid,
|
||||
word_scope,
|
||||
)
|
||||
else:
|
||||
if session.id1 not in bot.config.superusers:
|
||||
await Text("权限不足捏...").finish(reply=True)
|
||||
result, old_problem = await WordBankManage.update_word(
|
||||
replace,
|
||||
problem.result if problem.available else "",
|
||||
index.result if index.available else None,
|
||||
session.id3 or session.id2,
|
||||
word_scope,
|
||||
)
|
||||
await Text(result).send(reply=True)
|
||||
logger.info(
|
||||
f"更新词条词条: {old_problem} -> {replace}",
|
||||
arparma.header_result,
|
||||
session=session,
|
||||
)
|
||||
|
||||
|
||||
@_show_matcher.handle()
|
||||
async def _(
|
||||
session: EventSession,
|
||||
problem: Match[str],
|
||||
index: Match[int],
|
||||
gid: Match[str],
|
||||
arparma: Arparma,
|
||||
all: Query[bool] = AlconnaQuery("all.value", False),
|
||||
):
|
||||
word_scope = 1 if session.id3 or session.id2 else 2
|
||||
if all.result:
|
||||
word_scope = 0
|
||||
group_id = session.id3 or session.id2
|
||||
if gid.available:
|
||||
group_id = gid.result
|
||||
if problem.available:
|
||||
if index.available:
|
||||
if index.result < 0 or index.result > len(
|
||||
await WordBank.get_problem_by_scope(2)
|
||||
):
|
||||
await Text("id必须在范围内...").finish(reply=True)
|
||||
result = await WordBankManage.show_word(
|
||||
problem.result,
|
||||
index.result if index.available else None,
|
||||
group_id,
|
||||
word_scope,
|
||||
)
|
||||
else:
|
||||
result = await WordBankManage.show_word(
|
||||
None, index.result if index.available else None, group_id, word_scope
|
||||
)
|
||||
await result.send()
|
||||
logger.info(f"查看词条回答: {problem}", arparma.header_result, session=session)
|
||||
@ -110,11 +110,20 @@ class ImageTemplate:
|
||||
返回:
|
||||
BuildImage: 表格图片
|
||||
"""
|
||||
font = BuildImage.load_font(font_size=50)
|
||||
min_width, _ = BuildImage.get_text_size(head_text, font)
|
||||
table = await cls.table(
|
||||
column_name, data_list, row_space, column_space, padding, text_style
|
||||
column_name,
|
||||
data_list,
|
||||
row_space,
|
||||
column_space,
|
||||
padding,
|
||||
text_style,
|
||||
)
|
||||
await table.circle_corner()
|
||||
table_bk = BuildImage(table.width + 100, table.height + 50, "#EAEDF2")
|
||||
table_bk = BuildImage(
|
||||
max(table.width, min_width) + 100, table.height + 50, "#EAEDF2"
|
||||
)
|
||||
await table_bk.paste(table, center_type="center")
|
||||
height = table_bk.height + 200
|
||||
background = BuildImage(table_bk.width, height, (255, 255, 255), font_size=50)
|
||||
@ -144,13 +153,12 @@ class ImageTemplate:
|
||||
column_space: 列间距.
|
||||
padding: 文本内间距.
|
||||
text_style: 文本样式.
|
||||
min_width: 最低宽度
|
||||
|
||||
返回:
|
||||
BuildImage: 表格图片
|
||||
"""
|
||||
font = BuildImage.load_font("HYWenHei-85W.ttf", 20)
|
||||
column_num = max([len(l) for l in data_list])
|
||||
list_data = []
|
||||
column_data = []
|
||||
for i in range(len(column_name)):
|
||||
c = []
|
||||
@ -163,7 +171,7 @@ class ImageTemplate:
|
||||
build_data_list = []
|
||||
_, base_h = BuildImage.get_text_size("A", font)
|
||||
for i, column_list in enumerate(column_data):
|
||||
name_width, name_height = BuildImage.get_text_size(column_name[i], font)
|
||||
name_width, _ = BuildImage.get_text_size(column_name[i], font)
|
||||
_temp = {"width": name_width, "data": column_list}
|
||||
for s in column_list:
|
||||
if isinstance(s, tuple):
|
||||
@ -207,8 +215,8 @@ class ImageTemplate:
|
||||
)
|
||||
cur_h += base_h + row_space
|
||||
column_image_list.append(background)
|
||||
height = max([bk.height for bk in column_image_list])
|
||||
width = sum([bk.width for bk in column_image_list])
|
||||
# height = max([bk.height for bk in column_image_list])
|
||||
# width = sum([bk.width for bk in column_image_list])
|
||||
return await BuildImage.auto_paste(
|
||||
column_image_list, len(column_image_list), column_space
|
||||
)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user