nonebot-plugin-shitbot 0.2__tar.gz → 0.3.1__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.2 → nonebot_plugin_shitbot-0.3.1}/PKG-INFO +3 -2
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/README.md +11 -9
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/__init__.py +4 -0
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/commands/autoreply_cmd.py +55 -38
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/commands/autoreply_main_cmd.py +2 -1
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/commands/convert_cmd.py +70 -28
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/commands/pixiv_cmd.py +1 -1
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/commands/shitpost_cmd.py +2 -2
- nonebot_plugin_shitbot-0.3.1/nonebot_plugin_shitbot/config.py +246 -0
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/handlers.py +6 -2
- nonebot_plugin_shitbot-0.3.1/nonebot_plugin_shitbot/help/advrandpic.md +61 -0
- nonebot_plugin_shitbot-0.3.1/nonebot_plugin_shitbot/help/autoreply.md +165 -0
- nonebot_plugin_shitbot-0.3.1/nonebot_plugin_shitbot/help/convert.md +37 -0
- nonebot_plugin_shitbot-0.3.1/nonebot_plugin_shitbot/help/help.md +17 -0
- nonebot_plugin_shitbot-0.3.1/nonebot_plugin_shitbot/help/imgs/md2picex1.png +0 -0
- nonebot_plugin_shitbot-0.3.1/nonebot_plugin_shitbot/help/imgs/md2picex2.png +0 -0
- nonebot_plugin_shitbot-0.3.1/nonebot_plugin_shitbot/help/index.md +23 -0
- nonebot_plugin_shitbot-0.3.1/nonebot_plugin_shitbot/help/md2pic.md +57 -0
- nonebot_plugin_shitbot-0.3.1/nonebot_plugin_shitbot/help/perm.md +110 -0
- nonebot_plugin_shitbot-0.3.1/nonebot_plugin_shitbot/help/pixiv.md +42 -0
- nonebot_plugin_shitbot-0.3.1/nonebot_plugin_shitbot/help/randpic.md +34 -0
- nonebot_plugin_shitbot-0.3.1/nonebot_plugin_shitbot/help/session.md +38 -0
- nonebot_plugin_shitbot-0.3.1/nonebot_plugin_shitbot/help/shitpost.md +39 -0
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/msgdatabase.py +3 -3
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/msgutils.py +2 -2
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/permissions.py +31 -20
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot.egg-info/PKG-INFO +3 -2
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot.egg-info/SOURCES.txt +13 -5
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot.egg-info/requires.txt +2 -1
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/pyproject.toml +4 -4
- nonebot_plugin_shitbot-0.2/nonebot_plugin_shitbot/config.py +0 -191
- nonebot_plugin_shitbot-0.2/nonebot_plugin_shitbot/config.yaml +0 -26
- nonebot_plugin_shitbot-0.2/nonebot_plugin_shitbot/default_config.yaml +0 -24
- nonebot_plugin_shitbot-0.2/nonebot_plugin_shitbot/scripts/p2png.sh +0 -38
- nonebot_plugin_shitbot-0.2/nonebot_plugin_shitbot/scripts/png2fr.sh +0 -19
- nonebot_plugin_shitbot-0.2/nonebot_plugin_shitbot/scripts/png2v.sh +0 -24
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/LICENSE +0 -0
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/aux.py +0 -0
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/command.py +0 -0
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/commands/__init__.py +0 -0
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/commands/advrandpic_cmd.py +0 -0
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/commands/help_cmd.py +0 -0
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/commands/md2pic_cmd.py +0 -0
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/commands/perm_cmd.py +0 -0
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/commands/randpic_cmd.py +0 -0
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/commands/session_cmd.py +0 -0
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/css/github-markdown-dark-dimmed.css +0 -0
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/parser.py +0 -0
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/session.py +0 -0
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/tasks.py +0 -0
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot.egg-info/dependency_links.txt +0 -0
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot.egg-info/top_level.txt +0 -0
- {nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nonebot-plugin-shitbot
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: NoneBot shitpost plugin
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
License-File: LICENSE
|
|
@@ -11,7 +11,8 @@ Requires-Dist: nonebot-plugin-localstore>=0.3.0
|
|
|
11
11
|
Requires-Dist: PixivPy-Async>=1.2.14
|
|
12
12
|
Requires-Dist: Pillow>=12.2.0
|
|
13
13
|
Requires-Dist: httpx>=0.28.1
|
|
14
|
-
Requires-Dist:
|
|
14
|
+
Requires-Dist: python-magic>=0.4
|
|
15
|
+
Requires-Dist: pydantic>=2
|
|
15
16
|
Requires-Dist: PyYAML>=6.0.3
|
|
16
17
|
Requires-Dist: ruamel.yaml>=0.18.0
|
|
17
18
|
Dynamic: license-file
|
|
@@ -61,8 +61,7 @@
|
|
|
61
61
|
|
|
62
62
|
## 配置
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
之后编辑 `config.yaml` 即可。
|
|
64
|
+
插件配置通过 NoneBot 标准的 dotenv / 环境变量读取。
|
|
66
65
|
|
|
67
66
|
### 安装
|
|
68
67
|
|
|
@@ -70,14 +69,17 @@
|
|
|
70
69
|
nb plugin install nonebot-plugin-shitbot
|
|
71
70
|
```
|
|
72
71
|
|
|
73
|
-
###
|
|
72
|
+
### 环境变量
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
|
78
|
-
|
|
79
|
-
| `
|
|
80
|
-
| `
|
|
74
|
+
在 NoneBot 项目的 `.env` 文件中配置,变量如下:
|
|
75
|
+
|
|
76
|
+
| 配置项 | 类型 | 默认值 | 是否必填 | 说明 |
|
|
77
|
+
|---|---|---|---|---|
|
|
78
|
+
| `SHITBOT__CLIENT_CACHE` | 路径 | 本地缓存路径 | 否 | 容器端缓存路径。本地缓存路径为 `$LOCALSTORE_CACHE_DIR/nonebot_plugin_shitbot`,容器端需对应挂载 |
|
|
79
|
+
| `SHITBOT__CLIENT_DATA` | 路径 | 本地数据路径 | 否 | 容器端数据路径。本地数据路径为 `$LOCALSTORE_DATA_DIR/nonebot_plugin_shitbot`,容器端需对应挂载 |
|
|
80
|
+
| `SHITBOT__OWNERS` | JSON 字符串列表 | None | 是 | 机器人所有者 QQ 号列表,如 `'["114514"]'` |
|
|
81
|
+
| `SHITBOT__MAX_MESSAGE_DEPTH` | 整数 | `16` | 否 | 消息解析最大递归深度 |
|
|
82
|
+
| `SHITBOT__PIXIV_ACCESS_TOKEN` | 字符串 | "" | 部分必填 | Pixiv API 访问令牌(使用 `/pixiv` 时必填) |
|
|
81
83
|
|
|
82
84
|
### 触发规则
|
|
83
85
|
|
{nonebot_plugin_shitbot-0.2 → nonebot_plugin_shitbot-0.3.1}/nonebot_plugin_shitbot/__init__.py
RENAMED
|
@@ -5,11 +5,15 @@ from os import environ
|
|
|
5
5
|
# (e.g. nonebot_plugin_htmlrender → get_driver()). Pure-logic modules
|
|
6
6
|
# like parser.py should remain importable without a running NoneBot.
|
|
7
7
|
if not environ.get("PYTEST_RUNNING"):
|
|
8
|
+
from nonebot import require
|
|
8
9
|
from nonebot.plugin import PluginMetadata
|
|
9
10
|
|
|
10
11
|
from . import handlers # noqa: F401
|
|
11
12
|
from .config import ShitBotConfig
|
|
12
13
|
|
|
14
|
+
require("nonebot_plugin_htmlrender")
|
|
15
|
+
require("nonebot_plugin_localstore")
|
|
16
|
+
|
|
13
17
|
__plugin_meta__ = PluginMetadata(
|
|
14
18
|
name="shitbot",
|
|
15
19
|
description="个人自用自己手搓框架的多功能bot, 还在开发中",
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import yaml
|
|
3
|
-
|
|
4
|
-
from pathlib import Path
|
|
5
2
|
|
|
3
|
+
import yaml
|
|
6
4
|
from nonebot.adapters.onebot.v11 import Bot, Message, MessageEvent
|
|
7
5
|
|
|
8
6
|
from ..command import BotCommand
|
|
9
|
-
from ..session import BotSession
|
|
10
|
-
from ..parser import BotArgParser
|
|
11
7
|
from ..msgdatabase import BotMsgDataBase
|
|
12
|
-
from ..msgutils import
|
|
8
|
+
from ..msgutils import (
|
|
9
|
+
DataVariables,
|
|
10
|
+
DumpedMsg,
|
|
11
|
+
DumpedSeg,
|
|
12
|
+
dump_message,
|
|
13
|
+
modify_msg_data,
|
|
14
|
+
msg_filter,
|
|
15
|
+
)
|
|
16
|
+
from ..parser import BotArgParser
|
|
17
|
+
from ..session import BotSession
|
|
13
18
|
from ..tasks import autoreply_lock
|
|
14
19
|
from .autoreply_main_cmd import BotCommandAutoReplyMain
|
|
15
20
|
|
|
@@ -17,6 +22,7 @@ from .autoreply_main_cmd import BotCommandAutoReplyMain
|
|
|
17
22
|
class BotCommandAutoreply(BotCommand):
|
|
18
23
|
_name = "autoreply"
|
|
19
24
|
_autoreply_dir = BotCommandAutoReplyMain._autoreply_dir
|
|
25
|
+
_state_path = BotCommandAutoReplyMain._state_path
|
|
20
26
|
_rule_path = BotCommandAutoReplyMain._rule_path
|
|
21
27
|
_msg_table_path = BotCommandAutoReplyMain._msg_table_path
|
|
22
28
|
|
|
@@ -33,6 +39,8 @@ class BotCommandAutoreply(BotCommand):
|
|
|
33
39
|
self._rp: list[str] = []
|
|
34
40
|
self._T: list[str] = []
|
|
35
41
|
self._t: list[str] = []
|
|
42
|
+
self._D: list[str] = []
|
|
43
|
+
self._d: list[str] = []
|
|
36
44
|
self._K: list[str] = []
|
|
37
45
|
self._k: list[str] = []
|
|
38
46
|
self._R: list[str] = []
|
|
@@ -162,7 +170,7 @@ class BotCommandAutoreply(BotCommand):
|
|
|
162
170
|
stop.set_rule(max=0)
|
|
163
171
|
stop.add_opt("--auto", default=[0])
|
|
164
172
|
|
|
165
|
-
lst.set_rule(min=1,max=1)
|
|
173
|
+
lst.set_rule(min=1, max=1)
|
|
166
174
|
|
|
167
175
|
info.set_rule(max=None)
|
|
168
176
|
|
|
@@ -176,14 +184,24 @@ class BotCommandAutoreply(BotCommand):
|
|
|
176
184
|
delete.add_opt("--key", required=True, max_appeared=None)
|
|
177
185
|
delete.add_opt("--rp", required=True, max_appeared=None)
|
|
178
186
|
|
|
179
|
-
reply.set_rule(min=1,max=1, need_subcmd=True)
|
|
187
|
+
reply.set_rule(min=1, max=1, need_subcmd=True)
|
|
180
188
|
reply_start = reply.add_subparser("start")
|
|
181
189
|
reply_stop = reply.add_subparser("stop")
|
|
182
190
|
reply_modify = reply.add_subparser("modify")
|
|
183
191
|
|
|
184
|
-
key.set_rule(min=1,max=1)
|
|
185
|
-
key.add_opt(
|
|
186
|
-
|
|
192
|
+
key.set_rule(min=1, max=1)
|
|
193
|
+
key.add_opt(
|
|
194
|
+
"-T",
|
|
195
|
+
required=True,
|
|
196
|
+
choice=["delmark", "delspace", "upper", "lower"],
|
|
197
|
+
max_appeared=None,
|
|
198
|
+
)
|
|
199
|
+
key.add_opt(
|
|
200
|
+
"-t",
|
|
201
|
+
required=True,
|
|
202
|
+
choice=["delmark", "delspace", "upper", "lower"],
|
|
203
|
+
max_appeared=None,
|
|
204
|
+
)
|
|
187
205
|
key.add_opt("-D", required=True, max_appeared=None)
|
|
188
206
|
key.add_opt("-d", required=True, max_appeared=None)
|
|
189
207
|
key.add_opt("-K", required=True, max_appeared=None)
|
|
@@ -217,7 +235,7 @@ class BotCommandAutoreply(BotCommand):
|
|
|
217
235
|
if subsubcmd == "stop" and self._rstate != "start":
|
|
218
236
|
return await super()._guard_state()
|
|
219
237
|
return True
|
|
220
|
-
|
|
238
|
+
|
|
221
239
|
async def _start(self):
|
|
222
240
|
autoreply_session = BotSession.make("public", "autoreply")
|
|
223
241
|
main_command = BotCommandAutoReplyMain.make(
|
|
@@ -228,12 +246,9 @@ class BotCommandAutoreply(BotCommand):
|
|
|
228
246
|
else:
|
|
229
247
|
await main_command.run(Message())
|
|
230
248
|
if self._auto:
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
data = config_yaml.load(config_path)
|
|
235
|
-
data["if_auto_start_autoreply"] = True
|
|
236
|
-
config_yaml.dump(data, config_path)
|
|
249
|
+
self._state_path.write_text(
|
|
250
|
+
yaml.safe_dump(True, allow_unicode=True), encoding="utf-8"
|
|
251
|
+
)
|
|
237
252
|
await self.send_msg("自动回复已开启")
|
|
238
253
|
self.unlock()
|
|
239
254
|
|
|
@@ -251,12 +266,9 @@ class BotCommandAutoreply(BotCommand):
|
|
|
251
266
|
|
|
252
267
|
await _exe()
|
|
253
268
|
if self._auto:
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
data = config_yaml.load(config_path)
|
|
258
|
-
data["if_auto_start_autoreply"] = False
|
|
259
|
-
config_yaml.dump(data, config_path)
|
|
269
|
+
self._state_path.write_text(
|
|
270
|
+
yaml.safe_dump(False, allow_unicode=True), encoding="utf-8"
|
|
271
|
+
)
|
|
260
272
|
await self.send_msg("自动回复已关闭")
|
|
261
273
|
self.unlock()
|
|
262
274
|
|
|
@@ -334,7 +346,9 @@ class BotCommandAutoreply(BotCommand):
|
|
|
334
346
|
return
|
|
335
347
|
self._rstate = "start"
|
|
336
348
|
self._reply_name = reply_name
|
|
337
|
-
await self.send_msg(
|
|
349
|
+
await self.send_msg(
|
|
350
|
+
f"请发送仅包含图片或文字的信息, 或者发送 /autoreply reply {reply_name} stop 停止设置"
|
|
351
|
+
)
|
|
338
352
|
self._is_accept_msg = True
|
|
339
353
|
|
|
340
354
|
async def _reply_stop(self, reply_name: str):
|
|
@@ -371,7 +385,7 @@ class BotCommandAutoreply(BotCommand):
|
|
|
371
385
|
"summary": DataVariables(self._sum),
|
|
372
386
|
"sub_type": DataVariables(self._st),
|
|
373
387
|
},
|
|
374
|
-
["image"]
|
|
388
|
+
["image"],
|
|
375
389
|
)
|
|
376
390
|
await self._set_reply(reply_name)
|
|
377
391
|
self._temp_msg = []
|
|
@@ -383,6 +397,7 @@ class BotCommandAutoreply(BotCommand):
|
|
|
383
397
|
await self.send_msg(f"错误: 键 {key_name} 不存在")
|
|
384
398
|
self.unlock()
|
|
385
399
|
return
|
|
400
|
+
|
|
386
401
|
def _rm_dup(lst_A: list, lst_R: list):
|
|
387
402
|
dup = []
|
|
388
403
|
for elem in lst_A:
|
|
@@ -403,7 +418,12 @@ class BotCommandAutoreply(BotCommand):
|
|
|
403
418
|
lst.remove(elem)
|
|
404
419
|
return lst
|
|
405
420
|
|
|
406
|
-
for
|
|
421
|
+
for lst_A, lst_R in [
|
|
422
|
+
(self._T, self._t),
|
|
423
|
+
(self._D, self._d),
|
|
424
|
+
(self._K, self._k),
|
|
425
|
+
(self._R, self._r),
|
|
426
|
+
]:
|
|
407
427
|
_rm_dup(lst_A, lst_R)
|
|
408
428
|
for lst in [self._R, self._r]:
|
|
409
429
|
rem = []
|
|
@@ -414,9 +434,13 @@ class BotCommandAutoreply(BotCommand):
|
|
|
414
434
|
for elem in rem:
|
|
415
435
|
lst.remove(elem)
|
|
416
436
|
data = []
|
|
417
|
-
for
|
|
437
|
+
for lst_S, lst_A, lst_R in [
|
|
438
|
+
(self._rule[key_name]["trans"], self._T, self._t),
|
|
439
|
+
(self._rule[key_name]["dels"], self._D, self._d),
|
|
440
|
+
(self._rule[key_name]["keywords"], self._K, self._k),
|
|
441
|
+
(self._rule[key_name]["replys"], self._R, self._r),
|
|
442
|
+
]:
|
|
418
443
|
data.append(_get_lst(lst_S, lst_A, lst_R))
|
|
419
|
-
|
|
420
444
|
|
|
421
445
|
_m = None
|
|
422
446
|
if self._m == 0:
|
|
@@ -431,13 +455,11 @@ class BotCommandAutoreply(BotCommand):
|
|
|
431
455
|
dels=data[1],
|
|
432
456
|
is_contain=_m,
|
|
433
457
|
keywords=data[2],
|
|
434
|
-
replys=data[3]
|
|
458
|
+
replys=data[3],
|
|
435
459
|
)
|
|
436
460
|
await self.send_msg("修改成功")
|
|
437
461
|
self.unlock()
|
|
438
462
|
|
|
439
|
-
|
|
440
|
-
|
|
441
463
|
async def roger(self, event: MessageEvent):
|
|
442
464
|
async with self._roger_lock:
|
|
443
465
|
if not self._is_accept_msg:
|
|
@@ -453,11 +475,10 @@ class BotCommandAutoreply(BotCommand):
|
|
|
453
475
|
raise ValueError
|
|
454
476
|
asyncio.create_task(self.run(Message(f"reply {reply_name} stop")))
|
|
455
477
|
|
|
456
|
-
|
|
457
|
-
|
|
458
478
|
async def run(self, args: Message):
|
|
459
479
|
async with autoreply_lock:
|
|
460
480
|
self._database = BotMsgDataBase(self._autoreply_dir)
|
|
481
|
+
self._state_path.touch(exist_ok=True)
|
|
461
482
|
self._rule_path.touch(exist_ok=True)
|
|
462
483
|
self._msg_table_path.touch(exist_ok=True)
|
|
463
484
|
self._load_meta()
|
|
@@ -517,7 +538,6 @@ class BotCommandAutoreply(BotCommand):
|
|
|
517
538
|
self._st = subsubparser.opts_value.get("--st", [])
|
|
518
539
|
self._rstate = subsubcmd if subsubcmd != "modify" else ""
|
|
519
540
|
|
|
520
|
-
|
|
521
541
|
if subcmd == "start":
|
|
522
542
|
await self._start()
|
|
523
543
|
if subcmd == "stop":
|
|
@@ -545,6 +565,3 @@ class BotCommandAutoreply(BotCommand):
|
|
|
545
565
|
if subcmd == "key":
|
|
546
566
|
key_name = subparser.value[0]
|
|
547
567
|
await self._key_modify(key_name)
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import random
|
|
2
|
-
import re
|
|
3
2
|
import unicodedata
|
|
4
3
|
from typing import Any
|
|
5
4
|
|
|
@@ -25,6 +24,7 @@ _TERM_SCHEMA = {
|
|
|
25
24
|
class BotCommandAutoReplyMain(BotCommand):
|
|
26
25
|
_name = "autoreply_main_rec"
|
|
27
26
|
_autoreply_dir = config.data / "autoreply"
|
|
27
|
+
_state_path = _autoreply_dir / "state.yaml"
|
|
28
28
|
_rule_path = _autoreply_dir / "rule.yaml"
|
|
29
29
|
_msg_table_path = _autoreply_dir / "msg_table.yaml"
|
|
30
30
|
_rule: dict[str, dict[str, Any]]
|
|
@@ -94,6 +94,7 @@ class BotCommandAutoReplyMain(BotCommand):
|
|
|
94
94
|
random.seed()
|
|
95
95
|
async with autoreply_lock:
|
|
96
96
|
self._database = BotMsgDataBase(self._autoreply_dir)
|
|
97
|
+
self._state_path.touch(exist_ok=True)
|
|
97
98
|
self._rule_path.touch(exist_ok=True)
|
|
98
99
|
self._msg_table_path.touch(exist_ok=True)
|
|
99
100
|
self.load_meta()
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import random
|
|
4
5
|
import shutil
|
|
5
6
|
import uuid
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from typing import TYPE_CHECKING
|
|
8
9
|
|
|
9
10
|
import httpx
|
|
11
|
+
import magic
|
|
10
12
|
from nonebot.adapters.onebot.v11 import Bot, Message, MessageEvent, MessageSegment
|
|
11
13
|
from nonebot.log import logger
|
|
12
14
|
|
|
@@ -20,7 +22,7 @@ from ..tasks import EndOfQueue, prod_cons
|
|
|
20
22
|
if TYPE_CHECKING:
|
|
21
23
|
from ..session import BotSession
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
ffmpeg = shutil.which("ffmpeg") or "ffmpeg"
|
|
24
26
|
|
|
25
27
|
|
|
26
28
|
class BotCommandConvert(BotCommand):
|
|
@@ -94,26 +96,40 @@ class BotCommandConvert(BotCommand):
|
|
|
94
96
|
if download_path == "":
|
|
95
97
|
return ""
|
|
96
98
|
png_path = png_dir / f"{uuid.uuid4().hex}.png"
|
|
99
|
+
mime = magic.from_file(download_path, mime=True)
|
|
97
100
|
logger.info(
|
|
98
|
-
f"
|
|
101
|
+
f"检测文件类型: {mime}, 执行图片转 PNG: {download_path} -> {png_path}"
|
|
99
102
|
)
|
|
103
|
+
if mime == "image/png":
|
|
104
|
+
shutil.copy(download_path, png_path)
|
|
105
|
+
logger.info(f"转换 PNG 成功: {png_path}")
|
|
106
|
+
return str(png_path)
|
|
100
107
|
proc = await asyncio.create_subprocess_exec(
|
|
101
|
-
|
|
102
|
-
|
|
108
|
+
ffmpeg,
|
|
109
|
+
"-y",
|
|
110
|
+
"-i",
|
|
103
111
|
str(download_path),
|
|
112
|
+
"-vcodec",
|
|
113
|
+
"png",
|
|
114
|
+
"-pix_fmt",
|
|
115
|
+
"rgba",
|
|
116
|
+
"-update",
|
|
117
|
+
"1",
|
|
104
118
|
str(png_path),
|
|
105
119
|
stdout=asyncio.subprocess.PIPE,
|
|
106
120
|
stderr=asyncio.subprocess.PIPE,
|
|
107
121
|
)
|
|
108
122
|
stdout, stderr = await proc.communicate()
|
|
109
123
|
if stdout:
|
|
110
|
-
logger.info(f"
|
|
124
|
+
logger.info(f"ffmpeg stdout:\n{stdout.decode()}")
|
|
111
125
|
if stderr:
|
|
112
|
-
logger.warning(f"
|
|
126
|
+
logger.warning(f"ffmpeg stderr:\n{stderr.decode()}")
|
|
113
127
|
if proc.returncode != 0:
|
|
114
|
-
logger.error(
|
|
128
|
+
logger.error(
|
|
129
|
+
f"转换 PNG 失败 (returncode={proc.returncode}): {stderr.decode()[:] if stderr else ''}"
|
|
130
|
+
)
|
|
115
131
|
return ""
|
|
116
|
-
logger.info(f"
|
|
132
|
+
logger.info(f"转换 PNG 成功: {png_path}")
|
|
117
133
|
return str(png_path)
|
|
118
134
|
|
|
119
135
|
@staticmethod
|
|
@@ -121,24 +137,47 @@ class BotCommandConvert(BotCommand):
|
|
|
121
137
|
if png_path == "":
|
|
122
138
|
return ""
|
|
123
139
|
video_path = video_dir / f"{uuid.uuid4().hex}.mp4"
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
)
|
|
140
|
+
duration = round(random.uniform(1, 5), 3)
|
|
141
|
+
logger.info(f"执行图片转视频 (ffmpeg, {duration}s): {png_path} -> {video_path}")
|
|
127
142
|
proc = await asyncio.create_subprocess_exec(
|
|
128
|
-
|
|
129
|
-
|
|
143
|
+
ffmpeg,
|
|
144
|
+
"-y",
|
|
145
|
+
"-loop",
|
|
146
|
+
"1",
|
|
147
|
+
"-i",
|
|
130
148
|
str(png_path),
|
|
149
|
+
"-f",
|
|
150
|
+
"lavfi",
|
|
151
|
+
"-i",
|
|
152
|
+
"anullsrc",
|
|
153
|
+
"-c:v",
|
|
154
|
+
"libx264",
|
|
155
|
+
"-crf",
|
|
156
|
+
"1",
|
|
157
|
+
"-preset",
|
|
158
|
+
"ultrafast",
|
|
159
|
+
"-g",
|
|
160
|
+
"114514",
|
|
161
|
+
"-t",
|
|
162
|
+
str(duration),
|
|
163
|
+
"-pix_fmt",
|
|
164
|
+
"yuv420p",
|
|
165
|
+
"-vf",
|
|
166
|
+
"scale=trunc(iw/2)*2:trunc(ih/2)*2",
|
|
167
|
+
"-shortest",
|
|
131
168
|
str(video_path),
|
|
132
169
|
stdout=asyncio.subprocess.PIPE,
|
|
133
170
|
stderr=asyncio.subprocess.PIPE,
|
|
134
171
|
)
|
|
135
172
|
stdout, stderr = await proc.communicate()
|
|
136
173
|
if stdout:
|
|
137
|
-
logger.info(f"
|
|
174
|
+
logger.info(f"ffmpeg stdout:\n{stdout.decode()}")
|
|
138
175
|
if stderr:
|
|
139
|
-
logger.warning(f"
|
|
176
|
+
logger.warning(f"ffmpeg stderr:\n{stderr.decode()}")
|
|
140
177
|
if proc.returncode != 0:
|
|
141
|
-
logger.error(
|
|
178
|
+
logger.error(
|
|
179
|
+
f"转换视频失败 (returncode={proc.returncode}): {stderr.decode()[:] if stderr else ''}"
|
|
180
|
+
)
|
|
142
181
|
return ""
|
|
143
182
|
logger.info(f"转换视频成功: {video_path}")
|
|
144
183
|
return str(video_path)
|
|
@@ -148,26 +187,31 @@ class BotCommandConvert(BotCommand):
|
|
|
148
187
|
if png_path == "":
|
|
149
188
|
return ""
|
|
150
189
|
frame_path = frame_dir / f"{uuid.uuid4().hex}.png"
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
)
|
|
190
|
+
border = random.randint(10, 50)
|
|
191
|
+
logger.info(f"执行图片加黑框 (ffmpeg, {border}px): {png_path} -> {frame_path}")
|
|
154
192
|
proc = await asyncio.create_subprocess_exec(
|
|
155
|
-
|
|
156
|
-
|
|
193
|
+
ffmpeg,
|
|
194
|
+
"-i",
|
|
157
195
|
str(png_path),
|
|
196
|
+
"-vf",
|
|
197
|
+
f"pad=iw+{border}:ih+{border}:(ow-iw)/2:(oh-ih)/2:black",
|
|
198
|
+
"-update",
|
|
199
|
+
"1",
|
|
158
200
|
str(frame_path),
|
|
159
201
|
stdout=asyncio.subprocess.PIPE,
|
|
160
202
|
stderr=asyncio.subprocess.PIPE,
|
|
161
203
|
)
|
|
162
204
|
stdout, stderr = await proc.communicate()
|
|
163
205
|
if stdout:
|
|
164
|
-
logger.info(f"
|
|
206
|
+
logger.info(f"ffmpeg stdout:\n{stdout.decode()}")
|
|
165
207
|
if stderr:
|
|
166
|
-
logger.warning(f"
|
|
208
|
+
logger.warning(f"ffmpeg stderr:\n{stderr.decode()}")
|
|
167
209
|
if proc.returncode != 0:
|
|
168
|
-
logger.error(
|
|
210
|
+
logger.error(
|
|
211
|
+
f"加框失败 (returncode={proc.returncode}): {stderr.decode()[:] if stderr else ''}"
|
|
212
|
+
)
|
|
169
213
|
return ""
|
|
170
|
-
logger.info(f"
|
|
214
|
+
logger.info(f"加黑框成功: {frame_path}")
|
|
171
215
|
return str(frame_path)
|
|
172
216
|
|
|
173
217
|
async def _send_outputs(self):
|
|
@@ -195,9 +239,7 @@ class BotCommandConvert(BotCommand):
|
|
|
195
239
|
if not output_file.exists():
|
|
196
240
|
logger.error(f"输出文件不存在: {output_file}")
|
|
197
241
|
continue
|
|
198
|
-
container_path = config.
|
|
199
|
-
config.bot_base
|
|
200
|
-
)
|
|
242
|
+
container_path = config.client_cache / output_file.relative_to(config.cache)
|
|
201
243
|
try:
|
|
202
244
|
if self._mode == "video":
|
|
203
245
|
await self.send_msg(
|
|
@@ -30,7 +30,7 @@ class BotCommandPixiv(BotCommand):
|
|
|
30
30
|
parser.set_rule(min=1, max=1, types=[int])
|
|
31
31
|
parser.add_opt("-r", required=True, choice=["off", "on"], default=["off"])
|
|
32
32
|
parser.add_opt(
|
|
33
|
-
"-s", required=True, choice=["regular", "original"], default=["
|
|
33
|
+
"-s", required=True, choice=["regular", "original"], default=["original"]
|
|
34
34
|
)
|
|
35
35
|
return parser
|
|
36
36
|
|
|
@@ -103,8 +103,8 @@ class BotCommandShitpost(BotCommand):
|
|
|
103
103
|
media_path = media_dir / f"{uuid.uuid4().hex}"
|
|
104
104
|
async with httpx.AsyncClient() as client:
|
|
105
105
|
await stuff_download(client, url, media_path)
|
|
106
|
-
container_path = config.
|
|
107
|
-
config.
|
|
106
|
+
container_path = config.client_cache / media_path.relative_to(
|
|
107
|
+
config.cache
|
|
108
108
|
)
|
|
109
109
|
medias_path.append(media_path)
|
|
110
110
|
medias.append(str(container_path))
|