nonebot-plugin-randpic 1.2.2__tar.gz → 1.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nonebot-plugin-randpic
3
- Version: 1.2.2
3
+ Version: 1.3.0
4
4
  Summary: 一个通过指令随机发送你存储的图片的nonebot2插件
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -19,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.14
19
19
  Requires-Dist: aiosqlite (>=0.13.0,<0.14.0)
20
20
  Requires-Dist: alibabacloud_oss_v2 (>=1.1.0)
21
21
  Requires-Dist: httpx (>=0.20.0,<1.0.0)
22
+ Requires-Dist: imagehash (>=4.3.0)
22
23
  Requires-Dist: nonebot-adapter-onebot (>=2.1.5,<3.0.0)
23
24
  Requires-Dist: nonebot-plugin-localstore (>=0.6.0,<0.7.0)
24
25
  Requires-Dist: nonebot2[fastapi] (>=2.0.0,<3.0.0)
@@ -56,7 +57,7 @@ _✨ 一个发送指令就能让你的 bot 发出对应指令的图片的插件
56
57
 
57
58
  你可以很方便地直接通过在配置文件里设置好触发指令(支持多个触发指令)后,在群聊中发送对应指令,从而使 Bot 随机发送出你所存储的图片。
58
59
 
59
- 插件除了能随机发送图片,还能在群聊内直接通过指令存储图片,同时为了避免重复加入同一张图片,该插件添加了 md5 码检查。
60
+ 插件除了能随机发送图片,还能在群聊内直接通过指令存储图片,同时为了避免重复加入同一张图片,该插件添加了图片感知哈希算法,相似的图片无法重复添加。
60
61
 
61
62
  随机发送图片是利用 sql 语句随机选择指令。
62
63
 
@@ -224,7 +225,7 @@ randpic_oss_no_upload_list=["capoo"]
224
225
  ## TODO
225
226
  - [x] 指令触发 bot 发送图片
226
227
  - [x] 在 QQ 上让 bot 存储对应指令的图片
227
- - [x] 每次存储图片,判断图片是否已经存在,避免重复加入
228
+ - [x] 每次存储图片,判断相似图片是否已经存在,避免重复加入
228
229
  - [X] 由capoo插件衍生成一个模板插件,即仅需修改参数就能发送别的图片
229
230
  - [X] 添加图片压缩功能,避免图片文件夹空间过大
230
231
  - [X] 以阿里云OSS对象存储搭建静态网页,便于浏览器查看图片
@@ -27,7 +27,7 @@ _✨ 一个发送指令就能让你的 bot 发出对应指令的图片的插件
27
27
 
28
28
  你可以很方便地直接通过在配置文件里设置好触发指令(支持多个触发指令)后,在群聊中发送对应指令,从而使 Bot 随机发送出你所存储的图片。
29
29
 
30
- 插件除了能随机发送图片,还能在群聊内直接通过指令存储图片,同时为了避免重复加入同一张图片,该插件添加了 md5 码检查。
30
+ 插件除了能随机发送图片,还能在群聊内直接通过指令存储图片,同时为了避免重复加入同一张图片,该插件添加了图片感知哈希算法,相似的图片无法重复添加。
31
31
 
32
32
  随机发送图片是利用 sql 语句随机选择指令。
33
33
 
@@ -195,7 +195,7 @@ randpic_oss_no_upload_list=["capoo"]
195
195
  ## TODO
196
196
  - [x] 指令触发 bot 发送图片
197
197
  - [x] 在 QQ 上让 bot 存储对应指令的图片
198
- - [x] 每次存储图片,判断图片是否已经存在,避免重复加入
198
+ - [x] 每次存储图片,判断相似图片是否已经存在,避免重复加入
199
199
  - [X] 由capoo插件衍生成一个模板插件,即仅需修改参数就能发送别的图片
200
200
  - [X] 添加图片压缩功能,避免图片文件夹空间过大
201
201
  - [X] 以阿里云OSS对象存储搭建静态网页,便于浏览器查看图片
@@ -15,10 +15,10 @@ from nonebot.params import Fullmatch
15
15
  from urllib import parse
16
16
  from urllib.parse import urlparse
17
17
  import importlib
18
-
18
+ import imagehash
19
19
  from .config import *
20
20
  from .ali_oss import *
21
- from .compress import compress_image_from_bytes, get_image_extension
21
+ from .compress import compress_image_from_bytes, get_image_extension, compute_phash
22
22
  from .web import StaticImageGalleryGenerator
23
23
 
