nonebot-plugin-savepic 0.2.0__tar.gz → 0.2.6__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 (23) hide show
  1. {nonebot_plugin_savepic-0.2.0 → nonebot_plugin_savepic-0.2.6}/PKG-INFO +30 -3
  2. {nonebot_plugin_savepic-0.2.0 → nonebot_plugin_savepic-0.2.6}/README.md +26 -2
  3. nonebot_plugin_savepic-0.2.6/nonebot_plugin_savepic/__init__.py +340 -0
  4. nonebot_plugin_savepic-0.2.6/nonebot_plugin_savepic/ai_utils.py +66 -0
  5. nonebot_plugin_savepic-0.2.6/nonebot_plugin_savepic/config.py +35 -0
  6. nonebot_plugin_savepic-0.2.6/nonebot_plugin_savepic/countpic.py +22 -0
  7. nonebot_plugin_savepic-0.2.6/nonebot_plugin_savepic/error.py +24 -0
  8. {nonebot_plugin_savepic-0.2.0 → nonebot_plugin_savepic-0.2.6}/nonebot_plugin_savepic/ext_listener.py +5 -13
  9. nonebot_plugin_savepic-0.2.6/nonebot_plugin_savepic/model.py +108 -0
  10. nonebot_plugin_savepic-0.2.6/nonebot_plugin_savepic/mvpic.py +145 -0
  11. nonebot_plugin_savepic-0.2.6/nonebot_plugin_savepic/networks/__init__.py +0 -0
  12. nonebot_plugin_savepic-0.2.6/nonebot_plugin_savepic/networks/resnet_big.py +242 -0
  13. nonebot_plugin_savepic-0.2.6/nonebot_plugin_savepic/pic_sql.py +397 -0
  14. {nonebot_plugin_savepic-0.2.0 → nonebot_plugin_savepic-0.2.6}/nonebot_plugin_savepic/picture.py +40 -11
  15. nonebot_plugin_savepic-0.2.6/nonebot_plugin_savepic/randpic.py +47 -0
  16. {nonebot_plugin_savepic-0.2.0 → nonebot_plugin_savepic-0.2.6}/nonebot_plugin_savepic/rule.py +23 -4
  17. {nonebot_plugin_savepic-0.2.0 → nonebot_plugin_savepic-0.2.6}/pyproject.toml +3 -1
  18. nonebot_plugin_savepic-0.2.0/nonebot_plugin_savepic/__init__.py +0 -190
  19. nonebot_plugin_savepic-0.2.0/nonebot_plugin_savepic/config.py +0 -9
  20. nonebot_plugin_savepic-0.2.0/nonebot_plugin_savepic/migrations/1a654e02179b_.py +0 -34
  21. nonebot_plugin_savepic-0.2.0/nonebot_plugin_savepic/model.py +0 -26
  22. nonebot_plugin_savepic-0.2.0/nonebot_plugin_savepic/pic_sql.py +0 -219
  23. {nonebot_plugin_savepic-0.2.0 → nonebot_plugin_savepic-0.2.6}/LICENSE +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nonebot-plugin-savepic
3
- Version: 0.2.0
3
+ Version: 0.2.6
4
4
  Summary: 保存表情包(语录)与随机出图
5
5
  Home-page: https://github.com/Yan-Zero/nonebot-plugin-savepic
6
6
  License: MIT
@@ -11,13 +11,16 @@ Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Programming Language :: Python :: 3.10
13
13
  Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
14
15
  Requires-Dist: arclet-alconna (>=1.7.14,<2.0.0)
15
16
  Requires-Dist: asyncpg (>=0.28.0,<0.29.0)
17
+ Requires-Dist: dashscope (>=1.17.0,<2.0.0)
16
18
  Requires-Dist: nonebot-adapter-onebot (>=2.2.3,<3.0.0)
17
19
  Requires-Dist: nonebot-plugin-datastore (>=1.1.0,<2.0.0)
18
20
  Requires-Dist: nonebot2 (>=2.0.1,<3.0.0)
19
21
  Requires-Dist: numpy (>=1.25.2,<2.0.0)
20
22
  Requires-Dist: pillow (>=10.0.0,<11.0.0)
