nonebot-plugin-savepic 0.2.6__tar.gz → 0.3.0__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 (19) hide show
  1. {nonebot_plugin_savepic-0.2.6 → nonebot_plugin_savepic-0.3.0}/PKG-INFO +1 -1
  2. {nonebot_plugin_savepic-0.2.6 → nonebot_plugin_savepic-0.3.0}/nonebot_plugin_savepic/__init__.py +5 -40
  3. {nonebot_plugin_savepic-0.2.6 → nonebot_plugin_savepic-0.3.0}/nonebot_plugin_savepic/config.py +9 -0
  4. nonebot_plugin_savepic-0.3.0/nonebot_plugin_savepic/listpic.py +84 -0
  5. {nonebot_plugin_savepic-0.2.6 → nonebot_plugin_savepic-0.3.0}/nonebot_plugin_savepic/mvpic.py +9 -9
  6. {nonebot_plugin_savepic-0.2.6 → nonebot_plugin_savepic-0.3.0}/nonebot_plugin_savepic/pic_sql.py +49 -44
  7. {nonebot_plugin_savepic-0.2.6 → nonebot_plugin_savepic-0.3.0}/pyproject.toml +1 -1
  8. {nonebot_plugin_savepic-0.2.6 → nonebot_plugin_savepic-0.3.0}/LICENSE +0 -0
  9. {nonebot_plugin_savepic-0.2.6 → nonebot_plugin_savepic-0.3.0}/README.md +0 -0
  10. {nonebot_plugin_savepic-0.2.6 → nonebot_plugin_savepic-0.3.0}/nonebot_plugin_savepic/ai_utils.py +0 -0
  11. {nonebot_plugin_savepic-0.2.6 → nonebot_plugin_savepic-0.3.0}/nonebot_plugin_savepic/countpic.py +0 -0
  12. {nonebot_plugin_savepic-0.2.6 → nonebot_plugin_savepic-0.3.0}/nonebot_plugin_savepic/error.py +0 -0
  13. {nonebot_plugin_savepic-0.2.6 → nonebot_plugin_savepic-0.3.0}/nonebot_plugin_savepic/ext_listener.py +0 -0
  14. {nonebot_plugin_savepic-0.2.6 → nonebot_plugin_savepic-0.3.0}/nonebot_plugin_savepic/model.py +0 -0
  15. {nonebot_plugin_savepic-0.2.6 → nonebot_plugin_savepic-0.3.0}/nonebot_plugin_savepic/networks/__init__.py +0 -0
  16. {nonebot_plugin_savepic-0.2.6 → nonebot_plugin_savepic-0.3.0}/nonebot_plugin_savepic/networks/resnet_big.py +0 -0
  17. {nonebot_plugin_savepic-0.2.6 → nonebot_plugin_savepic-0.3.0}/nonebot_plugin_savepic/picture.py +0 -0
  18. {nonebot_plugin_savepic-0.2.6 → nonebot_plugin_savepic-0.3.0}/nonebot_plugin_savepic/randpic.py +0 -0
  19. {nonebot_plugin_savepic-0.2.6 → nonebot_plugin_savepic-0.3.0}/nonebot_plugin_savepic/rule.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nonebot-plugin-savepic
3
- Version: 0.2.6
3
+ Version: 0.3.0
4
4
  Summary: 保存表情包(语录)与随机出图
5
5
  Home-page: https://github.com/Yan-Zero/nonebot-plugin-savepic
6
6
  License: MIT
@@ -11,7 +11,7 @@ from nonebot.matcher import Matcher
11
11
  from nonebot.plugin import PluginMetadata
12
12
  from nonebot.dependencies import Dependent
13
13
  from sqlalchemy.exc import DBAPIError
14
- from arclet.alconna import Alconna, Option, Args, CommandMeta, append
14
+ from arclet.alconna import Alconna, Option, Args, CommandMeta
15
15
  import os
16
16
  import random
