nonebot-bison
nonebot-bison copied to clipboard
[Feature Request] 关键词拦截
概况
- [x] 我已阅读文档
- [x] 我已查阅Issues和PR
- [x] 我使用本插件/Bot并阅读过本插件的部分代码
- [x] 我已实现本issue提出的部分需求
- [ ] 我已实现本issue思路完善的完整需求
动机和需求
需求:支持关键词屏蔽
动机:以关注@明日方舟(微博) 为例,抽奖结果的转发推文比较刷屏,如下:
那我可以关闭转发么?是可以的,但是考虑到转发的推文不一定是抽奖结果,可能是别的重要信息(比如联动转发)
据此最好的办法应该是设置关键词屏蔽(Issue #50 曾提及过该功能),如方舟的微博抽奖带有“微博官方唯一抽奖工具”,配置读入后正则过滤。
但是直接做成“包含关键词就不转发”有点不灵活,用户可以选择不转发或者使用替代文本(比如抽奖信息就是太长了,可以转换成一段包含关键信息的文本即可)
在此之上,替换文本里可以包含变量如$source$携带原消息的重要部分(全量替换,原来构建的Post没要了,~~因为我不太会重新构建一个Post~~)
过滤后示意图:
添加的配置(bison_block_keyword项,值为json):
需求粗分析(打勾为已实现)
- [x] 读取关键词配置
- [x] 检查关键词
- [x] 替换文本/不转发(配置项为""则不转发,有文字则替换为文字)
- [x] 替换文本里加入变量(如$source$代表addon来源信息)
- [ ] 根据群号或者订阅id启用对应过滤规则(需在上面的基础多套一层对象)
关键代码示意:send.py
def is_contain_keyword(msg_str: str):
try:
for keyword in block_keywords:
if keyword in msg_str:
# 包含屏蔽词
logger.info(f"检测到屏蔽词 {keyword}")
if not block_set[keyword]:
return True, ''
return True, f"{block_set[keyword]}"
return False, '' # 不过滤
except Exception as e:
logger.error(f"过滤失败 {str(e)}")
logger.error(f"过滤失败消息: {msg_str}")
return False, '' # 不过滤
async def _do_send(send_target: PlatformTarget, msg: Sendable):
# 关键词屏蔽
msg_str = str(msg)
addon_info = "来源" + msg_str.split("来源")[-1]
logger.info(f"addon_info: {addon_info}")
is_blocked, block_info = is_contain_keyword(msg_str)
logger.info(f"检测到屏蔽词\nis_blocked: {is_blocked}\n替代消息:{block_info}")
if is_blocked and not block_info:
return; # 直接结束
to_send_msg = msg
if is_blocked:
to_send_msg = MessageFactory(block_info.replace("$source$", addon_info))
try:
await to_send_msg.send_to(send_target)
except ActionFailed: # TODO: catch exception of other adapters
await refresh_bots()
logger.warning("send msg failed, refresh bots")
问题
一是目前是全平台、全群组拦截的,不能具体到群组或者某个订阅,另一方面就算是实现了,考虑到配置里些多层嵌套,对用户来说,可能会没那么好配置,望指点
二是而且这种直接str(msg)的写法貌似不太符合规范
目前是能用了,可以极大避免抽奖转发消息的刷屏