mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-10-09 22:00:01 +08:00
update 0.0.6.2
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
from configs.config import Config
|
||||
import nonebot
|
||||
|
||||
|
||||
Config.add_plugin_config(
|
||||
"word_bank",
|
||||
"WORD_BANK_LEVEL [LEVEL]",
|
||||
5,
|
||||
name="词库问答",
|
||||
help_="设置增删词库的权限等级",
|
||||
default_value=5
|
||||
)
|
||||
|
||||
|
||||
nonebot.load_plugins("plugins/word_bank")
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
from .model import WordBank
|
||||
from typing import Union
|
||||
|
||||
|
||||
class WordBankBuilder:
|
||||
|
||||
def __init__(self, user_id: int, group_id: int, problem: str):
|
||||
|
||||
self._data = {
|
||||
"user_id": user_id,
|
||||
"group_id": group_id,
|
||||
"problem": problem
|
||||
}
|
||||
|
||||
def set_placeholder(self, id_: int, placeholder: Union[str, int]):
|
||||
"""
|
||||
设置占位符
|
||||
:param id_: 站位id
|
||||
:param placeholder: 占位符内容
|
||||
"""
|
||||
if self._data.get("placeholder") is None:
|
||||
self._data["placeholder"] = []
|
||||
self._data["placeholder"].append((id_, placeholder))
|
||||
|
||||
def set_answer(self, answer: str):
|
||||
"""
|
||||
设置回答
|
||||
:param answer: 回答
|
||||
"""
|
||||
self._data["answer"] = answer
|
||||
|
||||
async def save(self):
|
||||
user_id = self._data["user_id"]
|
||||
group_id = self._data["group_id"]
|
||||
problem = self._data["problem"]
|
||||
answer = self._data["answer"]
|
||||
placeholder = self._data.get("placeholder")
|
||||
await WordBank.add_problem_answer(user_id, group_id, problem, answer, placeholder)
|
||||
|
||||
def __str__(self):
|
||||
return str(self._data)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
from utils.utils import get_message_text
|
||||
from utils.message_builder import image, at
|
||||
from .rule import check
|
||||
from .model import WordBank
|
||||
from configs.path_config import DATA_PATH
|
||||
from pathlib import Path
|
||||
from nonebot.adapters.cqhttp import (
|
||||
Bot,
|
||||
GroupMessageEvent,
|
||||
)
|
||||
from nonebot.typing import T_State
|
||||
from nonebot import on_message
|
||||
|
||||
|
||||
__zx_plugin_name__ = "词库问答回复操作 [Hidden]"
|
||||
|
||||
|
||||
data_dir = Path(DATA_PATH) / "word_bank"
|
||||
data_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
message_handle = on_message(priority=7, block=True, rule=check)
|
||||
|
||||
|
||||
@message_handle.handle()
|
||||
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
|
||||
path = data_dir / f"{event.group_id}"
|
||||
q = await WordBank.check(event.group_id, get_message_text(event.json()))
|
||||
placeholder_list = [
|
||||
(x.split("<_s>")[0], x.split("<_s>")[1])
|
||||
for x in q.format.split("<format>")[:-1]
|
||||
] if q.format else []
|
||||
answer = ""
|
||||
_a = q.answer
|
||||
if not placeholder_list:
|
||||
answer = _a
|
||||
else:
|
||||
for idx, placeholder in placeholder_list:
|
||||
if placeholder.endswith("jpg"):
|
||||
answer += _a[:_a.find(f"[__placeholder_{idx}]")] + image(path / placeholder)
|
||||
else:
|
||||
answer += _a[:_a.find(f"[__placeholder_{idx}]")] + at(placeholder)
|
||||
_a = _a[_a.find(f"[__placeholder_{idx}]") + len(f"[__placeholder_{idx}]"):]
|
||||
await message_handle.send(answer)
|
||||
@@ -0,0 +1,189 @@
|
||||
from services.db_context import db
|
||||
from typing import Optional, List, Union, Tuple
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from configs.path_config import DATA_PATH
|
||||
import random
|
||||
|
||||
|
||||
class WordBank(db.Model):
|
||||
__tablename__ = "word_bank"
|
||||
|
||||
user_qq = db.Column(db.BigInteger(), nullable=False)
|
||||
group_id = db.Column(db.Integer())
|
||||
search_type = db.Column(db.Integer(), nullable=False, default=0)
|
||||
problem = db.Column(db.String(), nullable=False)
|
||||
answer = db.Column(db.String(), nullable=False)
|
||||
format = db.Column(db.String())
|
||||
create_time = db.Column(db.DateTime(), nullable=False)
|
||||
update_time = db.Column(db.DateTime(), nullable=False)
|
||||
|
||||
@classmethod
|
||||
async def add_problem_answer(
|
||||
cls,
|
||||
user_id: int,
|
||||
group_id: Optional[int],
|
||||
problem: str,
|
||||
answer: str,
|
||||
format_: Optional[List[Tuple[int, Union[int, str]]]],
|
||||
) -> bool:
|
||||
"""
|
||||
添加或新增一个问答
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
:param problem: 问题
|
||||
:param answer: 回答
|
||||
:param format_: 格式化数据
|
||||
"""
|
||||
_str = None
|
||||
if format_:
|
||||
_str = ""
|
||||
for x, y in format_:
|
||||
_str += f"{x}<_s>{y}<format>"
|
||||
return await cls._problem_answer_handle(
|
||||
user_id, group_id, problem, "add", answer=answer, format_=_str
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def delete_problem_answer(
|
||||
cls, user_id: int, group_id: Optional[int], problem: str, index: Optional[int]
|
||||
) -> str:
|
||||
"""
|
||||
删除某问题一个或全部回答
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
:param problem: 问题
|
||||
:param index: 回答下标
|
||||
"""
|
||||
return await cls._problem_answer_handle(
|
||||
user_id, group_id, problem, "delete", index=index
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_problem_answer(
|
||||
cls, user_id: int, group_id: Optional[int], problem: str
|
||||
) -> List[str]:
|
||||
"""
|
||||
获取问题的所有回答
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
:param problem: 问题
|
||||
"""
|
||||
return await cls._problem_answer_handle(user_id, group_id, problem, "get")
|
||||
|
||||
@classmethod
|
||||
async def get_group_all_answer(cls, group_id: int, problem: str) -> List[str]:
|
||||
"""
|
||||
获取群聊指定词条所有回答
|
||||
:param group_id: 群号
|
||||
:param problem: 问题
|
||||
"""
|
||||
if problem.startswith("id:"):
|
||||
problem_index = int(problem.split(":")[-1])
|
||||
q = await cls.get_group_all_problem(group_id)
|
||||
if len(q) > problem_index:
|
||||
problem = q[problem_index]
|
||||
q = await cls.query.where(
|
||||
(cls.group_id == group_id) & (cls.problem == problem)
|
||||
).gino.all()
|
||||
return [x.answer for x in q] if q else None
|
||||
|
||||
@classmethod
|
||||
async def get_group_all_problem(cls, group_id: int) -> List[str]:
|
||||
"""
|
||||
获取群聊所有词条
|
||||
:param group_id: 群号
|
||||
"""
|
||||
q = await cls.query.where(cls.group_id == group_id).gino.all()
|
||||
q = [x.problem for x in q]
|
||||
q.sort()
|
||||
return list(set(q))
|
||||
|
||||
@classmethod
|
||||
async def check(cls, group_id: int, problem: str) -> Optional["WordBank"]:
|
||||
"""
|
||||
检测词条并随机返回
|
||||
:param group_id: 群号
|
||||
:param problem: 问题
|
||||
"""
|
||||
q = await cls.query.where(
|
||||
(cls.group_id == group_id) & (cls.problem == problem)
|
||||
).gino.all()
|
||||
return random.choice(q) if q else None
|
||||
|
||||
@classmethod
|
||||
async def _problem_answer_handle(
|
||||
cls,
|
||||
user_id: int,
|
||||
group_id: Optional[int],
|
||||
problem: str,
|
||||
type_: str,
|
||||
*,
|
||||
answer: Optional[str] = None,
|
||||
index: Optional[int] = None,
|
||||
format_: Optional[str] = None,
|
||||
) -> Union[List[Union[str, Tuple[str, str]]], bool, str]:
|
||||
"""
|
||||
添加或新增一个问答
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
:param problem: 问题
|
||||
:param type_: 操作类型
|
||||
:param answer: 回答
|
||||
:param format_: 格式化数据
|
||||
"""
|
||||
if problem.startswith("id:"):
|
||||
problem_index = int(problem.split(":")[-1])
|
||||
q = await cls.get_group_all_problem(group_id)
|
||||
if not q:
|
||||
return []
|
||||
if len(q) > problem_index:
|
||||
problem = q[problem_index]
|
||||
if group_id:
|
||||
q = cls.query.where((cls.group_id == group_id) & (cls.problem == problem))
|
||||
else:
|
||||
q = cls.query.where((cls.user_qq == user_id) & (cls.problem == problem))
|
||||
if type_ == "add":
|
||||
q = await q.where(cls.answer == answer).gino.all()
|
||||
if not q or ".jpg" in format_:
|
||||
await cls.create(
|
||||
user_qq=user_id,
|
||||
group_id=group_id,
|
||||
problem=problem,
|
||||
answer=answer,
|
||||
format=format_,
|
||||
create_time=datetime.now().date(),
|
||||
update_time=datetime.now().date(),
|
||||
)
|
||||
return True
|
||||
elif type_ == "delete":
|
||||
q = await q.with_for_update().gino.all()
|
||||
if q:
|
||||
path = Path(DATA_PATH) / "word_bank" / f"{group_id}"
|
||||
if index is not None:
|
||||
_q = [x.problem for x in q]
|
||||
_q.sort()
|
||||
prob = _q[index]
|
||||
index = [x.problem for x in q].index(prob)
|
||||
q = [q[index]]
|
||||
answer = "\n".join([x.answer for x in q])
|
||||
for x in q:
|
||||
format_ = x.format
|
||||
if format_:
|
||||
for sp in format_.split("<format>")[:-1]:
|
||||
_, image_name = sp.split("<_s>")
|
||||
if image_name.endswith("jpg"):
|
||||
_path = path / image_name
|
||||
if _path.exists():
|
||||
_path.unlink()
|
||||
await cls.delete.where(
|
||||
(cls.problem == problem)
|
||||
& (cls.answer == x.answer)
|
||||
& (cls.group_id == group_id)
|
||||
).gino.status()
|
||||
return answer
|
||||
elif type_ == "get":
|
||||
q = await q.gino.all()
|
||||
if q:
|
||||
return [(x.answer, x.format.split("<format>")[:-1]) for x in q]
|
||||
return False
|
||||
@@ -0,0 +1,12 @@
|
||||
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, Event
|
||||
from utils.utils import get_message_text
|
||||
from nonebot.typing import T_State
|
||||
from .model import WordBank
|
||||
|
||||
|
||||
async def check(bot: Bot, event: Event, state: T_State) -> bool:
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
return bool(
|
||||
await WordBank.check(event.group_id, get_message_text(event.json()))
|
||||
)
|
||||
return False
|
||||
@@ -0,0 +1,155 @@
|
||||
from utils.utils import get_message_at, is_number, get_message_imgs, get_message_text
|
||||
from services.log import logger
|
||||
from configs.path_config import DATA_PATH
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from .data_source import WordBankBuilder
|
||||
from configs.config import Config
|
||||
from .model import WordBank
|
||||
from nonebot.adapters.cqhttp import (
|
||||
Bot,
|
||||
GroupMessageEvent,
|
||||
)
|
||||
from pathlib import Path
|
||||
from nonebot.typing import T_State
|
||||
from nonebot import on_command
|
||||
import random
|
||||
import re
|
||||
|
||||
__zx_plugin_name__ = "词库问答 [Admin]"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
对指定问题的随机回答,对相同问题可以设置多个不同回答
|
||||
删除词条后每个词条的id可能会变化,请查看后再删除
|
||||
指令:
|
||||
添加词条问...答...:添加问答词条,可重复添加相同问题的不同回答
|
||||
删除词条 [问题/下标] ?[下标]:删除指定词条指定或全部回答
|
||||
查看词条 ?[问题/下标]:查看全部词条或对应词条回答
|
||||
示例:添加词条问谁是萝莉答是我
|
||||
示例:删除词条 谁是萝莉
|
||||
示例:删除词条 谁是萝莉 0
|
||||
示例:删除词条 id:0
|
||||
示例:查看词条
|
||||
示例:查看词条 谁是萝莉
|
||||
示例:查看词条 id:0
|
||||
""".strip()
|
||||
__plugin_des__ = "自定义词条内容随机回复"
|
||||
__plugin_cmd__ = [
|
||||
"添加词条问...答..",
|
||||
"删除词条 [问题/下标] ?[下标]",
|
||||
"查看词条 ?[问题/下标]",
|
||||
]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_settings__ = {
|
||||
"admin_level": Config.get_config("word_bank", "WORD_BANK_LEVEL"),
|
||||
"cmd": ["词库问答", "添加词条", "删除词条", "查看词条"]
|
||||
}
|
||||
|
||||
data_dir = Path(DATA_PATH) / "word_bank"
|
||||
data_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
add_word = on_command("添加词条", priority=5, block=True)
|
||||
|
||||
delete_word = on_command("删除词条", priority=5, block=True)
|
||||
|
||||
show_word = on_command("显示词条", aliases={"查看词条"}, priority=5, block=True)
|
||||
|
||||
|
||||
@add_word.handle()
|
||||
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
|
||||
msg = str(event.get_message()).strip()
|
||||
r = re.search(r"^问(.+)\s?答(.*)", msg)
|
||||
if not r:
|
||||
await add_word.finish("未检测到词条问题...")
|
||||
problem = r.group(1).strip()
|
||||
answer = r.group(2).strip()
|
||||
if not answer:
|
||||
await add_word.finish("未检测到词条回答...")
|
||||
idx = 0
|
||||
_builder = WordBankBuilder(event.user_id, event.group_id, problem)
|
||||
for at_ in get_message_at(event.json()):
|
||||
r = re.search(rf"\[CQ:at,qq={at_}]", answer)
|
||||
if r:
|
||||
answer = answer.replace(f"[CQ:at,qq={at_}]", f"[__placeholder_{idx}]", 1)
|
||||
_builder.set_placeholder(idx, at_)
|
||||
idx += 1
|
||||
for img in get_message_imgs(event.json()):
|
||||
_x = img.split("?")[0]
|
||||
r = re.search(rf"\[CQ:image,file=(.*),url={_x}.*?]", answer)
|
||||
if r:
|
||||
rand = random.randint(1, 10000) + random.randint(1, 14514)
|
||||
for i in range(3):
|
||||
answer = answer.replace(f",subType={i}", "")
|
||||
answer = answer.replace(
|
||||
rf"[CQ:image,file={r.group(1)},url={img}]", f"[__placeholder_{idx}]",
|
||||
)
|
||||
await AsyncHttpx.download_file(
|
||||
img, data_dir / f"{event.group_id}" / f"__placeholder_{rand}_{idx}.jpg"
|
||||
)
|
||||
_builder.set_placeholder(idx, f"__placeholder_{rand}_{idx}.jpg")
|
||||
idx += 1
|
||||
_builder.set_answer(answer)
|
||||
await _builder.save()
|
||||
logger.info(f"已保存词条 问:{problem} 答:{msg}")
|
||||
await add_word.send(f"已保存词条:{problem}")
|
||||
|
||||
|
||||
@delete_word.handle()
|
||||
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
|
||||
msg = get_message_text(event.json())
|
||||
if not msg:
|
||||
await delete_word.finish("此命令之后需要跟随指定词条,通过“显示词条“查看")
|
||||
index = None
|
||||
_sp_msg = msg.split()
|
||||
if len(_sp_msg) > 1:
|
||||
if is_number(_sp_msg[-1]):
|
||||
index = int(_sp_msg[-1])
|
||||
msg = " ".join(_sp_msg[:-1])
|
||||
problem = msg
|
||||
if problem.startswith("id:"):
|
||||
x = problem.split(":")[-1]
|
||||
if not is_number(x) or int(x) < 0:
|
||||
await delete_word.finish("id必须为数字且符合规范!")
|
||||
p = (await WordBank.get_group_all_problem(event.group_id))
|
||||
if p:
|
||||
problem = p[int(x)]
|
||||
try:
|
||||
if answer := await WordBank.delete_problem_answer(event.user_id, event.group_id, problem, index):
|
||||
await delete_word.send(f"删除词条成功:{problem}\n回答:\n{answer}")
|
||||
logger.info(
|
||||
f"(USER {event.user_id}, GROUP "
|
||||
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
|
||||
f" 删除词条: {problem}"
|
||||
)
|
||||
else:
|
||||
await delete_word.send(f"删除词条:{problem} 失败,可能该词条不存在")
|
||||
except IndexError:
|
||||
await delete_word.send("指定下标错误...请通过查看词条来确定..")
|
||||
|
||||
|
||||
@show_word.handle()
|
||||
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
|
||||
msg = get_message_text(event.json())
|
||||
if not msg:
|
||||
_problem_list = await WordBank.get_group_all_problem(event.group_id)
|
||||
if not _problem_list:
|
||||
await show_word.finish("该群未收录任何词条..")
|
||||
_problem_list = [f"{i}. {x}" for i, x in enumerate(_problem_list)]
|
||||
await show_word.send(
|
||||
"该群已收录的词条:\n" + "\n".join(_problem_list)
|
||||
)
|
||||
else:
|
||||
_answer_list = await WordBank.get_group_all_answer(event.group_id, msg)
|
||||
if not _answer_list:
|
||||
await show_word.send(
|
||||
"未收录该词条..."
|
||||
)
|
||||
else:
|
||||
_answer_list = [f"{i}. {x}" for i, x in enumerate(_answer_list)]
|
||||
await show_word.send(
|
||||
f"词条 {msg} 回答:\n" + "\n".join(_answer_list)
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user