nonebot-plugin-savepic 0.4.1__tar.gz → 0.6.7__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_savepic-0.4.1/README.md → nonebot_plugin_savepic-0.6.7/PKG-INFO +20 -47
- nonebot_plugin_savepic-0.6.7/README.md +68 -0
- nonebot_plugin_savepic-0.6.7/nonebot_plugin_savepic/__init__.py +171 -0
- nonebot_plugin_savepic-0.6.7/nonebot_plugin_savepic/command.py +138 -0
- {nonebot_plugin_savepic-0.4.1 → nonebot_plugin_savepic-0.6.7}/nonebot_plugin_savepic/config.py +11 -16
- {nonebot_plugin_savepic-0.4.1 → nonebot_plugin_savepic-0.6.7}/nonebot_plugin_savepic/core/error.py +12 -2
- nonebot_plugin_savepic-0.6.7/nonebot_plugin_savepic/core/fileio.py +43 -0
- nonebot_plugin_savepic-0.6.7/nonebot_plugin_savepic/core/sql.py +769 -0
- nonebot_plugin_savepic-0.6.7/nonebot_plugin_savepic/core/types.py +9 -0
- nonebot_plugin_savepic-0.6.7/nonebot_plugin_savepic/core/utils.py +136 -0
- nonebot_plugin_savepic-0.6.7/nonebot_plugin_savepic/fun.py +78 -0
- nonebot_plugin_savepic-0.6.7/nonebot_plugin_savepic/listpic.py +163 -0
- nonebot_plugin_savepic-0.6.7/nonebot_plugin_savepic/mvpic.py +141 -0
- {nonebot_plugin_savepic-0.4.1 → nonebot_plugin_savepic-0.6.7}/nonebot_plugin_savepic/rule.py +2 -2
- nonebot_plugin_savepic-0.6.7/pyproject.toml +49 -0
- nonebot_plugin_savepic-0.4.1/PKG-INFO +0 -141
- nonebot_plugin_savepic-0.4.1/nonebot_plugin_savepic/__init__.py +0 -258
- nonebot_plugin_savepic-0.4.1/nonebot_plugin_savepic/command.py +0 -179
- nonebot_plugin_savepic-0.4.1/nonebot_plugin_savepic/core/fileio.py +0 -92
- nonebot_plugin_savepic-0.4.1/nonebot_plugin_savepic/core/model.py +0 -207
- nonebot_plugin_savepic-0.4.1/nonebot_plugin_savepic/core/sql.py +0 -409
- nonebot_plugin_savepic-0.4.1/nonebot_plugin_savepic/core/utils.py +0 -1170
- nonebot_plugin_savepic-0.4.1/nonebot_plugin_savepic/listpic.py +0 -75
- nonebot_plugin_savepic-0.4.1/nonebot_plugin_savepic/model.py +0 -37
- nonebot_plugin_savepic-0.4.1/nonebot_plugin_savepic/mvpic.py +0 -134
- nonebot_plugin_savepic-0.4.1/pyproject.toml +0 -32
- {nonebot_plugin_savepic-0.4.1 → nonebot_plugin_savepic-0.6.7}/LICENSE +0 -0
- {nonebot_plugin_savepic-0.4.1 → nonebot_plugin_savepic-0.6.7}/nonebot_plugin_savepic/core/__init__.py +0 -0
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: nonebot-plugin-savepic
|
|
3
|
+
Version: 0.6.7
|
|
4
|
+
Summary: 保存表情包(语录)与随机出图
|
|
5
|
+
Author-Email: Yan <1964649083@qq.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: homepage, https://github.com/Yan-Zero/nonebot-plugin-savepic
|
|
8
|
+
Requires-Python: <4.0,>=3.11
|
|
9
|
+
Requires-Dist: nonebot2<3.0.0,>=2.4.0
|
|
10
|
+
Requires-Dist: asyncpg<1.0.0,>=0.28.0
|
|
11
|
+
Requires-Dist: nonebot-adapter-onebot<3.0.0,>=2.4.6
|
|
12
|
+
Requires-Dist: numpy<3.0.0,>=2.1.3
|
|
13
|
+
Requires-Dist: httpx<1.0.0,>=0.28.0
|
|
14
|
+
Requires-Dist: nonebot-plugin-alconna>=0.59.4
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
1
17
|
<div align="center">
|
|
2
18
|
<a href="https://v2.nonebot.dev/store"><img src="https://github.com/A-kirami/nonebot-plugin-template/blob/resources/nbp_logo.png" width="180" height="180" alt="NoneBotPluginLogo"></a>
|
|
3
19
|
<br>
|
|
@@ -29,63 +45,25 @@ _✨ 一个存取图片的插件 ✨_
|
|
|
29
45
|
重命名表情包,或者修改表情包所属的群域
|
|
30
46
|
|
|
31
47
|
例如:
|
|
48
|
+
|
|
32
49
|
```
|
|
33
50
|
/mvpic -l name.jpg -g waaaaa.gif
|
|
34
51
|
```
|
|
52
|
+
|
|
35
53
|
就是把本群的 name.jpg 改成全局名为 waaaaa.gif 的表情包
|
|
36
54
|
|
|
37
55
|
同理
|
|
56
|
+
|
|
38
57
|
```
|
|
39
58
|
/mvpic -g waaaaa.gif -l waaaaa.gif
|
|
40
59
|
```
|
|
60
|
+
|
|
41
61
|
就是从全局移到本群(接收到命令的群)
|
|
42
62
|
|
|
43
63
|
### 直接发送文件名
|
|
44
64
|
|
|
45
65
|
发送文件名即可发送表情包
|
|
46
66
|
|
|
47
|
-
|
|
48
|
-
## 💿 安装
|
|
49
|
-
|
|
50
|
-
<details open>
|
|
51
|
-
<summary>使用 nb-cli 安装</summary>
|
|
52
|
-
在 nonebot2 项目的根目录下打开命令行, 输入以下指令即可安装
|
|
53
|
-
|
|
54
|
-
nb plugin install nonebot-plugin-savepic
|
|
55
|
-
|
|
56
|
-
</details>
|
|
57
|
-
|
|
58
|
-
<details>
|
|
59
|
-
<summary>使用包管理器安装</summary>
|
|
60
|
-
在 nonebot2 项目的插件目录下, 打开命令行, 根据你使用的包管理器, 输入相应的安装命令
|
|
61
|
-
|
|
62
|
-
<details>
|
|
63
|
-
<summary>pip</summary>
|
|
64
|
-
|
|
65
|
-
pip install nonebot-plugin-savepic
|
|
66
|
-
</details>
|
|
67
|
-
<details>
|
|
68
|
-
<summary>pdm</summary>
|
|
69
|
-
|
|
70
|
-
pdm add nonebot-plugin-savepic
|
|
71
|
-
</details>
|
|
72
|
-
<details>
|
|
73
|
-
<summary>poetry</summary>
|
|
74
|
-
|
|
75
|
-
poetry add nonebot-plugin-savepic
|
|
76
|
-
</details>
|
|
77
|
-
<details>
|
|
78
|
-
<summary>conda</summary>
|
|
79
|
-
|
|
80
|
-
conda install nonebot-plugin-savepic
|
|
81
|
-
</details>
|
|
82
|
-
|
|
83
|
-
打开 nonebot2 项目根目录下的 `pyproject.toml` 文件, 在 `[tool.nonebot]` 部分追加写入
|
|
84
|
-
|
|
85
|
-
plugins = ["nonebot_plugin_savepic"]
|
|
86
|
-
|
|
87
|
-
</details>
|
|
88
|
-
|
|
89
67
|
## ⚙️ 配置
|
|
90
68
|
|
|
91
69
|
在 nonebot2 项目的`.env`文件中添加下表中的必填配置
|
|
@@ -94,11 +72,6 @@ _✨ 一个存取图片的插件 ✨_
|
|
|
94
72
|
|:-----:|:----:|:----:|:----:|
|
|
95
73
|
| savepic_admin | 否 | 无 | 权限用户 |
|
|
96
74
|
| savepic_dir | 否 | savepic | 图片本地保存位置 |
|
|
97
|
-
| simpic_enable | 否 | False | 是否开启基于RNN模型的AI相似度检索功能,如果开启要填写 Pinecone 相关配置 |
|
|
98
|
-
| pinecone_apikey | 否 | 无 | pinecone 的 Key |
|
|
99
|
-
| pinecone_environment | 否 | 无 | pinecone 的 enviroment,是数据库的属性之一 |
|
|
100
|
-
| pinecone_index | 否 | savepic | 同上。 |
|
|
101
|
-
| dashscope_api | 否 | 无 | dashscope 的 APIKey。 用于开启基于语义的相似度检索功能 |
|
|
102
75
|
|
|
103
76
|
## 🎉 使用
|
|
104
77
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<a href="https://v2.nonebot.dev/store"><img src="https://github.com/A-kirami/nonebot-plugin-template/blob/resources/nbp_logo.png" width="180" height="180" alt="NoneBotPluginLogo"></a>
|
|
3
|
+
<br>
|
|
4
|
+
<p><img src="https://github.com/A-kirami/nonebot-plugin-template/blob/resources/NoneBotPlugin.svg" width="240" alt="NoneBotPluginText"></p>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<div align="center">
|
|
8
|
+
|
|
9
|
+
# nonebot-plugin-savepic
|
|
10
|
+
|
|
11
|
+
_✨ 一个存取图片的插件 ✨_
|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
## 📖 介绍
|
|
16
|
+
|
|
17
|
+
重写自 Fran 的 Savepic
|
|
18
|
+
|
|
19
|
+
### savepic
|
|
20
|
+
|
|
21
|
+
保存表情包
|
|
22
|
+
|
|
23
|
+
### randpic
|
|
24
|
+
|
|
25
|
+
抽取表情包
|
|
26
|
+
|
|
27
|
+
### mvpic
|
|
28
|
+
|
|
29
|
+
重命名表情包,或者修改表情包所属的群域
|
|
30
|
+
|
|
31
|
+
例如:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
/mvpic -l name.jpg -g waaaaa.gif
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
就是把本群的 name.jpg 改成全局名为 waaaaa.gif 的表情包
|
|
38
|
+
|
|
39
|
+
同理
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
/mvpic -g waaaaa.gif -l waaaaa.gif
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
就是从全局移到本群(接收到命令的群)
|
|
46
|
+
|
|
47
|
+
### 直接发送文件名
|
|
48
|
+
|
|
49
|
+
发送文件名即可发送表情包
|
|
50
|
+
|
|
51
|
+
## ⚙️ 配置
|
|
52
|
+
|
|
53
|
+
在 nonebot2 项目的`.env`文件中添加下表中的必填配置
|
|
54
|
+
|
|
55
|
+
| 配置项 | 必填 | 默认值 | 说明 |
|
|
56
|
+
|:-----:|:----:|:----:|:----:|
|
|
57
|
+
| savepic_admin | 否 | 无 | 权限用户 |
|
|
58
|
+
| savepic_dir | 否 | savepic | 图片本地保存位置 |
|
|
59
|
+
|
|
60
|
+
## 🎉 使用
|
|
61
|
+
|
|
62
|
+
### 指令表
|
|
63
|
+
|
|
64
|
+
| 指令 | 权限 | 需要@ | 范围 | 说明 |
|
|
65
|
+
|:-----:|:----:|:----:|:----:|:----:|
|
|
66
|
+
| savepic | 群员 | 否 | 群聊 | 保存图片 |
|
|
67
|
+
| randpic | 群员 | 否 | 全部 | 随机图片 |
|
|
68
|
+
| mvpic | 管理员 | 否 | 群聊 | 重命名图片 |
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
from nonebot import on_command
|
|
2
|
+
from nonebot.params import CommandArg, Arg
|
|
3
|
+
from nonebot.adapters.onebot.v11.message import Message as V11Msg
|
|
4
|
+
from nonebot.adapters.onebot.v11.message import MessageSegment as V11Seg
|
|
5
|
+
from nonebot.adapters.onebot.v11.event import GroupMessageEvent
|
|
6
|
+
from nonebot.matcher import Matcher
|
|
7
|
+
from nonebot.plugin import PluginMetadata
|
|
8
|
+
from nonebot.typing import T_State
|
|
9
|
+
from arclet.alconna import Alconna, Args, Option, CommandMeta, Arparma
|
|
10
|
+
from nonebot_plugin_alconna import on_alconna, Image
|
|
11
|
+
from nonebot.internal.adapter import Bot
|
|
12
|
+
|
|
13
|
+
from .fun import cipr # noqa: F401
|
|
14
|
+
from .rule import PIC_ADMIN
|
|
15
|
+
from .config import Config, plugin_config
|
|
16
|
+
from .listpic import rkey
|
|
17
|
+
from .command import url_to_image
|
|
18
|
+
from .core.sql import savepic, regexp_pic
|
|
19
|
+
from .core.utils import img2vec
|
|
20
|
+
from .core.error import SameNameException
|
|
21
|
+
from .core.error import SimilarPictureException
|
|
22
|
+
from .core.fileio import write_pic, del_pic
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
__plugin_meta__ = PluginMetadata(
|
|
26
|
+
name="Savepic",
|
|
27
|
+
description="表情包保存",
|
|
28
|
+
usage="""用法:
|
|
29
|
+
/savepic <文件名> <图片>
|
|
30
|
+
/savepic -g <文件名> <全局图片>
|
|
31
|
+
/savepic -d <文件名> 删除图图片
|
|
32
|
+
/randpic <关键词> 随机图片
|
|
33
|
+
<文件名> 发送图片""",
|
|
34
|
+
config=Config,
|
|
35
|
+
homepage="https://github.com/Yan-Zero/nonebot-plugin-savepic",
|
|
36
|
+
type="application",
|
|
37
|
+
supported_adapters=set(["~onebot.v11"]),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
repic = on_command("repic", priority=5)
|
|
41
|
+
spic = on_alconna(
|
|
42
|
+
Alconna(
|
|
43
|
+
"savepic",
|
|
44
|
+
Option("-g", help_text="全局"),
|
|
45
|
+
Option("-ac", help_text="允许相似碰撞"),
|
|
46
|
+
Args["filename", str],
|
|
47
|
+
Args["picture?", Image],
|
|
48
|
+
meta=CommandMeta(description="保存图片,默认保存到本群"),
|
|
49
|
+
),
|
|
50
|
+
use_cmd_start=True,
|
|
51
|
+
skip_for_unmatch=False,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@repic.handle()
|
|
56
|
+
async def _(bot: Bot, event, args: V11Msg = CommandArg()):
|
|
57
|
+
reg = args.extract_plain_text().strip()
|
|
58
|
+
group_id = (
|
|
59
|
+
"globe"
|
|
60
|
+
if not isinstance(event, GroupMessageEvent)
|
|
61
|
+
else f"qq_group:{event.group_id}"
|
|
62
|
+
)
|
|
63
|
+
try:
|
|
64
|
+
if pic := await regexp_pic(reg, group_id):
|
|
65
|
+
await bot.send(
|
|
66
|
+
event, V11Msg([V11Seg.text(pic.name), await url_to_image(pic.url)])
|
|
67
|
+
)
|
|
68
|
+
except Exception as ex:
|
|
69
|
+
await repic.finish(f"出错了。{ex}")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@spic.handle()
|
|
73
|
+
async def _(
|
|
74
|
+
bot: Bot,
|
|
75
|
+
matcher: Matcher,
|
|
76
|
+
event: GroupMessageEvent,
|
|
77
|
+
state: T_State,
|
|
78
|
+
command: Arparma,
|
|
79
|
+
):
|
|
80
|
+
if not command.matched:
|
|
81
|
+
await spic.finish(str(command.error_info))
|
|
82
|
+
|
|
83
|
+
filename = command.filename
|
|
84
|
+
if not isinstance(filename, str):
|
|
85
|
+
await spic.finish("文件名无效")
|
|
86
|
+
|
|
87
|
+
if not filename.endswith((".jpg", ".png", ".gif")):
|
|
88
|
+
filename += ".jpg"
|
|
89
|
+
|
|
90
|
+
state["savepiv_group"] = (
|
|
91
|
+
"globe"
|
|
92
|
+
if command.g and await PIC_ADMIN(bot, event)
|
|
93
|
+
else f"qq_group:{event.group_id}"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
if command.g and not await PIC_ADMIN(bot, event):
|
|
97
|
+
await spic.send("你的 -g 选项没有用哟")
|
|
98
|
+
|
|
99
|
+
state["savepiv_filename"] = filename
|
|
100
|
+
state["savepiv_ac"] = command.ac is not None
|
|
101
|
+
|
|
102
|
+
picture = event.message.get("image")
|
|
103
|
+
if not picture and event.reply:
|
|
104
|
+
picture = event.reply.message.get("image")
|
|
105
|
+
if picture:
|
|
106
|
+
matcher.set_arg("picture", picture)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@spic.got("picture", "图呢?")
|
|
110
|
+
async def _(bot: Bot, state: T_State, event, picture: V11Msg = Arg()):
|
|
111
|
+
picture = picture.get("image")
|
|
112
|
+
if not picture:
|
|
113
|
+
await spic.finish("6,这也不是图啊")
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
url = await rkey(
|
|
117
|
+
bot,
|
|
118
|
+
(
|
|
119
|
+
picture[0].data["url"]
|
|
120
|
+
if "url" in picture[0].data
|
|
121
|
+
else picture[0].data["file"]
|
|
122
|
+
),
|
|
123
|
+
)
|
|
124
|
+
dir = await write_pic(url, plugin_config.savepic_dir)
|
|
125
|
+
except Exception as ex:
|
|
126
|
+
await spic.finish("存图失败。" + "\n" + str(ex))
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
r = await savepic(
|
|
130
|
+
filename=state["savepiv_filename"],
|
|
131
|
+
url=dir,
|
|
132
|
+
scope=state["savepiv_group"],
|
|
133
|
+
uploader=f"{bot.adapter.get_name().split(maxsplit=1)[0].lower()}:{event.get_user_id()}",
|
|
134
|
+
vec=await img2vec(url, state["savepiv_filename"]),
|
|
135
|
+
collision_allow=state["savepiv_ac"],
|
|
136
|
+
)
|
|
137
|
+
except SameNameException:
|
|
138
|
+
await del_pic(dir)
|
|
139
|
+
await spic.finish("文件名重复")
|
|
140
|
+
except SimilarPictureException as ex:
|
|
141
|
+
if ex.similarity >= 0.999:
|
|
142
|
+
await spic.finish(
|
|
143
|
+
V11Msg(
|
|
144
|
+
[
|
|
145
|
+
V11Seg.text("这图存过了,无法再保存" + "\n\n" + ex.name + "\n"),
|
|
146
|
+
await url_to_image(ex.url),
|
|
147
|
+
]
|
|
148
|
+
)
|
|
149
|
+
)
|
|
150
|
+
await del_pic(dir)
|
|
151
|
+
await spic.finish(
|
|
152
|
+
V11Msg(
|
|
153
|
+
[
|
|
154
|
+
V11Seg.text(
|
|
155
|
+
"存在相似图片"
|
|
156
|
+
+ "\n\n"
|
|
157
|
+
+ ex.name
|
|
158
|
+
+ f"\n(相似度:{'%.4g' % (min(ex.similarity * 100, 100.0))}%)\n"
|
|
159
|
+
),
|
|
160
|
+
await url_to_image(ex.url),
|
|
161
|
+
]
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
except Exception as ex:
|
|
165
|
+
await del_pic(dir)
|
|
166
|
+
await spic.finish(f"出错了。{ex}")
|
|
167
|
+
|
|
168
|
+
if r:
|
|
169
|
+
await spic.send(f"保存成功,但是名字为`{r}`")
|
|
170
|
+
else:
|
|
171
|
+
await spic.send("保存成功")
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import traceback
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from nonebot import on_command
|
|
5
|
+
from nonebot.plugin import on_endswith
|
|
6
|
+
from nonebot.params import CommandArg
|
|
7
|
+
from nonebot.adapters import Bot
|
|
8
|
+
from nonebot.adapters import Message
|
|
9
|
+
from nonebot.adapters import Event
|
|
10
|
+
from nonebot.permission import SUPERUSER
|
|
11
|
+
from nonebot.adapters.onebot.v11.event import GroupMessageEvent as V11GME
|
|
12
|
+
from nonebot.adapters.onebot.v11.message import Message as V11Msg
|
|
13
|
+
from nonebot.adapters.onebot.v11.message import MessageSegment as V11Seg
|
|
14
|
+
|
|
15
|
+
from .listpic import rkey
|
|
16
|
+
from .core.sql import simpic, randpic, countpic, select_pic
|
|
17
|
+
from .core.utils import img2vec
|
|
18
|
+
|
|
19
|
+
cpic = on_command("countpic", priority=5)
|
|
20
|
+
rpic = on_command("randpic", priority=5)
|
|
21
|
+
s_simpic = on_command("simpic", priority=5)
|
|
22
|
+
pic_listen = on_endswith((".jpg", ".png", ".gif"), priority=50, block=False)
|
|
23
|
+
pic_clear = on_command("pic.clear", permission=SUPERUSER, priority=1, block=True)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def url_to_image(url: str) -> V11Seg:
|
|
27
|
+
if url.startswith("http"):
|
|
28
|
+
return V11Seg.image(url)
|
|
29
|
+
return V11Seg.image(Path(url))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@cpic.handle()
|
|
33
|
+
async def _(bot: Bot, event: Event, args: Message = CommandArg()):
|
|
34
|
+
reg = args.extract_plain_text().strip()
|
|
35
|
+
group_id = (
|
|
36
|
+
"globe" if not isinstance(event, V11GME) else f"qq_group:{event.group_id}"
|
|
37
|
+
)
|
|
38
|
+
try:
|
|
39
|
+
await bot.send(event, f"共查找到 {await countpic(reg, group_id)} 张图片。")
|
|
40
|
+
except Exception as ex:
|
|
41
|
+
await cpic.finish(f"出错了喵~\n\n{ex}")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@pic_listen.handle()
|
|
45
|
+
async def _(bot: Bot, event: Event):
|
|
46
|
+
name = event.get_plaintext().strip()
|
|
47
|
+
group_id = (
|
|
48
|
+
"globe" if not isinstance(event, V11GME) else f"qq_group:{event.group_id}"
|
|
49
|
+
)
|
|
50
|
+
try:
|
|
51
|
+
if url := await select_pic(name, group_id):
|
|
52
|
+
await pic_listen.send(await url_to_image(url))
|
|
53
|
+
except Exception as ex:
|
|
54
|
+
await pic_listen.finish(str(ex))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@rpic.handle()
|
|
58
|
+
async def _(bot: Bot, event: Event, args: V11Msg = CommandArg()):
|
|
59
|
+
name = args.extract_plain_text().strip()
|
|
60
|
+
group_id = (
|
|
61
|
+
"globe" if not isinstance(event, V11GME) else f"qq_group:{event.group_id}"
|
|
62
|
+
)
|
|
63
|
+
try:
|
|
64
|
+
pic, t = await randpic(name, group_id, True)
|
|
65
|
+
if not pic:
|
|
66
|
+
await rpic.send("404 Not Found.")
|
|
67
|
+
return
|
|
68
|
+
await rpic.send(
|
|
69
|
+
V11Msg(
|
|
70
|
+
[
|
|
71
|
+
V11Seg.text(pic.name + ("\n" + t if t else "")),
|
|
72
|
+
await url_to_image(pic.url),
|
|
73
|
+
]
|
|
74
|
+
)
|
|
75
|
+
)
|
|
76
|
+
except Exception as ex:
|
|
77
|
+
with open("error.txt", "w+") as f:
|
|
78
|
+
f.write("\n")
|
|
79
|
+
traceback.print_exc(file=f)
|
|
80
|
+
await rpic.finish(str(ex))
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@s_simpic.handle()
|
|
84
|
+
async def _(bot: Bot, event: Event, args: Message = CommandArg()):
|
|
85
|
+
picture = args.get("image")
|
|
86
|
+
if not picture and hasattr(event, "reply"):
|
|
87
|
+
picture = event.reply.message.get("image") if event.reply else None # type: ignore
|
|
88
|
+
if not picture:
|
|
89
|
+
picture = event.reply.message.get("mface") if event.reply else None # type: ignore
|
|
90
|
+
if not picture:
|
|
91
|
+
await s_simpic.finish("请发送图片后再使用该指令喵~")
|
|
92
|
+
try:
|
|
93
|
+
vec = await img2vec(
|
|
94
|
+
await rkey(bot, picture[0].data["url"]),
|
|
95
|
+
title=args.extract_plain_text().strip(),
|
|
96
|
+
)
|
|
97
|
+
if vec is None:
|
|
98
|
+
await s_simpic.finish("图片特征提取失败喵~")
|
|
99
|
+
group_id = (
|
|
100
|
+
"globe" if not isinstance(event, V11GME) else f"qq_group:{event.group_id}"
|
|
101
|
+
)
|
|
102
|
+
sim, pic = await simpic(vec, group_id)
|
|
103
|
+
except Exception as ex:
|
|
104
|
+
await s_simpic.finish(str(ex))
|
|
105
|
+
|
|
106
|
+
if pic:
|
|
107
|
+
ret = []
|
|
108
|
+
if event.reply: # type: ignore
|
|
109
|
+
ret.append(V11Seg.reply(event.reply.message_id)) # type: ignore
|
|
110
|
+
ret.append(f"{pic.name}\n(相似性:{'%.4g' % (min(sim * 100, 100.0))}%)")
|
|
111
|
+
ret.append(await url_to_image(pic.url))
|
|
112
|
+
await s_simpic.send(V11Msg(ret))
|
|
113
|
+
else:
|
|
114
|
+
await s_simpic.send("没有找到相似的图片喵~")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@pic_clear.handle()
|
|
118
|
+
async def _():
|
|
119
|
+
# 清理孤儿图片
|
|
120
|
+
from .core import sql
|
|
121
|
+
|
|
122
|
+
if not sql.POOL:
|
|
123
|
+
await pic_clear.finish("数据库未初始化")
|
|
124
|
+
await pic_clear.send("开始清理孤儿图片...")
|
|
125
|
+
img = set(Path("savepic").glob("*"))
|
|
126
|
+
async with sql.POOL.acquire() as conn, conn.transaction():
|
|
127
|
+
async for record in conn.cursor("SELECT name, url FROM picdata;"):
|
|
128
|
+
if not record["url"].startswith("savepic/"):
|
|
129
|
+
continue
|
|
130
|
+
p = Path(record["url"])
|
|
131
|
+
img.remove(p)
|
|
132
|
+
if img:
|
|
133
|
+
await pic_clear.send(f"发现 {len(img)} 张孤儿图片,正在删除...")
|
|
134
|
+
for p in img:
|
|
135
|
+
p.unlink(missing_ok=True)
|
|
136
|
+
await pic_clear.send("删除完成!")
|
|
137
|
+
else:
|
|
138
|
+
await pic_clear.send("没有发现孤儿图片!")
|
{nonebot_plugin_savepic-0.4.1 → nonebot_plugin_savepic-0.6.7}/nonebot_plugin_savepic/config.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from
|
|
2
|
-
from pydantic import BaseModel
|
|
1
|
+
from typing import Optional
|
|
3
2
|
from nonebot import get_plugin_config
|
|
3
|
+
from pydantic import BaseModel
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class Config(BaseModel):
|
|
@@ -10,16 +10,9 @@ class Config(BaseModel):
|
|
|
10
10
|
savepic_dir: str = "savepic"
|
|
11
11
|
savepic_sqlurl: str
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
embedding_sqlurl: str
|
|
18
|
-
openai_apikey: str
|
|
19
|
-
openai_baseurl: str
|
|
20
|
-
embedding_model: str = "BAAI/bge-m3"
|
|
21
|
-
simpic_model: str = Field(default="")
|
|
22
|
-
p_model_path: str = Field(default="")
|
|
13
|
+
embedding_key: str
|
|
14
|
+
embedding_url: str
|
|
15
|
+
embedding_model: str
|
|
23
16
|
|
|
24
17
|
notfound_with_jpg: bool = True
|
|
25
18
|
""" randpic 的时候,尝试带 .jpg 再度检索向量 """
|
|
@@ -30,9 +23,11 @@ class Config(BaseModel):
|
|
|
30
23
|
max_page_in_listpic: int = 20
|
|
31
24
|
""" 合并转发中所能显示的最大页数 """
|
|
32
25
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
cache_sqlurl: Optional[str] = None
|
|
27
|
+
""" 额外的只读连接池地址,若不填写则与 savepic_sqlurl 共用 """
|
|
28
|
+
|
|
29
|
+
normalize_vector: Optional[str] = None
|
|
30
|
+
""" 向量归一化,路径指向一个 txt 文件,文件内是均值向量 """
|
|
36
31
|
|
|
37
32
|
|
|
38
|
-
plugin_config = get_plugin_config(Config)
|
|
33
|
+
plugin_config: Config = get_plugin_config(Config)
|
{nonebot_plugin_savepic-0.4.1 → nonebot_plugin_savepic-0.6.7}/nonebot_plugin_savepic/core/error.py
RENAMED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
class SameNameException(Exception):
|
|
2
|
-
def __init__(self, name):
|
|
2
|
+
def __init__(self, name, scope):
|
|
3
3
|
self.name = name
|
|
4
|
+
self.scope = scope
|
|
4
5
|
|
|
5
6
|
def __str__(self):
|
|
6
|
-
return "Name {} already exists".format(self.name)
|
|
7
|
+
return "Name {} already exists in scope {}".format(self.name, self.scope)
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class SimilarPictureException(Exception):
|
|
@@ -22,3 +23,12 @@ class NoPictureException(Exception):
|
|
|
22
23
|
|
|
23
24
|
def __str__(self):
|
|
24
25
|
return "Picture {} not found".format(self.name)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class PermissionException(Exception):
|
|
29
|
+
def __init__(self, name, reason="No permission"):
|
|
30
|
+
self.name = name
|
|
31
|
+
self.reason = reason
|
|
32
|
+
|
|
33
|
+
def __str__(self):
|
|
34
|
+
return "No permission to access {}: {}".format(self.name, self.reason)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
import hashlib
|
|
3
|
+
import pathlib
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def del_pic(url: str | pathlib.Path):
|
|
7
|
+
if isinstance(url, pathlib.Path):
|
|
8
|
+
url = url.as_posix()
|
|
9
|
+
if url.startswith("http"):
|
|
10
|
+
return
|
|
11
|
+
if isinstance(url, str):
|
|
12
|
+
_ = pathlib.Path(url)
|
|
13
|
+
else:
|
|
14
|
+
_ = url
|
|
15
|
+
if _.exists():
|
|
16
|
+
_.unlink()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def load_pic(url: str) -> bytes:
|
|
20
|
+
if url.startswith("http"):
|
|
21
|
+
async with httpx.AsyncClient() as client:
|
|
22
|
+
resp = await client.get(url)
|
|
23
|
+
resp.raise_for_status()
|
|
24
|
+
return resp.content
|
|
25
|
+
|
|
26
|
+
if pathlib.Path(url).exists():
|
|
27
|
+
with open(url, "rb") as f:
|
|
28
|
+
return f.read()
|
|
29
|
+
raise Exception(f"不支持的 URL\n{url}")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
async def write_pic(url: str, des_dir: str | None = None) -> str:
|
|
33
|
+
if not des_dir:
|
|
34
|
+
des_dir = "savepic"
|
|
35
|
+
path = pathlib.Path(des_dir)
|
|
36
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
37
|
+
|
|
38
|
+
byte = await load_pic(url)
|
|
39
|
+
file = path / hashlib.sha256(byte).hexdigest()
|
|
40
|
+
|
|
41
|
+
with open(file, "wb") as f:
|
|
42
|
+
f.write(byte)
|
|
43
|
+
return file.as_posix()
|