24
24
  __plugin_meta__ = PluginMetadata(
@@ -77,8 +77,9 @@ async def create_dir():
77
77
  await cursor.execute('DROP table if exists Pic_of_{command};'.format(command=command))
78
78
  await cursor.execute('''
79
79
  CREATE TABLE IF NOT EXISTS Pic_of_{command} (
80
- md5 TEXT PRIMARY KEY,
81
- img_url TEXT
80
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
81
+ img_url TEXT NOT NULL,
82
+ phash TEXT NOT NULL
82
83
  )
83
84
  '''.format(command=command))
84
85
  await connection.commit()
@@ -123,12 +124,12 @@ async def create_dir():
123
124
  with (path / filename).open('wb') as f:
124
125
  f.write(data)
125
126
 
126
- fmd5 = hashlib.md5(data).hexdigest()
127
+ new_phash_str = compute_phash(data)
127
128
  cursor = await connection.cursor()
128
129
  command: str = randpic_command_tuple[index]
129
130
  await cursor.execute(
130
- 'INSERT or REPLACE INTO Pic_of_{command}(md5, img_url) VALUES (?, ?)'.format(command=command),
131
- (fmd5, str(Path() / command / filename)))
131
+ 'INSERT INTO Pic_of_{command}(img_url, phash) VALUES (?, ?)'.format(command=command),
132
+ (str(Path() / command / filename), new_phash_str))
132
133
  await connection.commit()
133
134
 
134
135
  def web_app_init(web_driver: Driver):
@@ -155,7 +156,7 @@ def web_app_init(web_driver: Driver):
155
156
  web_app_init(driver)
156
157
 
157
158
 
158
- picture = on_fullmatch(randpic_command_tuple, permission=GROUP, priority=1, block=True)
159
+ picture = on_fullmatch(randpic_command_tuple, permission=GROUP, priority=2, block=True)
159
160
 
160
161
 
161
162
  @picture.handle()
@@ -178,7 +179,7 @@ async def pic(event: GroupMessageEvent):
178
179
  await picture.send(f'{command}出不来了,稍后再试试吧~')
179
180
 
180
181
 
181
- add = on_fullmatch(randpic_command_add_tuple, permission=GROUP_ADMIN | GROUP_OWNER, priority=1, block=True)
182
+ add = on_fullmatch(randpic_command_add_tuple, permission=GROUP_ADMIN | GROUP_OWNER, priority=2, block=True)
182
183
 
183
184
 
184
185
  @add.got("pic", prompt="请发送图片!")
@@ -210,45 +211,48 @@ async def add_pic(args: str = Fullmatch(), pic_list: Message = Arg('pic')):
210
211
 
211
212
  data = resp.content
212
213
  data = compress_image_from_bytes(data) # 若图片超规格,压缩图片
