update 0.0.6.2

This commit is contained in:
HibiKi
2021-11-23 21:44:59 +08:00
parent c802dfda22
commit 3de487fb24
251 changed files with 14413 additions and 14002 deletions
+16
View File
@@ -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")
+52
View File
@@ -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)
+44
View File
@@ -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)
+189
View File
@@ -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
+12
View File
@@ -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
+155
View File
@@ -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)
)