17
17
  from typing import (
@@ -46,20 +46,19 @@ from .config import Config
46
46
  from .config import WORDS
47
47
  from .pic_sql import ( # noqa: E402
48
48
  savepic,
49
- rename,
50
49
  delete,
51
50
  regexp_pic,
52
- listpic,
53
51
  )
54
52
  from .rule import PIC_AMDIN
55
53
  from .rule import BLACK_GROUP
56
54
  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
55
+ from .picture import write_pic, load_pic
59
56
  from .ai_utils import img2vec
60
57
  from .randpic import url_to_image
61
- from .countpic import cpic
62
58
  from .mvpic import INVALID_FILENAME_CHARACTERS
59
+ from .listpic import s_listpic
60
+ from .ext_listener import pic_listen
61
+ from .countpic import cpic
63
62
 
64
63
 
65
64
  __plugin_meta__ = PluginMetadata(
@@ -89,7 +88,6 @@ a_spic = Alconna(
89
88
  meta=CommandMeta(description="保存图片,默认保存到本群"),
90
89
  )
91
90
  s_simpic = on_command("simpic", priority=5, permission=BLACK_GROUP)
92
- s_listpic = on_command("listpic", priority=5, permission=BLACK_GROUP)
93
91
 
94
92
 
95
93
  def got_random_prompt(
@@ -162,39 +160,6 @@ async def _(bot: Bot, event, args: V11Msg = CommandArg()):
162
160
  )
163
161
 
164
162
 
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
163
  @spic.handle()
199
164
  async def _(
200
165
  bot: Bot,
@@ -33,3 +33,12 @@ class Config(BaseModel):
33
33
 
34
34
  embedding_sqlurl: str
35
35
  black_group: list[str]
36
+
37
+ notfound_with_jpg: bool = True
38
+ """ randpic 的时候,尝试带 .jpg 再度检索向量 """
39
+ count_per_page_in_list: int = 7
40
+ """ 每页最多多少条 """
41
+ forward_when_listpic: bool = True
42
+ """ listpic 的时候合并转发 """
43
+ max_page_in_listpic: int = 20
44
+ """ 合并转发中所能显示的最大页数 """
@@ -0,0 +1,84 @@
1
+ import random
2
+ from nonebot import on_command
3
+ from nonebot import get_plugin_config
4
+ from nonebot.params import CommandArg
5
+ from nonebot.internal.adapter import Bot
6
+ from nonebot.adapters.onebot.v11.event import GroupMessageEvent as V11GME
7
+ from nonebot.adapters.onebot.v11.message import Message as V11Msg
8
+ from nonebot.adapters.onebot.v11.message import MessageSegment as V11Seg
9
+ from sqlalchemy.exc import DBAPIError
10
+
11
+ from .rule import BLACK_GROUP
12
+ from .config import WORDS
13
+ from .pic_sql import listpic
14
+ from .config import Config
15
+
16
+ p_config: Config = get_plugin_config(Config)
17
+ s_listpic = on_command("listpic", priority=5, permission=BLACK_GROUP)
18
+
19
+
20
+ @s_listpic.handle()
21
+ async def _(bot: Bot, event, args: V11Msg = CommandArg()):
22
+ reg = args.extract_plain_text().strip().rsplit("\\page", maxsplit=1)
23
+ try:
24
+ if len(reg) > 1:
25
+ reg, pages = reg
26
+ else:
27
+ reg, pages = reg[0], 1
28
+ pages = int(pages)
29
+ except Exception as ex:
30
+ await s_listpic.finish(
31
+ f'{random.choice(WORDS.get("error", ["出错了喵~"]))}\n\n{ex}'
32
+ )
33
+
34
+ group_id = (
35
+ "globe" if not isinstance(event, V11GME) else f"qq_group:{event.group_id}"
36
+ )
37
+ try:
38
+ pics = await listpic(reg, group_id, pages=pages)
39
+ if not pics:
40
+ return
41
+
42
+ cpp = max(p_config.count_per_page_in_list, 1)
43
+ if p_config.forward_when_listpic:
44
+ message = []
45
+ for i in range(len(pics) // cpp + 1):
46
+ if pics[i * cpp : (i + 1) * cpp]:
47
+ message.append(
48
+ {
49
+ "type": "node",
50
+ "data": {
51
+ "uin": str(event.get_user_id()),
52
+ "name": f"Page {pages+i}",
53
+ "content": V11Seg.text(
54
+ "\n".join(pics[i * cpp : (i + 1) * cpp])
55
+ + f"\n\nPage {pages+i}"
56
+ ),
57
+ },
58
+ },
59
+ )
60
+
61
+ if isinstance(event, V11GME):
62
+ await bot.call_api(
63
+ "send_group_forward_msg",
64
+ group_id=event.group_id,
65
+ messages=message,
66
+ )
67
+ else:
68
+ await s_listpic.send(
69
+ V11Seg.forward(
70
+ await bot.call_api("send_forward_msg", messages=message)
71
+ )
72
+ )
73
+ return
74
+
75
+ await s_listpic.send("\n".join(pics[:cpp]))
76
+
77
+ except DBAPIError as ex:
78
+ await s_listpic.finish(
79
+ f'{random.choice(WORDS.get("error", ["出错了喵~"]))}\n\n{ex.orig}'
80
+ )
81
+ except Exception as ex:
82
+ await s_listpic.finish(
83
+ f'{random.choice(WORDS.get("error", ["出错了喵~"]))}\n\n{ex}'
84
+ )
@@ -1,6 +1,6 @@
1
1
  import random
2
- import re
3
2
  from nonebot import on_command
3
+ from nonebot.params import CommandArg
4
4
  from nonebot.internal.adapter import Bot
5
5
  from nonebot.adapters.onebot.v11.event import GroupMessageEvent as V11GME
6
6
  from .rule import BLACK_GROUP
@@ -26,16 +26,13 @@ INVALID_FILENAME_CHARACTERS = r'\/:*?"<>|'
26
26
 
27
27
 
28
28
  @s_mvpic.handle()
29
- async def _(
30
- bot: Bot,
31
- event: V11GME,
32
- ):
29
+ async def _(bot: Bot, event: V11GME, args=CommandArg()):
33
30
  if not (await PIC_AMDIN(bot, event) or await GROUP_ADMIN(bot, event)):
34
31
  await s_mvpic.finish(
35
32
  random.choice(WORDS.get("permission denied", ["没有权限"]))
36
33
  )
37
34
 
38
- cmd = event.message.extract_plain_text().strip()
35
+ cmd = args.extract_plain_text().strip()
39
36
  name = []
40
37
  options = []
41
38
 
@@ -104,7 +101,6 @@ async def _(
104
101
  name.append(_str(""))
105
102
 
106
103
  parser()
107
-
108
104
  if not name:
109
105
  await s_mvpic.finish("文件名呢?")
110
106
  if not options:
@@ -124,8 +120,12 @@ async def _(
124
120
  if not dname.endswith((".jpg", ".png", ".gif")):
125
121
  dname += ".jpg"
126
122
 
127
- sg = options[0]
128
- dg = options[1] if len(options) > 1 else sg
123
+ sg = "globe" if options[0] == "g" else f"qq_group:{event.group_id}"
124
+ dg = options[1] if len(options) > 1 else options[0]
125
+ dg = "globe" if dg == "g" else f"qq_group:{event.group_id}"
126
+
127
+ if sname == dname and sg == dg:
128
+ await s_mvpic.finish("嗯,什么都没有变化嘛。")
129
129
 
130
130
  try:
131
131
  await rename(sname, dname, sg, dg)
@@ -30,14 +30,31 @@ def AsyncDatabase():
30
30
  return _async_database
31
31
 
32
32
 
33
+ async def update_vec(pic: PicData):
34
+ if pic is None:
35
+ return
36
+ if not pic.u_vec_text:
37
+ return
38
+
39
+ async with AsyncSession(_async_database) as db_session:
40
+ # if pic.u_vec_text:
41
+ pic.u_vec_text = False
42
+ await _async_embedding_database.execute(
43
+ "UPDATE savepic_word2vec SET embedding = $1 WHERE id = $2",
44
+ str(word2vec(pic.name)),
45
+ pic.id,
46
+ )
47
+ await db_session.merge(pic)
48
+ await db_session.commit()
49
+
50
+
33
51
  async def select_pic(filename: str, group: str):
34
52
  async with AsyncSession(_async_database) as db_session:
35
- pic = await db_session.scalar(
53
+ if pic := await db_session.scalar(
36
54
  select(PicData)
37
55
  .where(PicData.name == filename)
38
56
  .where(PicData.group == group)
39
- )
40
- if pic:
57
+ ):
41
58
  await update_vec(pic)
42
59
  return pic
43
60
  return await db_session.scalar(
@@ -97,19 +114,6 @@ async def savepic(
97
114
  if despic:
98
115
  raise SameNameException(despic.name)
99
116
 
100
- # if not collision_allow:
101
- # ret = _pincone_index.query(img_vec, top_k=25)["matches"]
102
- # for i in ret:
103
- # if i["score"] and i["score"] < 0.98:
104
- # break
105
- # despic = await db_session.scalar(
106
- # select(PicData)
107
- # .where(PicData.id == int(ret[0]["id"]))
108
- # .where(sa.or_(PicData.group == group_id, PicData.group == "globe"))
109
- # )
110
- # if despic:
111
- # raise SimilarPictureException(despic.name, i["score"], despic.url)
112
-
113
117
  empty = await db_session.scalar(select(PicData).where(PicData.name == ""))
114
118
  if empty:
115
119
  pic.id = empty.id
@@ -118,7 +122,6 @@ async def savepic(
118
122
  db_session.add(pic)
119
123
  await db_session.flush()
120
124
 
121
- # _pincone_index.upsert([(str(pic.id), img_vec)])
122
125
  await _async_embedding_database.execute(
123
126
  (
124
127
  "INSERT INTO savepic_word2vec (id, embedding) VALUES ($1, $2) "
@@ -145,9 +148,9 @@ async def rename(ori: str, des: str, s_group: str, d_group: str):
145
148
  )
146
149
  if despic:
147
150
  raise SameNameException(despic.name)
151
+
148
152
  pic.name = des
149
153
  pic.group = d_group
150
-
151
154
  pic.u_vec_text = False
152
155
  await _async_embedding_database.execute(
153
156
  "UPDATE savepic_word2vec SET embedding = $1 WHERE id = $2",
@@ -218,6 +221,7 @@ async def randpic(
218
221
  ):
219
222
  await update_vec(pic)
220
223
  return pic, ""
224
+
221
225
  if not vector:
222
226
  return None, ""
223
227
 
@@ -237,6 +241,25 @@ async def randpic(
237
241
  .order_by(sa.func.random())
238
242
  ):
239
243
  return pic, "(语义向量相似度检索)"
244
+
245
+ if p_config.notfound_with_jpg:
246
+ datas = await _async_embedding_database.fetch(
247
+ (
248
+ "SELECT id FROM savepic_word2vec "
249
+ "WHERE embedding IS NOT NULL and embedding <=> $1 <= 0.45 "
250
+ "ORDER BY embedding <#> $1 LIMIT 8;"
251
+ ),
252
+ str(word2vec(name + ".jpg")),
253
+ )
254
+ if pic := await db_session.scalar(
255
+ select(PicData)
256
+ .where(sa.or_(PicData.group == group, PicData.group == "globe"))
257
+ .where(PicData.id.in_([i["id"] for i in datas]))
258
+ .where(PicData.name != "")
259
+ .order_by(sa.func.random())
260
+ ):
261
+ return pic, "(语义向量相似度检索)"
262
+
240
263
  return None, False
241
264
 
242
265
 
@@ -258,34 +281,16 @@ async def countpic(reg: str, group: str = "globe") -> int:
258
281
  return 0
259
282
 
260
283
 
261
- async def update_vec(pic: PicData):
262
- if not pic:
263
- return
264
- if not pic.u_vec_text: # and not pic.u_vec_img:
265
- return
266
- if pic.u_vec_text:
267
- pic.u_vec_text = False
268
- await _async_embedding_database.execute(
269
- "UPDATE savepic_word2vec SET embedding = $1 WHERE id = $2",
270
- str(word2vec(pic.name)),
271
- pic.id,
272
- )
273
- # if pic.u_vec_img:
274
- # pic.u_vec_img = False
275
- # _pincone_index.upsert(
276
- # [(str(pic.id), file2vec(pathlib.Path(pic.url), pic.name))]
277
- # )
278
- async with AsyncSession(_async_database) as db_session:
279
- db_session.merge(pic)
280
- await db_session.flush()
281
- db_session.commit()
282
-
283
-
284
284
  async def listpic(reg: str, group: str = "globe", pages: int = 0) -> list[str]:
285
285
  reg = reg.strip()
286
286
  if not reg:
287
287
  reg = ".*"
288
- pages -= 1
288
+
289
+ pages = max(pages - 1, 0)
290
+ _count = min(
291
+ max(1, p_config.count_per_page_in_list * p_config.max_page_in_listpic), 1000
292
+ )
293
+
289
294
  async with AsyncSession(_async_database) as db_session:
290
295
  pics = await db_session.scalars(
291
296
  select(PicData)
@@ -293,8 +298,8 @@ async def listpic(reg: str, group: str = "globe", pages: int = 0) -> list[str]:
293
298
  .where(PicData.name != "")
294
299
  .where(PicData.name.regexp_match(reg, flags="i"))
295
300
  .order_by(PicData.name)
296
- .offset((0 if pages < 0 else pages) * 10)
297
- .limit(10)
301
+ .offset(pages * p_config.count_per_page_in_list)
302
+ .limit(_count)
298
303
  )
299
304
  if pics:
300
305
  return [str(pic.name) for pic in pics]
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "nonebot-plugin-savepic"
3
- version = "0.2.6"
3
+ version = "0.3.0"
4
4
  description = "保存表情包(语录)与随机出图"
5
5
  authors = ["Yan <1964649083@qq.com>"]
6
6
  license = "MIT"