213
- fmd5 = hashlib.md5(data).hexdigest()
214
-
215
- await cursor.execute(f'SELECT img_url FROM Pic_of_{command} where md5=?', (fmd5,))
216
- status = await cursor.fetchone()
217
-
218
- if status is not None:
219
- await add.send(pic_name + Message('\n这张已经有了,不能重复添加!'))
220
- else:
221
- randpic_cur_picnum = len(os.listdir(randpic_img_path / command))
222
- file_name = (randpic_filename.format(command=command, index=str(randpic_cur_picnum + 1).zfill(10))
223
- + get_image_extension(data))
224
- file_path = randpic_img_path / command / file_name
225
-
226
- try:
227
- with file_path.open("wb") as f:
228
- f.write(data)
229
- await cursor.execute('insert into Pic_of_{command}(md5, img_url) values (?, ?)'.format(command=command),
230
- (fmd5, str(Path() / command / file_name)))
231
- await connection.commit()
232
- except Exception as e:
233
- logger.warning(e)
234
- await add.finish(pic_name + Message("\n导入失败!"), at_sender=True)
235
-
236
- msg = "\n导入成功!"
237
- if isOss and command not in randpic_oss_no_upload_list:
238
- msg += f'可去 {endpoint}/{parse.quote(command)}/ 查看'
239
- # StaticImageGalleryGenerator(randpic_img_path, randpic_path / 'public').generate_static_site()
240
- StaticImageGalleryGenerator(randpic_img_path, randpic_path / 'public').generate_command_html(command, file_name, randpic_oss_no_upload_list)
241
- await OSSUploaderV2().upload_file(str(randpic_path/ 'public' / command / 'index.html'), f'{command}/index.html') # 修改index.html文件
242
- await OSSUploaderV2().upload_file(str(randpic_path / 'public' / 'index.html'), 'index.html')
243
- await OSSUploaderV2().upload_file(file_path, f'{command}/{file_name}') # 上传新增的图片到OSS
244
- await add.finish(pic_name + Message(msg), at_sender=True)
245
-
246
- oss = on_fullmatch('上传oss', ignorecase=True, permission=GROUP_ADMIN | GROUP_OWNER, priority=1, block=True, )
247
- @oss.handle()
214
+ new_phash_str = compute_phash(data)
215
+ new_phash = imagehash.hex_to_hash(new_phash_str)
216
+
217
+ await cursor.execute(f'SELECT phash FROM Pic_of_{command}')
218
+ existing = await cursor.fetchall()
219
+ SIMILARITY_THRESHOLD = 5 # 汉明距离 ≤5 认为相似
220
+ for ex_phash_str, in existing:
221
+ ex_phash = imagehash.hex_to_hash(ex_phash_str)
222
+ if (new_phash - ex_phash) < SIMILARITY_THRESHOLD:
223
+ await add.finish(pic_name + Message('\n这张已经有相似图,不能重复添加!'))
224
+
225
+ randpic_cur_picnum = len(os.listdir(randpic_img_path / command))
226
+ file_name = (randpic_filename.format(command=command, index=str(randpic_cur_picnum + 1).zfill(10))
227
+ + get_image_extension(data))
228
+ file_path = randpic_img_path / command / file_name
229
+
230
+ try:
231
+ with file_path.open("wb") as f:
232
+ f.write(data)
233
+ await cursor.execute('insert into Pic_of_{command}(img_url, phash) values (?, ?)'.format(command=command),
234
+ (str(Path() / command / file_name), new_phash_str))
235
+ await connection.commit()
236
+ except Exception as e:
237
+ logger.warning(e)
238
+ await add.finish(pic_name + Message("\n导入失败!"), at_sender=True)
239
+
240
+ msg = "\n导入成功!"
241
+ if isOss and command not in randpic_oss_no_upload_list:
242
+ msg += f'可去 {endpoint}/{parse.quote(command)}/ 查看'
243
+ # StaticImageGalleryGenerator(randpic_img_path, randpic_path / 'public').generate_static_site()
244
+ StaticImageGalleryGenerator(randpic_img_path, randpic_path / 'public').generate_command_html(command, file_name, randpic_oss_no_upload_list)
245
+ await OSSUploaderV2().upload_file(str(randpic_path/ 'public' / command / 'index.html'), f'{command}/index.html') # 修改index.html文件
246
+ await OSSUploaderV2().upload_file(str(randpic_path / 'public' / 'index.html'), 'index.html')
247
+ await OSSUploaderV2().upload_file(file_path, f'{command}/{file_name}') # 上传新增的图片到OSS
248
+ await add.finish(pic_name + Message(msg), at_sender=True)
249
+
250
+ OSS = on_fullmatch('上传oss', ignorecase=True, permission=GROUP_ADMIN | GROUP_OWNER, priority=1, block=True, )
251
+ @OSS.handle()
248
252
  async def handle_oss(event: GroupMessageEvent) -> None:
249
253
  if not isOss:
250
254
  return
251
- await oss.send('正在上传至OSS...')
255
+ await OSS.send('正在上传至OSS...')
252
256
  start_time = time.time()
253
257
 
254
258
  StaticImageGalleryGenerator(randpic_img_path, randpic_path / 'public').generate_static_site(randpic_oss_no_upload_list)
@@ -256,4 +260,4 @@ async def handle_oss(event: GroupMessageEvent) -> None:
256
260
 
257
261
  end_time = time.time()
258
262
  elapsed_time = end_time - start_time
259
- await oss.finish(f'上传完成,用时: {elapsed_time:.2f}秒,地址:{endpoint}/')
263
+ await OSS.finish(f'上传完成,用时: {elapsed_time:.2f}秒,地址:{endpoint}/')
@@ -2,6 +2,7 @@ import zipfile
2
2
  from pathlib import Path
3
3
  from nonebot.log import logger
4
4
  from PIL import Image
5
+ import imagehash
5
6
  import io
6
7
 
7
8
 
@@ -199,6 +200,11 @@ def compress_folder_basic(folder_path, zip_path, include_subfolders=True):
199
200
  return False
200
201
 
201
202
 
203
+ def compute_phash(image_bytes: bytes):
204
+ """计算图片感知哈希,返回十六进制字符串"""
205
+ with Image.open(io.BytesIO(image_bytes)) as img:
206
+ return str(imagehash.phash(img))
207
+
202
208
  # 测试
203
209
  if __name__ == '__main__':
204
210
  with open('C:\\Users\\hu_pa\\Desktop\\randpic\\lh\\randpic_lh_2.jpg', 'rb') as f:
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "nonebot-plugin-randpic"
3
- version = "1.2.2"
3
+ version = "1.3.0"
4
4
  description = "一个通过指令随机发送你存储的图片的nonebot2插件"
5
5
  authors = ["HuParry <hu_parry@qq.com>"]
6
6
  license = "MIT"
@@ -19,6 +19,7 @@ aiosqlite = "^0.13.0"
19
19
  httpx = ">=0.20.0,<1.0.0"
20
20
  alibabacloud_oss_v2 = ">=1.1.0"
21
21
  pillow = ">=11.0.0"
22
+ imagehash = ">=4.3.0"
22
23
 
23
24
  [build-system]
24
25
  requires = ["poetry-core"]