feature(question): 新增问答模块,用户可在数据库中添加预设好的问题和回答

TODO: 计划新增该模块的web控制面板
This commit is contained in:
ClovertaTheTrilobita 2025-07-12 19:04:53 +08:00
parent b4fd7b1332
commit e35bacffc1
6 changed files with 231 additions and 2 deletions

22
backend.py Normal file
View file

@ -0,0 +1,22 @@
from flask import Flask
from src.clover_sqlite.models.questions import Question
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
@app.route("/list")
async def list_data():
return await Question.fetch()
@app.route("/init")
async def init_data():
if await Question.insert_one("你好", "你好哦"):
return "success"
return "failed"
def start_flask():
print("Flask启动中...")
app.run(host='0.0.0.0', port=5000, debug=False, use_reloader=False)

16
bot.py
View file

@ -1,6 +1,8 @@
import os import os
import glob import glob
import threading
import logging import logging
import nonebot import nonebot
from pathlib import Path from pathlib import Path
from nonebot import logger from nonebot import logger
@ -10,6 +12,10 @@ from apscheduler.schedulers.background import BackgroundScheduler
from src.configs.path_config import log_path,temp_path,video_path,yuc_wiki_path from src.configs.path_config import log_path,temp_path,video_path,yuc_wiki_path
nonebot.init() nonebot.init()
from backend import start_flask
driver = nonebot.get_driver() driver = nonebot.get_driver()
driver.register_adapter(QQAdapter) # 注册QQ适配器 driver.register_adapter(QQAdapter) # 注册QQ适配器
nonebot.load_from_toml("pyproject.toml") nonebot.load_from_toml("pyproject.toml")
@ -35,5 +41,15 @@ scheduler = BackgroundScheduler()
scheduler.add_job(clean_temp_cache, 'cron', hour=0, minute=0) scheduler.add_job(clean_temp_cache, 'cron', hour=0, minute=0)
if __name__ == "__main__": if __name__ == "__main__":
# nonebot_thread = threading.Thread(target=nonebot.run(), daemon=True)
# nonebot_thread.start()
flask_thread = threading.Thread(target=start_flask, daemon=True)
flask_thread.start()
scheduler.start() scheduler.start()
nonebot.run() nonebot.run()

View file

@ -44,4 +44,5 @@ aiohttp
pydantic pydantic
matplotlib matplotlib
httpx httpx
crypto crypto
flask[async]

View file

@ -0,0 +1,176 @@
from lazy_object_proxy.utils import await_
from tortoise import fields
from src.clover_sqlite.data_init.db_connect import Model
class Question(Model):
id = fields.IntField(primary_key=True, generated=True)
question = fields.CharField(max_length=155, description="问题")
answer = fields.CharField(max_length=400,description="回答")
class Meta:
table = "question"
table_description = "问答表"
@classmethod
async def _fetch_data(cls) -> list:
return await cls.all().order_by("id").values_list("id", "question", "answer")
@classmethod
async def _get_data_by_id(cls, get_id: int | None) -> list | None:
"""
通过id获取问答内容
"""
if not get_id:
return None
else:
return await cls.filter(id=get_id).order_by("id").values_list("id", "question", "answer")
@classmethod
async def _get_data_by_ques_keyword_exact(cls, keyword: str | None) -> list | None:
"""
通过关键字精确地获取问题内容
"""
if not keyword:
return None
else:
return await cls.filter(question__icontains=keyword).order_by("id").values_list("id", "question", "answer")
@classmethod
async def _get_data_by_ques_keyword_fuzzy(cls, keyword: str | None) -> list | None:
"""
通过关键字模糊匹配问题内容
"""
if not keyword:
return None
else:
key_list = list(keyword)
result = cls.filter(question__icontains=key_list[0])
for key in key_list:
result = result.filter(question__icontains=key)
return await result.order_by("id").values_list("id", "question", "answer")
@classmethod
async def _get_data_by_ans_keyword_exact(cls, keyword: str | None) -> list | None:
"""
通过关键字精确地获取回答内容
"""
if not keyword:
return None
else:
return await cls.filter(answer__icontains=keyword).order_by("id").values_list("id", "question", "answer")
@classmethod
async def _get_data_by_ans_keyword_fuzzy(cls, keyword: str | None) -> list | None:
"""
通过关键字模糊匹配回答内容
"""
if not keyword:
return None
else:
key_list = list(keyword)
result = cls.filter(answer__icontains=key_list[0])
for key in key_list:
result = result.filter(answer__icontains=key)
return await result.order_by("id").values_list("id", "question", "answer")
@classmethod
async def _insert_data(cls,
question: str | None,
answer: str | None) -> bool:
data = {
"question": question,
"answer": answer
}
if await cls.create(**data):
return True
else:
return False
@classmethod
async def _delete_data(cls, del_id: int | None) -> bool:
if del_id is None:
return False
if await cls.filter(id=del_id).delete():
return True
else:
return False
@classmethod
async def _alter_data(cls,
alter_id: int | None,
question: str | None,
answer: str | None) -> bool:
updated_count = cls.filter(id=alter_id).update(
question=question,
answer=answer
)
print(f"更新了{updated_count}条数据")
return True
@classmethod
async def fetch(cls) -> list:
return await cls._fetch_data()
@classmethod
async def search(cls,
keyword,
via_id: bool = False,
via_question: bool = False,
via_ans: bool = False,
fuzzy: bool = False) -> list | None:
if (via_id and via_ans and via_question is False) or (fuzzy and via_id is True):
print("不合法传参")
return None
if via_id:
return await cls._get_data_by_id(keyword)
elif via_question:
if fuzzy:
return await cls._get_data_by_ques_keyword_fuzzy(keyword)
else:
return await cls._get_data_by_ques_keyword_exact(keyword)
elif via_ans:
if fuzzy:
return await cls._get_data_by_ans_keyword_fuzzy(keyword)
else:
return await cls._get_data_by_ans_keyword_exact(keyword)
return None
@classmethod
async def update(cls, update_id, question, answer) -> bool:
return await cls._alter_data(update_id, question, answer)
@classmethod
async def delete_one(cls, delete_id) -> bool:
return await cls._delete_data(delete_id)
@classmethod
async def delete_many(cls, delete_id_list: list | None) -> bool:
for delete_id in delete_id_list:
await cls._delete_data(delete_id)
return True
@classmethod
async def insert_one(cls,
question: str | None,
answer: str | None) -> bool:
return await cls._insert_data(question, answer)
@classmethod
async def insert_many(cls, data_list: list | None) -> bool:
for question, answer in data_list:
flag = await cls._insert_data(question, answer)
if flag is False:
return flag
return True

