mirror of
https://github.com/xuthus83/LittlePaimon.git
synced 2025-04-12 23:29:37 +08:00
🎨 格式化代码,适配新amis
This commit is contained in:
parent
7dad2158d7
commit
ebe4b67e6c
@ -16,51 +16,63 @@ from .config import config_manager
|
|||||||
from . import web_api, web_page
|
from . import web_api, web_page
|
||||||
|
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name='群聊学习',
|
name="群聊学习",
|
||||||
description='群聊学习',
|
description="群聊学习",
|
||||||
usage='群聊学习',
|
usage="群聊学习",
|
||||||
extra={
|
extra={
|
||||||
'author': '惜月',
|
"author": "惜月",
|
||||||
'priority': 16,
|
"priority": 16,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def ChatRule(event: GroupMessageEvent, state: T_State) -> bool:
|
async def ChatRule(event: GroupMessageEvent, state: T_State) -> bool:
|
||||||
if answers := await LearningChat(event).answer():
|
if answers := await LearningChat(event).answer():
|
||||||
state['answers'] = answers
|
state["answers"] = answers
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
learning_chat = on_message(priority=99, block=False, rule=Rule(ChatRule), permission=GROUP, state={
|
learning_chat = on_message(
|
||||||
'pm_name': '群聊学习',
|
priority=99,
|
||||||
'pm_description': '(被动技能)bot会学习群友们的发言',
|
block=False,
|
||||||
'pm_usage': '群聊学习',
|
rule=Rule(ChatRule),
|
||||||
'pm_priority': 1
|
permission=GROUP,
|
||||||
})
|
state={
|
||||||
|
"pm_name": "群聊学习",
|
||||||
|
"pm_description": "(被动技能)bot会学习群友们的发言",
|
||||||
|
"pm_usage": "群聊学习",
|
||||||
|
"pm_priority": 1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@learning_chat.handle()
|
@learning_chat.handle()
|
||||||
async def _(event: GroupMessageEvent, answers=Arg('answers')):
|
async def _(event: GroupMessageEvent, answers=Arg("answers")):
|
||||||
for answer in answers:
|
for answer in answers:
|
||||||
try:
|
try:
|
||||||
logger.info('群聊学习', f'{NICKNAME}将向群<m>{event.group_id}</m>回复<m>"{answer}"</m>')
|
logger.info(
|
||||||
|
"群聊学习", f'{NICKNAME}将向群<m>{event.group_id}</m>回复<m>"{answer}"</m>'
|
||||||
|
)
|
||||||
msg = await learning_chat.send(Message(answer))
|
msg = await learning_chat.send(Message(answer))
|
||||||
await ChatMessage.create(group_id=event.group_id,
|
await ChatMessage.create(
|
||||||
user_id=event.self_id,
|
group_id=event.group_id,
|
||||||
message_id=msg['message_id'],
|
user_id=event.self_id,
|
||||||
message=answer,
|
message_id=msg["message_id"],
|
||||||
raw_message=answer,
|
message=answer,
|
||||||
time=int(time.time()),
|
raw_message=answer,
|
||||||
plain_text=Message(answer).extract_plain_text())
|
time=int(time.time()),
|
||||||
|
plain_text=Message(answer).extract_plain_text(),
|
||||||
|
)
|
||||||
await asyncio.sleep(random.random() + 0.5)
|
await asyncio.sleep(random.random() + 0.5)
|
||||||
except ActionFailed:
|
except ActionFailed:
|
||||||
logger.info('群聊学习', f'{NICKNAME}向群<m>{event.group_id}</m>的回复<m>"{answer}"</m>发送<r>失败,可能处于风控中</r>')
|
logger.info(
|
||||||
|
"群聊学习",
|
||||||
|
f'{NICKNAME}向群<m>{event.group_id}</m>的回复<m>"{answer}"</m>发送<r>失败,可能处于风控中</r>',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@scheduler.scheduled_job('interval', minutes=3, misfire_grace_time=5)
|
@scheduler.scheduled_job("interval", minutes=3, misfire_grace_time=5)
|
||||||
async def speak_up():
|
async def speak_up():
|
||||||
if not config_manager.config.total_enable:
|
if not config_manager.config.total_enable:
|
||||||
return
|
return
|
||||||
@ -73,15 +85,22 @@ async def speak_up():
|
|||||||
group_id, messages = speak
|
group_id, messages = speak
|
||||||
for msg in messages:
|
for msg in messages:
|
||||||
try:
|
try:
|
||||||
logger.info('群聊学习', f'{NICKNAME}向群<m>{group_id}</m>主动发言<m>"{msg}"</m>')
|
logger.info("群聊学习", f'{NICKNAME}向群<m>{group_id}</m>主动发言<m>"{msg}"</m>')
|
||||||
send_result = await bot.send_group_msg(group_id=group_id, message=Message(msg))
|
send_result = await bot.send_group_msg(
|
||||||
await ChatMessage.create(group_id=group_id,
|
group_id=group_id, message=Message(msg)
|
||||||
user_id=int(bot.self_id),
|
)
|
||||||
message_id=send_result['message_id'],
|
await ChatMessage.create(
|
||||||
message=msg,
|
group_id=group_id,
|
||||||
raw_message=msg,
|
user_id=int(bot.self_id),
|
||||||
time=int(time.time()),
|
message_id=send_result["message_id"],
|
||||||
plain_text=Message(msg).extract_plain_text())
|
message=msg,
|
||||||
|
raw_message=msg,
|
||||||
|
time=int(time.time()),
|
||||||
|
plain_text=Message(msg).extract_plain_text(),
|
||||||
|
)
|
||||||
await asyncio.sleep(random.randint(2, 4))
|
await asyncio.sleep(random.randint(2, 4))
|
||||||
except ActionFailed:
|
except ActionFailed:
|
||||||
logger.info('群聊学习', f'{NICKNAME}向群<m>{group_id}</m>主动发言<m>"{msg}"</m><r>发送失败,可能处于风控中</r>')
|
logger.info(
|
||||||
|
"群聊学习",
|
||||||
|
f'{NICKNAME}向群<m>{group_id}</m>主动发言<m>"{msg}"</m><r>发送失败,可能处于风控中</r>',
|
||||||
|
)
|
||||||
|
@ -7,19 +7,19 @@ from LittlePaimon.utils.files import load_yaml, save_yaml
|
|||||||
|
|
||||||
|
|
||||||
class ChatGroupConfig(BaseModel):
|
class ChatGroupConfig(BaseModel):
|
||||||
enable: bool = Field(True, alias='群聊学习开关')
|
enable: bool = Field(default=True, alias="群聊学习开关")
|
||||||
ban_words: List[str] = Field([], alias='屏蔽词')
|
ban_words: List[str] = Field(default_factory=list, alias="屏蔽词")
|
||||||
ban_users: List[int] = Field([], alias='屏蔽用户')
|
ban_users: List[int] = Field(default_factory=list, alias="屏蔽用户")
|
||||||
answer_threshold: int = Field(4, alias='回复阈值')
|
answer_threshold: int = Field(default=4, alias="回复阈值")
|
||||||
answer_threshold_weights: List[int] = Field([10, 30, 60], alias='回复阈值权重')
|
answer_threshold_weights: List[int] = Field(default=[10, 30, 60], alias="回复阈值权重")
|
||||||
repeat_threshold: int = Field(3, alias='复读阈值')
|
repeat_threshold: int = Field(default=3, alias="复读阈值")
|
||||||
break_probability: float = Field(0.25, alias='打断复读概率')
|
break_probability: float = Field(default=0.25, alias="打断复读概率")
|
||||||
speak_enable: bool = Field(True, alias='主动发言开关')
|
speak_enable: bool = Field(default=True, alias="主动发言开关")
|
||||||
speak_threshold: int = Field(5, alias='主动发言阈值')
|
speak_threshold: int = Field(default=5, alias="主动发言阈值")
|
||||||
speak_min_interval: int = Field(300, alias='主动发言最小间隔')
|
speak_min_interval: int = Field(default=300, alias="主动发言最小间隔")
|
||||||
speak_continuously_probability: float = Field(0.5, alias='连续主动发言概率')
|
speak_continuously_probability: float = Field(default=0.5, alias="连续主动发言概率")
|
||||||
speak_continuously_max_len: int = Field(3, alias='最大连续主动发言句数')
|
speak_continuously_max_len: int = Field(default=3, alias="最大连续主动发言句数")
|
||||||
speak_poke_probability: float = Field(0.5, alias='主动发言附带戳一戳概率')
|
speak_poke_probability: float = Field(default=0.5, alias="主动发言附带戳一戳概率")
|
||||||
|
|
||||||
def update(self, **kwargs):
|
def update(self, **kwargs):
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
@ -28,14 +28,14 @@ class ChatGroupConfig(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class ChatConfig(BaseModel):
|
class ChatConfig(BaseModel):
|
||||||
total_enable: bool = Field(True, alias='群聊学习总开关')
|
total_enable: bool = Field(default=True, alias="群聊学习总开关")
|
||||||
ban_words: List[str] = Field([], alias='全局屏蔽词')
|
ban_words: List[str] = Field(default_factory=list, alias="全局屏蔽词")
|
||||||
ban_users: List[int] = Field([], alias='全局屏蔽用户')
|
ban_users: List[int] = Field(default_factory=list, alias="全局屏蔽用户")
|
||||||
KEYWORDS_SIZE: int = Field(3, alias='单句关键词分词数量')
|
KEYWORDS_SIZE: int = Field(default=3, alias="单句关键词分词数量")
|
||||||
cross_group_threshold: int = Field(3, alias='跨群回复阈值')
|
cross_group_threshold: int = Field(default=3, alias="跨群回复阈值")
|
||||||
learn_max_count: int = Field(6, alias='最高学习次数')
|
learn_max_count: int = Field(default=6, alias="最高学习次数")
|
||||||
dictionary: List[str] = Field([], alias='自定义词典')
|
dictionary: List[str] = Field(default_factory=list, alias="自定义词典")
|
||||||
group_config: Dict[int, ChatGroupConfig] = Field({}, alias='分群配置')
|
group_config: Dict[int, ChatGroupConfig] = Field(default_factory=dict, alias="分群配置")
|
||||||
|
|
||||||
def update(self, **kwargs):
|
def update(self, **kwargs):
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
@ -44,7 +44,6 @@ class ChatConfig(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class ChatConfigManager:
|
class ChatConfigManager:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.file_path = LEARNING_CHAT_CONFIG
|
self.file_path = LEARNING_CHAT_CONFIG
|
||||||
if self.file_path.exists():
|
if self.file_path.exists():
|
||||||
|
@ -21,16 +21,27 @@ from .config import config_manager
|
|||||||
|
|
||||||
chat_config = config_manager.config
|
chat_config = config_manager.config
|
||||||
command_start_ = command_start.copy()
|
command_start_ = command_start.copy()
|
||||||
if '' in command_start_:
|
if "" in command_start_:
|
||||||
command_start_.remove('')
|
command_start_.remove("")
|
||||||
|
|
||||||
NO_PERMISSION_WORDS = [f'{NICKNAME}就喜欢说这个,哼!', f'你管得着{NICKNAME}吗!']
|
NO_PERMISSION_WORDS = [f"{NICKNAME}就喜欢说这个,哼!", f"你管得着{NICKNAME}吗!"]
|
||||||
ENABLE_WORDS = [f'{NICKNAME}会尝试学你们说怪话!', f'好的呢,让{NICKNAME}学学你们的说话方式~']
|
ENABLE_WORDS = [f"{NICKNAME}会尝试学你们说怪话!", f"好的呢,让{NICKNAME}学学你们的说话方式~"]
|
||||||
DISABLE_WORDS = [f'好好好,{NICKNAME}不学说话就是了!', f'果面呐噻,{NICKNAME}以后不学了...']
|
DISABLE_WORDS = [f"好好好,{NICKNAME}不学说话就是了!", f"果面呐噻,{NICKNAME}以后不学了..."]
|
||||||
SORRY_WORDS = [f'{NICKNAME}知道错了...达咩!', f'{NICKNAME}不会再这么说了...', f'果面呐噻,{NICKNAME}说错话了...']
|
SORRY_WORDS = [
|
||||||
DOUBT_WORDS = [f'{NICKNAME}有说什么奇怪的话吗?']
|
f"{NICKNAME}知道错了...达咩!",
|
||||||
BREAK_REPEAT_WORDS = ['打断复读', '打断!']
|
f"{NICKNAME}不会再这么说了...",
|
||||||
ALL_WORDS = NO_PERMISSION_WORDS + SORRY_WORDS + DOUBT_WORDS + ENABLE_WORDS + DISABLE_WORDS + BREAK_REPEAT_WORDS
|
f"果面呐噻,{NICKNAME}说错话了...",
|
||||||
|
]
|
||||||
|
DOUBT_WORDS = [f"{NICKNAME}有说什么奇怪的话吗?"]
|
||||||
|
BREAK_REPEAT_WORDS = ["打断复读", "打断!"]
|
||||||
|
ALL_WORDS = (
|
||||||
|
NO_PERMISSION_WORDS
|
||||||
|
+ SORRY_WORDS
|
||||||
|
+ DOUBT_WORDS
|
||||||
|
+ ENABLE_WORDS
|
||||||
|
+ DISABLE_WORDS
|
||||||
|
+ BREAK_REPEAT_WORDS
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Result(IntEnum):
|
class Result(IntEnum):
|
||||||
@ -50,92 +61,106 @@ class LearningChat:
|
|||||||
group_id=event.group_id,
|
group_id=event.group_id,
|
||||||
user_id=event.user_id,
|
user_id=event.user_id,
|
||||||
message_id=event.message_id,
|
message_id=event.message_id,
|
||||||
message=re.sub(r'(\[CQ:at,qq=.+])|(\[CQ:reply,id=.+])', '',
|
message=re.sub(
|
||||||
re.sub(r'(,subType=\d+,url=.+])', r']', event.raw_message)).strip(),
|
r"(\[CQ:at,qq=.+])|(\[CQ:reply,id=.+])",
|
||||||
|
"",
|
||||||
|
re.sub(r"(,subType=\d+,url=.+])", r"]", event.raw_message),
|
||||||
|
).strip(),
|
||||||
raw_message=event.raw_message,
|
raw_message=event.raw_message,
|
||||||
plain_text=event.get_plaintext(),
|
plain_text=event.get_plaintext(),
|
||||||
time=event.time
|
time=event.time,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.data = ChatMessage(
|
self.data = ChatMessage(
|
||||||
group_id=event.group_id,
|
group_id=event.group_id,
|
||||||
user_id=event.user_id,
|
user_id=event.user_id,
|
||||||
message_id=event.message_id,
|
message_id=event.message_id,
|
||||||
message=re.sub(r'(\[CQ:at,qq=.+])', '',
|
message=re.sub(
|
||||||
re.sub(r'(,subType=\d+,url=.+])', r']', event.raw_message)).strip(),
|
r"(\[CQ:at,qq=.+])",
|
||||||
|
"",
|
||||||
|
re.sub(r"(,subType=\d+,url=.+])", r"]", event.raw_message),
|
||||||
|
).strip(),
|
||||||
raw_message=event.raw_message,
|
raw_message=event.raw_message,
|
||||||
plain_text=event.get_plaintext(),
|
plain_text=event.get_plaintext(),
|
||||||
time=event.time
|
time=event.time,
|
||||||
)
|
)
|
||||||
self.reply = None
|
self.reply = None
|
||||||
self.bot_id = event.self_id
|
self.bot_id = event.self_id
|
||||||
self.to_me = event.to_me or NICKNAME in self.data.message
|
self.to_me = event.to_me or NICKNAME in self.data.message
|
||||||
self.role = 'superuser' if event.user_id in SUPERUSERS else event.sender.role
|
self.role = "superuser" if event.user_id in SUPERUSERS else event.sender.role
|
||||||
self.config = config_manager.get_group_config(self.data.group_id)
|
self.config = config_manager.get_group_config(self.data.group_id)
|
||||||
self.ban_users = set(chat_config.ban_users + self.config.ban_users)
|
self.ban_users = set(chat_config.ban_users + self.config.ban_users)
|
||||||
self.ban_words = set(chat_config.ban_words + self.config.ban_words)
|
self.ban_words = set(chat_config.ban_words + self.config.ban_words)
|
||||||
|
|
||||||
async def _learn(self) -> Result:
|
async def _learn(self) -> Result:
|
||||||
if self.to_me and any(
|
if self.to_me and any(w in self.data.message for w in {"学说话", "快学", "开启学习"}):
|
||||||
w in self.data.message for w in {'学说话', '快学', '开启学习'}):
|
|
||||||
return Result.SetEnable
|
return Result.SetEnable
|
||||||
elif self.to_me and any(
|
elif self.to_me and any(w in self.data.message for w in {"闭嘴", "别学", "关闭学习"}):
|
||||||
w in self.data.message for w in {'闭嘴', '别学', '关闭学习'}):
|
|
||||||
return Result.SetDisable
|
return Result.SetDisable
|
||||||
elif not chat_config.total_enable or not self.config.enable:
|
elif not chat_config.total_enable or not self.config.enable:
|
||||||
logger.debug('群聊学习', f'➤该群<m>{self.data.group_id}</m>未开启群聊学习,跳过')
|
logger.debug("群聊学习", f"➤该群<m>{self.data.group_id}</m>未开启群聊学习,跳过")
|
||||||
# 如果未开启群聊学习,跳过
|
# 如果未开启群聊学习,跳过
|
||||||
return Result.Pass
|
return Result.Pass
|
||||||
elif command_start_ and self.data.message.startswith(tuple(command_start_)):
|
elif command_start_ and self.data.message.startswith(tuple(command_start_)):
|
||||||
# 以命令前缀开头的消息,跳过
|
# 以命令前缀开头的消息,跳过
|
||||||
logger.debug('群聊学习', '➤该消息以命令前缀开头,跳过')
|
logger.debug("群聊学习", "➤该消息以命令前缀开头,跳过")
|
||||||
return Result.Pass
|
return Result.Pass
|
||||||
elif self.data.user_id in self.ban_users:
|
elif self.data.user_id in self.ban_users:
|
||||||
# 发言人在屏蔽列表中,跳过
|
# 发言人在屏蔽列表中,跳过
|
||||||
logger.debug('群聊学习', f'➤发言人<m>{self.data.user_id}</m>在屏蔽列表中,跳过')
|
logger.debug("群聊学习", f"➤发言人<m>{self.data.user_id}</m>在屏蔽列表中,跳过")
|
||||||
return Result.Pass
|
return Result.Pass
|
||||||
elif self.to_me and any(w in self.data.message for w in {'不可以', '达咩', '不能说这'}):
|
elif self.to_me and any(w in self.data.message for w in {"不可以", "达咩", "不能说这"}):
|
||||||
# 如果是对某句话进行禁言
|
# 如果是对某句话进行禁言
|
||||||
return Result.Ban
|
return Result.Ban
|
||||||
elif not await self._check_allow(self.data):
|
elif not await self._check_allow(self.data):
|
||||||
# 本消息不合法,跳过
|
# 本消息不合法,跳过
|
||||||
logger.debug('群聊学习', '➤消息未通过校验,跳过')
|
logger.debug("群聊学习", "➤消息未通过校验,跳过")
|
||||||
return Result.Pass
|
return Result.Pass
|
||||||
elif self.reply:
|
elif self.reply:
|
||||||
# 如果是回复消息
|
# 如果是回复消息
|
||||||
if not (message := await ChatMessage.filter(message_id=self.reply.message_id).first()):
|
if not (
|
||||||
|
message := await ChatMessage.filter(
|
||||||
|
message_id=self.reply.message_id
|
||||||
|
).first()
|
||||||
|
):
|
||||||
# 回复的消息在数据库中有记录
|
# 回复的消息在数据库中有记录
|
||||||
logger.debug('群聊学习', '➤回复的消息不在数据库中,跳过')
|
logger.debug("群聊学习", "➤回复的消息不在数据库中,跳过")
|
||||||
return Result.Pass
|
return Result.Pass
|
||||||
if message.user_id in self.ban_users:
|
if message.user_id in self.ban_users:
|
||||||
# 且回复的人不在屏蔽列表中
|
# 且回复的人不在屏蔽列表中
|
||||||
logger.debug('群聊学习', '➤回复的人在屏蔽列表中,跳过')
|
logger.debug("群聊学习", "➤回复的人在屏蔽列表中,跳过")
|
||||||
return Result.Pass
|
return Result.Pass
|
||||||
if not await self._check_allow(message):
|
if not await self._check_allow(message):
|
||||||
# 且回复的内容通过校验
|
# 且回复的内容通过校验
|
||||||
logger.debug('群聊学习', '➤回复的消息未通过校验,跳过')
|
logger.debug("群聊学习", "➤回复的消息未通过校验,跳过")
|
||||||
return Result.Pass
|
return Result.Pass
|
||||||
# 则将该回复作为该消息的答案
|
# 则将该回复作为该消息的答案
|
||||||
await self._set_answer(message)
|
await self._set_answer(message)
|
||||||
return Result.Learn
|
return Result.Learn
|
||||||
elif messages := await ChatMessage.filter(group_id=self.data.group_id, time__gte=self.data.time - 3600).limit(
|
elif messages := await ChatMessage.filter(
|
||||||
5):
|
group_id=self.data.group_id, time__gte=self.data.time - 3600
|
||||||
|
).limit(5):
|
||||||
# 获取本群一个小时内的最后5条消息
|
# 获取本群一个小时内的最后5条消息
|
||||||
if messages[0].message == self.data.message:
|
if messages[0].message == self.data.message:
|
||||||
# 判断是否为复读中
|
# 判断是否为复读中
|
||||||
logger.debug('群聊学习', '➤复读中,跳过')
|
logger.debug("群聊学习", "➤复读中,跳过")
|
||||||
return Result.Repeat
|
return Result.Repeat
|
||||||
for message in messages:
|
for message in messages:
|
||||||
# 如果5条内有相关信息,就作为该消息的答案
|
# 如果5条内有相关信息,就作为该消息的答案
|
||||||
if message.user_id not in self.ban_users and set(self.data.keyword_list) & set(
|
if (
|
||||||
message.keyword_list) and self.data.keyword_list != message.keyword_list and await self._check_allow(
|
message.user_id not in self.ban_users
|
||||||
message):
|
and set(self.data.keyword_list) & set(message.keyword_list)
|
||||||
|
and self.data.keyword_list != message.keyword_list
|
||||||
|
and await self._check_allow(message)
|
||||||
|
):
|
||||||
await self._set_answer(message)
|
await self._set_answer(message)
|
||||||
return Result.Learn
|
return Result.Learn
|
||||||
# 如果没有相关信息
|
# 如果没有相关信息
|
||||||
if messages[0].user_id in self.ban_users or not await self._check_allow(messages[0]):
|
if messages[0].user_id in self.ban_users or not await self._check_allow(
|
||||||
|
messages[0]
|
||||||
|
):
|
||||||
# 且最后一条消息的发送者不在屏蔽列表中并通过校验
|
# 且最后一条消息的发送者不在屏蔽列表中并通过校验
|
||||||
logger.debug('群聊学习', '➤最后一条消息未通过校验,跳过')
|
logger.debug("群聊学习", "➤最后一条消息未通过校验,跳过")
|
||||||
return Result.Pass
|
return Result.Pass
|
||||||
# 则作为最后一条消息的答案
|
# 则作为最后一条消息的答案
|
||||||
await self._set_answer(messages[0])
|
await self._set_answer(messages[0])
|
||||||
@ -150,7 +175,7 @@ class LearningChat:
|
|||||||
await self.data.save()
|
await self.data.save()
|
||||||
if result == Result.Ban:
|
if result == Result.Ban:
|
||||||
# 禁用某句话
|
# 禁用某句话
|
||||||
if self.role not in {'superuser', 'admin', 'owner'}:
|
if self.role not in {"superuser", "admin", "owner"}:
|
||||||
# 检查权限
|
# 检查权限
|
||||||
return [random.choice(NO_PERMISSION_WORDS)]
|
return [random.choice(NO_PERMISSION_WORDS)]
|
||||||
if self.reply:
|
if self.reply:
|
||||||
@ -163,57 +188,79 @@ class LearningChat:
|
|||||||
return [random.choice(DOUBT_WORDS)]
|
return [random.choice(DOUBT_WORDS)]
|
||||||
elif result in [Result.SetEnable, Result.SetDisable]:
|
elif result in [Result.SetEnable, Result.SetDisable]:
|
||||||
# 检查权限
|
# 检查权限
|
||||||
if self.role not in {'superuser', 'admin', 'owner'}:
|
if self.role not in {"superuser", "admin", "owner"}:
|
||||||
return [random.choice(NO_PERMISSION_WORDS)]
|
return [random.choice(NO_PERMISSION_WORDS)]
|
||||||
self.config.update(enable=(result == Result.SetEnable))
|
self.config.update(enable=(result == Result.SetEnable))
|
||||||
config_manager.config.group_config[self.data.group_id] = self.config
|
config_manager.config.group_config[self.data.group_id] = self.config
|
||||||
config_manager.save()
|
config_manager.save()
|
||||||
logger.info('群聊学习',
|
logger.info(
|
||||||
f'群<m>{self.data.group_id}</m>{"开启" if result == Result.SetEnable else "关闭"}学习功能')
|
"群聊学习",
|
||||||
return [random.choice(ENABLE_WORDS if result == Result.SetEnable else DISABLE_WORDS)]
|
f'群<m>{self.data.group_id}</m>{"开启" if result == Result.SetEnable else "关闭"}学习功能',
|
||||||
|
)
|
||||||
|
return [
|
||||||
|
random.choice(
|
||||||
|
ENABLE_WORDS if result == Result.SetEnable else DISABLE_WORDS
|
||||||
|
)
|
||||||
|
]
|
||||||
elif result == Result.Pass:
|
elif result == Result.Pass:
|
||||||
# 跳过
|
# 跳过
|
||||||
return None
|
return None
|
||||||
elif result == Result.Repeat:
|
elif result == Result.Repeat:
|
||||||
if await ChatMessage.filter(group_id=self.data.group_id,
|
if (
|
||||||
time__gte=self.data.time - 3600).limit(
|
await ChatMessage.filter(
|
||||||
self.config.repeat_threshold + 5).filter(
|
group_id=self.data.group_id, time__gte=self.data.time - 3600
|
||||||
user_id=self.bot_id, message=self.data.message).exists():
|
)
|
||||||
|
.limit(self.config.repeat_threshold + 5)
|
||||||
|
.filter(user_id=self.bot_id, message=self.data.message)
|
||||||
|
.exists()
|
||||||
|
):
|
||||||
# 如果在阈值+5条消息内,bot已经回复过这句话,则跳过
|
# 如果在阈值+5条消息内,bot已经回复过这句话,则跳过
|
||||||
logger.debug('群聊学习', '➤➤已经复读过了,跳过')
|
logger.debug("群聊学习", "➤➤已经复读过了,跳过")
|
||||||
return None
|
return None
|
||||||
if not (messages := await ChatMessage.filter(
|
if not (
|
||||||
group_id=self.data.group_id,
|
messages := await ChatMessage.filter(
|
||||||
time__gte=self.data.time - 3600).limit(self.config.repeat_threshold)):
|
group_id=self.data.group_id, time__gte=self.data.time - 3600
|
||||||
|
).limit(self.config.repeat_threshold)
|
||||||
|
):
|
||||||
return None
|
return None
|
||||||
# 如果达到阈值,且不是全都为同一个人在说,则进行复读
|
# 如果达到阈值,且不是全都为同一个人在说,则进行复读
|
||||||
if len(messages) >= self.config.repeat_threshold and all(
|
if (
|
||||||
message.message == self.data.message
|
len(messages) >= self.config.repeat_threshold
|
||||||
for message in messages) and any(message.user_id != self.data.user_id
|
and all(message.message == self.data.message for message in messages)
|
||||||
for message in messages):
|
and any(message.user_id != self.data.user_id for message in messages)
|
||||||
|
):
|
||||||
if random.random() < self.config.break_probability:
|
if random.random() < self.config.break_probability:
|
||||||
logger.debug('群聊学习', '➤➤达到复读阈值,打断复读!')
|
logger.debug("群聊学习", "➤➤达到复读阈值,打断复读!")
|
||||||
return [random.choice(BREAK_REPEAT_WORDS)]
|
return [random.choice(BREAK_REPEAT_WORDS)]
|
||||||
else:
|
else:
|
||||||
logger.debug('群聊学习', f'➤➤达到复读阈值,复读<m>{messages[0].message}</m>')
|
logger.debug("群聊学习", f"➤➤达到复读阈值,复读<m>{messages[0].message}</m>")
|
||||||
return [self.data.message]
|
return [self.data.message]
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
# 回复
|
# 回复
|
||||||
if self.data.is_plain_text and len(self.data.plain_text) <= 1:
|
if self.data.is_plain_text and len(self.data.plain_text) <= 1:
|
||||||
logger.debug('群聊学习', '➤➤消息过短,不回复')
|
logger.debug("群聊学习", "➤➤消息过短,不回复")
|
||||||
return None
|
return None
|
||||||
if not (context := await ChatContext.filter(keywords=self.data.keywords).first()):
|
if not (
|
||||||
logger.debug('群聊学习', '➤➤尚未有已学习的回复,不回复')
|
context := await ChatContext.filter(keywords=self.data.keywords).first()
|
||||||
|
):
|
||||||
|
logger.debug("群聊学习", "➤➤尚未有已学习的回复,不回复")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# 获取回复阈值
|
# 获取回复阈值
|
||||||
if not self.to_me:
|
if not self.to_me:
|
||||||
answer_choices = list(
|
answer_choices = list(
|
||||||
range(self.config.answer_threshold - len(self.config.answer_threshold_weights) + 1,
|
range(
|
||||||
self.config.answer_threshold + 1))
|
self.config.answer_threshold
|
||||||
|
- len(self.config.answer_threshold_weights)
|
||||||
|
+ 1,
|
||||||
|
self.config.answer_threshold + 1,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
answer_count_threshold = random.choices(answer_choices, weights=self.config.answer_threshold_weights)[0]
|
answer_count_threshold = random.choices(
|
||||||
|
answer_choices, weights=self.config.answer_threshold_weights
|
||||||
|
)[0]
|
||||||
|
|
||||||
if len(self.data.keyword_list) == chat_config.KEYWORDS_SIZE:
|
if len(self.data.keyword_list) == chat_config.KEYWORDS_SIZE:
|
||||||
answer_count_threshold -= 1
|
answer_count_threshold -= 1
|
||||||
@ -221,17 +268,25 @@ class LearningChat:
|
|||||||
else:
|
else:
|
||||||
answer_count_threshold = 1
|
answer_count_threshold = 1
|
||||||
cross_group_threshold = 1
|
cross_group_threshold = 1
|
||||||
logger.debug('群聊学习',
|
logger.debug(
|
||||||
f'➤➤本次回复阈值为<m>{answer_count_threshold}</m>,跨群阈值为<m>{cross_group_threshold}</m>')
|
"群聊学习",
|
||||||
|
f"➤➤本次回复阈值为<m>{answer_count_threshold}</m>,跨群阈值为<m>{cross_group_threshold}</m>",
|
||||||
|
)
|
||||||
# 获取满足跨群条件的回复
|
# 获取满足跨群条件的回复
|
||||||
answers_cross = await ChatAnswer.filter(context=context, count__gte=answer_count_threshold,
|
answers_cross = await ChatAnswer.filter(
|
||||||
keywords__in=await ChatAnswer.annotate(
|
context=context,
|
||||||
cross=Count('keywords')).group_by('keywords').filter(
|
count__gte=answer_count_threshold,
|
||||||
cross__gte=cross_group_threshold).values_list('keywords',
|
keywords__in=await ChatAnswer.annotate(cross=Count("keywords"))
|
||||||
flat=True))
|
.group_by("keywords")
|
||||||
|
.filter(cross__gte=cross_group_threshold)
|
||||||
|
.values_list("keywords", flat=True),
|
||||||
|
)
|
||||||
|
|
||||||
answer_same_group = await ChatAnswer.filter(context=context, count__gte=answer_count_threshold,
|
answer_same_group = await ChatAnswer.filter(
|
||||||
group_id=self.data.group_id)
|
context=context,
|
||||||
|
count__gte=answer_count_threshold,
|
||||||
|
group_id=self.data.group_id,
|
||||||
|
)
|
||||||
|
|
||||||
candidate_answers: List[Optional[ChatAnswer]] = []
|
candidate_answers: List[Optional[ChatAnswer]] = []
|
||||||
# 检查候选回复是否在屏蔽列表中
|
# 检查候选回复是否在屏蔽列表中
|
||||||
@ -242,23 +297,32 @@ class LearningChat:
|
|||||||
# answer.count -= answer_count_threshold - 1
|
# answer.count -= answer_count_threshold - 1
|
||||||
candidate_answers.append(answer)
|
candidate_answers.append(answer)
|
||||||
if not candidate_answers:
|
if not candidate_answers:
|
||||||
logger.debug('群聊学习', '➤➤没有符合条件的候选回复')
|
logger.debug("群聊学习", "➤➤没有符合条件的候选回复")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# 从候选回复中进行选择
|
# 从候选回复中进行选择
|
||||||
sum_count = sum(answer.count for answer in candidate_answers)
|
sum_count = sum(answer.count for answer in candidate_answers)
|
||||||
per_list = [answer.count / sum_count * (1 - 1 / answer.count) for answer in candidate_answers]
|
per_list = [
|
||||||
|
answer.count / sum_count * (1 - 1 / answer.count)
|
||||||
|
for answer in candidate_answers
|
||||||
|
]
|
||||||
|
|
||||||
per_list.append(1 - sum(per_list))
|
per_list.append(1 - sum(per_list))
|
||||||
answer_dict = tuple(zip(candidate_answers, per_list))
|
answer_dict = tuple(zip(candidate_answers, per_list))
|
||||||
logger.debug('群聊学习',
|
logger.debug(
|
||||||
f'➤➤候选回复有<m>{"|".join([f"""{a.keywords}({round(p, 3)})""" for a, p in answer_dict])}|不回复({round(per_list[-1], 3)})</m>')
|
"群聊学习",
|
||||||
|
f'➤➤候选回复有<m>{"|".join([f"""{a.keywords}({round(p, 3)})""" for a, p in answer_dict])}|不回复({round(per_list[-1], 3)})</m>',
|
||||||
|
)
|
||||||
|
|
||||||
if (result := random.choices(candidate_answers + [None], weights=per_list)[0]) is None:
|
if (
|
||||||
logger.debug('群聊学习', '➤➤但不进行回复')
|
result := random.choices(candidate_answers + [None], weights=per_list)[
|
||||||
|
0
|
||||||
|
]
|
||||||
|
) is None:
|
||||||
|
logger.debug("群聊学习", "➤➤但不进行回复")
|
||||||
return None
|
return None
|
||||||
result_message = random.choice(result.messages)
|
result_message = random.choice(result.messages)
|
||||||
logger.debug('群聊学习', f'➤➤将回复<m>{result_message}</m>')
|
logger.debug("群聊学习", f"➤➤将回复<m>{result_message}</m>")
|
||||||
await asyncio.sleep(random.random() + 0.5)
|
await asyncio.sleep(random.random() + 0.5)
|
||||||
return [result_message]
|
return [result_message]
|
||||||
|
|
||||||
@ -266,21 +330,27 @@ class LearningChat:
|
|||||||
"""屏蔽消息"""
|
"""屏蔽消息"""
|
||||||
bot = get_bot()
|
bot = get_bot()
|
||||||
if message_id:
|
if message_id:
|
||||||
if not (message := await ChatMessage.filter(message_id=message_id).first()) or message.message in ALL_WORDS:
|
if (
|
||||||
|
not (message := await ChatMessage.filter(message_id=message_id).first())
|
||||||
|
or message.message in ALL_WORDS
|
||||||
|
):
|
||||||
return False
|
return False
|
||||||
keywords = message.keywords
|
keywords = message.keywords
|
||||||
try:
|
try:
|
||||||
await bot.delete_msg(message_id=message_id)
|
await bot.delete_msg(message_id=message_id)
|
||||||
except ActionFailed:
|
except ActionFailed:
|
||||||
logger.info('群聊学习', f'待禁用消息<m>{message_id}</m>尝试撤回<r>失败</r>')
|
logger.info("群聊学习", f"待禁用消息<m>{message_id}</m>尝试撤回<r>失败</r>")
|
||||||
elif (last_reply := await ChatMessage.filter(group_id=self.data.group_id, user_id=self.bot_id).first()) and (
|
elif (
|
||||||
last_reply.message not in ALL_WORDS):
|
last_reply := await ChatMessage.filter(
|
||||||
|
group_id=self.data.group_id, user_id=self.bot_id
|
||||||
|
).first()
|
||||||
|
) and (last_reply.message not in ALL_WORDS):
|
||||||
# 没有指定消息ID,则屏蔽最后一条回复
|
# 没有指定消息ID,则屏蔽最后一条回复
|
||||||
keywords = last_reply.keywords
|
keywords = last_reply.keywords
|
||||||
try:
|
try:
|
||||||
await bot.delete_msg(message_id=last_reply.message_id)
|
await bot.delete_msg(message_id=last_reply.message_id)
|
||||||
except ActionFailed:
|
except ActionFailed:
|
||||||
logger.info('群聊学习', f'待禁用消息<m>{last_reply.message_id}</m>尝试撤回<r>失败</r>')
|
logger.info("群聊学习", f"待禁用消息<m>{last_reply.message_id}</m>尝试撤回<r>失败</r>")
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
if ban_word := await ChatBlackList.filter(keywords=keywords).first():
|
if ban_word := await ChatBlackList.filter(keywords=keywords).first():
|
||||||
@ -291,16 +361,24 @@ class LearningChat:
|
|||||||
if len(ban_word.ban_group_id) >= 2:
|
if len(ban_word.ban_group_id) >= 2:
|
||||||
# 如果有超过2个群都屏蔽了该条消息,则全局屏蔽
|
# 如果有超过2个群都屏蔽了该条消息,则全局屏蔽
|
||||||
ban_word.global_ban = True
|
ban_word.global_ban = True
|
||||||
logger.info('群聊学习', f'学习词<m>{keywords}</m>将被全局禁用')
|
logger.info("群聊学习", f"学习词<m>{keywords}</m>将被全局禁用")
|
||||||
await ChatAnswer.filter(keywords=keywords).delete()
|
await ChatAnswer.filter(keywords=keywords).delete()
|
||||||
else:
|
else:
|
||||||
logger.info('群聊学习', f'群<m>{self.data.group_id}</m>禁用了学习词<m>{keywords}</m>')
|
logger.info(
|
||||||
await ChatAnswer.filter(keywords=keywords, group_id=self.data.group_id).delete()
|
"群聊学习", f"群<m>{self.data.group_id}</m>禁用了学习词<m>{keywords}</m>"
|
||||||
|
)
|
||||||
|
await ChatAnswer.filter(
|
||||||
|
keywords=keywords, group_id=self.data.group_id
|
||||||
|
).delete()
|
||||||
else:
|
else:
|
||||||
# 没有屏蔽记录,则新建
|
# 没有屏蔽记录,则新建
|
||||||
logger.info('群聊学习', f'群<m>{self.data.group_id}</m>禁用了学习词<m>{keywords}</m>')
|
logger.info("群聊学习", f"群<m>{self.data.group_id}</m>禁用了学习词<m>{keywords}</m>")
|
||||||
ban_word = ChatBlackList(keywords=keywords, ban_group_id=[self.data.group_id])
|
ban_word = ChatBlackList(
|
||||||
await ChatAnswer.filter(keywords=keywords, group_id=self.data.group_id).delete()
|
keywords=keywords, ban_group_id=[self.data.group_id]
|
||||||
|
)
|
||||||
|
await ChatAnswer.filter(
|
||||||
|
keywords=keywords, group_id=self.data.group_id
|
||||||
|
).delete()
|
||||||
await ChatContext.filter(keywords=keywords).delete()
|
await ChatContext.filter(keywords=keywords).delete()
|
||||||
await ban_word.save()
|
await ban_word.save()
|
||||||
return True
|
return True
|
||||||
@ -316,49 +394,70 @@ class LearningChat:
|
|||||||
if len(ban_word.ban_group_id) >= 2:
|
if len(ban_word.ban_group_id) >= 2:
|
||||||
# 如果有超过2个群都屏蔽了该条消息,则全局屏蔽
|
# 如果有超过2个群都屏蔽了该条消息,则全局屏蔽
|
||||||
ban_word.global_ban = True
|
ban_word.global_ban = True
|
||||||
logger.info('群聊学习', f'学习词<m>{data.keywords}</m>将被全局禁用')
|
logger.info("群聊学习", f"学习词<m>{data.keywords}</m>将被全局禁用")
|
||||||
await ChatAnswer.filter(keywords=data.keywords).delete()
|
await ChatAnswer.filter(keywords=data.keywords).delete()
|
||||||
else:
|
else:
|
||||||
logger.info('群聊学习', f'群<m>{data.group_id}</m>禁用了学习词<m>{data.keywords}</m>')
|
logger.info(
|
||||||
await ChatAnswer.filter(keywords=data.keywords, group_id=data.group_id).delete()
|
"群聊学习", f"群<m>{data.group_id}</m>禁用了学习词<m>{data.keywords}</m>"
|
||||||
|
)
|
||||||
|
await ChatAnswer.filter(
|
||||||
|
keywords=data.keywords, group_id=data.group_id
|
||||||
|
).delete()
|
||||||
else:
|
else:
|
||||||
ban_word.global_ban = True
|
ban_word.global_ban = True
|
||||||
logger.info('群聊学习', f'学习词<m>{data.keywords}</m>将被全局禁用')
|
logger.info("群聊学习", f"学习词<m>{data.keywords}</m>将被全局禁用")
|
||||||
await ChatAnswer.filter(keywords=data.keywords).delete()
|
await ChatAnswer.filter(keywords=data.keywords).delete()
|
||||||
else:
|
else:
|
||||||
# 没有屏蔽记录,则新建
|
# 没有屏蔽记录,则新建
|
||||||
if isinstance(data, ChatMessage):
|
if isinstance(data, ChatMessage):
|
||||||
logger.info('群聊学习', f'群<m>{data.group_id}</m>禁用了学习词<m>{data.keywords}</m>')
|
logger.info(
|
||||||
ban_word = ChatBlackList(keywords=data.keywords, ban_group_id=[data.group_id])
|
"群聊学习", f"群<m>{data.group_id}</m>禁用了学习词<m>{data.keywords}</m>"
|
||||||
await ChatAnswer.filter(keywords=data.keywords, group_id=data.group_id).delete()
|
)
|
||||||
|
ban_word = ChatBlackList(
|
||||||
|
keywords=data.keywords, ban_group_id=[data.group_id]
|
||||||
|
)
|
||||||
|
await ChatAnswer.filter(
|
||||||
|
keywords=data.keywords, group_id=data.group_id
|
||||||
|
).delete()
|
||||||
else:
|
else:
|
||||||
logger.info('群聊学习', f'学习词<m>{data.keywords}</m>将被全局禁用')
|
logger.info("群聊学习", f"学习词<m>{data.keywords}</m>将被全局禁用")
|
||||||
ban_word = ChatBlackList(keywords=data.keywords, global_ban=True)
|
ban_word = ChatBlackList(keywords=data.keywords, global_ban=True)
|
||||||
await ChatAnswer.filter(keywords=data.keywords).delete()
|
await ChatAnswer.filter(keywords=data.keywords).delete()
|
||||||
await ChatContext.filter(keywords=data.keywords).delete()
|
await ChatContext.filter(keywords=data.keywords).delete()
|
||||||
await ban_word.save()
|
await ban_word.save()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def speak(self_id: int) -> Optional[Tuple[int, List[Union[str, MessageSegment]]]]:
|
async def speak(
|
||||||
|
self_id: int,
|
||||||
|
) -> Optional[Tuple[int, List[Union[str, MessageSegment]]]]:
|
||||||
# 主动发言
|
# 主动发言
|
||||||
cur_time = int(time.time())
|
cur_time = int(time.time())
|
||||||
today_time = time.mktime(datetime.date.today().timetuple())
|
today_time = time.mktime(datetime.date.today().timetuple())
|
||||||
# 获取两小时内消息超过10条的群列表
|
# 获取两小时内消息超过10条的群列表
|
||||||
groups = await ChatMessage.filter(time__gte=today_time).annotate(count=Count('id')).group_by('group_id'). \
|
groups = (
|
||||||
filter(count__gte=10).values_list('group_id', flat=True)
|
await ChatMessage.filter(time__gte=today_time)
|
||||||
|
.annotate(count=Count("id"))
|
||||||
|
.group_by("group_id")
|
||||||
|
.filter(count__gte=10)
|
||||||
|
.values_list("group_id", flat=True)
|
||||||
|
)
|
||||||
if not groups:
|
if not groups:
|
||||||
return None
|
return None
|
||||||
total_messages = {}
|
total_messages = {}
|
||||||
# 获取这些群的两小时内的所有消息
|
# 获取这些群的两小时内的所有消息
|
||||||
for group_id in groups:
|
for group_id in groups:
|
||||||
if messages := await ChatMessage.filter(group_id=group_id, time__gte=today_time):
|
if messages := await ChatMessage.filter(
|
||||||
|
group_id=group_id, time__gte=today_time
|
||||||
|
):
|
||||||
total_messages[group_id] = messages
|
total_messages[group_id] = messages
|
||||||
if not total_messages:
|
if not total_messages:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# 根据消息平均间隔来对群进行排序
|
# 根据消息平均间隔来对群进行排序
|
||||||
def group_popularity_cmp(left_group: Tuple[int, List[ChatMessage]],
|
def group_popularity_cmp(
|
||||||
right_group: Tuple[int, List[ChatMessage]]):
|
left_group: Tuple[int, List[ChatMessage]],
|
||||||
|
right_group: Tuple[int, List[ChatMessage]],
|
||||||
|
):
|
||||||
def cmp(a, b):
|
def cmp(a, b):
|
||||||
return (a > b) - (a < b)
|
return (a > b) - (a < b)
|
||||||
|
|
||||||
@ -366,34 +465,54 @@ class LearningChat:
|
|||||||
right_group_id, right_messages = right_group
|
right_group_id, right_messages = right_group
|
||||||
left_duration = left_messages[0].time - left_messages[-1].time
|
left_duration = left_messages[0].time - left_messages[-1].time
|
||||||
right_duration = right_messages[0].time - right_messages[-1].time
|
right_duration = right_messages[0].time - right_messages[-1].time
|
||||||
return cmp(len(left_messages) / left_duration, len(right_messages) / right_duration)
|
return cmp(
|
||||||
|
len(left_messages) / left_duration, len(right_messages) / right_duration
|
||||||
|
)
|
||||||
|
|
||||||
popularity: List[Tuple[int, List[ChatMessage]]] = sorted(total_messages.items(),
|
popularity: List[Tuple[int, List[ChatMessage]]] = sorted(
|
||||||
key=cmp_to_key(group_popularity_cmp), reverse=True)
|
total_messages.items(), key=cmp_to_key(group_popularity_cmp), reverse=True
|
||||||
logger.debug('群聊学习', f'主动发言:群热度排行<m>{">>".join([str(g[0]) for g in popularity])}</m>')
|
)
|
||||||
|
logger.debug(
|
||||||
|
"群聊学习", f'主动发言:群热度排行<m>{">>".join([str(g[0]) for g in popularity])}</m>'
|
||||||
|
)
|
||||||
for group_id, messages in popularity:
|
for group_id, messages in popularity:
|
||||||
if len(messages) < 30:
|
if len(messages) < 30:
|
||||||
logger.debug('群聊学习', f'主动发言:群<m>{group_id}</m>消息小于30条,不发言')
|
logger.debug("群聊学习", f"主动发言:群<m>{group_id}</m>消息小于30条,不发言")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
config = config_manager.get_group_config(group_id)
|
config = config_manager.get_group_config(group_id)
|
||||||
ban_words = set(
|
ban_words = set(
|
||||||
chat_config.ban_words + config.ban_words + ['[CQ:xml', '[CQ:json', '[CQ:at', '[CQ:video', '[CQ:record',
|
chat_config.ban_words
|
||||||
'[CQ:share'])
|
+ config.ban_words
|
||||||
|
+ [
|
||||||
|
"[CQ:xml",
|
||||||
|
"[CQ:json",
|
||||||
|
"[CQ:at",
|
||||||
|
"[CQ:video",
|
||||||
|
"[CQ:record",
|
||||||
|
"[CQ:share",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
# 是否开启了主动发言
|
# 是否开启了主动发言
|
||||||
if not config.speak_enable or not config.enable:
|
if not config.speak_enable or not config.enable:
|
||||||
logger.debug('群聊学习', f'主动发言:群<m>{group_id}</m>未开启,不发言')
|
logger.debug("群聊学习", f"主动发言:群<m>{group_id}</m>未开启,不发言")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 如果最后一条消息是自己发的,则不主动发言
|
# 如果最后一条消息是自己发的,则不主动发言
|
||||||
if last_reply := await ChatMessage.filter(group_id=group_id, user_id=self_id).first():
|
if last_reply := await ChatMessage.filter(
|
||||||
|
group_id=group_id, user_id=self_id
|
||||||
|
).first():
|
||||||
if last_reply.time >= messages[0].time:
|
if last_reply.time >= messages[0].time:
|
||||||
logger.debug('群聊学习',
|
logger.debug(
|
||||||
f'主动发言:群<m>{group_id}</m>最后一条消息是{NICKNAME}发的{last_reply.message},不发言')
|
"群聊学习",
|
||||||
|
f"主动发言:群<m>{group_id}</m>最后一条消息是{NICKNAME}发的{last_reply.message},不发言",
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
elif cur_time - last_reply.time < config.speak_min_interval:
|
elif cur_time - last_reply.time < config.speak_min_interval:
|
||||||
logger.debug('群聊学习', f'主动发言:群<m>{group_id}</m>上次主动发言时间小于主动发言最小间隔,不发言')
|
logger.debug(
|
||||||
|
"群聊学习", f"主动发言:群<m>{group_id}</m>上次主动发言时间小于主动发言最小间隔,不发言"
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 该群每多少秒发一条消息
|
# 该群每多少秒发一条消息
|
||||||
@ -402,50 +521,79 @@ class LearningChat:
|
|||||||
silent_time = cur_time - messages[0].time
|
silent_time = cur_time - messages[0].time
|
||||||
threshold = avg_interval * config.speak_threshold
|
threshold = avg_interval * config.speak_threshold
|
||||||
if silent_time < threshold:
|
if silent_time < threshold:
|
||||||
logger.debug('群聊学习',
|
logger.debug(
|
||||||
f'主动发言:群<m>{group_id}</m>已沉默时间({silent_time})小于阈值({int(threshold)}),不发言')
|
"群聊学习",
|
||||||
|
f"主动发言:群<m>{group_id}</m>已沉默时间({silent_time})小于阈值({int(threshold)}),不发言",
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if contexts := await ChatContext.filter(count__gte=config.answer_threshold).all():
|
if contexts := await ChatContext.filter(
|
||||||
|
count__gte=config.answer_threshold
|
||||||
|
).all():
|
||||||
speak_list = []
|
speak_list = []
|
||||||
# context = random.choices(contexts, weights=[context.count for context in contexts])[0]
|
# context = random.choices(contexts, weights=[context.count for context in contexts])[0]
|
||||||
# contexts.sort(key=lambda x: x.count, reverse=True)
|
# contexts.sort(key=lambda x: x.count, reverse=True)
|
||||||
random.shuffle(contexts)
|
random.shuffle(contexts)
|
||||||
for context in contexts:
|
for context in contexts:
|
||||||
if (not speak_list or random.random() < config.speak_continuously_probability) and len(
|
if (
|
||||||
speak_list) < config.speak_continuously_max_len:
|
not speak_list
|
||||||
if answers := await ChatAnswer.filter(context=context,
|
or random.random() < config.speak_continuously_probability
|
||||||
group_id=group_id,
|
) and len(speak_list) < config.speak_continuously_max_len:
|
||||||
count__gte=config.answer_threshold):
|
if answers := await ChatAnswer.filter(
|
||||||
answer = random.choices(answers,
|
context=context,
|
||||||
weights=[
|
group_id=group_id,
|
||||||
answer.count + 1 if answer.time >= today_time else answer.count
|
count__gte=config.answer_threshold,
|
||||||
for answer in answers])[0]
|
):
|
||||||
|
answer = random.choices(
|
||||||
|
answers,
|
||||||
|
weights=[
|
||||||
|
answer.count + 1
|
||||||
|
if answer.time >= today_time
|
||||||
|
else answer.count
|
||||||
|
for answer in answers
|
||||||
|
],
|
||||||
|
)[0]
|
||||||
message = random.choice(answer.messages)
|
message = random.choice(answer.messages)
|
||||||
if len(message) < 2:
|
if len(message) < 2:
|
||||||
continue
|
continue
|
||||||
if message.startswith('[') and message.endswith(']'):
|
if message.startswith("[") and message.endswith(
|
||||||
|
"]"
|
||||||
|
):
|
||||||
continue
|
continue
|
||||||
if any(word in message for word in ban_words):
|
if any(word in message for word in ban_words):
|
||||||
continue
|
continue
|
||||||
speak_list.append(message)
|
speak_list.append(message)
|
||||||
follow_answer = answer
|
follow_answer = answer
|
||||||
while random.random() < config.speak_continuously_probability and len(
|
while (
|
||||||
speak_list) < config.speak_continuously_max_len:
|
random.random() < config.speak_continuously_probability
|
||||||
if (follow_context := await ChatContext.filter(
|
and len(speak_list) < config.speak_continuously_max_len
|
||||||
keywords=follow_answer.keywords).first()) and (
|
):
|
||||||
follow_answers := await ChatAnswer.filter(
|
if (
|
||||||
group_id=group_id,
|
follow_context := await ChatContext.filter(
|
||||||
context=follow_context,
|
keywords=follow_answer.keywords
|
||||||
count__gte=config.answer_threshold)):
|
).first()
|
||||||
follow_answer = random.choices(follow_answers,
|
) and (
|
||||||
weights=[
|
follow_answers := await ChatAnswer.filter(
|
||||||
a.count + 1 if a.time >= today_time else a.count
|
group_id=group_id,
|
||||||
for a in follow_answers])[0]
|
context=follow_context,
|
||||||
|
count__gte=config.answer_threshold,
|
||||||
|
)
|
||||||
|
):
|
||||||
|
follow_answer = random.choices(
|
||||||
|
follow_answers,
|
||||||
|
weights=[
|
||||||
|
a.count + 1
|
||||||
|
if a.time >= today_time
|
||||||
|
else a.count
|
||||||
|
for a in follow_answers
|
||||||
|
],
|
||||||
|
)[0]
|
||||||
message = random.choice(follow_answer.messages)
|
message = random.choice(follow_answer.messages)
|
||||||
if len(message) < 2:
|
if len(message) < 2:
|
||||||
continue
|
continue
|
||||||
if message.startswith('[') and message.endswith(']'):
|
if message.startswith("[") and message.endswith(
|
||||||
|
"]"
|
||||||
|
):
|
||||||
continue
|
continue
|
||||||
if all(word not in message for word in ban_words):
|
if all(word not in message for word in ban_words):
|
||||||
speak_list.append(message)
|
speak_list.append(message)
|
||||||
@ -455,13 +603,17 @@ class LearningChat:
|
|||||||
break
|
break
|
||||||
if speak_list:
|
if speak_list:
|
||||||
if random.random() < config.speak_poke_probability:
|
if random.random() < config.speak_poke_probability:
|
||||||
last_speak_users = {message.user_id for message in messages[:5] if message.user_id != self_id}
|
last_speak_users = {
|
||||||
|
message.user_id
|
||||||
|
for message in messages[:5]
|
||||||
|
if message.user_id != self_id
|
||||||
|
}
|
||||||
select_user = random.choice(list(last_speak_users))
|
select_user = random.choice(list(last_speak_users))
|
||||||
speak_list.append(MessageSegment('poke', {'qq': select_user}))
|
speak_list.append(MessageSegment("poke", {"qq": select_user}))
|
||||||
return group_id, speak_list
|
return group_id, speak_list
|
||||||
else:
|
else:
|
||||||
logger.debug('群聊学习', f'主动发言:群<m>{group_id}</m>没有找到符合条件的发言,不发言')
|
logger.debug("群聊学习", f"主动发言:群<m>{group_id}</m>没有找到符合条件的发言,不发言")
|
||||||
logger.debug('群聊学习', '主动发言:没有符合条件的群,不主动发言')
|
logger.debug("群聊学习", "主动发言:没有符合条件的群,不主动发言")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def _set_answer(self, message: ChatMessage):
|
async def _set_answer(self, message: ChatMessage):
|
||||||
@ -469,42 +621,62 @@ class LearningChat:
|
|||||||
if context.count < chat_config.learn_max_count:
|
if context.count < chat_config.learn_max_count:
|
||||||
context.count += 1
|
context.count += 1
|
||||||
context.time = self.data.time
|
context.time = self.data.time
|
||||||
if answer := await ChatAnswer.filter(keywords=self.data.keywords,
|
if answer := await ChatAnswer.filter(
|
||||||
group_id=self.data.group_id,
|
keywords=self.data.keywords,
|
||||||
context=context).first():
|
group_id=self.data.group_id,
|
||||||
|
context=context,
|
||||||
|
).first():
|
||||||
if answer.count < chat_config.learn_max_count:
|
if answer.count < chat_config.learn_max_count:
|
||||||
answer.count += 1
|
answer.count += 1
|
||||||
answer.time = self.data.time
|
answer.time = self.data.time
|
||||||
if self.data.message not in answer.messages:
|
if self.data.message not in answer.messages:
|
||||||
answer.messages.append(self.data.message)
|
answer.messages.append(self.data.message)
|
||||||
else:
|
else:
|
||||||
answer = ChatAnswer(keywords=self.data.keywords,
|
answer = ChatAnswer(
|
||||||
group_id=self.data.group_id,
|
keywords=self.data.keywords,
|
||||||
time=self.data.time,
|
group_id=self.data.group_id,
|
||||||
context=context,
|
time=self.data.time,
|
||||||
messages=[self.data.message])
|
context=context,
|
||||||
|
messages=[self.data.message],
|
||||||
|
)
|
||||||
await answer.save()
|
await answer.save()
|
||||||
await context.save()
|
await context.save()
|
||||||
else:
|
else:
|
||||||
context = await ChatContext.create(keywords=message.keywords,
|
context = await ChatContext.create(
|
||||||
time=self.data.time)
|
keywords=message.keywords, time=self.data.time
|
||||||
answer = await ChatAnswer.create(keywords=self.data.keywords,
|
)
|
||||||
group_id=self.data.group_id,
|
answer = await ChatAnswer.create(
|
||||||
time=self.data.time,
|
keywords=self.data.keywords,
|
||||||
context=context,
|
group_id=self.data.group_id,
|
||||||
messages=[self.data.message])
|
time=self.data.time,
|
||||||
logger.debug('群聊学习', f'➤将被学习为<m>{message.message}</m>的回答,已学次数为<m>{answer.count}</m>')
|
context=context,
|
||||||
|
messages=[self.data.message],
|
||||||
|
)
|
||||||
|
logger.debug(
|
||||||
|
"群聊学习", f"➤将被学习为<m>{message.message}</m>的回答,已学次数为<m>{answer.count}</m>"
|
||||||
|
)
|
||||||
|
|
||||||
async def _check_allow(self, message: Union[ChatMessage, ChatAnswer]) -> bool:
|
async def _check_allow(self, message: Union[ChatMessage, ChatAnswer]) -> bool:
|
||||||
raw_message = message.message if isinstance(message, ChatMessage) else message.messages[0]
|
raw_message = (
|
||||||
|
message.message if isinstance(message, ChatMessage) else message.messages[0]
|
||||||
|
)
|
||||||
# if len(raw_message) < 2:
|
# if len(raw_message) < 2:
|
||||||
# return False
|
# return False
|
||||||
if any(i in raw_message for i in
|
if any(
|
||||||
{'[CQ:xml', '[CQ:json', '[CQ:at', '[CQ:video', '[CQ:record', '[CQ:share'}):
|
i in raw_message
|
||||||
|
for i in {
|
||||||
|
"[CQ:xml",
|
||||||
|
"[CQ:json",
|
||||||
|
"[CQ:at",
|
||||||
|
"[CQ:video",
|
||||||
|
"[CQ:record",
|
||||||
|
"[CQ:share",
|
||||||
|
}
|
||||||
|
):
|
||||||
return False
|
return False
|
||||||
if any(i in raw_message for i in self.ban_words):
|
if any(i in raw_message for i in self.ban_words):
|
||||||
return False
|
return False
|
||||||
if raw_message.startswith('[') and raw_message.endswith(']'):
|
if raw_message.startswith("[") and raw_message.endswith("]"):
|
||||||
return False
|
return False
|
||||||
if ban_word := await ChatBlackList.filter(keywords=message.keywords).first():
|
if ban_word := await ChatBlackList.filter(keywords=message.keywords).first():
|
||||||
if ban_word.global_ban or message.group_id in ban_word.ban_group_id:
|
if ban_word.global_ban or message.group_id in ban_word.ban_group_id:
|
||||||
|
@ -23,7 +23,7 @@ config = config_manager.config
|
|||||||
|
|
||||||
JSON_DUMPS = functools.partial(json.dumps, ensure_ascii=False)
|
JSON_DUMPS = functools.partial(json.dumps, ensure_ascii=False)
|
||||||
jieba.setLogLevel(jieba.logging.INFO)
|
jieba.setLogLevel(jieba.logging.INFO)
|
||||||
jieba.load_userdict(str(Path(__file__).parent / 'genshin_word.txt')) # 加载原神词典
|
jieba.load_userdict(str(Path(__file__).parent / "genshin_word.txt")) # 加载原神词典
|
||||||
jieba.load_userdict(config.dictionary) # 加载用户自定义的词典
|
jieba.load_userdict(config.dictionary) # 加载用户自定义的词典
|
||||||
|
|
||||||
|
|
||||||
@ -46,14 +46,14 @@ class ChatMessage(Model):
|
|||||||
"""时间戳"""
|
"""时间戳"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table = 'message'
|
table = "message"
|
||||||
indexes = ('group_id', 'time')
|
indexes = ("group_id", "time")
|
||||||
ordering = ['-time']
|
ordering = ["-time"]
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def is_plain_text(self) -> bool:
|
def is_plain_text(self) -> bool:
|
||||||
"""是否纯文本"""
|
"""是否纯文本"""
|
||||||
return '[CQ:' not in self.message
|
return "[CQ:" not in self.message
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def keyword_list(self) -> List[str]:
|
def keyword_list(self) -> List[str]:
|
||||||
@ -67,7 +67,9 @@ class ChatMessage(Model):
|
|||||||
"""获取纯文本部分的关键词结果"""
|
"""获取纯文本部分的关键词结果"""
|
||||||
if not self.is_plain_text and not len(self.plain_text):
|
if not self.is_plain_text and not len(self.plain_text):
|
||||||
return self.message
|
return self.message
|
||||||
return self.message if len(self.keyword_list) < 2 else ' '.join(self.keyword_list)
|
return (
|
||||||
|
self.message if len(self.keyword_list) < 2 else " ".join(self.keyword_list)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ChatContext(Model):
|
class ChatContext(Model):
|
||||||
@ -79,13 +81,13 @@ class ChatContext(Model):
|
|||||||
"""时间戳"""
|
"""时间戳"""
|
||||||
count: int = fields.IntField(default=1)
|
count: int = fields.IntField(default=1)
|
||||||
"""次数"""
|
"""次数"""
|
||||||
answers: fields.ReverseRelation['ChatAnswer']
|
answers: fields.ReverseRelation["ChatAnswer"]
|
||||||
"""答案"""
|
"""答案"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table = 'context'
|
table = "context"
|
||||||
indexes = ('keywords', 'time')
|
indexes = ("keywords", "time")
|
||||||
ordering = ['-time']
|
ordering = ["-time"]
|
||||||
|
|
||||||
|
|
||||||
class ChatAnswer(Model):
|
class ChatAnswer(Model):
|
||||||
@ -103,12 +105,13 @@ class ChatAnswer(Model):
|
|||||||
"""消息列表"""
|
"""消息列表"""
|
||||||
|
|
||||||
context: fields.ForeignKeyNullableRelation[ChatContext] = fields.ForeignKeyField(
|
context: fields.ForeignKeyNullableRelation[ChatContext] = fields.ForeignKeyField(
|
||||||
'LearningChat.ChatContext', related_name='answers', null=True)
|
"LearningChat.ChatContext", related_name="answers", null=True
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table = 'answer'
|
table = "answer"
|
||||||
indexes = ('keywords', 'time')
|
indexes = ("keywords", "time")
|
||||||
ordering = ['-time']
|
ordering = ["-time"]
|
||||||
|
|
||||||
|
|
||||||
class ChatBlackList(Model):
|
class ChatBlackList(Model):
|
||||||
@ -122,8 +125,10 @@ class ChatBlackList(Model):
|
|||||||
"""禁用的群id"""
|
"""禁用的群id"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table = 'blacklist'
|
table = "blacklist"
|
||||||
indexes = ('keywords',)
|
indexes = ("keywords",)
|
||||||
|
|
||||||
|
|
||||||
register_database(db_name='LearningChat', models=__name__, db_path=DATABASE_PATH / 'LearningChat.db')
|
register_database(
|
||||||
|
db_name="LearningChat", models=__name__, db_path=DATABASE_PATH / "LearningChat.db"
|
||||||
|
)
|
||||||
|
@ -4,7 +4,12 @@ from fastapi import APIRouter
|
|||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
from nonebot import get_bot
|
from nonebot import get_bot
|
||||||
|
|
||||||
from LittlePaimon.plugins.Learning_Chat.models import ChatMessage, ChatContext, ChatAnswer, ChatBlackList
|
from LittlePaimon.plugins.Learning_Chat.models import (
|
||||||
|
ChatMessage,
|
||||||
|
ChatContext,
|
||||||
|
ChatAnswer,
|
||||||
|
ChatBlackList,
|
||||||
|
)
|
||||||
from LittlePaimon.web.api import BaseApiRouter
|
from LittlePaimon.web.api import BaseApiRouter
|
||||||
from LittlePaimon.web.api.utils import authentication
|
from LittlePaimon.web.api.utils import authentication
|
||||||
|
|
||||||
@ -19,232 +24,290 @@ from .config import config_manager
|
|||||||
route = APIRouter()
|
route = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@route.get('/chat_global_config', response_class=JSONResponse, dependencies=[authentication()])
|
@route.get(
|
||||||
|
"/chat_global_config", response_class=JSONResponse, dependencies=[authentication()]
|
||||||
|
)
|
||||||
async def get_chat_global_config():
|
async def get_chat_global_config():
|
||||||
try:
|
try:
|
||||||
bot = get_bot()
|
bot = get_bot()
|
||||||
groups = await bot.get_group_list()
|
groups = await bot.get_group_list()
|
||||||
member_list = []
|
member_list = []
|
||||||
for group in groups:
|
for group in groups:
|
||||||
members = await bot.get_group_member_list(group_id=group['group_id'])
|
members = await bot.get_group_member_list(group_id=group["group_id"])
|
||||||
member_list.extend(
|
member_list.extend(
|
||||||
[{'label': f'{member["nickname"] or member["card"]}({member["user_id"]})', 'value': member['user_id']} for
|
[
|
||||||
member in members])
|
{
|
||||||
config = config_manager.config.dict(exclude={'group_config'})
|
"label": f'{member["nickname"] or member["card"]}({member["user_id"]})',
|
||||||
config['member_list'] = member_list
|
"value": member["user_id"],
|
||||||
|
}
|
||||||
|
for member in members
|
||||||
|
]
|
||||||
|
)
|
||||||
|
config = config_manager.config.dict(exclude={"group_config"})
|
||||||
|
config["member_list"] = member_list
|
||||||
return config
|
return config
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return {
|
return {"status": -100, "msg": "获取群和好友列表失败,请确认已连接GOCQ"}
|
||||||
'status': -100,
|
|
||||||
'msg': '获取群和好友列表失败,请确认已连接GOCQ'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@route.post('/chat_global_config', response_class=JSONResponse, dependencies=[authentication()])
|
@route.post(
|
||||||
|
"/chat_global_config", response_class=JSONResponse, dependencies=[authentication()]
|
||||||
|
)
|
||||||
async def post_chat_global_config(data: dict):
|
async def post_chat_global_config(data: dict):
|
||||||
config_manager.config.update(**data)
|
config_manager.config.update(**data)
|
||||||
config_manager.save()
|
config_manager.save()
|
||||||
await ChatContext.filter(count__gt=config_manager.config.learn_max_count).update(
|
await ChatContext.filter(count__gt=config_manager.config.learn_max_count).update(
|
||||||
count=config_manager.config.learn_max_count)
|
count=config_manager.config.learn_max_count
|
||||||
|
)
|
||||||
await ChatAnswer.filter(count__gt=config_manager.config.learn_max_count).update(
|
await ChatAnswer.filter(count__gt=config_manager.config.learn_max_count).update(
|
||||||
count=config_manager.config.learn_max_count)
|
count=config_manager.config.learn_max_count
|
||||||
|
)
|
||||||
jieba.load_userdict(config_manager.config.dictionary)
|
jieba.load_userdict(config_manager.config.dictionary)
|
||||||
return {
|
return {"status": 0, "msg": "保存成功"}
|
||||||
'status': 0,
|
|
||||||
'msg': '保存成功'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@route.get('/chat_group_config', response_class=JSONResponse, dependencies=[authentication()])
|
@route.get(
|
||||||
|
"/chat_group_config", response_class=JSONResponse, dependencies=[authentication()]
|
||||||
|
)
|
||||||
async def get_chat_global_config(group_id: int):
|
async def get_chat_global_config(group_id: int):
|
||||||
try:
|
try:
|
||||||
members = await get_bot().get_group_member_list(group_id=group_id)
|
members = await get_bot().get_group_member_list(group_id=group_id)
|
||||||
member_list = [{'label': f'{member["nickname"] or member["card"]}({member["user_id"]})', 'value': member['user_id']}
|
member_list = [
|
||||||
for member in members]
|
{
|
||||||
|
"label": f'{member["nickname"] or member["card"]}({member["user_id"]})',
|
||||||
|
"value": member["user_id"],
|
||||||
|
}
|
||||||
|
for member in members
|
||||||
|
]
|
||||||
config = config_manager.get_group_config(group_id).dict()
|
config = config_manager.get_group_config(group_id).dict()
|
||||||
config['break_probability'] = config['break_probability'] * 100
|
config["break_probability"] = config["break_probability"] * 100
|
||||||
config['speak_continuously_probability'] = config['speak_continuously_probability'] * 100
|
config["speak_continuously_probability"] = (
|
||||||
config['speak_poke_probability'] = config['speak_poke_probability'] * 100
|
config["speak_continuously_probability"] * 100
|
||||||
config['member_list'] = member_list
|
)
|
||||||
|
config["speak_poke_probability"] = config["speak_poke_probability"] * 100
|
||||||
|
config["member_list"] = member_list
|
||||||
return config
|
return config
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return {
|
return {"status": -100, "msg": "获取群和好友列表失败,请确认已连接GOCQ"}
|
||||||
'status': -100,
|
|
||||||
'msg': '获取群和好友列表失败,请确认已连接GOCQ'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@route.post('/chat_group_config', response_class=JSONResponse, dependencies=[authentication()])
|
@route.post(
|
||||||
|
"/chat_group_config", response_class=JSONResponse, dependencies=[authentication()]
|
||||||
|
)
|
||||||
async def post_chat_global_config(group_id: Union[int, str], data: dict):
|
async def post_chat_global_config(group_id: Union[int, str], data: dict):
|
||||||
if not data['answer_threshold_weights']:
|
if not data["answer_threshold_weights"]:
|
||||||
return {
|
return {"status": 400, "msg": "回复阈值权重不能为空,必须至少有一个数值"}
|
||||||
'status': 400,
|
|
||||||
'msg': '回复阈值权重不能为空,必须至少有一个数值'
|
|
||||||
}
|
|
||||||
else:
|
else:
|
||||||
data['break_probability'] = data['break_probability'] / 100
|
data["break_probability"] = data["break_probability"] / 100
|
||||||
data['speak_continuously_probability'] = data['speak_continuously_probability'] / 100
|
data["speak_continuously_probability"] = (
|
||||||
data['speak_poke_probability'] = data['speak_poke_probability'] / 100
|
data["speak_continuously_probability"] / 100
|
||||||
if group_id != 'all':
|
)
|
||||||
groups = [{'group_id': group_id}]
|
data["speak_poke_probability"] = data["speak_poke_probability"] / 100
|
||||||
|
if group_id != "all":
|
||||||
|
groups = [{"group_id": group_id}]
|
||||||
else:
|
else:
|
||||||
groups = await get_bot().get_group_list()
|
groups = await get_bot().get_group_list()
|
||||||
for group in groups:
|
for group in groups:
|
||||||
config = config_manager.get_group_config(group['group_id'])
|
config = config_manager.get_group_config(group["group_id"])
|
||||||
config.update(**data)
|
config.update(**data)
|
||||||
config_manager.config.group_config[group['group_id']] = config
|
config_manager.config.group_config[group["group_id"]] = config
|
||||||
config_manager.save()
|
config_manager.save()
|
||||||
return {
|
return {"status": 0, "msg": "保存成功"}
|
||||||
'status': 0,
|
|
||||||
'msg': '保存成功'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@route.get('/get_chat_messages', response_class=JSONResponse, dependencies=[authentication()])
|
@route.get(
|
||||||
async def get_chat_messages(page: int = 1,
|
"/get_chat_messages", response_class=JSONResponse, dependencies=[authentication()]
|
||||||
perPage: int = 10,
|
)
|
||||||
orderBy: str = 'time',
|
async def get_chat_messages(
|
||||||
orderDir: str = 'desc',
|
page: int = 1,
|
||||||
group_id: Optional[str] = None,
|
perPage: int = 10,
|
||||||
user_id: Optional[str] = None,
|
orderBy: str = "time",
|
||||||
message: Optional[str] = None):
|
orderDir: str = "desc",
|
||||||
orderBy = (orderBy or 'time') if (orderDir or 'desc') == 'asc' else f'-{orderBy or "time"}'
|
group_id: Optional[str] = None,
|
||||||
filter_args = {f'{k}__contains': v for k, v in
|
user_id: Optional[str] = None,
|
||||||
{'group_id': group_id, 'user_id': user_id, 'raw_message': message}.items() if v}
|
message: Optional[str] = None,
|
||||||
|
):
|
||||||
|
orderBy = (
|
||||||
|
(orderBy or "time")
|
||||||
|
if (orderDir or "desc") == "asc"
|
||||||
|
else f'-{orderBy or "time"}'
|
||||||
|
)
|
||||||
|
filter_args = {
|
||||||
|
f"{k}__contains": v
|
||||||
|
for k, v in {
|
||||||
|
"group_id": group_id,
|
||||||
|
"user_id": user_id,
|
||||||
|
"raw_message": message,
|
||||||
|
}.items()
|
||||||
|
if v
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
'status': 0,
|
"status": 0,
|
||||||
'msg': 'ok',
|
"msg": "ok",
|
||||||
'data': {
|
"data": {
|
||||||
'items': await ChatMessage.filter(**filter_args).order_by(orderBy).offset((page - 1) * perPage).limit(
|
"items": await ChatMessage.filter(**filter_args)
|
||||||
perPage).values(),
|
.order_by(orderBy)
|
||||||
'total': await ChatMessage.filter(**filter_args).count()
|
.offset((page - 1) * perPage)
|
||||||
}
|
.limit(perPage)
|
||||||
|
.values(),
|
||||||
|
"total": await ChatMessage.filter(**filter_args).count(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@route.get('/get_chat_contexts', response_class=JSONResponse, dependencies=[authentication()])
|
@route.get(
|
||||||
async def get_chat_context(page: int = 1, perPage: int = 10, orderBy: str = 'time', orderDir: str = 'desc',
|
"/get_chat_contexts", response_class=JSONResponse, dependencies=[authentication()]
|
||||||
keywords: Optional[str] = None):
|
)
|
||||||
orderBy = (orderBy or 'time') if (orderDir or 'desc') == 'asc' else f'-{orderBy or "time"}'
|
async def get_chat_context(
|
||||||
filter_arg = {'keywords__contains': keywords} if keywords else {}
|
page: int = 1,
|
||||||
|
perPage: int = 10,
|
||||||
|
orderBy: str = "time",
|
||||||
|
orderDir: str = "desc",
|
||||||
|
keywords: Optional[str] = None,
|
||||||
|
):
|
||||||
|
orderBy = (
|
||||||
|
(orderBy or "time")
|
||||||
|
if (orderDir or "desc") == "asc"
|
||||||
|
else f'-{orderBy or "time"}'
|
||||||
|
)
|
||||||
|
filter_arg = {"keywords__contains": keywords} if keywords else {}
|
||||||
return {
|
return {
|
||||||
'status': 0,
|
"status": 0,
|
||||||
'msg': 'ok',
|
"msg": "ok",
|
||||||
'data': {
|
"data": {
|
||||||
'items': await ChatContext.filter(**filter_arg).order_by(orderBy).offset((page - 1) * perPage).limit(
|
"items": await ChatContext.filter(**filter_arg)
|
||||||
perPage).values(),
|
.order_by(orderBy)
|
||||||
'total': await ChatContext.filter(**filter_arg).count()
|
.offset((page - 1) * perPage)
|
||||||
}
|
.limit(perPage)
|
||||||
|
.values(),
|
||||||
|
"total": await ChatContext.filter(**filter_arg).count(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@route.get('/get_chat_answers', response_class=JSONResponse, dependencies=[authentication()])
|
@route.get(
|
||||||
async def get_chat_answers(context_id: Optional[int] = None, page: int = 1, perPage: int = 10, orderBy: str = 'count',
|
"/get_chat_answers", response_class=JSONResponse, dependencies=[authentication()]
|
||||||
orderDir: str = 'desc', keywords: Optional[str] = None):
|
)
|
||||||
filter_arg = {'context_id': context_id} if context_id else {}
|
async def get_chat_answers(
|
||||||
|
context_id: Optional[int] = None,
|
||||||
|
page: int = 1,
|
||||||
|
perPage: int = 10,
|
||||||
|
orderBy: str = "count",
|
||||||
|
orderDir: str = "desc",
|
||||||
|
keywords: Optional[str] = None,
|
||||||
|
):
|
||||||
|
filter_arg = {"context_id": context_id} if context_id else {}
|
||||||
if keywords:
|
if keywords:
|
||||||
filter_arg['keywords__contains'] = keywords # type: ignore
|
filter_arg["keywords__contains"] = keywords # type: ignore
|
||||||
orderBy = (orderBy or 'count') if (orderDir or 'desc') == 'asc' else f'-{orderBy or "count"}'
|
orderBy = (
|
||||||
|
(orderBy or "count")
|
||||||
|
if (orderDir or "desc") == "asc"
|
||||||
|
else f'-{orderBy or "count"}'
|
||||||
|
)
|
||||||
return {
|
return {
|
||||||
'status': 0,
|
"status": 0,
|
||||||
'msg': 'ok',
|
"msg": "ok",
|
||||||
'data': {
|
"data": {
|
||||||
'items': list(
|
"items": list(
|
||||||
map(lambda x: x.update({'messages': [{'msg': m} for m in x['messages']]}) or x,
|
map(
|
||||||
await ChatAnswer.filter(**filter_arg).order_by(orderBy).offset((page - 1) * perPage).limit(
|
lambda x: x.update(
|
||||||
perPage).values())),
|
{"messages": [{"msg": m} for m in x["messages"]]}
|
||||||
'total': await ChatAnswer.filter(**filter_arg).count()
|
)
|
||||||
}
|
or x,
|
||||||
|
await ChatAnswer.filter(**filter_arg)
|
||||||
|
.order_by(orderBy)
|
||||||
|
.offset((page - 1) * perPage)
|
||||||
|
.limit(perPage)
|
||||||
|
.values(),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"total": await ChatAnswer.filter(**filter_arg).count(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@route.get('/get_chat_blacklist', response_class=JSONResponse, dependencies=[authentication()])
|
@route.get(
|
||||||
async def get_chat_blacklist(page: int = 1, perPage: int = 10, keywords: Optional[str] = None,
|
"/get_chat_blacklist", response_class=JSONResponse, dependencies=[authentication()]
|
||||||
bans: Optional[str] = None):
|
)
|
||||||
filter_arg = {'keywords__contains': keywords} if keywords else {}
|
async def get_chat_blacklist(
|
||||||
items = await ChatBlackList.filter(**filter_arg).offset((page - 1) * perPage).limit(perPage).values()
|
page: int = 1,
|
||||||
|
perPage: int = 10,
|
||||||
|
keywords: Optional[str] = None,
|
||||||
|
bans: Optional[str] = None,
|
||||||
|
):
|
||||||
|
filter_arg = {"keywords__contains": keywords} if keywords else {}
|
||||||
|
items = (
|
||||||
|
await ChatBlackList.filter(**filter_arg)
|
||||||
|
.offset((page - 1) * perPage)
|
||||||
|
.limit(perPage)
|
||||||
|
.values()
|
||||||
|
)
|
||||||
for item in items:
|
for item in items:
|
||||||
item['bans'] = '全局禁用' if item['global_ban'] else str(item['ban_group_id'][0])
|
item["bans"] = "全局禁用" if item["global_ban"] else str(item["ban_group_id"][0])
|
||||||
if bans:
|
if bans:
|
||||||
items = list(filter(lambda x: bans in x['bans'], items))
|
items = list(filter(lambda x: bans in x["bans"], items))
|
||||||
return {
|
return {
|
||||||
'status': 0,
|
"status": 0,
|
||||||
'msg': 'ok',
|
"msg": "ok",
|
||||||
'data': {
|
"data": {
|
||||||
'items': items,
|
"items": items,
|
||||||
'total': await ChatBlackList.filter(**filter_arg).count()
|
"total": await ChatBlackList.filter(**filter_arg).count(),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@route.delete('/delete_chat', response_class=JSONResponse, dependencies=[authentication()])
|
@route.delete(
|
||||||
|
"/delete_chat", response_class=JSONResponse, dependencies=[authentication()]
|
||||||
|
)
|
||||||
async def delete_chat(id: int, type: str):
|
async def delete_chat(id: int, type: str):
|
||||||
try:
|
try:
|
||||||
if type == 'message':
|
if type == "message":
|
||||||
await ChatMessage.filter(id=id).delete()
|
await ChatMessage.filter(id=id).delete()
|
||||||
elif type == 'context':
|
elif type == "context":
|
||||||
c = await ChatContext.get(id=id)
|
c = await ChatContext.get(id=id)
|
||||||
await ChatAnswer.filter(context=c).delete()
|
await ChatAnswer.filter(context=c).delete()
|
||||||
await c.delete()
|
await c.delete()
|
||||||
elif type == 'answer':
|
elif type == "answer":
|
||||||
await ChatAnswer.filter(id=id).delete()
|
await ChatAnswer.filter(id=id).delete()
|
||||||
elif type == 'blacklist':
|
elif type == "blacklist":
|
||||||
await ChatBlackList.filter(id=id).delete()
|
await ChatBlackList.filter(id=id).delete()
|
||||||
return {
|
return {"status": 0, "msg": "删除成功"}
|
||||||
'status': 0,
|
|
||||||
'msg': '删除成功'
|
|
||||||
}
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {
|
return {"status": 500, "msg": f"删除失败,{e}"}
|
||||||
'status': 500,
|
|
||||||
'msg': f'删除失败,{e}'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@route.put('/ban_chat', response_class=JSONResponse, dependencies=[authentication()])
|
@route.put("/ban_chat", response_class=JSONResponse, dependencies=[authentication()])
|
||||||
async def ban_chat(id: int, type: str):
|
async def ban_chat(id: int, type: str):
|
||||||
try:
|
try:
|
||||||
if type == 'message':
|
if type == "message":
|
||||||
data = await ChatMessage.get(id=id)
|
data = await ChatMessage.get(id=id)
|
||||||
elif type == 'context':
|
elif type == "context":
|
||||||
data = await ChatContext.get(id=id)
|
data = await ChatContext.get(id=id)
|
||||||
else:
|
else:
|
||||||
data = await ChatAnswer.get(id=id)
|
data = await ChatAnswer.get(id=id)
|
||||||
await LearningChat.add_ban(data)
|
await LearningChat.add_ban(data)
|
||||||
return {
|
return {"status": 0, "msg": "禁用成功"}
|
||||||
'status': 0,
|
|
||||||
'msg': '禁用成功'
|
|
||||||
}
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {
|
return {"status": 500, "msg": f"禁用失败: {e}"}
|
||||||
'status': 500,
|
|
||||||
'msg': f'禁用失败: {e}'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@route.put('/delete_all', response_class=JSONResponse, dependencies=[authentication()])
|
@route.put("/delete_all", response_class=JSONResponse, dependencies=[authentication()])
|
||||||
async def delete_all(type: str, id: Optional[int] = None):
|
async def delete_all(type: str, id: Optional[int] = None):
|
||||||
try:
|
try:
|
||||||
if type == 'message':
|
if type == "message":
|
||||||
await ChatMessage.all().delete()
|
await ChatMessage.all().delete()
|
||||||
elif type == 'context':
|
elif type == "context":
|
||||||
await ChatContext.all().delete()
|
await ChatContext.all().delete()
|
||||||
elif type == 'answer':
|
elif type == "answer":
|
||||||
if id:
|
if id:
|
||||||
await ChatAnswer.filter(context_id=id).delete()
|
await ChatAnswer.filter(context_id=id).delete()
|
||||||
else:
|
else:
|
||||||
await ChatAnswer.all().delete()
|
await ChatAnswer.all().delete()
|
||||||
elif type == 'blacklist':
|
elif type == "blacklist":
|
||||||
await ChatBlackList.all().delete()
|
await ChatBlackList.all().delete()
|
||||||
return {
|
return {"status": 0, "msg": "操作成功"}
|
||||||
'status': 0,
|
|
||||||
'msg': '操作成功'
|
|
||||||
}
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {
|
return {"status": 500, "msg": f"操作失败,{e}"}
|
||||||
'status': 500,
|
|
||||||
'msg': f'操作失败,{e}'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
BaseApiRouter.include_router(route)
|
BaseApiRouter.include_router(route)
|
||||||
|
@ -5,275 +5,626 @@ from LittlePaimon.utils import NICKNAME
|
|||||||
from LittlePaimon.web.pages import admin_app
|
from LittlePaimon.web.pages import admin_app
|
||||||
|
|
||||||
global_config_form = Form(
|
global_config_form = Form(
|
||||||
title='全局配置',
|
title="全局配置",
|
||||||
name='global_config',
|
name="global_config",
|
||||||
initApi='/LittlePaimon/api/chat_global_config',
|
initApi="/LittlePaimon/api/chat_global_config",
|
||||||
api='post:/LittlePaimon/api/chat_global_config',
|
api="post:/LittlePaimon/api/chat_global_config",
|
||||||
|
interval=None,
|
||||||
body=[
|
body=[
|
||||||
Switch(label='群聊学习总开关', name='total_enable', value='${total_enable}', onText='开启', offText='关闭',
|
Switch(
|
||||||
labelRemark=Remark(shape='circle', content='关闭后,全局都将不会再学习和回复(但是仍会对收到的消息进行记录)。')),
|
label="群聊学习总开关",
|
||||||
InputNumber(label='单句关键词数量', name='KEYWORDS_SIZE', value='${KEYWORDS_SIZE}', visibleOn='${total_enable}',
|
name="total_enable",
|
||||||
min=2,
|
value="${total_enable}",
|
||||||
labelRemark=Remark(shape='circle', content='单句语句标签数量,影响对一句话的主题词提取效果,建议保持默认为3。')),
|
onText="开启",
|
||||||
InputNumber(label='跨群回复阈值', name='cross_group_threshold', value='${cross_group_threshold}',
|
offText="关闭",
|
||||||
visibleOn='${total_enable}', min=1,
|
labelRemark=Remark(
|
||||||
labelRemark=Remark(shape='circle', content='当学习到的一种回复在N个群都有,那么这个回复就会变为全局回复。')),
|
shape="circle", content="关闭后,全局都将不会再学习和回复(但是仍会对收到的消息进行记录)。"
|
||||||
InputNumber(label='最高学习次数', name='learn_max_count', value='${learn_max_count}',
|
),
|
||||||
visibleOn='${total_enable}', min=2, labelRemark=Remark(shape='circle',
|
),
|
||||||
content='学习的回复最高能累计到的次数,值越高,这个回复就会学习得越深,越容易进行回复,如果不想每次都大概率固定回复某一句话,可以将该值设低点。')),
|
InputNumber(
|
||||||
InputTag(label='全局屏蔽词', name='ban_words', value='${ban_words}', enableBatchAdd=True,
|
label="单句关键词数量",
|
||||||
placeholder='添加全局屏蔽词', visibleOn='${total_enable}', joinValues=False, extractValue=True,
|
name="KEYWORDS_SIZE",
|
||||||
labelRemark=Remark(shape='circle', content='全局屏蔽词,含有这些词的消息不会学习和回复,默认已屏蔽at、分享、语音、和视频等消息。(回车进行添加)')),
|
value="${KEYWORDS_SIZE}",
|
||||||
InputTag(label='全局屏蔽用户', source='${member_list}', name='ban_users', value='${ban_users}',
|
visibleOn="${total_enable}",
|
||||||
enableBatchAdd=True,
|
min=2,
|
||||||
placeholder='添加全局屏蔽用户', visibleOn='${total_enable}', joinValues=False, extractValue=True,
|
labelRemark=Remark(
|
||||||
labelRemark=Remark(shape='circle', content='全局屏蔽用户,和这些用户有关的消息不会学习和回复。(回车进行添加)')),
|
shape="circle", content="单句语句标签数量,影响对一句话的主题词提取效果,建议保持默认为3。"
|
||||||
InputTag(label='自定义词典', name='dictionary', value='${dictionary}',
|
),
|
||||||
enableBatchAdd=True,
|
),
|
||||||
placeholder='添加自定义词语', visibleOn='${total_enable}', joinValues=False, extractValue=True,
|
InputNumber(
|
||||||
labelRemark=Remark(shape='circle', content='添加自定义词语,让分词能够识别未收录的词汇,提高学习的准确性。你可以添加特殊名词,这样学习时就会将该词看作一个整体,目前词典中已默认添加部分原神相关词汇。(回车进行添加)')),
|
label="跨群回复阈值",
|
||||||
|
name="cross_group_threshold",
|
||||||
|
value="${cross_group_threshold}",
|
||||||
|
visibleOn="${total_enable}",
|
||||||
|
min=1,
|
||||||
|
labelRemark=Remark(
|
||||||
|
shape="circle", content="当学习到的一种回复在N个群都有,那么这个回复就会变为全局回复。"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
InputNumber(
|
||||||
|
label="最高学习次数",
|
||||||
|
name="learn_max_count",
|
||||||
|
value="${learn_max_count}",
|
||||||
|
visibleOn="${total_enable}",
|
||||||
|
min=2,
|
||||||
|
labelRemark=Remark(
|
||||||
|
shape="circle",
|
||||||
|
content="学习的回复最高能累计到的次数,值越高,这个回复就会学习得越深,越容易进行回复,如果不想每次都大概率固定回复某一句话,可以将该值设低点。",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
InputTag(
|
||||||
|
label="全局屏蔽词",
|
||||||
|
name="ban_words",
|
||||||
|
value="${ban_words}",
|
||||||
|
enableBatchAdd=True,
|
||||||
|
placeholder="添加全局屏蔽词",
|
||||||
|
visibleOn="${total_enable}",
|
||||||
|
joinValues=False,
|
||||||
|
extractValue=True,
|
||||||
|
labelRemark=Remark(
|
||||||
|
shape="circle",
|
||||||
|
content="全局屏蔽词,含有这些词的消息不会学习和回复,默认已屏蔽at、分享、语音、和视频等消息。(回车进行添加)",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
InputTag(
|
||||||
|
label="全局屏蔽用户",
|
||||||
|
source="${member_list}",
|
||||||
|
name="ban_users",
|
||||||
|
value="${ban_users}",
|
||||||
|
enableBatchAdd=True,
|
||||||
|
placeholder="添加全局屏蔽用户",
|
||||||
|
visibleOn="${total_enable}",
|
||||||
|
joinValues=False,
|
||||||
|
extractValue=True,
|
||||||
|
labelRemark=Remark(
|
||||||
|
shape="circle", content="全局屏蔽用户,和这些用户有关的消息不会学习和回复。(回车进行添加)"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
InputTag(
|
||||||
|
label="自定义词典",
|
||||||
|
name="dictionary",
|
||||||
|
value="${dictionary}",
|
||||||
|
enableBatchAdd=True,
|
||||||
|
placeholder="添加自定义词语",
|
||||||
|
visibleOn="${total_enable}",
|
||||||
|
joinValues=False,
|
||||||
|
extractValue=True,
|
||||||
|
labelRemark=Remark(
|
||||||
|
shape="circle",
|
||||||
|
content="添加自定义词语,让分词能够识别未收录的词汇,提高学习的准确性。你可以添加特殊名词,这样学习时就会将该词看作一个整体,目前词典中已默认添加部分原神相关词汇。(回车进行添加)",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
actions=[
|
||||||
|
Action(label="保存", level=LevelEnum.success, type="submit"),
|
||||||
|
Action(label="重置", level=LevelEnum.warning, type="reset"),
|
||||||
],
|
],
|
||||||
actions=[Action(label='保存', level=LevelEnum.success, type='submit'),
|
|
||||||
Action(label='重置', level=LevelEnum.warning, type='reset')]
|
|
||||||
)
|
)
|
||||||
group_select = Select(label='分群配置', name='group_id', source='${group_list}',
|
group_select = Select(
|
||||||
placeholder='选择群')
|
label="分群配置", name="group_id", source="${group_list}", placeholder="选择群"
|
||||||
|
)
|
||||||
group_config_form = Form(
|
group_config_form = Form(
|
||||||
title='分群配置',
|
title="分群配置",
|
||||||
visibleOn='group_id != null',
|
visibleOn="group_id != null",
|
||||||
initApi='/LittlePaimon/api/chat_group_config?group_id=${group_id}',
|
initApi="/LittlePaimon/api/chat_group_config?group_id=${group_id}",
|
||||||
api='post:/LittlePaimon/api/chat_group_config?group_id=${group_id}',
|
api="post:/LittlePaimon/api/chat_group_config?group_id=${group_id}",
|
||||||
|
interval=None,
|
||||||
body=[
|
body=[
|
||||||
Switch(label='群聊学习开关', name='enable', value='${enable}', onText='开启', offText='关闭',
|
Switch(
|
||||||
labelRemark=Remark(shape='circle', content='针对该群的群聊学习开关,关闭后,仅该群不会学习和回复。')),
|
label="群聊学习开关",
|
||||||
InputNumber(label='回复阈值', name='answer_threshold', value='${answer_threshold}', visibleOn='${enable}',
|
name="enable",
|
||||||
min=2, labelRemark=Remark(shape='circle', content='可以理解为学习成功所需要的次数,值越低学得越快。')),
|
value="${enable}",
|
||||||
InputArray(label='回复阈值权重', name='answer_threshold_weights', value='${answer_threshold_weights}',
|
onText="开启",
|
||||||
items=InputNumber(min=1, max=100, value=25, suffix='%'), inline=True, visibleOn='${enable}',
|
offText="关闭",
|
||||||
labelRemark=Remark(shape='circle',
|
labelRemark=Remark(shape="circle", content="针对该群的群聊学习开关,关闭后,仅该群不会学习和回复。"),
|
||||||
content='影响回复阈值的计算方式,以默认的回复阈值4、权重[10, 30, 60]为例,在计算阈值时,60%概率为4,30%概率为3,10%概率为2。')),
|
),
|
||||||
InputNumber(label='复读阈值', name='repeat_threshold', value='${repeat_threshold}', visibleOn='${enable}',
|
InputNumber(
|
||||||
min=2,
|
label="回复阈值",
|
||||||
labelRemark=Remark(shape='circle', content=f'跟随复读所需要的阈值,有N个人复读后,{NICKNAME}就会跟着复读。')),
|
name="answer_threshold",
|
||||||
InputNumber(label='打断复读概率', name='break_probability', value='${break_probability}',
|
value="${answer_threshold}",
|
||||||
min=0, max=100, suffix='%', visibleOn='${AND(enable, speak_enable)}',
|
visibleOn="${enable}",
|
||||||
labelRemark=Remark(shape='circle', content='达到复读阈值时,打断复读而不是跟随复读的概率。')),
|
min=2,
|
||||||
InputTag(label='屏蔽词', name='ban_words', value='${ban_words}', enableBatchAdd=True,
|
labelRemark=Remark(shape="circle", content="可以理解为学习成功所需要的次数,值越低学得越快。"),
|
||||||
placeholder='添加屏蔽词', visibleOn='${enable}', joinValues=False, extractValue=True,
|
),
|
||||||
labelRemark=Remark(shape='circle', content='含有这些词的消息不会学习和回复。(回车进行添加)')),
|
InputArray(
|
||||||
InputTag(label='屏蔽用户', source='${member_list}', name='ban_users', value='${ban_users}', enableBatchAdd=True,
|
label="回复阈值权重",
|
||||||
placeholder='添加屏蔽用户', visibleOn='${enable}', joinValues=False, extractValue=True,
|
name="answer_threshold_weights",
|
||||||
labelRemark=Remark(shape='circle', content='和该群中这些用户有关的消息不会学习和回复。(回车进行添加)')),
|
value="${answer_threshold_weights}",
|
||||||
Switch(label='主动发言开关', name='speak_enable', value='${speak_enable}', visibleOn='${enable}',
|
items=InputNumber(min=1, max=100, value=25, suffix="%"),
|
||||||
labelRemark=Remark(shape='circle', content=f'是否允许{NICKNAME}在该群主动发言,主动发言是指每隔一段时间挑选一个热度较高的群,主动发一些学习过的内容。')),
|
inline=True,
|
||||||
InputNumber(label='主动发言阈值', name='speak_threshold', value='${speak_threshold}',
|
visibleOn="${enable}",
|
||||||
visibleOn='${AND(enable, speak_enable)}', min=0,
|
labelRemark=Remark(
|
||||||
labelRemark=Remark(shape='circle', content='值越低,主动发言的可能性越高。')),
|
shape="circle",
|
||||||
InputNumber(label='主动发言最小间隔', name='speak_min_interval', value='${speak_min_interval}', min=0,
|
content="影响回复阈值的计算方式,以默认的回复阈值4、权重[10, 30, 60]为例,在计算阈值时,60%概率为4,30%概率为3,10%概率为2。",
|
||||||
visibleOn='${AND(enable, speak_enable)}', suffix='秒',
|
),
|
||||||
labelRemark=Remark(shape='circle', content='进行主动发言的最小时间间隔。')),
|
),
|
||||||
InputNumber(label='连续主动发言概率', name='speak_continuously_probability',
|
InputNumber(
|
||||||
value='${speak_continuously_probability}', min=0, max=100, suffix='%',
|
label="复读阈值",
|
||||||
visibleOn='${AND(enable, speak_enable)}', labelRemark=Remark(shape='circle', content='触发主动发言时,连续进行发言的概率。')),
|
name="repeat_threshold",
|
||||||
InputNumber(label='最大连续主动发言句数', name='speak_continuously_max_len',
|
value="${repeat_threshold}",
|
||||||
value='${speak_continuously_max_len}', visibleOn='${AND(enable, speak_enable)}', min=1,
|
visibleOn="${enable}",
|
||||||
labelRemark=Remark(shape='circle', content='连续主动发言的最大句数。')),
|
min=2,
|
||||||
InputNumber(label='主动发言附带戳一戳概率', name='speak_poke_probability', value='${speak_poke_probability}',
|
labelRemark=Remark(
|
||||||
min=0, max=100, suffix='%', visibleOn='${AND(enable, speak_enable)}',
|
shape="circle", content=f"跟随复读所需要的阈值,有N个人复读后,{NICKNAME}就会跟着复读。"
|
||||||
labelRemark=Remark(shape='circle', content='主动发言时附带戳一戳的概率,会在最近5个发言者中随机选一个戳。')),
|
),
|
||||||
|
),
|
||||||
|
InputNumber(
|
||||||
|
label="打断复读概率",
|
||||||
|
name="break_probability",
|
||||||
|
value="${break_probability}",
|
||||||
|
min=0,
|
||||||
|
max=100,
|
||||||
|
suffix="%",
|
||||||
|
visibleOn="${AND(enable, speak_enable)}",
|
||||||
|
labelRemark=Remark(shape="circle", content="达到复读阈值时,打断复读而不是跟随复读的概率。"),
|
||||||
|
),
|
||||||
|
InputTag(
|
||||||
|
label="屏蔽词",
|
||||||
|
name="ban_words",
|
||||||
|
value="${ban_words}",
|
||||||
|
enableBatchAdd=True,
|
||||||
|
placeholder="添加屏蔽词",
|
||||||
|
visibleOn="${enable}",
|
||||||
|
joinValues=False,
|
||||||
|
extractValue=True,
|
||||||
|
labelRemark=Remark(shape="circle", content="含有这些词的消息不会学习和回复。(回车进行添加)"),
|
||||||
|
),
|
||||||
|
InputTag(
|
||||||
|
label="屏蔽用户",
|
||||||
|
source="${member_list}",
|
||||||
|
name="ban_users",
|
||||||
|
value="${ban_users}",
|
||||||
|
enableBatchAdd=True,
|
||||||
|
placeholder="添加屏蔽用户",
|
||||||
|
visibleOn="${enable}",
|
||||||
|
joinValues=False,
|
||||||
|
extractValue=True,
|
||||||
|
labelRemark=Remark(shape="circle", content="和该群中这些用户有关的消息不会学习和回复。(回车进行添加)"),
|
||||||
|
),
|
||||||
|
Switch(
|
||||||
|
label="主动发言开关",
|
||||||
|
name="speak_enable",
|
||||||
|
value="${speak_enable}",
|
||||||
|
visibleOn="${enable}",
|
||||||
|
labelRemark=Remark(
|
||||||
|
shape="circle",
|
||||||
|
content=f"是否允许{NICKNAME}在该群主动发言,主动发言是指每隔一段时间挑选一个热度较高的群,主动发一些学习过的内容。",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
InputNumber(
|
||||||
|
label="主动发言阈值",
|
||||||
|
name="speak_threshold",
|
||||||
|
value="${speak_threshold}",
|
||||||
|
visibleOn="${AND(enable, speak_enable)}",
|
||||||
|
min=0,
|
||||||
|
labelRemark=Remark(shape="circle", content="值越低,主动发言的可能性越高。"),
|
||||||
|
),
|
||||||
|
InputNumber(
|
||||||
|
label="主动发言最小间隔",
|
||||||
|
name="speak_min_interval",
|
||||||
|
value="${speak_min_interval}",
|
||||||
|
min=0,
|
||||||
|
visibleOn="${AND(enable, speak_enable)}",
|
||||||
|
suffix="秒",
|
||||||
|
labelRemark=Remark(shape="circle", content="进行主动发言的最小时间间隔。"),
|
||||||
|
),
|
||||||
|
InputNumber(
|
||||||
|
label="连续主动发言概率",
|
||||||
|
name="speak_continuously_probability",
|
||||||
|
value="${speak_continuously_probability}",
|
||||||
|
min=0,
|
||||||
|
max=100,
|
||||||
|
suffix="%",
|
||||||
|
visibleOn="${AND(enable, speak_enable)}",
|
||||||
|
labelRemark=Remark(shape="circle", content="触发主动发言时,连续进行发言的概率。"),
|
||||||
|
),
|
||||||
|
InputNumber(
|
||||||
|
label="最大连续主动发言句数",
|
||||||
|
name="speak_continuously_max_len",
|
||||||
|
value="${speak_continuously_max_len}",
|
||||||
|
visibleOn="${AND(enable, speak_enable)}",
|
||||||
|
min=1,
|
||||||
|
labelRemark=Remark(shape="circle", content="连续主动发言的最大句数。"),
|
||||||
|
),
|
||||||
|
InputNumber(
|
||||||
|
label="主动发言附带戳一戳概率",
|
||||||
|
name="speak_poke_probability",
|
||||||
|
value="${speak_poke_probability}",
|
||||||
|
min=0,
|
||||||
|
max=100,
|
||||||
|
suffix="%",
|
||||||
|
visibleOn="${AND(enable, speak_enable)}",
|
||||||
|
labelRemark=Remark(
|
||||||
|
shape="circle", content="主动发言时附带戳一戳的概率,会在最近5个发言者中随机选一个戳。"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
actions=[
|
||||||
|
Action(label="保存", level=LevelEnum.success, type="submit"),
|
||||||
|
ActionType.Ajax(
|
||||||
|
label="保存至所有群",
|
||||||
|
level=LevelEnum.primary,
|
||||||
|
confirmText="确认将当前配置保存至所有群?",
|
||||||
|
api="post:/LittlePaimon/api/chat_group_config?group_id=all",
|
||||||
|
),
|
||||||
|
Action(label="重置", level=LevelEnum.warning, type="reset"),
|
||||||
],
|
],
|
||||||
actions=[Action(label='保存', level=LevelEnum.success, type='submit'),
|
|
||||||
ActionType.Ajax(
|
|
||||||
label='保存至所有群',
|
|
||||||
level=LevelEnum.primary,
|
|
||||||
confirmText='确认将当前配置保存至所有群?',
|
|
||||||
api='post:/LittlePaimon/api/chat_group_config?group_id=all'
|
|
||||||
),
|
|
||||||
Action(label='重置', level=LevelEnum.warning, type='reset')]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
blacklist_table = TableCRUD(mode='table',
|
blacklist_table = TableCRUD(
|
||||||
title='',
|
mode="table",
|
||||||
syncLocation=False,
|
title="",
|
||||||
api='/LittlePaimon/api/get_chat_blacklist',
|
syncLocation=False,
|
||||||
interval=15000,
|
api="/LittlePaimon/api/get_chat_blacklist",
|
||||||
headerToolbar=[ActionType.Ajax(label='取消所有禁用',
|
interval=15000,
|
||||||
level=LevelEnum.warning,
|
headerToolbar=[
|
||||||
confirmText='确定要取消所有禁用吗?',
|
ActionType.Ajax(
|
||||||
api='put:/LittlePaimon/api/delete_all?type=blacklist')],
|
label="取消所有禁用",
|
||||||
itemActions=[ActionType.Ajax(tooltip='取消禁用',
|
level=LevelEnum.warning,
|
||||||
icon='fa fa-check-circle-o text-info',
|
confirmText="确定要取消所有禁用吗?",
|
||||||
confirmText='取消该被禁用的内容/关键词,但是仍然需要重新学习哦!',
|
api="put:/LittlePaimon/api/delete_all?type=blacklist",
|
||||||
api='delete:/LittlePaimon/api/delete_chat?type=blacklist&id=${id}')
|
)
|
||||||
],
|
],
|
||||||
footable=True,
|
itemActions=[
|
||||||
columns=[TableColumn(type='tpl', tpl='${keywords|truncate:20}', label='内容/关键词',
|
ActionType.Ajax(
|
||||||
name='keywords',
|
tooltip="取消禁用",
|
||||||
searchable=True, popOver={'mode': 'dialog', 'title': '全文',
|
icon="fa fa-check-circle-o text-info",
|
||||||
'className': 'break-all',
|
confirmText="取消该被禁用的内容/关键词,但是仍然需要重新学习哦!",
|
||||||
'body': {'type': 'tpl',
|
api="delete:/LittlePaimon/api/delete_chat?type=blacklist&id=${id}",
|
||||||
'tpl': '${keywords}'}}),
|
)
|
||||||
TableColumn(label='已禁用的群', name='bans', searchable=True),
|
],
|
||||||
])
|
footable=True,
|
||||||
message_table = TableCRUD(mode='table',
|
columns=[
|
||||||
title='',
|
TableColumn(
|
||||||
syncLocation=False,
|
type="tpl",
|
||||||
api='/LittlePaimon/api/get_chat_messages',
|
tpl="${keywords|truncate:20}",
|
||||||
interval=12000,
|
label="内容/关键词",
|
||||||
headerToolbar=[ActionType.Ajax(label='删除所有聊天记录',
|
name="keywords",
|
||||||
level=LevelEnum.warning,
|
searchable=True,
|
||||||
confirmText='确定要删除所有聊天记录吗?',
|
popOver={
|
||||||
api='put:/LittlePaimon/api/delete_all?type=message')],
|
"mode": "dialog",
|
||||||
itemActions=[ActionType.Ajax(tooltip='禁用',
|
"title": "全文",
|
||||||
icon='fa fa-ban text-danger',
|
"className": "break-all",
|
||||||
confirmText='禁用该聊天记录相关的学习内容和回复',
|
"body": {"type": "tpl", "tpl": "${keywords}"},
|
||||||
api='put:/LittlePaimon/api/ban_chat?type=message&id=${id}'),
|
},
|
||||||
ActionType.Ajax(tooltip='删除',
|
),
|
||||||
icon='fa fa-times text-danger',
|
TableColumn(label="已禁用的群", name="bans", searchable=True),
|
||||||
confirmText='删除该条聊天记录',
|
],
|
||||||
api='delete:/LittlePaimon/api/delete_chat?type=message&id=${id}')
|
)
|
||||||
],
|
message_table = TableCRUD(
|
||||||
footable=True,
|
mode="table",
|
||||||
columns=[TableColumn(label='消息ID', name='message_id'),
|
title="",
|
||||||
TableColumn(label='群ID', name='group_id', searchable=True),
|
syncLocation=False,
|
||||||
TableColumn(label='用户ID', name='user_id', searchable=True),
|
api="/LittlePaimon/api/get_chat_messages",
|
||||||
TableColumn(type='tpl', tpl='${raw_message|truncate:20}', label='消息', name='message',
|
interval=12000,
|
||||||
searchable=True, popOver={'mode': 'dialog', 'title': '消息全文',
|
headerToolbar=[
|
||||||
'className': 'break-all',
|
ActionType.Ajax(
|
||||||
'body': {'type': 'tpl',
|
label="删除所有聊天记录",
|
||||||
'tpl': '${raw_message}'}}),
|
level=LevelEnum.warning,
|
||||||
TableColumn(type='tpl', tpl='${time|date:YYYY-MM-DD HH\\:mm\\:ss}', label='时间',
|
confirmText="确定要删除所有聊天记录吗?",
|
||||||
name='time', sortable=True)
|
api="put:/LittlePaimon/api/delete_all?type=message",
|
||||||
])
|
)
|
||||||
|
],
|
||||||
|
itemActions=[
|
||||||
|
ActionType.Ajax(
|
||||||
|
tooltip="禁用",
|
||||||
|
icon="fa fa-ban text-danger",
|
||||||
|
confirmText="禁用该聊天记录相关的学习内容和回复",
|
||||||
|
api="put:/LittlePaimon/api/ban_chat?type=message&id=${id}",
|
||||||
|
),
|
||||||
|
ActionType.Ajax(
|
||||||
|
tooltip="删除",
|
||||||
|
icon="fa fa-times text-danger",
|
||||||
|
confirmText="删除该条聊天记录",
|
||||||
|
api="delete:/LittlePaimon/api/delete_chat?type=message&id=${id}",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
footable=True,
|
||||||
|
columns=[
|
||||||
|
TableColumn(label="消息ID", name="message_id"),
|
||||||
|
TableColumn(label="群ID", name="group_id", searchable=True),
|
||||||
|
TableColumn(label="用户ID", name="user_id", searchable=True),
|
||||||
|
TableColumn(
|
||||||
|
type="tpl",
|
||||||
|
tpl="${raw_message|truncate:20}",
|
||||||
|
label="消息",
|
||||||
|
name="message",
|
||||||
|
searchable=True,
|
||||||
|
popOver={
|
||||||
|
"mode": "dialog",
|
||||||
|
"title": "消息全文",
|
||||||
|
"className": "break-all",
|
||||||
|
"body": {"type": "tpl", "tpl": "${raw_message}"},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TableColumn(
|
||||||
|
type="tpl",
|
||||||
|
tpl="${time|date:YYYY-MM-DD HH\\:mm\\:ss}",
|
||||||
|
label="时间",
|
||||||
|
name="time",
|
||||||
|
sortable=True,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
answer_table = TableCRUD(
|
answer_table = TableCRUD(
|
||||||
mode='table',
|
mode="table",
|
||||||
syncLocation=False,
|
syncLocation=False,
|
||||||
footable=True,
|
footable=True,
|
||||||
api='/LittlePaimon/api/get_chat_answers',
|
api="/LittlePaimon/api/get_chat_answers",
|
||||||
interval=12000,
|
interval=12000,
|
||||||
headerToolbar=[ActionType.Ajax(label='删除所有已学习的回复',
|
headerToolbar=[
|
||||||
level=LevelEnum.warning,
|
ActionType.Ajax(
|
||||||
confirmText='确定要删除所有已学习的回复吗?',
|
label="删除所有已学习的回复",
|
||||||
api='put:/LittlePaimon/api/delete_all?type=answer')],
|
level=LevelEnum.warning,
|
||||||
itemActions=[ActionType.Ajax(tooltip='禁用',
|
confirmText="确定要删除所有已学习的回复吗?",
|
||||||
icon='fa fa-ban text-danger',
|
api="put:/LittlePaimon/api/delete_all?type=answer",
|
||||||
confirmText='禁用并删除该已学回复',
|
)
|
||||||
api='put:/LittlePaimon/api/ban_chat?type=answer&id=${id}'),
|
],
|
||||||
ActionType.Ajax(tooltip='删除',
|
itemActions=[
|
||||||
icon='fa fa-times text-danger',
|
ActionType.Ajax(
|
||||||
confirmText='仅删除该已学回复,不会禁用,所以依然能继续学',
|
tooltip="禁用",
|
||||||
api='delete:/LittlePaimon/api/delete_chat?type=answer&id=${id}')],
|
icon="fa fa-ban text-danger",
|
||||||
columns=[TableColumn(label='ID', name='id', visible=False),
|
confirmText="禁用并删除该已学回复",
|
||||||
TableColumn(label='群ID', name='group_id', searchable=True),
|
api="put:/LittlePaimon/api/ban_chat?type=answer&id=${id}",
|
||||||
TableColumn(type='tpl', tpl='${keywords|truncate:20}', label='内容/关键词', name='keywords',
|
),
|
||||||
searchable=True, popOver={'mode': 'dialog', 'title': '内容全文', 'className': 'break-all',
|
ActionType.Ajax(
|
||||||
'body': {'type': 'tpl', 'tpl': '${keywords}'}}),
|
tooltip="删除",
|
||||||
TableColumn(type='tpl', tpl='${time|date:YYYY-MM-DD HH\\:mm\\:ss}', label='最后学习时间', name='time',
|
icon="fa fa-times text-danger",
|
||||||
sortable=True),
|
confirmText="仅删除该已学回复,不会禁用,所以依然能继续学",
|
||||||
TableColumn(label='次数', name='count', sortable=True),
|
api="delete:/LittlePaimon/api/delete_chat?type=answer&id=${id}",
|
||||||
ColumnList(label='完整消息', name='messages', breakpoint='*', source='${messages}',
|
),
|
||||||
listItem=AmisList.Item(body={'name': 'msg'}))
|
],
|
||||||
])
|
columns=[
|
||||||
|
TableColumn(label="ID", name="id", visible=False),
|
||||||
|
TableColumn(label="群ID", name="group_id", searchable=True),
|
||||||
|
TableColumn(
|
||||||
|
type="tpl",
|
||||||
|
tpl="${keywords|truncate:20}",
|
||||||
|
label="内容/关键词",
|
||||||
|
name="keywords",
|
||||||
|
searchable=True,
|
||||||
|
popOver={
|
||||||
|
"mode": "dialog",
|
||||||
|
"title": "内容全文",
|
||||||
|
"className": "break-all",
|
||||||
|
"body": {"type": "tpl", "tpl": "${keywords}"},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TableColumn(
|
||||||
|
type="tpl",
|
||||||
|
tpl="${time|date:YYYY-MM-DD HH\\:mm\\:ss}",
|
||||||
|
label="最后学习时间",
|
||||||
|
name="time",
|
||||||
|
sortable=True,
|
||||||
|
),
|
||||||
|
TableColumn(label="次数", name="count", sortable=True),
|
||||||
|
ColumnList(
|
||||||
|
label="完整消息",
|
||||||
|
name="messages",
|
||||||
|
breakpoint="*",
|
||||||
|
source="${messages}",
|
||||||
|
listItem=AmisList.Item(body=[AmisList.Item.ListBodyField(name="msg")]),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
answer_table_on_context = TableCRUD(
|
answer_table_on_context = TableCRUD(
|
||||||
mode='table',
|
mode="table",
|
||||||
syncLocation=False,
|
syncLocation=False,
|
||||||
footable=True,
|
footable=True,
|
||||||
api='/LittlePaimon/api/get_chat_answers?context_id=${id}&page=${page}&perPage=${perPage}&orderBy=${orderBy}&orderDir=${orderDir}',
|
api="/LittlePaimon/api/get_chat_answers?context_id=${id}&page=${page}&perPage=${perPage}&orderBy=${orderBy}&orderDir=${orderDir}",
|
||||||
interval=12000,
|
interval=12000,
|
||||||
headerToolbar=[ActionType.Ajax(label='删除该内容所有回复',
|
headerToolbar=[
|
||||||
level=LevelEnum.warning,
|
ActionType.Ajax(
|
||||||
confirmText='确定要删除该条内容已学习的回复吗?',
|
label="删除该内容所有回复",
|
||||||
api='put:/LittlePaimon/api/delete_all?type=answer&id=${id}')],
|
level=LevelEnum.warning,
|
||||||
itemActions=[ActionType.Ajax(tooltip='禁用',
|
confirmText="确定要删除该条内容已学习的回复吗?",
|
||||||
icon='fa fa-ban text-danger',
|
api="put:/LittlePaimon/api/delete_all?type=answer&id=${id}",
|
||||||
confirmText='禁用并删除该已学回复',
|
)
|
||||||
api='put:/LittlePaimon/api/ban_chat?type=answer&id=${id}'),
|
],
|
||||||
ActionType.Ajax(tooltip='删除',
|
itemActions=[
|
||||||
icon='fa fa-times text-danger',
|
ActionType.Ajax(
|
||||||
confirmText='仅删除该已学回复,但不禁用,依然能继续学',
|
tooltip="禁用",
|
||||||
api='delete:/LittlePaimon/api/delete_chat?type=answer&id=${id}')],
|
icon="fa fa-ban text-danger",
|
||||||
columns=[TableColumn(label='ID', name='id', visible=False),
|
confirmText="禁用并删除该已学回复",
|
||||||
TableColumn(label='群ID', name='group_id'),
|
api="put:/LittlePaimon/api/ban_chat?type=answer&id=${id}",
|
||||||
TableColumn(type='tpl', tpl='${keywords|truncate:20}', label='内容/关键词', name='keywords',
|
),
|
||||||
searchable=True, popOver={'mode': 'dialog', 'title': '内容全文', 'className': 'break-all',
|
ActionType.Ajax(
|
||||||
'body': {'type': 'tpl', 'tpl': '${keywords}'}}),
|
tooltip="删除",
|
||||||
TableColumn(type='tpl', tpl='${time|date:YYYY-MM-DD HH\\:mm\\:ss}', label='最后学习时间', name='time',
|
icon="fa fa-times text-danger",
|
||||||
sortable=True),
|
confirmText="仅删除该已学回复,但不禁用,依然能继续学",
|
||||||
TableColumn(label='次数', name='count', sortable=True),
|
api="delete:/LittlePaimon/api/delete_chat?type=answer&id=${id}",
|
||||||
ColumnList(label='完整消息', name='messages', breakpoint='*', source='${messages}',
|
),
|
||||||
listItem=AmisList.Item(body={'name': 'msg'}))
|
],
|
||||||
])
|
columns=[
|
||||||
context_table = TableCRUD(mode='table',
|
TableColumn(label="ID", name="id", visible=False),
|
||||||
title='',
|
TableColumn(label="群ID", name="group_id"),
|
||||||
syncLocation=False,
|
TableColumn(
|
||||||
api='/LittlePaimon/api/get_chat_contexts',
|
type="tpl",
|
||||||
interval=12000,
|
tpl="${keywords|truncate:20}",
|
||||||
headerToolbar=[ActionType.Ajax(label='删除所有学习内容',
|
label="内容/关键词",
|
||||||
level=LevelEnum.warning,
|
name="keywords",
|
||||||
confirmText='确定要删除所有已学习的内容吗?',
|
searchable=True,
|
||||||
api='put:/LittlePaimon/api/delete_all?type=context')],
|
popOver={
|
||||||
itemActions=[ActionType.Dialog(tooltip='回复列表',
|
"mode": "dialog",
|
||||||
icon='fa fa-book text-info',
|
"title": "内容全文",
|
||||||
dialog=Dialog(title='回复列表',
|
"className": "break-all",
|
||||||
size='lg',
|
"body": {"type": "tpl", "tpl": "${keywords}"},
|
||||||
body=answer_table_on_context)),
|
},
|
||||||
ActionType.Ajax(tooltip='禁用',
|
),
|
||||||
icon='fa fa-ban text-danger',
|
TableColumn(
|
||||||
confirmText='禁用并删除该学习的内容及其所有回复',
|
type="tpl",
|
||||||
api='put:/LittlePaimon/api/ban_chat?type=context&id=${id}'),
|
tpl="${time|date:YYYY-MM-DD HH\\:mm\\:ss}",
|
||||||
ActionType.Ajax(tooltip='删除',
|
label="最后学习时间",
|
||||||
icon='fa fa-times text-danger',
|
name="time",
|
||||||
confirmText='仅删除该学习的内容及其所有回复,但不禁用,依然能继续学',
|
sortable=True,
|
||||||
api='delete:/LittlePaimon/api/delete_chat?type=context&id=${id}')
|
),
|
||||||
],
|
TableColumn(label="次数", name="count", sortable=True),
|
||||||
footable=True,
|
ColumnList(
|
||||||
columns=[TableColumn(label='ID', name='id', visible=False),
|
label="完整消息",
|
||||||
TableColumn(type='tpl', tpl='${keywords|truncate:20}', label='内容/关键词',
|
name="messages",
|
||||||
name='keywords', searchable=True,
|
breakpoint="*",
|
||||||
popOver={'mode': 'dialog', 'title': '内容全文', 'className': 'break-all',
|
source="${messages}",
|
||||||
'body': {'type': 'tpl', 'tpl': '${keywords}'}}),
|
listItem=AmisList.Item(body=[AmisList.Item.ListBodyField(name="msg")]),
|
||||||
TableColumn(type='tpl', tpl='${time|date:YYYY-MM-DD HH\\:mm\\:ss}',
|
),
|
||||||
label='最后学习时间', name='time', sortable=True),
|
],
|
||||||
TableColumn(label='已学次数', name='count', sortable=True),
|
)
|
||||||
])
|
context_table = TableCRUD(
|
||||||
|
mode="table",
|
||||||
|
title="",
|
||||||
|
syncLocation=False,
|
||||||
|
api="/LittlePaimon/api/get_chat_contexts",
|
||||||
|
interval=12000,
|
||||||
|
headerToolbar=[
|
||||||
|
ActionType.Ajax(
|
||||||
|
label="删除所有学习内容",
|
||||||
|
level=LevelEnum.warning,
|
||||||
|
confirmText="确定要删除所有已学习的内容吗?",
|
||||||
|
api="put:/LittlePaimon/api/delete_all?type=context",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
itemActions=[
|
||||||
|
ActionType.Dialog(
|
||||||
|
tooltip="回复列表",
|
||||||
|
icon="fa fa-book text-info",
|
||||||
|
dialog=Dialog(title="回复列表", size="lg", body=answer_table_on_context),
|
||||||
|
),
|
||||||
|
ActionType.Ajax(
|
||||||
|
tooltip="禁用",
|
||||||
|
icon="fa fa-ban text-danger",
|
||||||
|
confirmText="禁用并删除该学习的内容及其所有回复",
|
||||||
|
api="put:/LittlePaimon/api/ban_chat?type=context&id=${id}",
|
||||||
|
),
|
||||||
|
ActionType.Ajax(
|
||||||
|
tooltip="删除",
|
||||||
|
icon="fa fa-times text-danger",
|
||||||
|
confirmText="仅删除该学习的内容及其所有回复,但不禁用,依然能继续学",
|
||||||
|
api="delete:/LittlePaimon/api/delete_chat?type=context&id=${id}",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
footable=True,
|
||||||
|
columns=[
|
||||||
|
TableColumn(label="ID", name="id", visible=False),
|
||||||
|
TableColumn(
|
||||||
|
type="tpl",
|
||||||
|
tpl="${keywords|truncate:20}",
|
||||||
|
label="内容/关键词",
|
||||||
|
name="keywords",
|
||||||
|
searchable=True,
|
||||||
|
popOver={
|
||||||
|
"mode": "dialog",
|
||||||
|
"title": "内容全文",
|
||||||
|
"className": "break-all",
|
||||||
|
"body": {"type": "tpl", "tpl": "${keywords}"},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TableColumn(
|
||||||
|
type="tpl",
|
||||||
|
tpl="${time|date:YYYY-MM-DD HH\\:mm\\:ss}",
|
||||||
|
label="最后学习时间",
|
||||||
|
name="time",
|
||||||
|
sortable=True,
|
||||||
|
),
|
||||||
|
TableColumn(label="已学次数", name="count", sortable=True),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
message_page = PageSchema(url='/chat/messages', icon='fa fa-comments', label='群聊消息',
|
message_page = PageSchema(
|
||||||
schema=Page(title='群聊消息', body=[
|
url="/chat/messages",
|
||||||
Alert(level=LevelEnum.info,
|
icon="fa fa-comments",
|
||||||
className='white-space-pre-wrap',
|
label="群聊消息",
|
||||||
body=(f'此数据库记录了{NICKNAME}收到的除指令外的聊天记录。\n'
|
schema=Page(
|
||||||
'· 点击"禁用"可以将某条聊天记录进行禁用,这样其相关的学习就会列入禁用列表。\n'
|
title="群聊消息",
|
||||||
'· 点击"删除"可以删除某条记录,但不会影响它的学习。\n'
|
body=[
|
||||||
f'· 可以通过搜索{NICKNAME}的QQ号,来查看它的回复记录。')),
|
Alert(
|
||||||
message_table]))
|
level=LevelEnum.info,
|
||||||
context_page = PageSchema(url='/chat/contexts', icon='fa fa-comment', label='学习内容',
|
className="white-space-pre-wrap",
|
||||||
schema=Page(title='内容',
|
body=(
|
||||||
body=[Alert(level=LevelEnum.info,
|
f"此数据库记录了{NICKNAME}收到的除指令外的聊天记录。\n"
|
||||||
className='white-space-pre-wrap',
|
'· 点击"禁用"可以将某条聊天记录进行禁用,这样其相关的学习就会列入禁用列表。\n'
|
||||||
body=(f'此数据库记录了{NICKNAME}所学习的内容。\n'
|
'· 点击"删除"可以删除某条记录,但不会影响它的学习。\n'
|
||||||
'· 点击"回复列表"可以查看该条内容已学习到的可能的回复。\n'
|
f"· 可以通过搜索{NICKNAME}的QQ号,来查看它的回复记录。"
|
||||||
'· 点击"禁用"可以将该学习进行禁用,以后不会再学。\n'
|
),
|
||||||
'· 点击"删除"可以删除该学习,让它重新开始学习这句话。')),
|
),
|
||||||
context_table]))
|
message_table,
|
||||||
answer_page = PageSchema(url='/chat/answers', icon='fa fa-commenting-o', label='内容回复',
|
],
|
||||||
schema=Page(title='回复',
|
),
|
||||||
body=[Alert(level=LevelEnum.info,
|
)
|
||||||
className='white-space-pre-wrap',
|
context_page = PageSchema(
|
||||||
body=(f'此数据库记录了{NICKNAME}已学习到的所有回复,但看不到这些回复属于哪些内容,推荐到"学习内容"表进行操作。\n'
|
url="/chat/contexts",
|
||||||
'· 点击"禁用"可以将该回复进行禁用,以后不会再学。\n'
|
icon="fa fa-comment",
|
||||||
'· 点击"删除"可以删除该回复,让它重新开始学习。')),
|
label="学习内容",
|
||||||
answer_table]))
|
schema=Page(
|
||||||
blacklist_page = PageSchema(url='/chat/blacklist', icon='fa fa-ban', label='禁用列表',
|
title="内容",
|
||||||
schema=Page(title='禁用列表',
|
body=[
|
||||||
body=[Alert(level=LevelEnum.info,
|
Alert(
|
||||||
className='white-space-pre-wrap',
|
level=LevelEnum.info,
|
||||||
body=f'此数据库记录了{NICKNAME}被禁用的内容/关键词。\n'
|
className="white-space-pre-wrap",
|
||||||
'· 可以取消禁用,使其能够重新继续学习。\n'
|
body=(
|
||||||
'· 不能在此添加禁用,只能在群中回复[不可以]或者在<配置>中添加屏蔽词来达到禁用效果。'),
|
f"此数据库记录了{NICKNAME}所学习的内容。\n"
|
||||||
blacklist_table]))
|
'· 点击"回复列表"可以查看该条内容已学习到的可能的回复。\n'
|
||||||
database_page = PageSchema(label='数据库', icon='fa fa-database',
|
'· 点击"禁用"可以将该学习进行禁用,以后不会再学。\n'
|
||||||
children=[message_page, context_page, answer_page, blacklist_page])
|
'· 点击"删除"可以删除该学习,让它重新开始学习这句话。'
|
||||||
config_page = PageSchema(url='/chat/configs', icon='fa fa-wrench', label='配置',
|
),
|
||||||
schema=Page(title='配置', initApi='/LittlePaimon/api/get_group_list', body=[global_config_form, group_select, group_config_form]))
|
),
|
||||||
chat_page = PageSchema(label='群聊学习', icon='fa fa-wechat (alias)', children=[config_page, database_page])
|
context_table,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
answer_page = PageSchema(
|
||||||
|
url="/chat/answers",
|
||||||
|
icon="fa fa-commenting-o",
|
||||||
|
label="内容回复",
|
||||||
|
schema=Page(
|
||||||
|
title="回复",
|
||||||
|
body=[
|
||||||
|
Alert(
|
||||||
|
level=LevelEnum.info,
|
||||||
|
className="white-space-pre-wrap",
|
||||||
|
body=(
|
||||||
|
f'此数据库记录了{NICKNAME}已学习到的所有回复,但看不到这些回复属于哪些内容,推荐到"学习内容"表进行操作。\n'
|
||||||
|
'· 点击"禁用"可以将该回复进行禁用,以后不会再学。\n'
|
||||||
|
'· 点击"删除"可以删除该回复,让它重新开始学习。'
|
||||||
|
),
|
||||||
|
),
|
||||||
|
answer_table,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
blacklist_page = PageSchema(
|
||||||
|
url="/chat/blacklist",
|
||||||
|
icon="fa fa-ban",
|
||||||
|
label="禁用列表",
|
||||||
|
schema=Page(
|
||||||
|
title="禁用列表",
|
||||||
|
interval=60000,
|
||||||
|
body=[
|
||||||
|
Alert(
|
||||||
|
level=LevelEnum.info,
|
||||||
|
className="white-space-pre-wrap",
|
||||||
|
body=f"此数据库记录了{NICKNAME}被禁用的内容/关键词。\n"
|
||||||
|
"· 可以取消禁用,使其能够重新继续学习。\n"
|
||||||
|
"· 不能在此添加禁用,只能在群中回复[不可以]或者在<配置>中添加屏蔽词来达到禁用效果。",
|
||||||
|
),
|
||||||
|
blacklist_table,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
database_page = PageSchema(
|
||||||
|
label="数据库",
|
||||||
|
icon="fa fa-database",
|
||||||
|
children=[message_page, context_page, answer_page, blacklist_page],
|
||||||
|
)
|
||||||
|
config_page = PageSchema(
|
||||||
|
url="/chat/configs",
|
||||||
|
icon="fa fa-wrench",
|
||||||
|
label="配置",
|
||||||
|
schema=Page(
|
||||||
|
title="配置",
|
||||||
|
initApi="/LittlePaimon/api/get_group_list",
|
||||||
|
body=[global_config_form, group_select, group_config_form],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
chat_page = PageSchema(
|
||||||
|
label="群聊学习", icon="fa fa-wechat (alias)", children=[config_page, database_page]
|
||||||
|
)
|
||||||
admin_app.pages[0].children.append(chat_page)
|
admin_app.pages[0].children.append(chat_page)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user