23
+ Requires-Dist: pinecone-client (>=3.2.2,<4.0.0)
21
24
  Requires-Dist: scipy (>=1.11.1,<2.0.0)
22
25
  Description-Content-Type: text/markdown
23
26
 
@@ -45,10 +48,27 @@ _✨ 一个存取图片的插件 ✨_
45
48
 
46
49
  抽取表情包
47
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
+
48
67
  ### 直接发送文件名
49
68
 
50
69
  发送文件名即可发送表情包
51
70
 
71
+
52
72
  ## 💿 安装
53
73
 
54
74
  <details open>
@@ -98,11 +118,18 @@ _✨ 一个存取图片的插件 ✨_
98
118
  |:-----:|:----:|:----:|:----:|
99
119
  | savepic_admin | 否 | 无 | 权限用户 |
100
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。 用于开启基于语义的相似度检索功能 |
101
126
 
102
127
  ## 🎉 使用
103
128
  ### 指令表
104
129
  | 指令 | 权限 | 需要@ | 范围 | 说明 |
105
130
  |:-----:|:----:|:----:|:----:|:----:|
106
- | savepic | 群员 | 是 | 群聊 | 保存图片 |
107
- | randpic | 群员 | 是 | 全部 | 随机图片 |
131
+ | savepic | 群员 | 否 | 群聊 | 保存图片 |
132
+ | randpic | 群员 | 否 | 全部 | 随机图片 |
133
+ | mvpic | 管理员 | 否 | 群聊 | 重命名图片 |
134
+
108
135
 
@@ -22,10 +22,27 @@ _✨ 一个存取图片的插件 ✨_
22
22
 
23
23
  抽取表情包
24
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
+
25
41
  ### 直接发送文件名
26
42
 
27
43
  发送文件名即可发送表情包
28
44
 
45
+
29
46
  ## 💿 安装
30
47
 
31
48
  <details open>
@@ -75,10 +92,17 @@ _✨ 一个存取图片的插件 ✨_
75
92
  |:-----:|:----:|:----:|:----:|
76
93
  | savepic_admin | 否 | 无 | 权限用户 |
77
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。 用于开启基于语义的相似度检索功能 |
78
100
 
79
101
  ## 🎉 使用
80
102
  ### 指令表
81
103
  | 指令 | 权限 | 需要@ | 范围 | 说明 |
82
104
  |:-----:|:----:|:----:|:----:|:----:|
