|
1 | 1 | import asyncio
|
2 | 2 | import re
|
3 |
| -from asyncio import Lock |
4 | 3 | from collections import defaultdict
|
5 | 4 | from contextlib import suppress
|
6 |
| -from typing import Any, DefaultDict, Dict, List, Optional, Tuple, Union |
| 5 | +from typing import ( |
| 6 | + Any, |
| 7 | + DefaultDict, |
| 8 | + Dict, |
| 9 | + List, |
| 10 | + Optional, |
| 11 | + Tuple, |
| 12 | + Union, |
| 13 | +) |
7 | 14 |
|
8 | 15 | import arrow
|
9 | 16 | from aiohttp import ClientSession
|
10 |
| -from diskcache import Cache |
| 17 | +from cachetools import TTLCache |
11 | 18 | from nonebot.adapters.onebot.v11 import (
|
12 | 19 | ActionFailed,
|
13 | 20 | Bot,
|
|
22 | 29 | from nonebot.plugin.on import on_message, on_metaevent
|
23 | 30 | from nonebot.rule import Rule
|
24 | 31 | from PicImageSearch import Network
|
| 32 | +from shelved_cache import PersistentCache |
25 | 33 | from tenacity import retry, stop_after_attempt, stop_after_delay
|
26 | 34 |
|
27 | 35 | from .ascii2d import ascii2d_search
|
28 | 36 | from .baidu import baidu_search
|
29 |
| -from .cache import exist_in_cache, upsert_cache |
30 | 37 | from .config import config
|
31 | 38 | from .ehentai import ehentai_search
|
32 | 39 | from .iqdb import iqdb_search
|
33 | 40 | from .saucenao import saucenao_search
|
34 |
| -from .utils import DEFAULT_HEADERS, get_bot_friend_list, handle_img, handle_reply_msg |
| 41 | +from .utils import ( |
| 42 | + DEFAULT_HEADERS, |
| 43 | + SEARCH_FUNCTION_TYPE, |
| 44 | + get_bot_friend_list, |
| 45 | + handle_reply_msg, |
| 46 | +) |
35 | 47 |
|
36 | 48 | sending_lock: DefaultDict[Tuple[Union[int, str], str], asyncio.Lock] = defaultdict(
|
37 | 49 | asyncio.Lock
|
38 | 50 | )
|
| 51 | +pic_search_cache = PersistentCache( |
| 52 | + TTLCache, |
| 53 | + filename="pic_search_cache", |
| 54 | + maxsize=config.cache_expire * 100, |
| 55 | + ttl=config.cache_expire * 24 * 60 * 60, |
| 56 | +) |
39 | 57 |
|
40 | 58 |
|
41 | 59 | def check_first_connect(_: LifecycleMetaEvent) -> bool:
|
@@ -91,42 +109,58 @@ async def handle_first_receive(event: MessageEvent, matcher: Matcher) -> None:
|
91 | 109 |
|
92 | 110 |
|
93 | 111 | async def image_search(
|
| 112 | + bot: Bot, |
| 113 | + event: MessageEvent, |
94 | 114 | url: str,
|
95 | 115 | md5: str,
|
96 | 116 | mode: str,
|
97 | 117 | purge: bool,
|
98 |
| - _cache: Cache, |
99 | 118 | client: ClientSession,
|
100 |
| -) -> List[str]: |
| 119 | + index: Optional[int] = None, |
| 120 | +) -> None: |
101 | 121 | url = await get_universal_img_url(url)
|
102 |
| - if not purge and (result := exist_in_cache(_cache, md5, mode)): |
103 |
| - return [f"[缓存] {i}" for i in result] |
| 122 | + cache_key = f"{md5}_{mode}" |
104 | 123 | try:
|
105 |
| - result = await handle_search_mode(url, md5, mode, _cache, client) |
| 124 | + if not purge and cache_key in pic_search_cache: |
| 125 | + result, extra_handle = pic_search_cache[cache_key] |
| 126 | + await send_result_message(bot, event, [f"[缓存] {i}" for i in result], index) |
| 127 | + if callable(extra_handle): |
| 128 | + await send_result_message( |
| 129 | + bot, event, await extra_handle(url, client), index |
| 130 | + ) |
| 131 | + return |
| 132 | + |
| 133 | + result, extra_handle = await handle_search_mode(url, md5, mode, client) |
| 134 | + await send_result_message(bot, event, result, index) |
| 135 | + if callable(extra_handle): |
| 136 | + await send_result_message( |
| 137 | + bot, event, await extra_handle(url, client), index |
| 138 | + ) |
106 | 139 | except Exception as e:
|
107 | 140 | logger.exception(f"该图 [{url}] 搜图失败")
|
108 |
| - result = [f"该图搜图失败\nE: {repr(e)}"] |
109 |
| - return result |
| 141 | + await send_result_message(bot, event, [f"该图搜图失败\nE: {repr(e)}"], index) |
110 | 142 |
|
111 | 143 |
|
112 | 144 | @retry(stop=(stop_after_attempt(3) | stop_after_delay(30)), reraise=True)
|
113 | 145 | async def handle_search_mode(
|
114 |
| - url: str, md5: str, mode: str, _cache: Cache, client: ClientSession |
115 |
| -) -> List[str]: |
| 146 | + url: str, md5: str, mode: str, client: ClientSession |
| 147 | +) -> Tuple[List[str], Optional[SEARCH_FUNCTION_TYPE]]: |
| 148 | + extra_handle = None |
| 149 | + |
116 | 150 | if mode == "a2d":
|
117 | 151 | result = await ascii2d_search(url, client)
|
118 | 152 | elif mode == "ex":
|
119 |
| - result = await ehentai_search(url, client) |
| 153 | + result, extra_handle = await ehentai_search(url, client) |
120 | 154 | elif mode == "iqdb":
|
121 |
| - result = await iqdb_search(url, client) |
| 155 | + result, extra_handle = await iqdb_search(url, client) |
122 | 156 | elif mode == "baidu":
|
123 | 157 | result = await baidu_search(url, client)
|
124 | 158 | else:
|
125 |
| - result = await saucenao_search(url, client, mode) |
126 |
| - # 仅对涉及到 saucenao 的搜图结果做缓存 |
127 |
| - upsert_cache(_cache, md5, mode, result) |
| 159 | + result, extra_handle = await saucenao_search(url, client, mode) |
128 | 160 |
|
129 |
| - return result |
| 161 | + pic_search_cache[f"{md5}_{mode}"] = result, extra_handle |
| 162 | + |
| 163 | + return result, extra_handle |
130 | 164 |
|
131 | 165 |
|
132 | 166 | async def get_universal_img_url(url: str) -> str:
|
@@ -196,7 +230,7 @@ async def send_message_with_lock(
|
196 | 230 | bot: Bot,
|
197 | 231 | event: MessageEvent,
|
198 | 232 | msg_list: List[str],
|
199 |
| - current_sending_lock: Lock, |
| 233 | + current_sending_lock: asyncio.Lock, |
200 | 234 | index: Optional[int] = None,
|
201 | 235 | ) -> None:
|
202 | 236 | start_time = arrow.now()
|
@@ -269,14 +303,16 @@ async def handle_image_search(bot: Bot, event: MessageEvent, matcher: Matcher) -
|
269 | 303 | else Network(proxies=config.proxy)
|
270 | 304 | )
|
271 | 305 | async with network as client:
|
272 |
| - with Cache("picsearch_cache") as _cache: |
273 |
| - for index, (url, md5) in enumerate(image_urls_with_md5): |
274 |
| - await send_result_message( |
275 |
| - bot, |
276 |
| - event, |
277 |
| - await image_search(url, md5, mode, purge, _cache, client), |
278 |
| - index if len(image_urls_with_md5) > 1 else None, |
279 |
| - ) |
280 |
| - _cache.expire() |
| 306 | + for index, (url, md5) in enumerate(image_urls_with_md5): |
| 307 | + await image_search( |
| 308 | + bot, |
| 309 | + event, |
| 310 | + url, |
| 311 | + md5, |
| 312 | + mode, |
| 313 | + purge, |
| 314 | + client, |
| 315 | + index if len(image_urls_with_md5) > 1 else None, |
| 316 | + ) |
281 | 317 |
|
282 | 318 | await bot.delete_msg(message_id=searching_tips["message_id"])
|
0 commit comments