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.
Files changed (55) hide show
  1. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/PKG-INFO +1 -1
  2. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/README.md +25 -12
  3. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/aux.py +10 -0
  4. nonebot_plugin_shitbot-0.2/nonebot_plugin_shitbot/commands/autoreply_cmd.py +550 -0
  5. nonebot_plugin_shitbot-0.2/nonebot_plugin_shitbot/commands/autoreply_main_cmd.py +99 -0
  6. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/convert_cmd.py +2 -2
  7. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/help_cmd.py +3 -1
  8. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/shitpost_cmd.py +3 -1
  9. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/config.py +5 -0
  10. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/config.yaml +1 -0
  11. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/default_config.yaml +7 -6
  12. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/handlers.py +7 -3
  13. nonebot_plugin_shitbot-0.2/nonebot_plugin_shitbot/msgdatabase.py +323 -0
  14. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/msgutils.py +59 -19
  15. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot.egg-info/PKG-INFO +1 -1
  16. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot.egg-info/SOURCES.txt +1 -14
  17. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/pyproject.toml +1 -1
  18. nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/commands/autoreply_cmd.py +0 -486
  19. nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/commands/autoreply_main_cmd.py +0 -105
  20. nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/advrandpic.md +0 -61
  21. nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/convert.md +0 -37
  22. nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/help.md +0 -17
  23. nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/imgs/md2picex1.png +0 -0
  24. nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/imgs/md2picex2.png +0 -0
  25. nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/index.md +0 -22
  26. nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/md2pic.md +0 -57
  27. nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/perm.md +0 -110
  28. nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/pixiv.md +0 -42
  29. nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/randpic.md +0 -34
  30. nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/session.md +0 -38
  31. nonebot_plugin_shitbot-0.1.2rc1/nonebot_plugin_shitbot/docs/help/shitpost.md +0 -39
  32. 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
  33. 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
  34. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/LICENSE +0 -0
  35. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/__init__.py +0 -0
  36. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/command.py +0 -0
  37. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/__init__.py +0 -0
  38. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/advrandpic_cmd.py +0 -0
  39. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/md2pic_cmd.py +0 -0
  40. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/perm_cmd.py +0 -0
  41. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/pixiv_cmd.py +0 -0
  42. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/randpic_cmd.py +0 -0
  43. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/commands/session_cmd.py +0 -0
  44. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/css/github-markdown-dark-dimmed.css +0 -0
  45. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/parser.py +0 -0
  46. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/permissions.py +0 -0
  47. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/scripts/p2png.sh +0 -0
  48. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/scripts/png2fr.sh +0 -0
  49. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/scripts/png2v.sh +0 -0
  50. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/session.py +0 -0
  51. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot/tasks.py +0 -0
  52. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot.egg-info/dependency_links.txt +0 -0
  53. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot.egg-info/requires.txt +0 -0
  54. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/nonebot_plugin_shitbot.egg-info/top_level.txt +0 -0
  55. {nonebot_plugin_shitbot-0.1.2rc1 → nonebot_plugin_shitbot-0.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nonebot-plugin-shitbot
3
- Version: 0.1.2rc1
3
+ Version: 0.2
4
4
  Summary: NoneBot shitpost plugin
5
5
  Requires-Python: >=3.10
6
6
  License-File: LICENSE
@@ -16,25 +16,38 @@
16
16
 
17
17
  - 用户多命令管理
18
18
 
19
+ - 参数解析(见docs/参数解析.md)
20
+
19
21
  - 权限管理系统(见docs/权限系统.md)
20
22
 
21
- ## TODO List(大致按优先级排序 但随缘想先解决哪个就解决哪个)
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
- - 为代码添加更多注释 并按照nonebot所说的插件规范修改代码(?)
44
+ - 为代码添加更多注释和写文档 (依旧技术债务 依旧最烦 依旧随缘)
32
45
 
33
- - 给史搭一个数据库存起来管理 智能化/shitpost
34
- - 提一嘴我打算怎么搭建数据库吧 将史的message键保存为json 如果是forward类型的则将其转换为node列表后打包为{"type": forward, "data": {"content": [node]}} 将json中提及的富媒体信息全部下载后重命名为其0xsha256sum值保存 并将json富媒体信息的url替换为对应的本地路径 最后将json重命名为其0xsha256sum值 这样无论是富媒体还是代表史的json文件都可以通过计算哈希值与已有的文件名比对来去重
35
- - 添加一个记录json文件元数据的json文件(这算不算元元数据?)类型大概是Dict[str, Dict[str, Any]] 键是史的文件名 也就是哈希值 值中保存着史的:real_id列表 时间戳 好评数 发送失败次数 存储着已经发送过该史的群号的列表
46
+ - 智能化/shitpost (鉴于数据库搓好了 如果我后面认为这个写起来也不难的话我就来搓)
47
+ - 使用用上面提到的数据库
48
+ - 添加一个元信息映射表(yaml) 类型大概是Dict[str, Dict[str, Any]] 键是史的文件名 也就是哈希值 值中保存着史的:real_id列表 时间戳 好评数 发送失败次数 存储着已经发送过该史的群号的列表
36
49
 
37
- - 添加一个json文件用于记录各个real_id对应的史的哈希值
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
 
@@ -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
+