nonebot-plugin-shitbot 0.1.2rc1__tar.gz → 0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/PKG-INFO +1 -1
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/README.md +25 -12
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/aux.py +10 -0
- nonebot_plugin_shitbot-0.2/nonebot_plugin_shitbot/commands/autoreply_cmd.py +550 -0
- nonebot_plugin_shitbot-0.2/nonebot_plugin_shitbot/commands/autoreply_main_cmd.py +99 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/convert_cmd.py +2 -2
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/help_cmd.py +3 -1
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/shitpost_cmd.py +3 -1
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/config.py +5 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/config.yaml +1 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/default_config.yaml +7 -6
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/handlers.py +7 -3
- nonebot_plugin_shitbot-0.2/nonebot_plugin_shitbot/msgdatabase.py +323 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/msgutils.py +59 -19
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot.egg-info/PKG-INFO +1 -1
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot.egg-info/SOURCES.txt +1 -14
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/pyproject.toml +1 -1
- nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/commands/autoreply_cmd.py +0 -486
- nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/commands/autoreply_main_cmd.py +0 -105
- nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/advrandpic.md +0 -61
- nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/convert.md +0 -37
- nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/help.md +0 -17
- nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/imgs/md2picex1.png +0 -0
- nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/imgs/md2picex2.png +0 -0
- nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/index.md +0 -22
- nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/md2pic.md +0 -57
- nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/perm.md +0 -110
- nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/pixiv.md +0 -42
- nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/randpic.md +0 -34
- nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/session.md +0 -38
- nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/shitpost.md +0 -39
- nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs//345/217/202/346/225/260/350/247/243/346/236/220.md +0 -338
- nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs//346/235/203/351/231/220/347/263/273/347/273/237.md +0 -220
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/LICENSE +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/__init__.py +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/command.py +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/__init__.py +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/advrandpic_cmd.py +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/md2pic_cmd.py +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/perm_cmd.py +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/pixiv_cmd.py +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/randpic_cmd.py +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/session_cmd.py +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/css/github-markdown-dark-dimmed.css +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/parser.py +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/permissions.py +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/scripts/p2png.sh +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/scripts/png2fr.sh +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/scripts/png2v.sh +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/session.py +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/tasks.py +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot.egg-info/dependency_links.txt +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot.egg-info/requires.txt +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot.egg-info/top_level.txt +0 -0
- {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/setup.cfg +0 -0
|
@@ -16,25 +16,38 @@
|
|
|
16
16
|
|
|
17
17
|
- 用户多命令管理
|
|
18
18
|
|
|
19
|
+
- 参数解析(见docs/参数解析.md)
|
|
20
|
+
|
|
19
21
|
- 权限管理系统(见docs/权限系统.md)
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
- 信息处理函数库(msgutils) 涵盖对信息对象的转换、处理、发送 运用函数式方案处理信息
|
|
24
|
+
|
|
25
|
+
- 手搓信息管理数据库
|
|
26
|
+
- 数据库由两部分组成: messages以及medias
|
|
27
|
+
- messages用于存储信息 通过将 Message 转化为 DumpedMsg 后以 yaml 格式存储
|
|
28
|
+
- 文件名为该文件的sha256sum值 通过比较文件名去重
|
|
29
|
+
- 维护一个映射表(的yaml文件) 用于记录信息的引用数(由数据库用户维护) 通常若元信息的引用数为0 则删除元信息
|
|
30
|
+
- medias用于messages中信息中可能存在的多媒体文件
|
|
31
|
+
- 文件名为该文件的sha256sum值 通过比较文件名去重
|
|
32
|
+
- 维护一个映射表(的yaml文件) 用于记录文件的引用数(只会被messages中的信息引用 因此仅由数据库本身维护) 通常若元信息的引用数为0 则删除元信息
|
|
22
33
|
|
|
23
|
-
-
|
|
34
|
+
- 自动回复系统
|
|
35
|
+
|
|
36
|
+
## TODO List(大致按优先级排序 但随缘想先解决哪个就解决哪个)
|
|
24
37
|
|
|
25
|
-
- 添加单元测试
|
|
38
|
+
- 添加单元测试 (技术债务这块 最讨厌的一集 想写哪个就写哪个)
|
|
26
39
|
|
|
27
|
-
- B站视频解析并下载(支持链接块或者BV
|
|
40
|
+
- B站视频解析并下载(支持链接块或者BV号)(6.12更新:我鬼脑意淫后越想越觉得写这个好累 所以把优先级后调)
|
|
28
41
|
|
|
29
|
-
- 实现 /stop
|
|
42
|
+
- 实现 /stop 用于终止当前会话(暂时没啥思路 我只能想到装饰器+CancelTask注入 但要怎么找到需要被注入的任务我还没思路)
|
|
30
43
|
|
|
31
|
-
-
|
|
44
|
+
- 为代码添加更多注释和写文档 (依旧技术债务 依旧最烦 依旧随缘)
|
|
32
45
|
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
46
|
+
- 智能化/shitpost (鉴于数据库搓好了 如果我后面认为这个写起来也不难的话我就来搓)
|
|
47
|
+
- 使用用上面提到的数据库
|
|
48
|
+
- 添加一个元信息映射表(yaml) 类型大概是Dict[str, Dict[str, Any]] 键是史的文件名 也就是哈希值 值中保存着史的:real_id列表 时间戳 好评数 发送失败次数 存储着已经发送过该史的群号的列表
|
|
36
49
|
|
|
37
|
-
-
|
|
50
|
+
- 维护一个yaml文件用于记录各个real_id对应的史的哈希值
|
|
38
51
|
|
|
39
52
|
- 根据以上信息我瞎编了一个可能有用的发史优先级公式:
|
|
40
53
|
|
|
@@ -44,7 +57,7 @@
|
|
|
44
57
|
\end{aligned}
|
|
45
58
|
$$
|
|
46
59
|
|
|
47
|
-
- 暂时想到这些
|
|
60
|
+
- 暂时想到这些 哪天我又有什么点子也说不定(6.12更新:一个月下来能看到 TODO List 越来越少还是挺感慨的 为什么补上这条括号内容?因为上面的项我都加了 这条不加我会犯强迫症)
|
|
48
61
|
|
|
49
62
|
## 配置
|
|
50
63
|
|
|
@@ -79,7 +92,7 @@ nb plugin install nonebot-plugin-shitbot
|
|
|
79
92
|
| `/convert` | 图片格式转换 |
|
|
80
93
|
| `/shitpost` | 搬史 |
|
|
81
94
|
| `/md2pic` | Markdown 渲染为图片 |
|
|
82
|
-
| `/autoreply` |
|
|
95
|
+
| `/autoreply` | 自动回复管理 |
|
|
83
96
|
|
|
84
97
|
## 鸣谢
|
|
85
98
|
|
{nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/aux.py
RENAMED
|
@@ -51,6 +51,16 @@ async def rm_cache(group_id: str, user_id: str, pid: str):
|
|
|
51
51
|
logger.info(f"已删除缓存: {cache_path}")
|
|
52
52
|
|
|
53
53
|
|
|
54
|
+
def get_file_sha256(file_path: Path | str):
|
|
55
|
+
file = Path(file_path) if isinstance(file_path, str) else file_path
|
|
56
|
+
if not file.is_file():
|
|
57
|
+
raise FileNotFoundError(f"No such file or not a regular file: {file}")
|
|
58
|
+
# Compute sha256sum
|
|
59
|
+
with file.open("rb") as f:
|
|
60
|
+
hash_hex = hashlib.file_digest(f, "sha256").hexdigest()
|
|
61
|
+
return hash_hex
|
|
62
|
+
|
|
63
|
+
|
|
54
64
|
def rename_file_to_sha256(file_path: Path | str):
|
|
55
65
|
file = Path(file_path) if isinstance(file_path, str) else file_path
|
|
56
66
|
if not file.is_file():
|
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import yaml
|
|
3
|
+
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from nonebot.adapters.onebot.v11 import Bot, Message, MessageEvent
|
|
7
|
+
|
|
8
|
+
from ..command import BotCommand
|
|
9
|
+
from ..session import BotSession
|
|
10
|
+
from ..parser import BotArgParser
|
|
11
|
+
from ..msgdatabase import BotMsgDataBase
|
|
12
|
+
from ..msgutils import DumpedMsg, DumpedSeg, DataVariables, dump_message, msg_filter, modify_msg_data
|
|
13
|
+
from ..tasks import autoreply_lock
|
|
14
|
+
from .autoreply_main_cmd import BotCommandAutoReplyMain
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BotCommandAutoreply(BotCommand):
|
|
18
|
+
_name = "autoreply"
|
|
19
|
+
_autoreply_dir = BotCommandAutoReplyMain._autoreply_dir
|
|
20
|
+
_rule_path = BotCommandAutoReplyMain._rule_path
|
|
21
|
+
_msg_table_path = BotCommandAutoReplyMain._msg_table_path
|
|
22
|
+
|
|
23
|
+
def __init__(self, bot: Bot, session: BotSession, *, _pid: int, _internal=None):
|
|
24
|
+
super().__init__(bot, session, _pid=_pid, _internal=_internal)
|
|
25
|
+
self._roger_lock = asyncio.Lock()
|
|
26
|
+
self._is_accept_msg = False
|
|
27
|
+
self._rule = {}
|
|
28
|
+
self._msg_table = {}
|
|
29
|
+
self._temp_msg: DumpedMsg = []
|
|
30
|
+
|
|
31
|
+
self._auto: bool = False
|
|
32
|
+
self._key: list[str] = []
|
|
33
|
+
self._rp: list[str] = []
|
|
34
|
+
self._T: list[str] = []
|
|
35
|
+
self._t: list[str] = []
|
|
36
|
+
self._K: list[str] = []
|
|
37
|
+
self._k: list[str] = []
|
|
38
|
+
self._R: list[str] = []
|
|
39
|
+
self._r: list[str] = []
|
|
40
|
+
self._m: int = -1
|
|
41
|
+
self._l: int = 0
|
|
42
|
+
self._sum: list[str] = []
|
|
43
|
+
self._st: list[int] = []
|
|
44
|
+
|
|
45
|
+
self._rstate: str = ""
|
|
46
|
+
|
|
47
|
+
def _load_meta(self):
|
|
48
|
+
if not self._rule_path.exists() or not self._msg_table_path.exists():
|
|
49
|
+
raise FileNotFoundError
|
|
50
|
+
_rule = yaml.safe_load(self._rule_path.read_text(encoding="utf-8"))
|
|
51
|
+
_msg_table = yaml.safe_load(self._msg_table_path.read_text(encoding="utf-8"))
|
|
52
|
+
if (_rule is not None and not isinstance(_rule, dict)) or (
|
|
53
|
+
_msg_table is not None and not isinstance(_msg_table, dict)
|
|
54
|
+
):
|
|
55
|
+
raise ValueError
|
|
56
|
+
self._rule = _rule if _rule is not None else {}
|
|
57
|
+
self._msg_table = _msg_table if _msg_table is not None else {}
|
|
58
|
+
|
|
59
|
+
def _update_meta(self):
|
|
60
|
+
self._rule_path.write_text(
|
|
61
|
+
yaml.safe_dump(self._rule, allow_unicode=True), encoding="utf-8"
|
|
62
|
+
)
|
|
63
|
+
self._msg_table_path.write_text(
|
|
64
|
+
yaml.safe_dump(self._msg_table, allow_unicode=True), encoding="utf-8"
|
|
65
|
+
)
|
|
66
|
+
arsession = BotSession.get_obj("public", "autoreply")
|
|
67
|
+
if arsession is None:
|
|
68
|
+
return
|
|
69
|
+
main_command = arsession.commands.get(arsession.curpid)
|
|
70
|
+
if main_command is None or not isinstance(
|
|
71
|
+
main_command, BotCommandAutoReplyMain
|
|
72
|
+
):
|
|
73
|
+
return
|
|
74
|
+
main_command.load_meta()
|
|
75
|
+
|
|
76
|
+
async def _set_reply(self, reply: str):
|
|
77
|
+
old_msg_hash = self._msg_table.get(reply, "")
|
|
78
|
+
if self._temp_msg == []:
|
|
79
|
+
if old_msg_hash:
|
|
80
|
+
return
|
|
81
|
+
self._msg_table[reply] = ""
|
|
82
|
+
self._update_meta()
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
def _filter(seg: DumpedSeg) -> bool:
|
|
86
|
+
return seg["type"] in ["text", "image", "face"]
|
|
87
|
+
|
|
88
|
+
filtered_msg = msg_filter(_filter, self._temp_msg)
|
|
89
|
+
msg_path = await self._database.save_msg(filtered_msg)
|
|
90
|
+
msg_hash = msg_path.name
|
|
91
|
+
self._msg_table[reply] = msg_hash
|
|
92
|
+
self._database.inc_msg_rc(msg_hash)
|
|
93
|
+
if old_msg_hash:
|
|
94
|
+
self._database.dec_msg_rc(old_msg_hash)
|
|
95
|
+
self._database.del_msg(old_msg_hash)
|
|
96
|
+
self._update_meta()
|
|
97
|
+
|
|
98
|
+
def _del_reply(self, reply: str):
|
|
99
|
+
for key in self._rule.keys():
|
|
100
|
+
if reply in self._rule[key]["replys"]:
|
|
101
|
+
self._rule[key]["replys"].remove(reply)
|
|
102
|
+
msg_hash = self._msg_table.get(reply, "")
|
|
103
|
+
if not msg_hash:
|
|
104
|
+
self._msg_table.pop(reply, None)
|
|
105
|
+
self._update_meta()
|
|
106
|
+
return
|
|
107
|
+
self._database.dec_msg_rc(msg_hash)
|
|
108
|
+
self._database.del_msg(msg_hash)
|
|
109
|
+
self._msg_table.pop(reply, None)
|
|
110
|
+
self._update_meta()
|
|
111
|
+
|
|
112
|
+
def _set_key(
|
|
113
|
+
self,
|
|
114
|
+
key: str,
|
|
115
|
+
*,
|
|
116
|
+
trans: list[str] | None = None,
|
|
117
|
+
dels: list[str] | None = None,
|
|
118
|
+
is_contain: bool | None = None,
|
|
119
|
+
keywords: list[str] | None = None,
|
|
120
|
+
replys: list[str] | None = None,
|
|
121
|
+
):
|
|
122
|
+
if key not in self._rule:
|
|
123
|
+
self._rule[key] = {
|
|
124
|
+
"trans": [],
|
|
125
|
+
"dels": [],
|
|
126
|
+
"is_contain": False,
|
|
127
|
+
"keywords": [],
|
|
128
|
+
"replys": [],
|
|
129
|
+
}
|
|
130
|
+
if trans is not None:
|
|
131
|
+
self._rule[key]["trans"] = trans
|
|
132
|
+
if dels is not None:
|
|
133
|
+
self._rule[key]["dels"] = dels
|
|
134
|
+
if is_contain is not None:
|
|
135
|
+
self._rule[key]["is_contain"] = is_contain
|
|
136
|
+
if keywords is not None:
|
|
137
|
+
self._rule[key]["keywords"] = keywords
|
|
138
|
+
if replys is not None:
|
|
139
|
+
self._rule[key]["replys"] = replys
|
|
140
|
+
self._update_meta()
|
|
141
|
+
|
|
142
|
+
def _del_key(self, key: str):
|
|
143
|
+
self._rule.pop(key, None)
|
|
144
|
+
self._update_meta()
|
|
145
|
+
|
|
146
|
+
def _init_parser(self):
|
|
147
|
+
parser = BotArgParser()
|
|
148
|
+
parser.set_rule(max=0, need_subcmd=True)
|
|
149
|
+
start = parser.add_subparser("start")
|
|
150
|
+
stop = parser.add_subparser("stop")
|
|
151
|
+
lst = parser.add_subparser("list")
|
|
152
|
+
info = parser.add_subparser("info")
|
|
153
|
+
prnt = parser.add_subparser("print")
|
|
154
|
+
create = parser.add_subparser("create")
|
|
155
|
+
delete = parser.add_subparser("delete")
|
|
156
|
+
reply = parser.add_subparser("reply")
|
|
157
|
+
key = parser.add_subparser("key")
|
|
158
|
+
|
|
159
|
+
start.set_rule(max=0)
|
|
160
|
+
start.add_opt("--auto", default=[0])
|
|
161
|
+
|
|
162
|
+
stop.set_rule(max=0)
|
|
163
|
+
stop.add_opt("--auto", default=[0])
|
|
164
|
+
|
|
165
|
+
lst.set_rule(min=1,max=1)
|
|
166
|
+
|
|
167
|
+
info.set_rule(max=None)
|
|
168
|
+
|
|
169
|
+
prnt.set_rule(max=None)
|
|
170
|
+
|
|
171
|
+
create.set_rule(max=0)
|
|
172
|
+
create.add_opt("--key", required=True, max_appeared=None)
|
|
173
|
+
create.add_opt("--rp", required=True, max_appeared=None)
|
|
174
|
+
|
|
175
|
+
delete.set_rule(max=0)
|
|
176
|
+
delete.add_opt("--key", required=True, max_appeared=None)
|
|
177
|
+
delete.add_opt("--rp", required=True, max_appeared=None)
|
|
178
|
+
|
|
179
|
+
reply.set_rule(min=1,max=1, need_subcmd=True)
|
|
180
|
+
reply_start = reply.add_subparser("start")
|
|
181
|
+
reply_stop = reply.add_subparser("stop")
|
|
182
|
+
reply_modify = reply.add_subparser("modify")
|
|
183
|
+
|
|
184
|
+
key.set_rule(min=1,max=1)
|
|
185
|
+
key.add_opt("-T", required=True, choice=["delmark", "delspace", "upper", "lower"], max_appeared=None)
|
|
186
|
+
key.add_opt("-t", required=True, choice=["delmark", "delspace", "upper", "lower"], max_appeared=None)
|
|
187
|
+
key.add_opt("-D", required=True, max_appeared=None)
|
|
188
|
+
key.add_opt("-d", required=True, max_appeared=None)
|
|
189
|
+
key.add_opt("-K", required=True, max_appeared=None)
|
|
190
|
+
key.add_opt("-k", required=True, max_appeared=None)
|
|
191
|
+
key.add_opt("-R", required=True, max_appeared=None)
|
|
192
|
+
key.add_opt("-r", required=True, max_appeared=None)
|
|
193
|
+
key.add_opt("-m", required=True, choice=["equal", "contain"], default=[""])
|
|
194
|
+
|
|
195
|
+
reply_start.set_rule(max=0)
|
|
196
|
+
reply_start.add_opt("-l", required=True, type=int, default=[1])
|
|
197
|
+
|
|
198
|
+
reply_stop.set_rule(max=0)
|
|
199
|
+
|
|
200
|
+
reply_modify.set_rule(max=0)
|
|
201
|
+
reply_modify.add_opt("--sum", required=True, max_appeared=None)
|
|
202
|
+
reply_modify.add_opt("--st", required=True, type=int, max_appeared=None)
|
|
203
|
+
|
|
204
|
+
return parser
|
|
205
|
+
|
|
206
|
+
async def _guard_state(self, subsubcmd=None):
|
|
207
|
+
if subsubcmd is None:
|
|
208
|
+
return await super()._guard_state()
|
|
209
|
+
if subsubcmd == "start" and self._argv is not None:
|
|
210
|
+
return await super()._guard_state()
|
|
211
|
+
if subsubcmd == "stop" and self._argv is None:
|
|
212
|
+
tip = "错误:会话未开始\n"
|
|
213
|
+
tip += "尚未开始收集自动信息,请先使用 /autoreply value start 开始收集。"
|
|
214
|
+
await self.send_msg(tip)
|
|
215
|
+
self.unlock()
|
|
216
|
+
return False
|
|
217
|
+
if subsubcmd == "stop" and self._rstate != "start":
|
|
218
|
+
return await super()._guard_state()
|
|
219
|
+
return True
|
|
220
|
+
|
|
221
|
+
async def _start(self):
|
|
222
|
+
autoreply_session = BotSession.make("public", "autoreply")
|
|
223
|
+
main_command = BotCommandAutoReplyMain.make(
|
|
224
|
+
self.bot, autoreply_session, _pid=autoreply_session.curpid
|
|
225
|
+
)
|
|
226
|
+
if main_command is None:
|
|
227
|
+
await self.send_msg("警告: 自动回复正在运行中")
|
|
228
|
+
else:
|
|
229
|
+
await main_command.run(Message())
|
|
230
|
+
if self._auto:
|
|
231
|
+
from ruamel.yaml import YAML
|
|
232
|
+
config_path = Path(__file__).parent.parent / "config.yaml"
|
|
233
|
+
config_yaml = YAML()
|
|
234
|
+
data = config_yaml.load(config_path)
|
|
235
|
+
data["if_auto_start_autoreply"] = True
|
|
236
|
+
config_yaml.dump(data, config_path)
|
|
237
|
+
await self.send_msg("自动回复已开启")
|
|
238
|
+
self.unlock()
|
|
239
|
+
|
|
240
|
+
async def _stop(self):
|
|
241
|
+
async def _exe():
|
|
242
|
+
autoreply_session = BotSession.get_obj("public", "autoreply")
|
|
243
|
+
if autoreply_session is None:
|
|
244
|
+
await self.send_msg("警告: 自动回复未开启")
|
|
245
|
+
return
|
|
246
|
+
main_command = autoreply_session.commands.get(autoreply_session.curpid)
|
|
247
|
+
if main_command is None:
|
|
248
|
+
await self.send_msg("警告: 自动回复未开启")
|
|
249
|
+
return
|
|
250
|
+
main_command.unlock()
|
|
251
|
+
|
|
252
|
+
await _exe()
|
|
253
|
+
if self._auto:
|
|
254
|
+
from ruamel.yaml import YAML
|
|
255
|
+
config_path = Path(__file__).parent.parent / "config.yaml"
|
|
256
|
+
config_yaml = YAML()
|
|
257
|
+
data = config_yaml.load(config_path)
|
|
258
|
+
data["if_auto_start_autoreply"] = False
|
|
259
|
+
config_yaml.dump(data, config_path)
|
|
260
|
+
await self.send_msg("自动回复已关闭")
|
|
261
|
+
self.unlock()
|
|
262
|
+
|
|
263
|
+
async def _list(self, tpe: str):
|
|
264
|
+
lst = []
|
|
265
|
+
if tpe == "key":
|
|
266
|
+
lst = self._rule.keys()
|
|
267
|
+
if tpe == "reply":
|
|
268
|
+
lst = self._msg_table.keys()
|
|
269
|
+
await self.send_msg("\n".join(lst))
|
|
270
|
+
self.unlock()
|
|
271
|
+
|
|
272
|
+
async def _info(self, keys: list[str]):
|
|
273
|
+
tip = ""
|
|
274
|
+
for key in keys:
|
|
275
|
+
tip += f"{key}: {self._rule[key] if key in self._rule else '未创建'}\n"
|
|
276
|
+
|
|
277
|
+
await self.send_msg(tip)
|
|
278
|
+
self.unlock()
|
|
279
|
+
|
|
280
|
+
async def _print(self, replys: list[str]):
|
|
281
|
+
for reply in replys:
|
|
282
|
+
await self.send_msg(f"{reply} 内容:")
|
|
283
|
+
if reply not in self._msg_table:
|
|
284
|
+
await self.send_msg(f"警告: {reply} 未被创建")
|
|
285
|
+
continue
|
|
286
|
+
if self._msg_table[reply] == "":
|
|
287
|
+
await self.send_msg(f"警告: {reply} 未被设置")
|
|
288
|
+
continue
|
|
289
|
+
msg_hash = self._msg_table[reply]
|
|
290
|
+
async with autoreply_lock:
|
|
291
|
+
msg = self._database.prepare_send_msg(msg_hash)
|
|
292
|
+
try:
|
|
293
|
+
await self.send_msg(msg)
|
|
294
|
+
except Exception as e:
|
|
295
|
+
await self.send_msg(f"警告: {reply} 发送失败: {e}")
|
|
296
|
+
|
|
297
|
+
self.unlock()
|
|
298
|
+
|
|
299
|
+
async def _create(self):
|
|
300
|
+
async with autoreply_lock:
|
|
301
|
+
for key in self._key:
|
|
302
|
+
if key in self._rule:
|
|
303
|
+
await self.send_msg(f"警告: 键 {key} 已存在")
|
|
304
|
+
continue
|
|
305
|
+
self._set_key(key)
|
|
306
|
+
for reply in self._rp:
|
|
307
|
+
if reply in self._msg_table:
|
|
308
|
+
await self.send_msg(f"警告: 回复 {reply} 已存在")
|
|
309
|
+
continue
|
|
310
|
+
await self._set_reply(reply)
|
|
311
|
+
await self.send_msg("创建成功")
|
|
312
|
+
|
|
313
|
+
self.unlock()
|
|
314
|
+
|
|
315
|
+
async def _delete(self):
|
|
316
|
+
async with autoreply_lock:
|
|
317
|
+
for key in self._key:
|
|
318
|
+
if key not in self._rule:
|
|
319
|
+
await self.send_msg(f"警告: 键 {key} 不存在")
|
|
320
|
+
continue
|
|
321
|
+
self._del_key(key)
|
|
322
|
+
for reply in self._rp:
|
|
323
|
+
if reply not in self._msg_table:
|
|
324
|
+
await self.send_msg(f"警告: 回复 {reply} 不存在")
|
|
325
|
+
continue
|
|
326
|
+
self._del_reply(reply)
|
|
327
|
+
await self.send_msg("删除成功")
|
|
328
|
+
|
|
329
|
+
self.unlock()
|
|
330
|
+
|
|
331
|
+
async def _reply_start(self, reply_name: str):
|
|
332
|
+
if reply_name not in self._msg_table:
|
|
333
|
+
await self.send_msg(f"警告: 回复信息 {reply_name} 不存在")
|
|
334
|
+
return
|
|
335
|
+
self._rstate = "start"
|
|
336
|
+
self._reply_name = reply_name
|
|
337
|
+
await self.send_msg(f"请发送仅包含图片或文字的信息, 或者发送 /autoreply reply {reply_name} stop 停止设置")
|
|
338
|
+
self._is_accept_msg = True
|
|
339
|
+
|
|
340
|
+
async def _reply_stop(self, reply_name: str):
|
|
341
|
+
if reply_name not in self._msg_table:
|
|
342
|
+
await self.send_msg(f"警告: 回复信息 {reply_name} 不存在")
|
|
343
|
+
return
|
|
344
|
+
self._rstate = "stop"
|
|
345
|
+
self._is_accept_msg = False
|
|
346
|
+
async with self._roger_lock:
|
|
347
|
+
if not self._temp_msg:
|
|
348
|
+
await self.send_msg("错误: 未收到任何信息")
|
|
349
|
+
self.unlock()
|
|
350
|
+
return
|
|
351
|
+
async with autoreply_lock:
|
|
352
|
+
await self._set_reply(reply_name)
|
|
353
|
+
self._temp_msg = []
|
|
354
|
+
await self.send_msg("设置成功")
|
|
355
|
+
self.unlock()
|
|
356
|
+
|
|
357
|
+
async def _reply_modify(self, reply_name: str):
|
|
358
|
+
reply_hash = self._msg_table.get(reply_name, "")
|
|
359
|
+
if reply_hash == "":
|
|
360
|
+
await self.send_msg(f"警告: 回复信息 {reply_name} 不存在或未设置")
|
|
361
|
+
return
|
|
362
|
+
async with autoreply_lock:
|
|
363
|
+
self._temp_msg = self._database.get_msg(reply_hash)
|
|
364
|
+
if not self._temp_msg:
|
|
365
|
+
await self.send_msg("错误: 未收到任何信息")
|
|
366
|
+
self.unlock()
|
|
367
|
+
return
|
|
368
|
+
self._temp_msg = modify_msg_data(
|
|
369
|
+
self._temp_msg,
|
|
370
|
+
{
|
|
371
|
+
"summary": DataVariables(self._sum),
|
|
372
|
+
"sub_type": DataVariables(self._st),
|
|
373
|
+
},
|
|
374
|
+
["image"]
|
|
375
|
+
)
|
|
376
|
+
await self._set_reply(reply_name)
|
|
377
|
+
self._temp_msg = []
|
|
378
|
+
await self.send_msg("修改成功")
|
|
379
|
+
self.unlock()
|
|
380
|
+
|
|
381
|
+
async def _key_modify(self, key_name: str):
|
|
382
|
+
if key_name not in self._rule:
|
|
383
|
+
await self.send_msg(f"错误: 键 {key_name} 不存在")
|
|
384
|
+
self.unlock()
|
|
385
|
+
return
|
|
386
|
+
def _rm_dup(lst_A: list, lst_R: list):
|
|
387
|
+
dup = []
|
|
388
|
+
for elem in lst_A:
|
|
389
|
+
if elem in lst_R:
|
|
390
|
+
dup.append(elem)
|
|
391
|
+
for elem in dup:
|
|
392
|
+
lst_A.remove(elem)
|
|
393
|
+
lst_R.remove(elem)
|
|
394
|
+
return dup
|
|
395
|
+
|
|
396
|
+
def _get_lst(lst_S, lst_A, lst_R):
|
|
397
|
+
lst = lst_S[:]
|
|
398
|
+
for elem in lst_A:
|
|
399
|
+
if elem not in lst_R:
|
|
400
|
+
lst.append(elem)
|
|
401
|
+
for elem in lst_R:
|
|
402
|
+
if elem in lst_S:
|
|
403
|
+
lst.remove(elem)
|
|
404
|
+
return lst
|
|
405
|
+
|
|
406
|
+
for (lst_A, lst_R) in [(self._T, self._t), (self._D, self._d), (self._K, self._k), (self._R, self._r)]:
|
|
407
|
+
_rm_dup(lst_A, lst_R)
|
|
408
|
+
for lst in [self._R, self._r]:
|
|
409
|
+
rem = []
|
|
410
|
+
for elem in lst:
|
|
411
|
+
if self._msg_table.get(elem, "") == "":
|
|
412
|
+
await self.send_msg(f"警告: 回复信息 {elem} 不存在或未设置")
|
|
413
|
+
rem.append(elem)
|
|
414
|
+
for elem in rem:
|
|
415
|
+
lst.remove(elem)
|
|
416
|
+
data = []
|
|
417
|
+
for (lst_S, lst_A, lst_R) in [(self._rule[key_name]["trans"], self._T, self._t), (self._rule[key_name]["dels"], self._D, self._d), (self._rule[key_name]["keywords"], self._K, self._k), (self._rule[key_name]["replys"], self._R, self._r)]:
|
|
418
|
+
data.append(_get_lst(lst_S, lst_A, lst_R))
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
_m = None
|
|
422
|
+
if self._m == 0:
|
|
423
|
+
_m = False
|
|
424
|
+
elif self._m == 1:
|
|
425
|
+
_m = True
|
|
426
|
+
|
|
427
|
+
async with autoreply_lock:
|
|
428
|
+
self._set_key(
|
|
429
|
+
key_name,
|
|
430
|
+
trans=data[0],
|
|
431
|
+
dels=data[1],
|
|
432
|
+
is_contain=_m,
|
|
433
|
+
keywords=data[2],
|
|
434
|
+
replys=data[3]
|
|
435
|
+
)
|
|
436
|
+
await self.send_msg("修改成功")
|
|
437
|
+
self.unlock()
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
async def roger(self, event: MessageEvent):
|
|
442
|
+
async with self._roger_lock:
|
|
443
|
+
if not self._is_accept_msg:
|
|
444
|
+
return
|
|
445
|
+
if self._l <= 0:
|
|
446
|
+
return
|
|
447
|
+
dumped_msg = await dump_message(self.bot, event.message)
|
|
448
|
+
self._temp_msg.extend(dumped_msg)
|
|
449
|
+
self._l -= 1
|
|
450
|
+
if self._l <= 0:
|
|
451
|
+
reply_name = getattr(self, "_reply_name", None)
|
|
452
|
+
if reply_name is None:
|
|
453
|
+
raise ValueError
|
|
454
|
+
asyncio.create_task(self.run(Message(f"reply {reply_name} stop")))
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
async def run(self, args: Message):
|
|
459
|
+
async with autoreply_lock:
|
|
460
|
+
self._database = BotMsgDataBase(self._autoreply_dir)
|
|
461
|
+
self._rule_path.touch(exist_ok=True)
|
|
462
|
+
self._msg_table_path.touch(exist_ok=True)
|
|
463
|
+
self._load_meta()
|
|
464
|
+
|
|
465
|
+
if not self.session:
|
|
466
|
+
return
|
|
467
|
+
new_argv = args.extract_plain_text().strip().split()
|
|
468
|
+
|
|
469
|
+
if not await self._legal_case(new_argv):
|
|
470
|
+
if self._argv is None:
|
|
471
|
+
self.unlock()
|
|
472
|
+
return
|
|
473
|
+
subsubcmd = None
|
|
474
|
+
self._parser.parse_argv(new_argv)
|
|
475
|
+
subcmd = self._parser.subcmd
|
|
476
|
+
if subcmd is None:
|
|
477
|
+
self.unlock()
|
|
478
|
+
return
|
|
479
|
+
subparser = self._parser.subparsers[subcmd]
|
|
480
|
+
subsubcmd = subparser.subcmd
|
|
481
|
+
|
|
482
|
+
if not await self._guard_state(subsubcmd):
|
|
483
|
+
return
|
|
484
|
+
|
|
485
|
+
if not self._check_perm("autoreplymanager"):
|
|
486
|
+
await self.send_msg("权限不足")
|
|
487
|
+
self.unlock()
|
|
488
|
+
return
|
|
489
|
+
|
|
490
|
+
self._argv = new_argv
|
|
491
|
+
|
|
492
|
+
_auto = subparser.opts_value.get("--auto", [0])[0]
|
|
493
|
+
self._auto = _auto >= 1
|
|
494
|
+
|
|
495
|
+
self._key = subparser.opts_value.get("--key", [])
|
|
496
|
+
self._rp = subparser.opts_value.get("--rp", [])
|
|
497
|
+
|
|
498
|
+
self._T = subparser.opts_value.get("-T", [])
|
|
499
|
+
self._t = subparser.opts_value.get("-t", [])
|
|
500
|
+
self._D = subparser.opts_value.get("-D", [])
|
|
501
|
+
self._d = subparser.opts_value.get("-d", [])
|
|
502
|
+
self._K = subparser.opts_value.get("-K", [])
|
|
503
|
+
self._k = subparser.opts_value.get("-k", [])
|
|
504
|
+
self._R = subparser.opts_value.get("-R", [])
|
|
505
|
+
self._r = subparser.opts_value.get("-r", [])
|
|
506
|
+
_ms = subparser.opts_value.get("-m", [""])
|
|
507
|
+
if _ms[0] == "equal":
|
|
508
|
+
self._m = 0
|
|
509
|
+
if _ms[0] == "contain":
|
|
510
|
+
self._m = 1
|
|
511
|
+
|
|
512
|
+
if subsubcmd is not None:
|
|
513
|
+
subsubparser = subparser.subparsers[subsubcmd]
|
|
514
|
+
self._l = subsubparser.opts_value.get("-l", [1])[0]
|
|
515
|
+
self._l = max(1, self._l)
|
|
516
|
+
self._sum = subsubparser.opts_value.get("--sum", [])
|
|
517
|
+
self._st = subsubparser.opts_value.get("--st", [])
|
|
518
|
+
self._rstate = subsubcmd if subsubcmd != "modify" else ""
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
if subcmd == "start":
|
|
522
|
+
await self._start()
|
|
523
|
+
if subcmd == "stop":
|
|
524
|
+
await self._stop()
|
|
525
|
+
if subcmd == "list":
|
|
526
|
+
lst = subparser.value[0]
|
|
527
|
+
await self._list(lst)
|
|
528
|
+
if subcmd == "info":
|
|
529
|
+
await self._info(subparser.value)
|
|
530
|
+
if subcmd == "print":
|
|
531
|
+
await self._print(subparser.value)
|
|
532
|
+
if subcmd == "create":
|
|
533
|
+
await self._create()
|
|
534
|
+
if subcmd == "delete":
|
|
535
|
+
await self._delete()
|
|
536
|
+
reply_name = ""
|
|
537
|
+
if subcmd == "reply":
|
|
538
|
+
reply_name = subparser.value[0]
|
|
539
|
+
if subsubcmd == "start":
|
|
540
|
+
await self._reply_start(reply_name)
|
|
541
|
+
if subsubcmd == "stop":
|
|
542
|
+
await self._reply_stop(reply_name)
|
|
543
|
+
if subsubcmd == "modify":
|
|
544
|
+
await self._reply_modify(reply_name)
|
|
545
|
+
if subcmd == "key":
|
|
546
|
+
key_name = subparser.value[0]
|
|
547
|
+
await self._key_modify(key_name)
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
|