83
- | savepic | 群员 | 是 | 群聊 | 保存图片 |
84
- | randpic | 群员 | 是 | 全部 | 随机图片 |
105
+ | savepic | 群员 | 否 | 群聊 | 保存图片 |
106
+ | randpic | 群员 | 否 | 全部 | 随机图片 |
107
+ | mvpic | 管理员 | 否 | 群聊 | 重命名图片 |
108
+
@@ -0,0 +1,340 @@
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.event import GroupMessageEvent
9
+ from nonebot.adapters.onebot.v11.event import MessageEvent
10
+ from nonebot.matcher import Matcher
11
+ from nonebot.plugin import PluginMetadata
12
+ from nonebot.dependencies import Dependent
13
+ from sqlalchemy.exc import DBAPIError
14
+ from arclet.alconna import Alconna, Option, Args, CommandMeta, append
15
+ import os
16
+ import random
17
+ from typing import (
18
+ Any,
19
+ Union,
20
+ Callable,
21
+ Iterable,
22
+ Optional,
23
+ )
24
+ from nonebot.typing import (
25
+ T_State,
26
+ T_Handler,
27
+ )
28
+ from nonebot.consts import ARG_KEY
29
+ from nonebot.internal.params import Depends
30
+ from nonebot.internal.adapter import (
31
+ Bot,
32
+ Event,
33
+ Message,
34
+ MessageSegment,
35
+ MessageTemplate,
36
+ )
37
+
38
+
39
+ require("nonebot_plugin_datastore")
40
+
41
+ from .error import (
42
+ SimilarPictureException,
43
+ SameNameException,
44
+ ) # noqa: E402, E501
45
+ from .config import Config
46
+ from .config import WORDS
47
+ from .pic_sql import ( # noqa: E402
48
+ savepic,
49
+ rename,
50
+ delete,
51
+ regexp_pic,
52
+ listpic,
53
+ )
54
+ from .rule import PIC_AMDIN
55
+ from .rule import BLACK_GROUP
56
+ from .rule import GROUP_ADMIN
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 url_to_image
61
+ from .countpic import cpic
62
+ from .mvpic import INVALID_FILENAME_CHARACTERS
63
+
64
+
65
+ __plugin_meta__ = PluginMetadata(
66
+ name="Savepic",
67
+ description="表情包保存",
68
+ usage="""用法:
69
+ /savepic <文件名> <图片>
70
+ /savepic -g <文件名> <全局图片>
71
+ /savepic -d <文件名> 删除图图片
72
+ /randpic <关键词> 随机图片
73
+ <文件名> 发送图片""",
74
+ config=Config,
75
+ homepage="https://github.com/Yan-Zero/nonebot-plugin-savepic",
76
+ type="application",
77
+ supported_adapters=["~onebot.v11"],
78
+ )
79
+
80
+ p_config = get_plugin_config(Config)
81
+ repic = on_command("repic", priority=5, permission=BLACK_GROUP)
82
+ spic = on_command("savepic", priority=5, permission=BLACK_GROUP)
83
+ a_spic = Alconna(
84
+ "/savepic",
85
+ Option("-d", help_text="删除图片"),
86
+ Option("-g", help_text="全局"),
87
+ Option("-ac", help_text="允许相似碰撞"),
88
+ Args.filename[str],
89
+ meta=CommandMeta(description="保存图片,默认保存到本群"),
90
+ )
91
+ s_simpic = on_command("simpic", priority=5, permission=BLACK_GROUP)
92
+ s_listpic = on_command("listpic", priority=5, permission=BLACK_GROUP)
93
+
94
+
95
+ def got_random_prompt(
96
+ cls: Matcher,
97
+ key: str,
98
+ prompt: Optional[list[Union[str, Message, MessageSegment, MessageTemplate]]] = None,
99
+ parameterless: Optional[Iterable[Any]] = None,
100
+ ) -> Callable[[T_Handler], T_Handler]:
101
+ """装饰一个函数来指示 NoneBot 获取一个参数 `key`
102
+
103
+ 当要获取的 `key` 不存在时接收用户新的一条消息再运行该函数,
104
+ 如果 `key` 已存在则直接继续运行
105
+
106
+ 参数:
107
+ key: 参数名
108
+ prompt: 在参数不存在时向用户发送的消息
109
+ parameterless: 非参数类型依赖列表
110
+ """
111
+
112
+ async def _key_getter(event: Event, matcher: "Matcher"):
113
+ matcher.set_target(ARG_KEY.format(key=key))
114
+ if matcher.get_target() == ARG_KEY.format(key=key):
115
+ matcher.set_arg(key, event.get_message())
116
+ return
117
+ if matcher.get_arg(key, ...) is not ...:
118
+ return
119
+ await matcher.reject("" if not prompt else random.choice(prompt))
120
+
121
+ _parameterless = (Depends(_key_getter), *(parameterless or ()))
122
+
123
+ def _decorator(func: T_Handler) -> T_Handler:
124
+ if cls.handlers and cls.handlers[-1].call is func:
125
+ func_handler = cls.handlers[-1]
126
+ new_handler = Dependent(
127
+ call=func_handler.call,
128
+ params=func_handler.params,
129
+ parameterless=Dependent.parse_parameterless(
130
+ tuple(_parameterless), cls.HANDLER_PARAM_TYPES
131
+ )
132
+ + func_handler.parameterless,
133
+ )
134
+ cls.handlers[-1] = new_handler
135
+ else:
136
+ cls.append_handler(func, parameterless=_parameterless)
137
+
138
+ return func
139
+
140
+ return _decorator
141
+
142
+
143
+ @repic.handle()
144
+ async def _(bot: Bot, event, args: V11Msg = CommandArg()):
145
+ reg = args.extract_plain_text().strip()
146
+ group_id = (
147
+ "globe"
148
+ if not isinstance(event, GroupMessageEvent)
149
+ else f"qq_group:{event.group_id}"
150
+ )
151
+ try:
152
+ pic = await regexp_pic(reg, group_id)
153
+ if pic:
154
+ await bot.send(event, V11Msg([pic.name, url_to_image(pic.url)]))
155
+ except DBAPIError as ex:
156
+ await repic.finish(
157
+ f'{random.choice(WORDS.get("error", ["出错了喵~"]))}\n\n{ex.orig}'
158
+ )
159
+ except Exception as ex:
160
+ await repic.finish(
161
+ f'{random.choice(WORDS.get("error", ["出错了喵~"]))}\n\n{ex}'
162
+ )
163
+
164
+
165
+ @s_listpic.handle()
166
+ async def _(bot: Bot, event, args: V11Msg = CommandArg()):
167
+ reg = args.extract_plain_text().strip().rsplit("\\page", maxsplit=1)
168
+ try:
169
+ if len(reg) > 1:
170
+ reg, pages = reg
171
+ else:
172
+ reg, pages = reg[0], 0
173
+ pages = int(pages)
174
+ except Exception as ex:
175
+ await repic.finish(
176
+ f'{random.choice(WORDS.get("error", ["出错了喵~"]))}\n\n{ex}'
177
+ )
178
+
179
+ group_id = (
180
+ "globe"
181
+ if not isinstance(event, GroupMessageEvent)
182
+ else f"qq_group:{event.group_id}"
183
+ )
184
+ try:
185
+ pic = await listpic(reg, group_id, pages=pages)
186
+ if pic:
187
+ await bot.send(event, "\n".join(pic))
188
+ except DBAPIError as ex:
189
+ await repic.finish(
190
+ f'{random.choice(WORDS.get("error", ["出错了喵~"]))}\n\n{ex.orig}'
191
+ )
192
+ except Exception as ex:
193
+ await repic.finish(
194
+ f'{random.choice(WORDS.get("error", ["出错了喵~"]))}\n\n{ex}'
195
+ )
196
+
197
+
198
+ @spic.handle()
199
+ async def _(
200
+ bot: Bot,
201
+ matcher: Matcher,
202
+ event: GroupMessageEvent,
203
+ state: T_State,
204
+ ):
205
+ command = a_spic.parse(event.message.extract_plain_text())
206
+ if not command.matched:
207
+ await spic.finish(str(command.error_info) + "\n\n" + a_spic.get_help())
208
+
209
+ state["savepiv_group"] = (
210
+ "globe"
211
+ if command.g and await PIC_AMDIN(bot, event)
212
+ else f"qq_group:{event.group_id}"
213
+ )
214
+ state["savepiv_warning"] = (
215
+ ""
216
+ if not command.g or await PIC_AMDIN(bot, event)
217
+ else "\n\n你的 -g 选项没有用哟"
218
+ )
219
+ filename = command.filename
220
+ for c in INVALID_FILENAME_CHARACTERS:
221
+ filename = filename.replace(c, "-")
222
+ if not filename.endswith((".jpg", ".png", ".gif")):
223
+ filename += ".jpg"
224
+ state["savepiv_filename"] = filename
225
+ state["savepiv_ac"] = command.ac is not None
226
+
227
+ if command.d:
228
+ if not (await PIC_AMDIN(bot, event) or await GROUP_ADMIN(bot, event)):
229
+ await spic.finish(
230
+ random.choice(WORDS.get("permission denied", ["没有权限"]))
231
+ )
232
+ try:
233
+ await delete(filename, state["savepiv_group"])
234
+ except Exception as ex:
235
+ await spic.finish(str(ex))
236
+ await spic.finish("图片已删除" + state["savepiv_warning"])
237
+
238
+ picture = event.message.get("image")
239
+ if not picture and event.reply:
240
+ picture = event.reply.message.get("image")
241
+ if picture:
242
+ matcher.set_arg("picture", picture)
243
+
244
+
245
+ @got_random_prompt(spic, "picture", WORDS.get("wait for image", ["图呢"]))
246
+ async def _(state: T_State, picture: V11Msg = Arg()):
247
+ picture = picture.get("image")
248
+ if not picture:
249
+ await spic.finish(random.choice(WORDS.get("not image", ["6,这也不是图啊"])))
250
+
251
+ try:
252
+ dir = await write_pic(picture[0].data["url"], p_config.savepic_dir)
253
+ except Exception as ex:
254
+ await spic.finish("存图失败。" + "\n" + str(ex))
255
+ try:
256
+ await savepic(
257
+ state["savepiv_filename"],
258
+ dir,
259
+ img2vec(await load_pic(dir), state["savepiv_filename"]),
260
+ state["savepiv_group"],
261
+ state["savepiv_ac"],
262
+ )
263
+ except SameNameException:
264
+ os.remove(dir)
265
+ await spic.finish(
266
+ random.choice(WORDS.get("name has been taken", ["文件名重复"]))
267
+ )
268
+ except SimilarPictureException as ex:
269
+ os.remove(dir)
270
+ try:
271
+ image = await load_pic(ex.url)
272
+ except Exception as exc:
273
+ await spic.finish(
274
+ f'{random.choice(WORDS.get("error", ["出错了喵~"]))}\n\n{exc}'
275
+ )
276
+
277
+ await spic.finish(
278
+ V11Msg(
279
+ [
280
+ random.choice(WORDS.get("similar picture", ["存在相似图片"])),
281
+ "\n\n" + ex.name,
282
+ f"\n(相似度:{'%.4g' % (min(ex.similarity * 100, 100.0))}%)\n",
283
+ V11Seg.image(file=image),
284
+ ]
285
+ )
286
+ )
287
+ except Exception as ex:
288
+ os.remove(dir)
289
+ await spic.finish(f'{random.choice(WORDS.get("error", ["出错了喵~"]))}\n\n{ex}')
290
+ await spic.send(
291
+ random.choice(WORDS.get("save succeed", ["保存成功"]))
292
+ + state["savepiv_warning"]
293
+ )
294
+
295
+
296
+ @s_simpic.handle()
297
+ async def _(bot: Bot, event: MessageEvent, args: V11Msg = CommandArg()):
298
+ if True:
299
+ await s_simpic.finish("simpic 并未开启喵。")
300
+
301
+ picture = event.reply.message.get("image") if event.reply else None
302
+ if not picture:
303
+ await s_simpic.finish(
304
+ random.choice(
305
+ WORDS.get("missing image reference", ["没找到引用消息中的图片"])
306
+ )
307
+ )
308
+ vec = img2vec(await load_pic(picture[0].data["url"]))
309
+ ignore_diagonal = True
310
+ ignore_min = False
311
+ if args.extract_plain_text().strip() == "-n":
312
+ ignore_diagonal = False
313
+ elif args.extract_plain_text().strip() == "-i":
314
+ ignore_min = True
315
+ group_id = (
316
+ "globe"
317
+ if not isinstance(event, GroupMessageEvent)
318
+ else f"qq_group:{event.group_id}"
319
+ )
320
+ try:
321
+ sim, pic = await get_most_similar_pic(
322
+ vec, group_id, ignore_diagonal, ignore_min
323
+ )
324
+ if pic:
325
+ await bot.send(
326
+ event,
327
+ V11Msg(
328
+ [
329
+ V11Seg.reply(event.reply.message_id),
330
+ f"{pic.name}\n(相似性:{'%.4g' % (min(sim * 100, 100.0))}%)\n",
331
+ url_to_image(pic.url),
332
+ ]
333
+ ),
334
+ )
335
+ else:
336
+ await s_simpic.send(random.choice(WORDS.get("not found", ["Nope."])))
337
+ except Exception as ex:
338
+ await s_simpic.finish(
339
+ f'{random.choice(WORDS.get("error", ["出错了喵~"]))}\n\n{ex}'
340
+ )
@@ -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"]
@@ -0,0 +1,35 @@
1
+ import os
2
+ import json
3
+ from pydantic import BaseModel
4
+
5
+
6
+ WORDS = {}
7
+ for root, _, files in os.walk("words"):
8
+ for file in files:
9
+ if not file.endswith(".json"):
10
+ continue
11
+ with open(os.path.join(root, file), "r", encoding="utf-8") as f:
12
+ WORDS.update(json.load(f))
13
+
14
+
15
+ class Config(BaseModel):
16
+ """Plugin Config Here"""
17
+
18
+ savepic_admin: list[str] = []
19
+ savepic_dir: str = "savepic"
20
+ savepic_sqlurl: str
21
+
22
+ pinecone_apikey: str
23
+ pinecone_index: str = "savepic"
24
+ pinecone_environment: str
25
+
26
+ dashscope_api: str
27
+ simpic_enable: bool = False
28
+ simpic_model: str = "one-peach"
29
+
30
+ p_model_path: str = "networks/ckpt_epoch_100_rein.pth"
31
+ q_model_path: str = "networks/ckpt_epoch_100_rein.pth.qt.pth"
32
+ # 基于模型的相似度判断
33
+
34
+ embedding_sqlurl: str
35
+ black_group: list[str]
@@ -0,0 +1,22 @@
1
+ from nonebot import on_command
2
+ from nonebot.params import CommandArg
3
+ from nonebot.adapters.onebot.v11.event import GroupMessageEvent as V11GME
4
+ from sqlalchemy.exc import DBAPIError
5
+ from .rule import BLACK_GROUP
6
+ from .pic_sql import countpic
7
+
8
+ cpic = on_command("countpic", priority=5, permission=BLACK_GROUP)
9
+
10
+
11
+ @cpic.handle()
12
+ async def _(bot, event, args=CommandArg()):
13
+ reg = args.extract_plain_text().strip()
14
+ group_id = (
15
+ "globe" if not isinstance(event, V11GME) else f"qq_group:{event.group_id}"
16
+ )
17
+ try:
18
+ await bot.send(event, f"共查找到 {await countpic(reg, group_id)} 张图片。")
19
+ except DBAPIError as ex:
20
+ await cpic.finish(f"出错了喵~\n\n{ex.orig}")
21
+ except Exception as ex:
22
+ await cpic.finish(f"出错了喵~\n\n{ex}")
@@ -0,0 +1,24 @@
1
+ class SameNameException(Exception):
2
+ def __init__(self, name):
3
+ self.name = name
4
+
5
+ def __str__(self):
6
+ return "Name {} already exists".format(self.name)
7
+
8
+
9
+ class SimilarPictureException(Exception):
10
+ def __init__(self, name, similarity: float, url: str):
11
+ self.name = name
12
+ self.similarity = max(min(1, similarity), 0)
13
+ self.url = url
14
+
15
+ def __str__(self):
16
+ return "Picture {} is similar to {}%".format(self.name, self.similarity * 100)
17
+
18
+
19
+ class NoPictureException(Exception):
20
+ def __init__(self, name):
21
+ self.name = name
22
+
23
+ def __str__(self):
24
+ return "Picture {} not found".format(self.name)
@@ -1,25 +1,17 @@
1
- from nonebot import on_message
1
+ from nonebot.plugin import on_endswith
2
2
  from nonebot.adapters.onebot.v11.event import MessageEvent
3
3
  from nonebot.internal.adapter.bot import Bot
4
4
  from nonebot.adapters.onebot.v11.message import MessageSegment as V11Seg
5
5
  from nonebot.adapters.onebot.v11.event import GroupMessageEvent
6
+ from .pic_sql import select_pic
7
+ from .picture import load_pic
6
8
 
7
- from .pic_sql import load_pic, select_pic
8
-
9
-
10
- async def endswith_pic(event: MessageEvent):
11
- text = event.message.extract_plain_text().strip()
12
- if " " in text:
13
- return False
14
- return text.endswith((".jpg", ".png", ".gif"))
15
-
16
-
17
- pic_listen = on_message(rule=endswith_pic)
9
+ pic_listen = on_endswith((".jpg", ".png", ".gif"), priority=50, block=False)
18
10
 
19
11
 
20
12
  @pic_listen.handle()
21
13
  async def _(bot: Bot, event: MessageEvent):
22
- name = event.message.extract_plain_text().strip()
14
+ name = event.get_plaintext().strip()
23
15
  group_id = (
24
16
  "globe"
25
17
  if not isinstance(event, GroupMessageEvent)