View file

@ -10,7 +10,7 @@ from src.clover_sqlite.models.user import UserList
menu = ["/重启","/今日运势","/今日塔罗","/图","/随机图","/搜番","/日报","/点歌","/摸摸头","/群老婆","/今日老婆", "/开启ai","/关闭ai","/角色列表","/添加人设", "/更新人设", "/删除人设", "/切换人设", "/管理员注册", menu = ["/重启","/今日运势","/今日塔罗","/图","/随机图","/搜番","/日报","/点歌","/摸摸头","/群老婆","/今日老婆", "/开启ai","/关闭ai","/角色列表","/添加人设", "/更新人设", "/删除人设", "/切换人设", "/管理员注册",
"/待办", "/test","/天气","我喜欢你", "", "/待办查询", "/新建待办", "/删除待办" ,"/cf","/B站搜索", "/BV搜索", "/喜报", "/悲报", "/luxun","/鲁迅说", "/待办", "/test","/天气","我喜欢你", "", "/待办查询", "/新建待办", "/删除待办" ,"/cf","/B站搜索", "/BV搜索", "/喜报", "/悲报", "/luxun","/鲁迅说",
"/奶龙", "/repo", "/info", "/menu", "/轻小说","/本季新番","/下季新番","/新番观察","/绝对色感" ,"/jm", "/cfrt"] "/奶龙", "/repo", "/info", "/menu", "/轻小说","/本季新番","/下季新番","/新番观察","/绝对色感" ,"/jm", "/cfrt", "/问答"]
send_menu = ["/menu","/今日运势","/今日塔罗","/图","/随机图","搜番","/日报","/点歌","/摸摸头","/群老婆","/待办","/天气", send_menu = ["/menu","/今日运势","/今日塔罗","/图","/随机图","搜番","/日报","/点歌","/摸摸头","/群老婆","/待办","/天气",
"/待办查询", "/新建待办", "/删除待办" , "/cf", "/cfrt", "/B站搜索", "/BV搜索", "/喜报", "/悲报","/鲁迅说", "/待办查询", "/新建待办", "/删除待办" , "/cf", "/cfrt", "/B站搜索", "/BV搜索", "/喜报", "/悲报","/鲁迅说",

14
src/plugins/question.py Normal file
View file

@ -0,0 +1,14 @@
from nonebot.adapters.qq import MessageEvent
from nonebot.plugin import on_command
from nonebot.rule import to_me
from src.clover_sqlite.models.questions import Question
get_question = on_command("问答", rule=to_me(), priority=10)
@get_question.handle()
async def answer_question(message: MessageEvent):
content = message.get_plaintext().replace("/问答", "").strip(" ")
reply = await Question.search(content, via_question=True, fuzzy=False)
if len(reply) > 0:
await get_question.finish(reply[0][2])
else:
await get_question.finish("这个问题我没听说过哦")