nonebot-plugin-savepic 0.1.3__tar.gz → 0.2.5__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.2.5/PKG-INFO +135 -0
- nonebot_plugin_savepic-0.1.3/PKG-INFO → nonebot_plugin_savepic-0.2.5/README.md +25 -21
- nonebot_plugin_savepic-0.2.5/nonebot_plugin_savepic/__init__.py +418 -0
- nonebot_plugin_savepic-0.2.5/nonebot_plugin_savepic/ai_utils.py +66 -0
- nonebot_plugin_savepic-0.2.5/nonebot_plugin_savepic/config.py +25 -0
- nonebot_plugin_savepic-0.2.5/nonebot_plugin_savepic/countpic.py +22 -0
- nonebot_plugin_savepic-0.2.5/nonebot_plugin_savepic/error.py +24 -0
- nonebot_plugin_savepic-0.2.5/nonebot_plugin_savepic/ext_listener.py +26 -0
- nonebot_plugin_savepic-0.2.5/nonebot_plugin_savepic/model.py +108 -0
- nonebot_plugin_savepic-0.2.5/nonebot_plugin_savepic/networks/__init__.py +0 -0
- nonebot_plugin_savepic-0.2.5/nonebot_plugin_savepic/networks/resnet_big.py +242 -0
- nonebot_plugin_savepic-0.2.5/nonebot_plugin_savepic/pic_sql.py +397 -0
- nonebot_plugin_savepic-0.2.5/nonebot_plugin_savepic/picture.py +77 -0
- nonebot_plugin_savepic-0.2.5/nonebot_plugin_savepic/randpic.py +47 -0
- {nonebot_plugin_savepic-0.1.3 → nonebot_plugin_savepic-0.2.5}/nonebot_plugin_savepic/rule.py +22 -5
- {nonebot_plugin_savepic-0.1.3 → nonebot_plugin_savepic-0.2.5}/pyproject.toml +8 -2
- nonebot_plugin_savepic-0.1.3/README.md +0 -84
- nonebot_plugin_savepic-0.1.3/nonebot_plugin_savepic/__init__.py +0 -157
- nonebot_plugin_savepic-0.1.3/nonebot_plugin_savepic/config.py +0 -7
- nonebot_plugin_savepic-0.1.3/nonebot_plugin_savepic/migrations/1a654e02179b_.py +0 -34
- nonebot_plugin_savepic-0.1.3/nonebot_plugin_savepic/model.py +0 -20
- nonebot_plugin_savepic-0.1.3/nonebot_plugin_savepic/pic_sql.py +0 -150
- {nonebot_plugin_savepic-0.1.3 → nonebot_plugin_savepic-0.2.5}/LICENSE +0 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: nonebot-plugin-savepic
|
|
3
|
+
Version: 0.2.5
|
|
4
|
+
Summary: 保存表情包(语录)与随机出图
|
|
5
|
+
Home-page: https://github.com/Yan-Zero/nonebot-plugin-savepic
|
|
6
|
+
License: MIT
|
|
7
|
+
Author: Yan
|
|
8
|
+
Author-email: 1964649083@qq.com
|
|
9
|
+
Requires-Python: >=3.10,<3.13
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Dist: arclet-alconna (>=1.7.14,<2.0.0)
|
|
16
|
+
Requires-Dist: asyncpg (>=0.28.0,<0.29.0)
|
|
17
|
+
Requires-Dist: dashscope (>=1.17.0,<2.0.0)
|
|
18
|
+
Requires-Dist: nonebot-adapter-onebot (>=2.2.3,<3.0.0)
|
|
19
|
+
Requires-Dist: nonebot-plugin-datastore (>=1.1.0,<2.0.0)
|
|
20
|
+
Requires-Dist: nonebot2 (>=2.0.1,<3.0.0)
|
|
21
|
+
Requires-Dist: numpy (>=1.25.2,<2.0.0)
|
|
22
|
+
Requires-Dist: pillow (>=10.0.0,<11.0.0)
|
|
23
|
+
Requires-Dist: pinecone-client (>=3.2.2,<4.0.0)
|
|
24
|
+
Requires-Dist: scipy (>=1.11.1,<2.0.0)
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
<div align="center">
|
|
28
|
+
<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>
|
|
29
|
+
<br>
|
|
30
|
+
<p><img src="https://github.com/A-kirami/nonebot-plugin-template/blob/resources/NoneBotPlugin.svg" width="240" alt="NoneBotPluginText"></p>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div align="center">
|
|
34
|
+
|
|
35
|
+
# nonebot-plugin-savepic
|
|
36
|
+
|
|
37
|
+
_✨ 一个存取图片的插件 ✨_
|
|
38
|
+
|
|
39
|
+
## 📖 介绍
|
|
40
|
+
|
|
41
|
+
重写自 Fran 的 Savepic
|
|
42
|
+
|
|
43
|
+
### savepic
|
|
44
|
+
|
|
45
|
+
保存表情包
|
|
46
|
+
|
|
47
|
+
### randpic
|
|
48
|
+
|
|
49
|
+
抽取表情包
|
|
50
|
+
|
|
51
|
+
### mvpic
|
|
52
|
+
|
|
53
|
+
重命名表情包,或者修改表情包所属的群域
|
|
54
|
+
|
|
55
|
+
例如:
|
|
56
|
+
```
|
|
57
|
+
/mvpic -l name.jpg -g waaaaa.gif
|
|
58
|
+
```
|
|
59
|
+
就是把本群的 name.jpg 改成全局名为 waaaaa.gif 的表情包
|
|
60
|
+
|
|
61
|
+
同理
|
|
62
|
+
```
|
|
63
|
+
/mvpic -g waaaaa.gif -l waaaaa.gif
|
|
64
|
+
```
|
|
65
|
+
就是从全局移到本群(接收到命令的群)
|
|
66
|
+
|
|
67
|
+
### 直接发送文件名
|
|
68
|
+
|
|
69
|
+
发送文件名即可发送表情包
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
## 💿 安装
|
|
73
|
+
|
|
74
|
+
<details open>
|
|
75
|
+
<summary>使用 nb-cli 安装</summary>
|
|
76
|
+
在 nonebot2 项目的根目录下打开命令行, 输入以下指令即可安装
|
|
77
|
+
|
|
78
|
+
nb plugin install nonebot-plugin-savepic
|
|
79
|
+
|
|
80
|
+
</details>
|
|
81
|
+
|
|
82
|
+
<details>
|
|
83
|
+
<summary>使用包管理器安装</summary>
|
|
84
|
+
在 nonebot2 项目的插件目录下, 打开命令行, 根据你使用的包管理器, 输入相应的安装命令
|
|
85
|
+
|
|
86
|
+
<details>
|
|
87
|
+
<summary>pip</summary>
|
|
88
|
+
|
|
89
|
+
pip install nonebot-plugin-savepic
|
|
90
|
+
</details>
|
|
91
|
+
<details>
|
|
92
|
+
<summary>pdm</summary>
|
|
93
|
+
|
|
94
|
+
pdm add nonebot-plugin-savepic
|
|
95
|
+
</details>
|
|
96
|
+
<details>
|
|
97
|
+
<summary>poetry</summary>
|
|
98
|
+
|
|
99
|
+
poetry add nonebot-plugin-savepic
|
|
100
|
+
</details>
|
|
101
|
+
<details>
|
|
102
|
+
<summary>conda</summary>
|
|
103
|
+
|
|
104
|
+
conda install nonebot-plugin-savepic
|
|
105
|
+
</details>
|
|
106
|
+
|
|
107
|
+
打开 nonebot2 项目根目录下的 `pyproject.toml` 文件, 在 `[tool.nonebot]` 部分追加写入
|
|
108
|
+
|
|
109
|
+
plugins = ["nonebot_plugin_savepic"]
|
|
110
|
+
|
|
111
|
+
</details>
|
|
112
|
+
|
|
113
|
+
## ⚙️ 配置
|
|
114
|
+
|
|
115
|
+
在 nonebot2 项目的`.env`文件中添加下表中的必填配置
|
|
116
|
+
|
|
117
|
+
| 配置项 | 必填 | 默认值 | 说明 |
|
|
118
|
+
|:-----:|:----:|:----:|:----:|
|
|
119
|
+
| savepic_admin | 否 | 无 | 权限用户 |
|
|
120
|
+
| savepic_dir | 否 | savepic | 图片本地保存位置 |
|
|
121
|
+
| simpic_enable | 否 | False | 是否开启基于RNN模型的AI相似度检索功能,如果开启要填写 Pinecone 相关配置 |
|
|
122
|
+
| pinecone_apikey | 否 | 无 | pinecone 的 Key |
|
|
123
|
+
| pinecone_environment | 否 | 无 | pinecone 的 enviroment,是数据库的属性之一 |
|
|
124
|
+
| pinecone_index | 否 | savepic | 同上。 |
|
|
125
|
+
| dashscope_api | 否 | 无 | dashscope 的 APIKey。 用于开启基于语义的相似度检索功能 |
|
|
126
|
+
|
|
127
|
+
## 🎉 使用
|
|
128
|
+
### 指令表
|
|
129
|
+
| 指令 | 权限 | 需要@ | 范围 | 说明 |
|
|
130
|
+
|:-----:|:----:|:----:|:----:|:----:|
|
|
131
|
+
| savepic | 群员 | 否 | 群聊 | 保存图片 |
|
|
132
|
+
| randpic | 群员 | 否 | 全部 | 随机图片 |
|
|
133
|
+
| mvpic | 管理员 | 否 | 群聊 | 重命名图片 |
|
|
134
|
+
|
|
135
|
+
|
|
@@ -1,22 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: nonebot-plugin-savepic
|
|
3
|
-
Version: 0.1.3
|
|
4
|
-
Summary: 保存表情包(语录)与随机出图
|
|
5
|
-
Home-page: https://github.com/Yan-Zero/nonebot-plugin-savepic
|
|
6
|
-
License: MIT
|
|
7
|
-
Author: Yan
|
|
8
|
-
Author-email: 1964649083@qq.com
|
|
9
|
-
Requires-Python: >=3.10,<4.0
|
|
10
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
-
Requires-Dist: asyncpg (>=0.28.0,<0.29.0)
|
|
15
|
-
Requires-Dist: nonebot-adapter-onebot (>=2.2.3,<3.0.0)
|
|
16
|
-
Requires-Dist: nonebot-plugin-datastore (>=1.1.0,<2.0.0)
|
|
17
|
-
Requires-Dist: nonebot2 (>=2.0.1,<3.0.0)
|
|
18
|
-
Description-Content-Type: text/markdown
|
|
19
|
-
|
|
20
1
|
<div align="center">
|
|
21
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>
|
|
22
3
|
<br>
|
|
@@ -41,10 +22,27 @@ _✨ 一个存取图片的插件 ✨_
|
|
|
41
22
|
|
|
42
23
|
抽取表情包
|
|
43
24
|
|
|
25
|
+
### mvpic
|
|
26
|
+
|
|
27
|
+
重命名表情包,或者修改表情包所属的群域
|
|
28
|
+
|
|
29
|
+
例如:
|
|
30
|
+
```
|
|
31
|
+
/mvpic -l name.jpg -g waaaaa.gif
|
|
32
|
+
```
|
|
33
|
+
就是把本群的 name.jpg 改成全局名为 waaaaa.gif 的表情包
|
|
34
|
+
|
|
35
|
+
同理
|
|
36
|
+
```
|
|
37
|
+
/mvpic -g waaaaa.gif -l waaaaa.gif
|
|
38
|
+
```
|
|
39
|
+
就是从全局移到本群(接收到命令的群)
|
|
40
|
+
|
|
44
41
|
### 直接发送文件名
|
|
45
42
|
|
|
46
43
|
发送文件名即可发送表情包
|
|
47
44
|
|
|
45
|
+
|
|
48
46
|
## 💿 安装
|
|
49
47
|
|
|
50
48
|
<details open>
|
|
@@ -94,11 +92,17 @@ _✨ 一个存取图片的插件 ✨_
|
|
|
94
92
|
|:-----:|:----:|:----:|:----:|
|
|
95
93
|
| savepic_admin | 否 | 无 | 权限用户 |
|
|
96
94
|
| savepic_dir | 否 | savepic | 图片本地保存位置 |
|
|
95
|
+
| simpic_enable | 否 | False | 是否开启基于RNN模型的AI相似度检索功能,如果开启要填写 Pinecone 相关配置 |
|
|
96
|
+
| pinecone_apikey | 否 | 无 | pinecone 的 Key |
|
|
97
|
+
| pinecone_environment | 否 | 无 | pinecone 的 enviroment,是数据库的属性之一 |
|
|
98
|
+
| pinecone_index | 否 | savepic | 同上。 |
|
|
99
|
+
| dashscope_api | 否 | 无 | dashscope 的 APIKey。 用于开启基于语义的相似度检索功能 |
|
|
97
100
|
|
|
98
101
|
## 🎉 使用
|
|
99
102
|
### 指令表
|
|
100
103
|
| 指令 | 权限 | 需要@ | 范围 | 说明 |
|
|
101
104
|
|:-----:|:----:|:----:|:----:|:----:|
|
|
102
|
-
| savepic | 群员 |
|
|
103
|
-
| randpic | 群员 |
|
|
105
|
+
| savepic | 群员 | 否 | 群聊 | 保存图片 |
|
|
106
|
+
| randpic | 群员 | 否 | 全部 | 随机图片 |
|
|
107
|
+
| mvpic | 管理员 | 否 | 群聊 | 重命名图片 |
|
|
104
108
|
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from nonebot import require
|
|
3
|
+
from nonebot import get_plugin_config
|
|
4
|
+
from nonebot import on_command
|
|
5
|
+
from nonebot.params import CommandArg, Arg
|
|
6
|
+
from nonebot.adapters.onebot.v11.message import Message as V11Msg
|
|
7
|
+
from nonebot.adapters.onebot.v11.message import MessageSegment as V11Seg
|
|
8
|
+
from nonebot.adapters.onebot.v11.permission import GROUP_ADMIN
|
|
9
|
+
from nonebot.adapters.onebot.v11.event import GroupMessageEvent
|
|
10
|
+
from nonebot.adapters.onebot.v11.event import MessageEvent
|
|
11
|
+
from nonebot.matcher import Matcher
|
|
12
|
+
from nonebot.plugin import PluginMetadata
|
|
13
|
+
from nonebot.dependencies import Dependent
|
|
14
|
+
from sqlalchemy.exc import DBAPIError
|
|
15
|
+
from arclet.alconna import Alconna, Option, Args, CommandMeta, append
|
|
16
|
+
import os
|
|
17
|
+
import random
|
|
18
|
+
import json
|
|
19
|
+
from typing import (
|
|
20
|
+
Any,
|
|
21
|
+
Union,
|
|
22
|
+
Callable,
|
|
23
|
+
Iterable,
|
|
24
|
+
Optional,
|
|
25
|
+
)
|
|
26
|
+
from nonebot.typing import (
|
|
27
|
+
T_State,
|
|
28
|
+
T_Handler,
|
|
29
|
+
)
|
|
30
|
+
from nonebot.consts import ARG_KEY
|
|
31
|
+
from nonebot.internal.params import Depends
|
|
32
|
+
from nonebot.internal.adapter import (
|
|
33
|
+
Bot,
|
|
34
|
+
Event,
|
|
35
|
+
Message,
|
|
36
|
+
MessageSegment,
|
|
37
|
+
MessageTemplate,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
require("nonebot_plugin_datastore")
|
|
42
|
+
|
|
43
|
+
from .error import (
|
|
44
|
+
NoPictureException,
|
|
45
|
+
SimilarPictureException,
|
|
46
|
+
SameNameException,
|
|
47
|
+
) # noqa: E402, E501
|
|
48
|
+
from .config import Config # noqa: E402
|
|
49
|
+
from .pic_sql import ( # noqa: E402
|
|
50
|
+
savepic,
|
|
51
|
+
rename,
|
|
52
|
+
delete,
|
|
53
|
+
regexp_pic,
|
|
54
|
+
listpic,
|
|
55
|
+
)
|
|
56
|
+
from .rule import PIC_AMDIN, BLACK_GROUP
|
|
57
|
+
from .ext_listener import pic_listen # noqa: E402, F401
|
|
58
|
+
from .picture import write_pic, load_pic # noqa: E402
|
|
59
|
+
from .ai_utils import img2vec
|
|
60
|
+
from .randpic import rpic
|
|
61
|
+
from .countpic import cpic
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def url_to_image(url: str):
|
|
65
|
+
if url.startswith("http"):
|
|
66
|
+
return V11Seg.image(url)
|
|
67
|
+
return V11Seg.image(Path(url))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
words = {}
|
|
71
|
+
for root, _, files in os.walk("words"):
|
|
72
|
+
for file in files:
|
|
73
|
+
if not file.endswith(".json"):
|
|
74
|
+
continue
|
|
75
|
+
with open(os.path.join(root, file), "r", encoding="utf-8") as f:
|
|
76
|
+
words.update(json.load(f))
|
|
77
|
+
|
|
78
|
+
__plugin_meta__ = PluginMetadata(
|
|
79
|
+
name="Savepic",
|
|
80
|
+
description="表情包保存",
|
|
81
|
+
usage="""用法:
|
|
82
|
+
/savepic <文件名> <图片>
|
|
83
|
+
/savepic -g <文件名> <全局图片>
|
|
84
|
+
/savepic -d <文件名> 删除图图片
|
|
85
|
+
/randpic <关键词> 随机图片
|
|
86
|
+
<文件名> 发送图片""",
|
|
87
|
+
config=Config,
|
|
88
|
+
homepage="https://github.com/Yan-Zero/nonebot-plugin-savepic",
|
|
89
|
+
type="application",
|
|
90
|
+
supported_adapters=["~onebot.v11"],
|
|
91
|
+
)
|
|
92
|
+
INVALID_FILENAME_CHARACTERS = r'\/:*?"<>|'
|
|
93
|
+
|
|
94
|
+
p_config = get_plugin_config(Config)
|
|
95
|
+
repic = on_command("repic", priority=5, permission=BLACK_GROUP)
|
|
96
|
+
spic = on_command("savepic", priority=5, permission=BLACK_GROUP)
|
|
97
|
+
a_spic = Alconna(
|
|
98
|
+
"/savepic",
|
|
99
|
+
Option("-d", help_text="删除图片"),
|
|
100
|
+
Option("-g", help_text="全局"),
|
|
101
|
+
Option("-ac", help_text="允许相似碰撞"),
|
|
102
|
+
Args.filename[str],
|
|
103
|
+
meta=CommandMeta(description="保存图片,默认保存到本群"),
|
|
104
|
+
)
|
|
105
|
+
s_mvpic = on_command("mvpic", priority=5, permission=BLACK_GROUP)
|
|
106
|
+
a_mvpic = Alconna(
|
|
107
|
+
"/mvpic",
|
|
108
|
+
Option("-l", args=Args.filename[str], help_text="本地图片", action=append),
|
|
109
|
+
Option("-g", args=Args.filename[str], help_text="全局图片", action=append),
|
|
110
|
+
meta=CommandMeta(description="重命名图片,按照参数先后判断"),
|
|
111
|
+
)
|
|
112
|
+
s_simpic = on_command("simpic", priority=5, permission=BLACK_GROUP)
|
|
113
|
+
s_listpic = on_command("listpic", priority=5, permission=BLACK_GROUP)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def got_random_prompt(
|
|
117
|
+
cls: Matcher,
|
|
118
|
+
key: str,
|
|
119
|
+
prompt: Optional[list[Union[str, Message, MessageSegment, MessageTemplate]]] = None,
|
|
120
|
+
parameterless: Optional[Iterable[Any]] = None,
|
|
121
|
+
) -> Callable[[T_Handler], T_Handler]:
|
|
122
|
+
"""装饰一个函数来指示 NoneBot 获取一个参数 `key`
|
|
123
|
+
|
|
124
|
+
当要获取的 `key` 不存在时接收用户新的一条消息再运行该函数,
|
|
125
|
+
如果 `key` 已存在则直接继续运行
|
|
126
|
+
|
|
127
|
+
参数:
|
|
128
|
+
key: 参数名
|
|
129
|
+
prompt: 在参数不存在时向用户发送的消息
|
|
130
|
+
parameterless: 非参数类型依赖列表
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
async def _key_getter(event: Event, matcher: "Matcher"):
|
|
134
|
+
matcher.set_target(ARG_KEY.format(key=key))
|
|
135
|
+
if matcher.get_target() == ARG_KEY.format(key=key):
|
|
136
|
+
matcher.set_arg(key, event.get_message())
|
|
137
|
+
return
|
|
138
|
+
if matcher.get_arg(key, ...) is not ...:
|
|
139
|
+
return
|
|
140
|
+
await matcher.reject("" if not prompt else random.choice(prompt))
|
|
141
|
+
|
|
142
|
+
_parameterless = (Depends(_key_getter), *(parameterless or ()))
|
|
143
|
+
|
|
144
|
+
def _decorator(func: T_Handler) -> T_Handler:
|
|
145
|
+
if cls.handlers and cls.handlers[-1].call is func:
|
|
146
|
+
func_handler = cls.handlers[-1]
|
|
147
|
+
new_handler = Dependent(
|
|
148
|
+
call=func_handler.call,
|
|
149
|
+
params=func_handler.params,
|
|
150
|
+
parameterless=Dependent.parse_parameterless(
|
|
151
|
+
tuple(_parameterless), cls.HANDLER_PARAM_TYPES
|
|
152
|
+
)
|
|
153
|
+
+ func_handler.parameterless,
|
|
154
|
+
)
|
|
155
|
+
cls.handlers[-1] = new_handler
|
|
156
|
+
else:
|
|
157
|
+
cls.append_handler(func, parameterless=_parameterless)
|
|
158
|
+
|
|
159
|
+
return func
|
|
160
|
+
|
|
161
|
+
return _decorator
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@repic.handle()
|
|
165
|
+
async def _(bot: Bot, event, args: V11Msg = CommandArg()):
|
|
166
|
+
reg = args.extract_plain_text().strip()
|
|
167
|
+
group_id = (
|
|
168
|
+
"globe"
|
|
169
|
+
if not isinstance(event, GroupMessageEvent)
|
|
170
|
+
else f"qq_group:{event.group_id}"
|
|
171
|
+
)
|
|
172
|
+
try:
|
|
173
|
+
pic = await regexp_pic(reg, group_id)
|
|
174
|
+
if pic:
|
|
175
|
+
await bot.send(event, V11Msg([pic.name, url_to_image(pic.url)]))
|
|
176
|
+
except DBAPIError as ex:
|
|
177
|
+
await repic.finish(
|
|
178
|
+
f'{random.choice(words.get("error", ["出错了喵~"]))}\n\n{ex.orig}'
|
|
179
|
+
)
|
|
180
|
+
except Exception as ex:
|
|
181
|
+
await repic.finish(
|
|
182
|
+
f'{random.choice(words.get("error", ["出错了喵~"]))}\n\n{ex}'
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@s_listpic.handle()
|
|
187
|
+
async def _(bot: Bot, event, args: V11Msg = CommandArg()):
|
|
188
|
+
reg = args.extract_plain_text().strip().rsplit("\\page", maxsplit=1)
|
|
189
|
+
try:
|
|
190
|
+
if len(reg) > 1:
|
|
191
|
+
reg, pages = reg
|
|
192
|
+
else:
|
|
193
|
+
reg, pages = reg[0], 0
|
|
194
|
+
pages = int(pages)
|
|
195
|
+
except Exception as ex:
|
|
196
|
+
await repic.finish(
|
|
197
|
+
f'{random.choice(words.get("error", ["出错了喵~"]))}\n\n{ex}'
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
group_id = (
|
|
201
|
+
"globe"
|
|
202
|
+
if not isinstance(event, GroupMessageEvent)
|
|
203
|
+
else f"qq_group:{event.group_id}"
|
|
204
|
+
)
|
|
205
|
+
try:
|
|
206
|
+
pic = await listpic(reg, group_id, pages=pages)
|
|
207
|
+
if pic:
|
|
208
|
+
await bot.send(event, "\n".join(pic))
|
|
209
|
+
except DBAPIError as ex:
|
|
210
|
+
await repic.finish(
|
|
211
|
+
f'{random.choice(words.get("error", ["出错了喵~"]))}\n\n{ex.orig}'
|
|
212
|
+
)
|
|
213
|
+
except Exception as ex:
|
|
214
|
+
await repic.finish(
|
|
215
|
+
f'{random.choice(words.get("error", ["出错了喵~"]))}\n\n{ex}'
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@spic.handle()
|
|
220
|
+
async def _(
|
|
221
|
+
bot: Bot,
|
|
222
|
+
matcher: Matcher,
|
|
223
|
+
event: GroupMessageEvent,
|
|
224
|
+
state: T_State,
|
|
225
|
+
):
|
|
226
|
+
command = a_spic.parse(event.message.extract_plain_text())
|
|
227
|
+
if not command.matched:
|
|
228
|
+
await spic.finish(str(command.error_info) + "\n\n" + a_spic.get_help())
|
|
229
|
+
|
|
230
|
+
state["savepiv_group"] = (
|
|
231
|
+
"globe"
|
|
232
|
+
if command.g and await PIC_AMDIN(bot, event)
|
|
233
|
+
else f"qq_group:{event.group_id}"
|
|
234
|
+
)
|
|
235
|
+
state["savepiv_warning"] = (
|
|
236
|
+
""
|
|
237
|
+
if not command.g or await PIC_AMDIN(bot, event)
|
|
238
|
+
else "\n\n你的 -g 选项没有用哟"
|
|
239
|
+
)
|
|
240
|
+
filename = command.filename
|
|
241
|
+
for c in INVALID_FILENAME_CHARACTERS:
|
|
242
|
+
filename = filename.replace(c, "-")
|
|
243
|
+
if not filename.endswith((".jpg", ".png", ".gif")):
|
|
244
|
+
filename += ".jpg"
|
|
245
|
+
state["savepiv_filename"] = filename
|
|
246
|
+
state["savepiv_ac"] = command.ac is not None
|
|
247
|
+
|
|
248
|
+
if command.d:
|
|
249
|
+
if not (await PIC_AMDIN(bot, event) or await GROUP_ADMIN(bot, event)):
|
|
250
|
+
await spic.finish(
|
|
251
|
+
random.choice(words.get("permission denied", ["没有权限"]))
|
|
252
|
+
)
|
|
253
|
+
try:
|
|
254
|
+
await delete(filename, state["savepiv_group"])
|
|
255
|
+
except Exception as ex:
|
|
256
|
+
await spic.finish(str(ex))
|
|
257
|
+
await spic.finish("图片已删除" + state["savepiv_warning"])
|
|
258
|
+
|
|
259
|
+
picture = event.message.get("image")
|
|
260
|
+
if not picture and event.reply:
|
|
261
|
+
picture = event.reply.message.get("image")
|
|
262
|
+
if picture:
|
|
263
|
+
matcher.set_arg("picture", picture)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
@got_random_prompt(spic, "picture", words.get("wait for image", ["图呢"]))
|
|
267
|
+
async def _(state: T_State, picture: V11Msg = Arg()):
|
|
268
|
+
picture = picture.get("image")
|
|
269
|
+
if not picture:
|
|
270
|
+
await spic.finish(random.choice(words.get("not image", ["6,这也不是图啊"])))
|
|
271
|
+
|
|
272
|
+
try:
|
|
273
|
+
dir = await write_pic(picture[0].data["url"], p_config.savepic_dir)
|
|
274
|
+
except Exception as ex:
|
|
275
|
+
await spic.finish("存图失败。" + "\n" + str(ex))
|
|
276
|
+
try:
|
|
277
|
+
await savepic(
|
|
278
|
+
state["savepiv_filename"],
|
|
279
|
+
dir,
|
|
280
|
+
img2vec(await load_pic(dir), state["savepiv_filename"]),
|
|
281
|
+
state["savepiv_group"],
|
|
282
|
+
state["savepiv_ac"],
|
|
283
|
+
)
|
|
284
|
+
except SameNameException:
|
|
285
|
+
os.remove(dir)
|
|
286
|
+
await spic.finish(
|
|
287
|
+
random.choice(words.get("name has been taken", ["文件名重复"]))
|
|
288
|
+
)
|
|
289
|
+
except SimilarPictureException as ex:
|
|
290
|
+
os.remove(dir)
|
|
291
|
+
try:
|
|
292
|
+
image = await load_pic(ex.url)
|
|
293
|
+
except Exception as exc:
|
|
294
|
+
await spic.finish(
|
|
295
|
+
f'{random.choice(words.get("error", ["出错了喵~"]))}\n\n{exc}'
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
await spic.finish(
|
|
299
|
+
V11Msg(
|
|
300
|
+
[
|
|
301
|
+
random.choice(words.get("similar picture", ["存在相似图片"])),
|
|
302
|
+
"\n\n" + ex.name,
|
|
303
|
+
f"\n(相似度:{'%.4g' % (min(ex.similarity * 100, 100.0))}%)\n",
|
|
304
|
+
V11Seg.image(file=image),
|
|
305
|
+
]
|
|
306
|
+
)
|
|
307
|
+
)
|
|
308
|
+
except Exception as ex:
|
|
309
|
+
os.remove(dir)
|
|
310
|
+
await spic.finish(f'{random.choice(words.get("error", ["出错了喵~"]))}\n\n{ex}')
|
|
311
|
+
await spic.send(
|
|
312
|
+
random.choice(words.get("save succeed", ["保存成功"]))
|
|
313
|
+
+ state["savepiv_warning"]
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
@s_mvpic.handle()
|
|
318
|
+
async def _(
|
|
319
|
+
bot: Bot,
|
|
320
|
+
event: GroupMessageEvent,
|
|
321
|
+
):
|
|
322
|
+
if not (await PIC_AMDIN(bot, event) or await GROUP_ADMIN(bot, event)):
|
|
323
|
+
await spic.finish(random.choice(words.get("permission denied", ["没有权限"])))
|
|
324
|
+
|
|
325
|
+
cmd = a_mvpic.parse(event.message.extract_plain_text())
|
|
326
|
+
if not cmd.matched:
|
|
327
|
+
await s_mvpic.finish(str(cmd.error_info) + "\n\n" + a_mvpic.get_help())
|
|
328
|
+
options = cmd.options
|
|
329
|
+
if not options:
|
|
330
|
+
await s_mvpic.finish("文件名呢?" + "\n\n" + a_mvpic.get_help())
|
|
331
|
+
|
|
332
|
+
sg = "globe" if list(options.keys())[0] == "g" else f"qq_group:{event.group_id}"
|
|
333
|
+
if len(options["g" if sg == "globe" else "l"].args.get("filename", [])) >= 2:
|
|
334
|
+
dg = sg
|
|
335
|
+
else:
|
|
336
|
+
dg = "globe" if sg != "globe" else f"qq_group:{event.group_id}"
|
|
337
|
+
if ("g" if dg == "globe" else "l") not in options:
|
|
338
|
+
await s_mvpic.finish("至多只有一个文件名哦?" + "\n\n" + a_mvpic.get_help())
|
|
339
|
+
|
|
340
|
+
if (not await PIC_AMDIN(bot, event)) and await GROUP_ADMIN(bot, event):
|
|
341
|
+
if sg == "globe":
|
|
342
|
+
await spic.finish("管理员不能改全局名称哦~")
|
|
343
|
+
if dg == "globe":
|
|
344
|
+
await spic.finish("管理员不能改全局名称哦~")
|
|
345
|
+
|
|
346
|
+
sname = options["g" if sg == "globe" else "l"].args["filename"][0]
|
|
347
|
+
dname = options["g" if dg == "globe" else "l"].args["filename"][
|
|
348
|
+
1 if sg == dg else 0
|
|
349
|
+
]
|
|
350
|
+
for c in INVALID_FILENAME_CHARACTERS:
|
|
351
|
+
sname = sname.replace(c, "-")
|
|
352
|
+
dname = dname.replace(c, "-")
|
|
353
|
+
if not sname.endswith((".jpg", ".png", ".gif")):
|
|
354
|
+
sname += ".jpg"
|
|
355
|
+
if not dname.endswith((".jpg", ".png", ".gif")):
|
|
356
|
+
dname += ".jpg"
|
|
357
|
+
|
|
358
|
+
try:
|
|
359
|
+
await rename(sname, dname, sg, dg)
|
|
360
|
+
except NoPictureException as ex:
|
|
361
|
+
await spic.finish(
|
|
362
|
+
random.choice(words.get("not found", [ex.name + " 没有找到哦"]))
|
|
363
|
+
+ f"\n{ex.name} 没有找到哦"
|
|
364
|
+
)
|
|
365
|
+
except SameNameException:
|
|
366
|
+
await spic.finish(
|
|
367
|
+
random.choice(words.get("name has been taken", ["文件名重复"]))
|
|
368
|
+
)
|
|
369
|
+
except Exception as ex:
|
|
370
|
+
await spic.finish(f'{random.choice(words.get("error", ["出错了喵~"]))}\n\n{ex}')
|
|
371
|
+
await spic.finish(random.choice(words.get("rename succeed", ["图片已重命名"])))
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
@s_simpic.handle()
|
|
375
|
+
async def _(bot: Bot, event: MessageEvent, args: V11Msg = CommandArg()):
|
|
376
|
+
if True:
|
|
377
|
+
await s_simpic.finish("simpic 并未开启喵。")
|
|
378
|
+
|
|
379
|
+
picture = event.reply.message.get("image") if event.reply else None
|
|
380
|
+
if not picture:
|
|
381
|
+
await s_simpic.finish(
|
|
382
|
+
random.choice(
|
|
383
|
+
words.get("missing image reference", ["没找到引用消息中的图片"])
|
|
384
|
+
)
|
|
385
|
+
)
|
|
386
|
+
vec = img2vec(await load_pic(picture[0].data["url"]))
|
|
387
|
+
ignore_diagonal = True
|
|
388
|
+
ignore_min = False
|
|
389
|
+
if args.extract_plain_text().strip() == "-n":
|
|
390
|
+
ignore_diagonal = False
|
|
391
|
+
elif args.extract_plain_text().strip() == "-i":
|
|
392
|
+
ignore_min = True
|
|
393
|
+
group_id = (
|
|
394
|
+
"globe"
|
|
395
|
+
if not isinstance(event, GroupMessageEvent)
|
|
396
|
+
else f"qq_group:{event.group_id}"
|
|
397
|
+
)
|
|
398
|
+
try:
|
|
399
|
+
sim, pic = await get_most_similar_pic(
|
|
400
|
+
vec, group_id, ignore_diagonal, ignore_min
|
|
401
|
+
)
|
|
402
|
+
if pic:
|
|
403
|
+
await bot.send(
|
|
404
|
+
event,
|
|
405
|
+
V11Msg(
|
|
406
|
+
[
|
|
407
|
+
V11Seg.reply(event.reply.message_id),
|
|
408
|
+
f"{pic.name}\n(相似性:{'%.4g' % (min(sim * 100, 100.0))}%)\n",
|
|
409
|
+
url_to_image(pic.url),
|
|
410
|
+
]
|
|
411
|
+
),
|
|
412
|
+
)
|
|
413
|
+
else:
|
|
414
|
+
await s_simpic.send(random.choice(words.get("not found", ["Nope."])))
|
|
415
|
+
except Exception as ex:
|
|
416
|
+
await s_simpic.finish(
|
|
417
|
+
f'{random.choice(words.get("error", ["出错了喵~"]))}\n\n{ex}'
|
|
418
|
+
)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import tempfile
|
|
2
|
+
import hashlib
|
|
3
|
+
import pathlib
|
|
4
|
+
|
|
5
|
+
from nonebot import get_plugin_config
|
|
6
|
+
from dashscope import MultiModalEmbedding
|
|
7
|
+
from dashscope import TextEmbedding
|
|
8
|
+
from http import HTTPStatus
|
|
9
|
+
|
|
10
|
+
from .config import Config
|
|
11
|
+
from .picture import _emo_same
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
plugin_config = get_plugin_config(Config)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def file2vec(path: pathlib.Path, title: str = None) -> list:
|
|
18
|
+
input = [
|
|
19
|
+
{
|
|
20
|
+
"factor": 5,
|
|
21
|
+
"image": path.absolute().as_uri(),
|
|
22
|
+
},
|
|
23
|
+
]
|
|
24
|
+
if title:
|
|
25
|
+
input.append(
|
|
26
|
+
{
|
|
27
|
+
"factor": 1,
|
|
28
|
+
"text": title,
|
|
29
|
+
},
|
|
30
|
+
)
|
|
31
|
+
resp = MultiModalEmbedding.call(
|
|
32
|
+
model=MultiModalEmbedding.Models.multimodal_embedding_one_peace_v1,
|
|
33
|
+
input=input,
|
|
34
|
+
auto_truncation=True,
|
|
35
|
+
)
|
|
36
|
+
if resp.status_code != HTTPStatus.OK:
|
|
37
|
+
raise RuntimeError("Dashscope API Error")
|
|
38
|
+
return resp.output["embedding"]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def img2vec(img: bytes, title: str = None) -> list:
|
|
42
|
+
"""1536 D"""
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
if plugin_config.simpic_model.lower() == "one-peach":
|
|
46
|
+
path = pathlib.Path(tempfile.gettempdir) / "img2vec"
|
|
47
|
+
if not path.exists():
|
|
48
|
+
path.mkdir()
|
|
49
|
+
path /= hashlib.sha256(img).hexdigest() + ".png"
|
|
50
|
+
with open(path, "wb+") as f:
|
|
51
|
+
f.write(img)
|
|
52
|
+
|
|
53
|
+
return file2vec(path=path, title=title)
|
|
54
|
+
|
|
55
|
+
if not _emo_same:
|
|
56
|
+
return None
|
|
57
|
+
return _emo_same.quantify_tolist(img)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def word2vec(word: str) -> list[float]:
|
|
61
|
+
resp = TextEmbedding.call(
|
|
62
|
+
model=TextEmbedding.Models.text_embedding_v2, input=word, text_type="query"
|
|
63
|
+
)
|
|
64
|
+
if resp.status_code != HTTPStatus.OK:
|
|
65
|
+
raise RuntimeError("Dashscope API Error")
|
|
66
|
+
return resp.output["embeddings"][0]["embedding"]
|