From 28b95ead7c3c8e5a101253e6d0885bcbcf881e3f Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Sat, 20 Aug 2022 20:00:07 +0800 Subject: [PATCH 01/26] Upgrade: Dependencies - Upgrade dependencies --- bot.py | 6 ++++-- requirements.txt | 13 +++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/bot.py b/bot.py index 73875386..f1cb1566 100644 --- a/bot.py +++ b/bot.py @@ -5,10 +5,12 @@ from nonebot.adapters.onebot.v11.adapter import Adapter as OneBotAdapter from nonebot.log import logger, default_format -# win环境下proxy配置 +# win 环境下 asyncio.loop 配置 import asyncio -if sys.version_info[0] == 3 and sys.version_info[1] >= 8 and sys.platform.startswith('win'): +if sys.version_info[0] == 3 and 10 > sys.version_info[1] >= 8 and sys.platform.startswith('win'): asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) +elif sys.version_info[0] == 3 and sys.version_info[1] >= 10 and sys.platform.startswith('win'): + asyncio.set_event_loop(asyncio.ProactorEventLoop()) # Log file path bot_log_path = os.path.abspath(os.path.join(sys.path[0], 'log')) diff --git a/requirements.txt b/requirements.txt index 60b45954..09d34220 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,9 @@ nonebot2==2.0.0b5 -nonebot-adapter-onebot==2.1.1 +nonebot-adapter-onebot==2.1.3 fastapi==0.79.0 pydantic==1.9.1 APScheduler~=3.9.1 -pytz~=2022.1 +pytz~=2022.2.1 aiohttp~=3.8.1 SQLAlchemy~=1.4.40 asyncmy~=0.2.5 @@ -13,13 +13,14 @@ ujson~=5.4.0 beautifulsoup4~=4.11.1 bs4~=0.0.1 lxml~=4.9.1 -numpy~=1.23.1 -matplotlib~=3.5.2 +numpy~=1.23.2 +matplotlib~=3.5.3 Pillow~=9.2.0 imageio~=2.21.1 msgpack~=1.0.4 pycryptodome~=3.15.0 py7zr~=0.20.0 zhconv~=1.4.3 -rapidfuzz~=2.4.3 -emoji~=2.0.0 \ No newline at end of file +rapidfuzz~=2.5.0 +emoji~=2.0.0 +rich~=12.5.1 \ No newline at end of file From 9ed94da8da086d02c487d49ecf3f8dc54d2ce8e6 Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Sat, 20 Aug 2022 20:00:32 +0800 Subject: [PATCH 02/26] Change: HttpFetcher default headers - Change HttpFetcher default headers --- omega_miya/web_resource/http_fetcher/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/omega_miya/web_resource/http_fetcher/__init__.py b/omega_miya/web_resource/http_fetcher/__init__.py index 9789cfc2..6afb5434 100644 --- a/omega_miya/web_resource/http_fetcher/__init__.py +++ b/omega_miya/web_resource/http_fetcher/__init__.py @@ -28,7 +28,7 @@ class HttpFetcher(object): 'sec-gpc': '1', 'upgrade-insecure-requests': '1', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' - 'Chrome/101.0.4951.54 Safari/537.36' + 'Chrome/104.0.0.0 Safari/537.36' } _http_proxy_config = http_proxy_config From c56df815b99cb2f3d843e66f3cfb3965330e190e Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Sat, 20 Aug 2022 20:01:01 +0800 Subject: [PATCH 03/26] =?UTF-8?q?Upgrade:=20=E6=8A=BD=E5=8D=A1=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E5=8D=A1=E6=B1=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新抽卡插件卡池 --- omega_miya/plugins/draw/deck/arknights.py | 17 +++++++++++------ omega_miya/plugins/draw/deck/superpower.py | 13 +++++++++++-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/omega_miya/plugins/draw/deck/arknights.py b/omega_miya/plugins/draw/deck/arknights.py index 52d88317..94cb9571 100644 --- a/omega_miya/plugins/draw/deck/arknights.py +++ b/omega_miya/plugins/draw/deck/arknights.py @@ -26,17 +26,17 @@ class UpEvent(BaseModel): UpEvent( star=6, operator=[ - Operator(name='多萝西/Dorothy', star=6, limited=False, recruit_only=False, event_only=False, - special_only=False) + Operator(name='百炼嘉维尔/Gavial the Invincible', star=6, limited=True, recruit_only=False, + event_only=False, special_only=False), + Operator(name='鸿雪/Позёмка', star=6, limited=False, recruit_only=False, event_only=False, + special_only=False), ], - zoom=0.5 + zoom=0.7 ), UpEvent( star=5, operator=[ - Operator(name='承曦格雷伊/Greyy the Lightningbearer', star=5, limited=False, recruit_only=False, - event_only=False, special_only=False), - Operator(name='白面鸮/Ptilopsis', star=5, limited=False, recruit_only=False, event_only=False, + Operator(name='晓歌/Cantabile', star=5, limited=False, recruit_only=False, event_only=False, special_only=False), ], zoom=0.5 @@ -44,6 +44,11 @@ class UpEvent(BaseModel): ] ALL_OPERATOR: list[Operator] = [ + Operator(name='百炼嘉维尔/Gavial the Invincible', star=6, limited=True, recruit_only=False, event_only=False, + special_only=False), + Operator(name='鸿雪/Позёмка', star=6, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='晓歌/Cantabile', star=5, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='至简/Minimalist', star=5, limited=False, recruit_only=False, event_only=True, special_only=False), Operator(name='多萝西/Dorothy', star=6, limited=False, recruit_only=False, event_only=False, special_only=False), Operator(name='承曦格雷伊/Greyy the Lightningbearer', star=5, limited=False, recruit_only=False, event_only=False, special_only=False), diff --git a/omega_miya/plugins/draw/deck/superpower.py b/omega_miya/plugins/draw/deck/superpower.py index c63d116f..b283ba2e 100644 --- a/omega_miya/plugins/draw/deck/superpower.py +++ b/omega_miya/plugins/draw/deck/superpower.py @@ -88,8 +88,13 @@ '无限剑制', '把人变成可爱猫娘的魔法', '把人变成兽耳娘的魔法', + '把人变成萌萝莉的魔法', '让人性转的魔法', - '复制别人的能力' + '复制别人的能力', + '不让别人知道你有超能力', + '百分之百空手接白刃', + '百分之百被空手接白刃', + '看出别人的超能力' ] but = [ @@ -121,7 +126,11 @@ '得先给自己来一发才能对其他目标施放', '只有在学校里面才能用', '只有在高中校园里面才能用', - '考试一旦挂科超能力就会消失' + '考试一旦挂科超能力就会消失', + '获得之后逃课必被点名', + '获得之后将永远蒙不对正确答案', + '获得之后点菜永远只会点到最难吃的', + '只能在做梦的时候使用' ] From 63c29f6ef32d1a56ac5a9bffb0e95ef7a4666f5e Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Sat, 20 Aug 2022 20:01:24 +0800 Subject: [PATCH 04/26] =?UTF-8?q?Upgrade:=20=E7=BB=99=20semaphore=5Fgather?= =?UTF-8?q?=20=E6=B7=BB=E5=8A=A0=E4=BA=86=E7=AE=80=E5=8D=95=E7=9A=84?= =?UTF-8?q?=E8=BF=9B=E5=BA=A6=E6=9D=A1,=20=E5=87=8F=E5=B0=91=E7=AD=89?= =?UTF-8?q?=E5=BE=85=E7=84=A6=E8=99=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 给 semaphore_gather 添加了简单的进度条, 减少等待焦虑 --- omega_miya/utils/process_utils/__init__.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/omega_miya/utils/process_utils/__init__.py b/omega_miya/utils/process_utils/__init__.py index 5aa6e05d..cdcd4d37 100644 --- a/omega_miya/utils/process_utils/__init__.py +++ b/omega_miya/utils/process_utils/__init__.py @@ -12,6 +12,7 @@ import asyncio from asyncio import Future from asyncio.exceptions import TimeoutError as _TimeoutError +from rich.progress_bar import Console, ProgressBar from typing import TypeVar, ParamSpec, Callable, Generator, Coroutine, Awaitable, Any from functools import wraps, partial @@ -177,15 +178,30 @@ async def semaphore_gather( :param filter_exception: 是否过滤掉返回值中的异常 """ _semaphore = asyncio.Semaphore(semaphore_num) + console = Console() + bar = ProgressBar(width=50, total=len(tasks)) + complete: int = 0 + + def _add_complete() -> int: + """增加完成数""" + nonlocal complete + complete += 1 + return complete async def _wrap_coro( coro: Future[T] | Coroutine[Any, Any, T] | Generator[Any, Any, T] | Awaitable[T] ) -> Coroutine[Any, Any, T]: """使用 asyncio.Semaphore 限制单个任务""" async with _semaphore: - return await coro + _result = await coro + bar.update(_add_complete()) + console.print(bar) + console.file.write("\r") + return _result + console.show_cursor(False) result = await asyncio.gather(*(_wrap_coro(coro) for coro in tasks), return_exceptions=return_exceptions) + console.show_cursor(True) # 输出错误日志 _stack_frame = inspect.stack()[1].frame From 9ecf95e581d1ec25616627878c4c63d478486595 Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Sat, 20 Aug 2022 20:01:58 +0800 Subject: [PATCH 05/26] Fix: Dependencies import - Fix dependencies import --- omega_miya/service/message_send_failed_patch/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/omega_miya/service/message_send_failed_patch/__init__.py b/omega_miya/service/message_send_failed_patch/__init__.py index b7c5ffc0..05fc9530 100644 --- a/omega_miya/service/message_send_failed_patch/__init__.py +++ b/omega_miya/service/message_send_failed_patch/__init__.py @@ -14,7 +14,7 @@ from nonebot.log import logger from nonebot.adapters.onebot.v11 import Bot from nonebot.adapters.onebot.v11.message import Message -from nonebot.adapters.onebot.exception import ActionFailed +from nonebot.adapters.onebot.v11 import ActionFailed from .config import message_send_failed_patch_config From 22934f260ed4c56dee7bb5f5d742e722803b668a Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Sat, 20 Aug 2022 20:02:14 +0800 Subject: [PATCH 06/26] =?UTF-8?q?Upgrade:=20=E8=90=8C=E5=9B=BE=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E5=AF=BC=E5=85=A5=E5=8A=9F=E8=83=BD=E6=A0=B9=E6=8D=AE?= =?UTF-8?q?=20Pixiv=20=E6=B5=81=E6=8E=A7=E7=AD=96=E7=95=A5=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 萌图插件导入功能根据 Pixiv 流控策略进行调整 --- omega_miya/plugins/moe/__init__.py | 43 +++++++++++++++++++------- omega_miya/web_resource/pixiv/pixiv.py | 1 + 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/omega_miya/plugins/moe/__init__.py b/omega_miya/plugins/moe/__init__.py index 7a4b4194..7daa7d02 100644 --- a/omega_miya/plugins/moe/__init__.py +++ b/omega_miya/plugins/moe/__init__.py @@ -8,6 +8,8 @@ @Software : PyCharm """ +import asyncio +import random import re from copy import deepcopy from nonebot.log import logger @@ -261,26 +263,43 @@ async def handle_parse_import(state: T_State, cmd_arg: Message = CommandArg()): @database_import.got('mode', prompt='请输入导入模式:\n【setu/moe】') async def handle_database_import(state: T_State, matcher: Matcher, mode: str = ArgStr('mode')): mode = mode.strip() - pids: list[int] = [int(x) for x in state.get('pids', [])] - if not pids: - await matcher.send('尝试从文件中读取导入文件列表') - pids = await get_database_import_pids() - if not pids: - await matcher.finish('导入列表不存在, 详情请查看日志') - pids = sorted(list(set(pids))) - match mode: case 'moe': - tasks = [add_artwork_into_database(PixivArtwork(pid=pid), nsfw_tag=0, add_only=False) for pid in pids] + nsfw_tag = 0 case 'setu': - tasks = [add_artwork_into_database(PixivArtwork(pid=pid), nsfw_tag=1, add_only=False) for pid in pids] + nsfw_tag = 1 case _: await matcher.reject('不支持的导入模式参数, 请在【setu/moe】中选择并重新输入:') return + pids: list[int] = [int(x) for x in state.get('pids', [])] + if not pids: + await matcher.send('尝试从文件中读取导入文件列表') + pids = await get_database_import_pids() + if not pids: + await matcher.finish('导入列表不存在, 详情请查看日志') + pids = sorted(list(set(pids)), reverse=True) all_count = len(pids) await matcher.send(f'已获取导入列表, 总计: {all_count}, 开始获取作品信息~') - import_result = await semaphore_gather(tasks=tasks, semaphore_num=25) - fail_count = len([x for x in import_result if isinstance(x, Exception)]) + + # 应对 pixiv 流控, 对获取作品信息进行分段处理 + handle_pids = [] + fail_count = 0 + while pids: + while len(handle_pids) < 20: + try: + handle_pids.append(pids.pop()) + except IndexError: + break + + tasks = [add_artwork_into_database(PixivArtwork(pid), nsfw_tag=nsfw_tag, add_only=False) for pid in handle_pids] + handle_pids.clear() + import_result = await semaphore_gather(tasks=tasks, semaphore_num=20) + fail_count += len([x for x in import_result if isinstance(x, Exception)]) + + if pids: + logger.info(f'MoeDatabaseImport | 导入操作中, 剩余: {len(pids)}, 预计时间: {int(len(pids) / 20 * 25)} 秒') + await asyncio.sleep(random.randint(16, 24)) + logger.success(f'MoeDatabaseImport | 导入操作已完成, 成功: {all_count - fail_count}, 总计: {all_count}') await matcher.finish(f'导入操作已完成, 成功: {all_count - fail_count}, 总计: {all_count}') diff --git a/omega_miya/web_resource/pixiv/pixiv.py b/omega_miya/web_resource/pixiv/pixiv.py index 5c2d92f8..c964ca05 100644 --- a/omega_miya/web_resource/pixiv/pixiv.py +++ b/omega_miya/web_resource/pixiv/pixiv.py @@ -329,6 +329,7 @@ async def get_artwork_model(self) -> PixivArtworkCompleteDataModel: _artwork_data = await query_data_task if _artwork_data.error: + query_page_data_task.cancel() raise PixivApiError(f'PixivApiError, query artwork data failed, {_artwork_data.message}') _page_data = await query_page_data_task From 28ecc7e3c2a0f99aa54a370741e5e709f88053f4 Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Sat, 20 Aug 2022 20:02:28 +0800 Subject: [PATCH 07/26] =?UTF-8?q?Upgrade:=20=E6=96=B0=E5=A2=9E=20yandex=20?= =?UTF-8?q?=E8=AF=86=E5=9B=BE=E5=BC=95=E6=93=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 yandex 识图引擎 --- .../web_resource/image_searcher/__init__.py | 4 + .../web_resource/image_searcher/config.py | 1 + .../web_resource/image_searcher/yandex.py | 90 +++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 omega_miya/web_resource/image_searcher/yandex.py diff --git a/omega_miya/web_resource/image_searcher/__init__.py b/omega_miya/web_resource/image_searcher/__init__.py index 22eff567..b91fc3c1 100644 --- a/omega_miya/web_resource/image_searcher/__init__.py +++ b/omega_miya/web_resource/image_searcher/__init__.py @@ -16,6 +16,7 @@ from .iqdb import Iqdb from .saucenao import Saucenao from .trace_moe import TraceMoe +from .yandex import Yandex from .config import image_searcher_config from .model import ImageSearcher, ImageSearchingResult @@ -32,6 +33,9 @@ class ComplexImageSearcher(object): if image_searcher_config.image_searcher_enable_iqdb: _searcher.append(Iqdb) + if image_searcher_config.image_searcher_enable_yandex: + _searcher.append(Yandex) + if image_searcher_config.image_searcher_enable_ascii2d: _searcher.append(Ascii2d) diff --git a/omega_miya/web_resource/image_searcher/config.py b/omega_miya/web_resource/image_searcher/config.py index 3ed9b575..8677760c 100644 --- a/omega_miya/web_resource/image_searcher/config.py +++ b/omega_miya/web_resource/image_searcher/config.py @@ -18,6 +18,7 @@ class ImageSearcherConfig(BaseModel): image_searcher_enable_saucenao: bool = True image_searcher_enable_iqdb: bool = True + image_searcher_enable_yandex: bool = True image_searcher_enable_ascii2d: bool = True image_searcher_enable_trace_moe: bool = True diff --git a/omega_miya/web_resource/image_searcher/yandex.py b/omega_miya/web_resource/image_searcher/yandex.py new file mode 100644 index 00000000..3d76b53f --- /dev/null +++ b/omega_miya/web_resource/image_searcher/yandex.py @@ -0,0 +1,90 @@ +""" +@Author : Ailitonia +@Date : 2022/08/20 18:47 +@FileName : yandex.py +@Project : nonebot2_miya +@Description : yandex 识图引擎 +@GitHub : https://github.com/Ailitonia +@Software : PyCharm +""" + +from bs4 import BeautifulSoup +from pydantic import parse_obj_as + +from nonebot.log import logger + +from omega_miya.web_resource import HttpFetcher +from omega_miya.exception import WebSourceException + +from .model import ImageSearcher, ImageSearchingResult + + +class YandexNetworkError(WebSourceException): + """yandex 网络异常""" + + +class Yandex(ImageSearcher): + """yandex 识图引擎""" + _searcher_name: str = 'yandex' + _api: str = 'https://yandex.com/images/search' + + @staticmethod + def _parser(content: str) -> list[dict] | None: + """解析结果页面""" + gallery_soup = BeautifulSoup(content, 'lxml') + # 定位到相似图片区域 + container = gallery_soup.find(name='section', + attrs={'class': 'CbirSection CbirSection_decorated CbirSites CbirSites_infinite'}) + + container_title = container.find(name='div', attrs={'class': 'CbirSection-Title'}).get_text() + if container_title != 'Sites containing information about the image': + logger.error('Yandex | Css style change, unable to locate page element') + return None + + image_result_container = container.find(name='div', attrs={'class': 'CbirSites-Items'}) + if not image_result_container: + return None + + image_results = image_result_container.find_all(name='div', attrs={'class': 'CbirSites-Item'}) + result = [] + for item in image_results: + try: + title_info = item.find(name='div', attrs={'class': 'CbirSites-ItemTitle'}) + title_href = title_info.find(name='a', attrs={'target': '_blank', 'class': 'Link Link_view_default'}) + + thumbnail_info = item.find(name='div', attrs={'class': 'CbirSites-ItemThumb'}) + thumbnail_href = thumbnail_info.find(name='a', attrs={'target': '_blank'}) + thumbnail_url_elm = thumbnail_href.find(name='img', attrs={'class': 'MMImage Thumb-Image'}) + thumbnail_url = thumbnail_url_elm.attrs.get('src') + thumbnail_url = f'https:{thumbnail_url}' if thumbnail_url else None + + result.append({'similarity': None, + 'thumbnail': thumbnail_url, + 'source': f'Yandex - {title_href.get_text()}', + 'source_urls': [thumbnail_href.attrs.get('href'), title_href.attrs.get('href')]}) + except (KeyError, AttributeError): + continue + return result + + async def search(self) -> list[ImageSearchingResult]: + headers = HttpFetcher.get_default_headers() + headers.update({ + 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,' + 'image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', + 'Host': 'yandex.com', + 'Origin': 'https://yandex.com/images', + 'Referer': 'https://yandex.com/images/' + }) + + params = {'rpt': 'imageview', 'url': self.image_url} + yandex_result = await HttpFetcher(timeout=15).get_text(url=self._api, params=params) + if yandex_result.status != 200: + logger.error(f'Yandex | YandexNetworkError, {yandex_result}') + raise YandexNetworkError(f'YandexNetworkError, {yandex_result}') + + return parse_obj_as(list[ImageSearchingResult], self._parser(content=yandex_result.result)[:10]) + + +__all__ = [ + 'Yandex' +] From adac78f52b615e2cfc5ccb6b92d2c8d978570f37 Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Wed, 7 Sep 2022 22:38:28 +0800 Subject: [PATCH 08/26] =?UTF-8?q?Change:=20=E6=8F=92=E4=BB=B6=E8=B0=83?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 插件调整 --- bot.py | 4 ++-- omega_miya/plugins/moe/__init__.py | 5 ++--- omega_miya/web_resource/image_searcher/yandex.py | 8 +++++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/bot.py b/bot.py index f1cb1566..1f368b7d 100644 --- a/bot.py +++ b/bot.py @@ -18,8 +18,8 @@ os.makedirs(bot_log_path) # Custom logger -log_info_name = f'{datetime.today().strftime("%Y%m%d-%H%M%S")}-INFO.log' -log_error_name = f'{datetime.today().strftime("%Y%m%d-%H%M%S")}-ERROR.log' +log_info_name = f'{datetime.now().strftime("%Y%m%d-%H%M%S")}-INFO.log' +log_error_name = f'{datetime.now().strftime("%Y%m%d-%H%M%S")}-ERROR.log' log_info_path = os.path.join(bot_log_path, log_info_name) log_error_path = os.path.join(bot_log_path, log_error_name) diff --git a/omega_miya/plugins/moe/__init__.py b/omega_miya/plugins/moe/__init__.py index 7daa7d02..a4ec3f26 100644 --- a/omega_miya/plugins/moe/__init__.py +++ b/omega_miya/plugins/moe/__init__.py @@ -9,7 +9,6 @@ """ import asyncio -import random import re from copy import deepcopy from nonebot.log import logger @@ -298,8 +297,8 @@ async def handle_database_import(state: T_State, matcher: Matcher, mode: str = A fail_count += len([x for x in import_result if isinstance(x, Exception)]) if pids: - logger.info(f'MoeDatabaseImport | 导入操作中, 剩余: {len(pids)}, 预计时间: {int(len(pids) / 20 * 25)} 秒') - await asyncio.sleep(random.randint(16, 24)) + logger.info(f'MoeDatabaseImport | 导入操作中, 剩余: {len(pids)}, 预计时间: {int(len(pids) * 1.52)} 秒') + await asyncio.sleep(int((len(pids) if len(pids) < 20 else 20) * 1.5)) logger.success(f'MoeDatabaseImport | 导入操作已完成, 成功: {all_count - fail_count}, 总计: {all_count}') await matcher.finish(f'导入操作已完成, 成功: {all_count - fail_count}, 总计: {all_count}') diff --git a/omega_miya/web_resource/image_searcher/yandex.py b/omega_miya/web_resource/image_searcher/yandex.py index 3d76b53f..2c96fb73 100644 --- a/omega_miya/web_resource/image_searcher/yandex.py +++ b/omega_miya/web_resource/image_searcher/yandex.py @@ -51,6 +51,8 @@ def _parser(content: str) -> list[dict] | None: try: title_info = item.find(name='div', attrs={'class': 'CbirSites-ItemTitle'}) title_href = title_info.find(name='a', attrs={'target': '_blank', 'class': 'Link Link_view_default'}) + source_text = title_href.get_text() + source_text = source_text if len(source_text) <= 20 else f'{source_text[:20]}...' thumbnail_info = item.find(name='div', attrs={'class': 'CbirSites-ItemThumb'}) thumbnail_href = thumbnail_info.find(name='a', attrs={'target': '_blank'}) @@ -60,8 +62,8 @@ def _parser(content: str) -> list[dict] | None: result.append({'similarity': None, 'thumbnail': thumbnail_url, - 'source': f'Yandex - {title_href.get_text()}', - 'source_urls': [thumbnail_href.attrs.get('href'), title_href.attrs.get('href')]}) + 'source': f'Yandex - {source_text}', + 'source_urls': [title_href.attrs.get('href'), thumbnail_href.attrs.get('href')]}) except (KeyError, AttributeError): continue return result @@ -82,7 +84,7 @@ async def search(self) -> list[ImageSearchingResult]: logger.error(f'Yandex | YandexNetworkError, {yandex_result}') raise YandexNetworkError(f'YandexNetworkError, {yandex_result}') - return parse_obj_as(list[ImageSearchingResult], self._parser(content=yandex_result.result)[:10]) + return parse_obj_as(list[ImageSearchingResult], self._parser(content=yandex_result.result)[:8]) __all__ = [ From 1109aa4915273d14121c014aa8580f60db5e6eea Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Wed, 7 Sep 2022 22:38:36 +0800 Subject: [PATCH 09/26] =?UTF-8?q?Upgrade:=20Pixiv=20=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E4=BD=9C=E5=93=81=E8=AE=A2=E9=98=85=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E6=A0=B9=E6=8D=AE=20Pixiv=20=E6=B5=81=E6=8E=A7?= =?UTF-8?q?=E7=AD=96=E7=95=A5=E8=BF=9B=E8=A1=8C=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Pixiv 插件用户作品订阅功能根据 Pixiv 流控策略进行调整 --- omega_miya/plugins/pixiv/__init__.py | 2 +- omega_miya/plugins/pixiv/utils.py | 26 +++++++++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/omega_miya/plugins/pixiv/__init__.py b/omega_miya/plugins/pixiv/__init__.py index 772f3f98..a0320b0b 100644 --- a/omega_miya/plugins/pixiv/__init__.py +++ b/omega_miya/plugins/pixiv/__init__.py @@ -566,7 +566,7 @@ async def handle_add_user_subscription(bot: Bot, matcher: Matcher, event: Messag check = check.strip() if check != '是': await matcher.finish('那就不订阅了哦') - await matcher.send('正在更新Pixiv用户订阅信息, 请稍候') + await matcher.send('正在更新Pixiv用户订阅信息, 若首次订阅可能需要较长时间刷新用户作品信息, 请稍候') user = PixivUser(uid=int(user_id)) scheduler.pause() # 暂停计划任务避免中途检查更新 diff --git a/omega_miya/plugins/pixiv/utils.py b/omega_miya/plugins/pixiv/utils.py index d719b444..b5f26ca1 100644 --- a/omega_miya/plugins/pixiv/utils.py +++ b/omega_miya/plugins/pixiv/utils.py @@ -8,6 +8,8 @@ @Software : PyCharm """ +import asyncio +from copy import deepcopy from typing import Literal from pydantic import BaseModel from nonebot.log import logger @@ -193,9 +195,27 @@ async def add_artwork_into_database(artwork: PixivArtwork, *, upgrade_pages: boo async def _add_pixiv_user_artworks_to_database(pixiv_user: PixivUser) -> BoolResult: """在数据库中新增目标用户的全部作品(仅新增不更新)""" user_data = await pixiv_user.get_user_model() - tasks = [add_artwork_into_database(artwork=PixivArtwork(pid=pid), upgrade_pages=False) - for pid in user_data.manga_illusts] - await semaphore_gather(tasks=tasks, semaphore_num=25, return_exceptions=False) + + # 应对 pixiv 流控, 对获取作品信息进行分段处理 + all_pids = deepcopy(user_data.manga_illusts) + handle_pids = [] + fail_count = 0 + while all_pids: + while len(handle_pids) < 20: + try: + handle_pids.append(all_pids.pop()) + except IndexError: + break + + tasks = [add_artwork_into_database(artwork=PixivArtwork(pid=pid), upgrade_pages=False) for pid in handle_pids] + handle_pids.clear() + import_result = await semaphore_gather(tasks=tasks, semaphore_num=20) + fail_count += len([x for x in import_result if isinstance(x, Exception)]) + + if all_pids: + logger.debug(f'PixivUserAdder | Adding user({user_data.user_id}) artworks, {len(all_pids)} remaining') + await asyncio.sleep(int((len(all_pids) if len(all_pids) < 20 else 20) * 1.5)) + return BoolResult(error=False, info='Success', result=True) From 63d56e18db486956552455c7533f118b656c3fd2 Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Mon, 10 Oct 2022 18:22:06 +0800 Subject: [PATCH 10/26] =?UTF-8?q?Upgrade:=20=E6=98=8E=E6=97=A5=E6=96=B9?= =?UTF-8?q?=E8=88=9F=E5=8D=A1=E6=B1=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新明日方舟卡池 --- omega_miya/plugins/draw/deck/arknights.py | 34 +++++++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/omega_miya/plugins/draw/deck/arknights.py b/omega_miya/plugins/draw/deck/arknights.py index 94cb9571..67df026a 100644 --- a/omega_miya/plugins/draw/deck/arknights.py +++ b/omega_miya/plugins/draw/deck/arknights.py @@ -26,24 +26,46 @@ class UpEvent(BaseModel): UpEvent( star=6, operator=[ - Operator(name='百炼嘉维尔/Gavial the Invincible', star=6, limited=True, recruit_only=False, - event_only=False, special_only=False), - Operator(name='鸿雪/Позёмка', star=6, limited=False, recruit_only=False, event_only=False, + Operator(name='泥岩/Mudrock', star=6, limited=False, recruit_only=False, event_only=False, + special_only=False), + Operator(name='琴柳/Saileach', star=6, limited=False, recruit_only=False, event_only=False, + special_only=False), + Operator(name='号角/Horn', star=6, limited=False, recruit_only=False, event_only=False, special_only=False), ], - zoom=0.7 + zoom=1.0 ), UpEvent( star=5, operator=[ - Operator(name='晓歌/Cantabile', star=5, limited=False, recruit_only=False, event_only=False, + Operator(name='絮雨/Whisperain', star=5, limited=False, recruit_only=False, event_only=False, + special_only=False), + Operator(name='桑葚/Mulberry', star=5, limited=False, recruit_only=False, event_only=False, + special_only=False), + Operator(name='洛洛/Rockrock', star=5, limited=False, recruit_only=False, event_only=False, + special_only=False), + ], + zoom=0.6 + ), + UpEvent( + star=4, + operator=[ + Operator(name='杰克/Jackie', star=4, limited=False, recruit_only=False, event_only=False, + special_only=False), + Operator(name='罗比菈塔/Roberta', star=4, limited=False, recruit_only=False, event_only=False, + special_only=False), + Operator(name='褐果/Chestnut', star=4, limited=False, recruit_only=False, event_only=False, special_only=False), ], - zoom=0.5 + zoom=0.45 ) ] ALL_OPERATOR: list[Operator] = [ + Operator(name='海沫/Highmore', star=5, limited=False, recruit_only=False, event_only=True, special_only=False), + Operator(name='罗小黑/Luo Xiaohei', star=4, limited=False, recruit_only=False, event_only=True, special_only=False), + Operator(name='玛恩纳/Młynar', star=6, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='但书/Proviso', star=5, limited=False, recruit_only=False, event_only=False, special_only=False), Operator(name='百炼嘉维尔/Gavial the Invincible', star=6, limited=True, recruit_only=False, event_only=False, special_only=False), Operator(name='鸿雪/Позёмка', star=6, limited=False, recruit_only=False, event_only=False, special_only=False), From 19a4f63ec4ea93157f4c5099b9895503b316f63f Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Mon, 10 Oct 2022 18:22:35 +0800 Subject: [PATCH 11/26] =?UTF-8?q?Fix:=20Pixivision=20=E7=89=B9=E8=BE=91?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E8=A7=A3=E6=9E=90=E9=97=AE=E9=A2=98=20(#114)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 Pixivision 特辑内容解析问题 --- omega_miya/web_resource/pixiv/helper.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/omega_miya/web_resource/pixiv/helper.py b/omega_miya/web_resource/pixiv/helper.py index e1a9f165..1e152c91 100644 --- a/omega_miya/web_resource/pixiv/helper.py +++ b/omega_miya/web_resource/pixiv/helper.py @@ -151,8 +151,6 @@ def parse_pixivision_article_page(content: str, root_url: str) -> PixivisionArti # 解析 article 描述部分 article_main = page_bs.find(name='div', attrs={'class': '_article-main'}) article_title = article_main.find(name='h1', attrs={'class': 'am__title'}).get_text(strip=True) - article_description = article_main.find( - name='div', attrs={'class': 'am__description _medium-editor-text'}).get_text(strip=True) article_eyecatch = article_main.find(name='div', attrs={'class': '_article-illust-eyecatch'}) article_eyecatch_image = None if article_eyecatch is None else article_eyecatch.find('img').attrs.get('src') @@ -177,6 +175,9 @@ def parse_pixivision_article_page(content: str, root_url: str) -> PixivisionArti if feature_article_body is not None: article_description = article_main.find( name='div', attrs={'class': 'fab__paragraph _medium-editor-text'}).get_text(strip=True) + else: + article_description = article_main.find( + name='div', attrs={'class': 'am__description _medium-editor-text'}).get_text(strip=True) # 获取所有作品内容 article_illusts = article_body.find_all(name='div', attrs={'class': 'am__work__main'}) From 2e64562386cac8aee1574475d823b42926075a48 Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Fri, 25 Nov 2022 22:47:27 +0800 Subject: [PATCH 12/26] Upgrade: Dependencies - Upgrade Dependencies --- requirements.txt | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/requirements.txt b/requirements.txt index 09d34220..0ec1c372 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,26 +1,26 @@ -nonebot2==2.0.0b5 -nonebot-adapter-onebot==2.1.3 -fastapi==0.79.0 -pydantic==1.9.1 -APScheduler~=3.9.1 -pytz~=2022.2.1 -aiohttp~=3.8.1 -SQLAlchemy~=1.4.40 +nonebot2==2.0.0rc2 +nonebot-adapter-onebot==2.1.5 +fastapi==0.87.0 +pydantic==1.10.2 +APScheduler~=3.9.1.post1 +pytz~=2022.6 +aiohttp~=3.8.3 +SQLAlchemy~=1.4.44 asyncmy~=0.2.5 aiomysql~=0.1.1 -aiofiles==0.8.0 -ujson~=5.4.0 +aiofiles==22.1.0 +ujson~=5.5.0 beautifulsoup4~=4.11.1 bs4~=0.0.1 lxml~=4.9.1 -numpy~=1.23.2 -matplotlib~=3.5.3 -Pillow~=9.2.0 -imageio~=2.21.1 +numpy~=1.23.5 +matplotlib~=3.6.2 +Pillow~=9.3.0 +imageio~=2.22.4 msgpack~=1.0.4 pycryptodome~=3.15.0 -py7zr~=0.20.0 +py7zr~=0.20.2 zhconv~=1.4.3 -rapidfuzz~=2.5.0 -emoji~=2.0.0 -rich~=12.5.1 \ No newline at end of file +rapidfuzz~=2.13.2 +emoji~=2.2.0 +rich~=12.6.0 \ No newline at end of file From d4d6b3a47414443d8c1b71d4573d60830cc01446 Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Fri, 25 Nov 2022 22:47:43 +0800 Subject: [PATCH 13/26] =?UTF-8?q?Upgrade:=20=E6=98=8E=E6=97=A5=E6=96=B9?= =?UTF-8?q?=E8=88=9F=E5=8D=A1=E6=B1=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新明日方舟卡池 --- omega_miya/plugins/draw/deck/arknights.py | 42 ++++++++++------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/omega_miya/plugins/draw/deck/arknights.py b/omega_miya/plugins/draw/deck/arknights.py index 67df026a..f689b930 100644 --- a/omega_miya/plugins/draw/deck/arknights.py +++ b/omega_miya/plugins/draw/deck/arknights.py @@ -26,42 +26,38 @@ class UpEvent(BaseModel): UpEvent( star=6, operator=[ - Operator(name='泥岩/Mudrock', star=6, limited=False, recruit_only=False, event_only=False, - special_only=False), - Operator(name='琴柳/Saileach', star=6, limited=False, recruit_only=False, event_only=False, - special_only=False), - Operator(name='号角/Horn', star=6, limited=False, recruit_only=False, event_only=False, + Operator(name='焰尾/Flametail', star=6, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='傀影/Phantom', star=6, limited=False, recruit_only=False, event_only=False, + special_only=False) ], - zoom=1.0 + zoom=0.5 ), UpEvent( star=5, operator=[ - Operator(name='絮雨/Whisperain', star=5, limited=False, recruit_only=False, event_only=False, - special_only=False), - Operator(name='桑葚/Mulberry', star=5, limited=False, recruit_only=False, event_only=False, + Operator(name='赫默/Silence', star=5, limited=False, recruit_only=False, event_only=False, special_only=False), - Operator(name='洛洛/Rockrock', star=5, limited=False, recruit_only=False, event_only=False, + Operator(name='守林人/Firewatch', star=5, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='吽/Hung', star=5, limited=False, recruit_only=False, event_only=False, special_only=False) ], - zoom=0.6 - ), - UpEvent( - star=4, - operator=[ - Operator(name='杰克/Jackie', star=4, limited=False, recruit_only=False, event_only=False, - special_only=False), - Operator(name='罗比菈塔/Roberta', star=4, limited=False, recruit_only=False, event_only=False, - special_only=False), - Operator(name='褐果/Chestnut', star=4, limited=False, recruit_only=False, event_only=False, - special_only=False), - ], - zoom=0.45 + zoom=0.5 ) ] ALL_OPERATOR: list[Operator] = [ + Operator(name='缄默德克萨斯/Texas the Omertosa', star=6, limited=True, recruit_only=False, event_only=False, + special_only=False), + Operator(name='斥罪/Penance', star=6, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='伺夜/Vigil', star=6, limited=False, recruit_only=False, event_only=True, special_only=False), + Operator(name='子月/Lunacub', star=5, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='雪绒/Qanipalaat', star=5, limited=False, recruit_only=True, event_only=False, special_only=False), + Operator(name='石英/Quartz', star=4, limited=False, recruit_only=True, event_only=False, special_only=False), + Operator(name='白铁/Stainless', star=6, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='明椒/Paprika', star=5, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='达格达/Dagda', star=5, limited=False, recruit_only=False, event_only=True, special_only=False), + Operator(name='铅踝/Totter', star=4, limited=False, recruit_only=False, event_only=False, special_only=False), Operator(name='海沫/Highmore', star=5, limited=False, recruit_only=False, event_only=True, special_only=False), Operator(name='罗小黑/Luo Xiaohei', star=4, limited=False, recruit_only=False, event_only=True, special_only=False), Operator(name='玛恩纳/Młynar', star=6, limited=False, recruit_only=False, event_only=False, special_only=False), From 90ac771709969d05fa5ed7342e4b56f5560def66 Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Fri, 25 Nov 2022 22:48:54 +0800 Subject: [PATCH 14/26] =?UTF-8?q?Upgrade:=20pixiv=20=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=20AI=20=E7=94=9F=E6=88=90=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新 pixiv 模块新增 AI 生成判断 --- omega_miya/database/model.py | 2 +- omega_miya/plugins/moe/utils.py | 5 +++-- omega_miya/plugins/pixiv/utils.py | 3 ++- omega_miya/web_resource/pixiv/model/artwork.py | 2 ++ omega_miya/web_resource/pixiv/pixiv.py | 11 +++++++++++ 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/omega_miya/database/model.py b/omega_miya/database/model.py index b05a096c..01ba1581 100644 --- a/omega_miya/database/model.py +++ b/omega_miya/database/model.py @@ -419,7 +419,7 @@ class PixivArtworkOrm(Base): uid = Column(BigInteger, nullable=False, index=True, comment='作者uid') title = Column(String(128), nullable=False, index=True, comment='作品标题title') uname = Column(String(128), nullable=False, index=True, comment='作者名') - classified = Column(Integer, nullable=False, index=True, comment='标记标签, 0=未标记, 1=已人工标记或从可信已标记来源获取') + classified = Column(Integer, nullable=False, index=True, comment='标记标签, 0=未标记, 1=已人工标记或从可信已标记来源获取, 2=AI生成作品') nsfw_tag = Column(Integer, nullable=False, index=True, comment='nsfw标签, -1=未标记, 0=safe, 1=setu. 2=r18') width = Column(Integer, nullable=False, comment='原始图片宽度') height = Column(Integer, nullable=False, comment='原始图片高度') diff --git a/omega_miya/plugins/moe/utils.py b/omega_miya/plugins/moe/utils.py index ae8e5c21..5d488b9b 100644 --- a/omega_miya/plugins/moe/utils.py +++ b/omega_miya/plugins/moe/utils.py @@ -139,12 +139,13 @@ async def add_artwork_into_database( """在数据库中添加作品信息""" artwork_data = await artwork.get_artwork_model() nsfw_tag = 2 if artwork_data.is_r18 else nsfw_tag + classified = 2 if artwork_data.is_ai else 1 if add_only: result = await InternalPixiv(pid=artwork.pid).add_only(artwork_data=artwork_data, nsfw_tag=nsfw_tag, - classified=1, upgrade_pages=upgrade_pages) + classified=classified, upgrade_pages=upgrade_pages) else: result = await InternalPixiv(pid=artwork.pid).add_upgrade(artwork_data=artwork_data, nsfw_tag=nsfw_tag, - classified=1, upgrade_pages=upgrade_pages) + classified=classified, upgrade_pages=upgrade_pages) return result diff --git a/omega_miya/plugins/pixiv/utils.py b/omega_miya/plugins/pixiv/utils.py index b5f26ca1..0a98232e 100644 --- a/omega_miya/plugins/pixiv/utils.py +++ b/omega_miya/plugins/pixiv/utils.py @@ -187,8 +187,9 @@ async def add_artwork_into_database(artwork: PixivArtwork, *, upgrade_pages: boo """在数据库中添加作品信息(仅新增不更新)""" artwork_data = await artwork.get_artwork_model() nsfw_tag = 2 if artwork_data.is_r18 else -1 + classified = 2 if artwork_data.is_ai else 0 result = await InternalPixiv(pid=artwork.pid).add_only(artwork_data=artwork_data, nsfw_tag=nsfw_tag, - upgrade_pages=upgrade_pages) + classified=classified, upgrade_pages=upgrade_pages) return result diff --git a/omega_miya/web_resource/pixiv/model/artwork.py b/omega_miya/web_resource/pixiv/model/artwork.py index 9d594d6e..700c07b4 100644 --- a/omega_miya/web_resource/pixiv/model/artwork.py +++ b/omega_miya/web_resource/pixiv/model/artwork.py @@ -66,6 +66,7 @@ class PixivArtworkBody(BasePixivModel): width: int height: int xRestrict: int + aiType: int tags: PixivArtworkTags urls: PixivArtworkMainUrl pageCount: int @@ -168,6 +169,7 @@ class PixivArtworkCompleteDataModel(BasePixivModel): title: str sanity_level: int is_r18: bool + is_ai: bool uid: int uname: str description: str diff --git a/omega_miya/web_resource/pixiv/pixiv.py b/omega_miya/web_resource/pixiv/pixiv.py index c964ca05..841223f0 100644 --- a/omega_miya/web_resource/pixiv/pixiv.py +++ b/omega_miya/web_resource/pixiv/pixiv.py @@ -349,6 +349,16 @@ async def get_artwork_model(self) -> PixivArtworkCompleteDataModel: if _sanity_level >= 1: _is_r18 = True + # 判断是否 AI 生成 + _is_ai = False + for tag in _tags: + if re.match(r'^([Nn]ovel[Aa][Ii]([Dd]iffusion)?|[Ss]table[Dd]iffusion|AIイラスト)$', tag): + _is_ai = True + break + _ai_level = _artwork_data.body.aiType + if _ai_level >= 2: + _is_ai = True + # 导出多图列表 _all_url = _page_data.type_page _all_page = _page_data.index_page @@ -369,6 +379,7 @@ async def get_artwork_model(self) -> PixivArtworkCompleteDataModel: 'title': _artwork_data.body.illustTitle, 'sanity_level': _sanity_level, 'is_r18': _is_r18, + 'is_ai': _is_ai, 'uid': _artwork_data.body.userId, 'uname': _artwork_data.body.userName, 'description': _artwork_data.body.parsed_description, From 76e49b1b70def052ed3352f645f6241c10546c38 Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Mon, 28 Nov 2022 21:12:27 +0800 Subject: [PATCH 15/26] =?UTF-8?q?Remove:=20=E5=B7=B2=E5=A4=B1=E6=95=88?= =?UTF-8?q?=E7=9A=84=E5=8F=91=E9=80=81=E9=97=AA=E7=85=A7=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除已失效的发送闪照功能 --- omega_miya/plugins/moe/__init__.py | 9 ++------- omega_miya/plugins/moe/config.py | 10 +--------- omega_miya/plugins/moe/utils.py | 16 ++-------------- 3 files changed, 5 insertions(+), 30 deletions(-) diff --git a/omega_miya/plugins/moe/__init__.py b/omega_miya/plugins/moe/__init__.py index a4ec3f26..139436e0 100644 --- a/omega_miya/plugins/moe/__init__.py +++ b/omega_miya/plugins/moe/__init__.py @@ -47,7 +47,6 @@ "可用参数:\n" "'-s', '--nsfw-tag': 指定nsfw_tag\n" "'-n', '--num': 指定获取的图片数量\n" - "'-nf', '--no-flash': 强制不使用闪照发送图片\n\n" "仅限管理员使用:\n" "/图库统计\n" "/图库查询 [关键词, ...]\n" @@ -120,9 +119,7 @@ async def handle_parse_success(bot: Bot, event: MessageEvent, matcher: Matcher, await matcher.send('稍等, 正在下载图片~') - flash_mode = True if moe_plugin_config.moe_plugin_enforce_setu_enable_flash_mode else not args.no_flash - - image_message_tasks = [prepare_send_image(pid=x.pid, enable_flash_mode=flash_mode) for x in artworks] + image_message_tasks = [prepare_send_image(pid=x.pid) for x in artworks] message_result = await semaphore_gather(tasks=image_message_tasks, semaphore_num=5, filter_exception=True) send_messages = list(message_result) if not send_messages: @@ -181,9 +178,7 @@ async def handle_parse_success(bot: Bot, event: MessageEvent, matcher: Matcher, await matcher.send('稍等, 正在下载图片~') - flash_mode = False if moe_plugin_config.moe_plugin_enforce_moe_disable_flash_mode else not args.no_flash - - image_message_tasks = [prepare_send_image(pid=x.pid, enable_flash_mode=flash_mode) for x in artworks] + image_message_tasks = [prepare_send_image(pid=x.pid) for x in artworks] message_result = await semaphore_gather(tasks=image_message_tasks, semaphore_num=5, filter_exception=True) send_messages = list(message_result) if not send_messages: diff --git a/omega_miya/plugins/moe/config.py b/omega_miya/plugins/moe/config.py index 98d32334..1e559a87 100644 --- a/omega_miya/plugins/moe/config.py +++ b/omega_miya/plugins/moe/config.py @@ -25,16 +25,8 @@ class MoePluginConfig(BaseModel): moe_plugin_query_image_num: int = 3 # 允许用户通过参数调整的每次查询的图片数量上限 moe_plugin_query_image_limit: int = 10 - - # 启用使用闪照模式发送图片 - moe_plugin_enable_flash_mode: bool = True - # 强制不使用闪照发送萌图(会覆盖部分 moe_plugin_enable_flash_mode 配置行为) - moe_plugin_enforce_moe_disable_flash_mode: bool = False - # 强制使用闪照发送涩图(会覆盖部分 moe_plugin_enable_flash_mode 配置行为) - moe_plugin_enforce_setu_enable_flash_mode: bool = False - # 萌图默认自动撤回消息时间(设置 0 为不撤回) - moe_plugin_moe_auto_recall_time: int = 60 + moe_plugin_moe_auto_recall_time: int = 90 # 涩图默认自动撤回消息时间(设置 0 为不撤回) moe_plugin_setu_auto_recall_time: int = 30 diff --git a/omega_miya/plugins/moe/utils.py b/omega_miya/plugins/moe/utils.py index 5d488b9b..9b3da90a 100644 --- a/omega_miya/plugins/moe/utils.py +++ b/omega_miya/plugins/moe/utils.py @@ -51,15 +51,10 @@ async def has_allow_r18_node(bot: Bot, event: MessageEvent, matcher: Matcher) -> @run_async_catching_exception -async def prepare_send_image( - pid: int, - *, - enable_flash_mode: bool = moe_plugin_config.moe_plugin_enable_flash_mode -) -> MessageSegment: +async def prepare_send_image(pid: int) -> MessageSegment: """预处理待发送图片 :param pid: 作品 PID - :param enable_flash_mode: 是否启用闪照模式 :return: 发送的消息 """ @@ -88,11 +83,7 @@ async def _handle_mark(image: TmpResource) -> TmpResource: else: artwork_image = await _handle_mark(image=artwork_image) - if enable_flash_mode: - send_msg = MessageSegment.image(file=artwork_image.file_uri, type_='flash') - else: - send_msg = MessageSegment.image(file=artwork_image.file_uri) - return send_msg + return MessageSegment.image(file=artwork_image.file_uri) def get_query_argument_parser() -> ArgumentParser: @@ -104,8 +95,6 @@ def get_query_argument_parser() -> ArgumentParser: choices=['random', 'pid', 'pid_desc', 'create_time', 'create_time_desc']) parser.add_argument('-n', '--num', type=int, default=moe_plugin_config.moe_plugin_query_image_num) parser.add_argument('-a', '--acc-mode', type=bool, default=moe_plugin_config.moe_plugin_enable_acc_mode) - parser.add_argument('-nf', '--no-flash', action='store_true', - default=(not moe_plugin_config.moe_plugin_enable_flash_mode)) parser.add_argument('word', nargs='*') return parser @@ -117,7 +106,6 @@ class QueryArguments(BaseModel): order: Literal['random', 'pid', 'pid_desc', 'create_time', 'create_time_desc'] num: int acc_mode: bool - no_flash: bool word: list[str] class Config: From abc972fae12ac11ecb25517b8e68b72b7fe95265 Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Mon, 28 Nov 2022 21:12:48 +0800 Subject: [PATCH 16/26] =?UTF-8?q?Fix:=20Yandex=20=E8=AF=86=E5=9B=BE?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E8=A7=A3=E6=9E=90=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Yandex 识图页面解析问题 --- omega_miya/web_resource/image_searcher/iqdb.py | 2 +- omega_miya/web_resource/image_searcher/yandex.py | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/omega_miya/web_resource/image_searcher/iqdb.py b/omega_miya/web_resource/image_searcher/iqdb.py index d3219df0..bc45c8f6 100644 --- a/omega_miya/web_resource/image_searcher/iqdb.py +++ b/omega_miya/web_resource/image_searcher/iqdb.py @@ -104,7 +104,7 @@ async def search(self) -> list[ImageSearchingResult]: data.add_field(name='file', value=b'', content_type='application/octet-stream', filename='') data.add_field(name='url', value=self.image_url) - iqdb_result = await HttpFetcher(timeout=15).post_text(url=self._api, data=data) + iqdb_result = await HttpFetcher(timeout=20).post_text(url=self._api, data=data) if iqdb_result.status != 200: logger.error(f'Iqdb | IqdbNetworkError, {iqdb_result}') raise IqdbNetworkError(f'IqdbNetworkError, {iqdb_result}') diff --git a/omega_miya/web_resource/image_searcher/yandex.py b/omega_miya/web_resource/image_searcher/yandex.py index 2c96fb73..d7d0d57e 100644 --- a/omega_miya/web_resource/image_searcher/yandex.py +++ b/omega_miya/web_resource/image_searcher/yandex.py @@ -36,16 +36,15 @@ def _parser(content: str) -> list[dict] | None: container = gallery_soup.find(name='section', attrs={'class': 'CbirSection CbirSection_decorated CbirSites CbirSites_infinite'}) - container_title = container.find(name='div', attrs={'class': 'CbirSection-Title'}).get_text() + container_title = container.find(name='h2', attrs={'class': 'CbirSection-Title'}).get_text() if container_title != 'Sites containing information about the image': - logger.error('Yandex | Css style change, unable to locate page element') return None - image_result_container = container.find(name='div', attrs={'class': 'CbirSites-Items'}) + image_result_container = container.find(name='ul', attrs={'class': 'CbirSites-Items'}) if not image_result_container: return None - image_results = image_result_container.find_all(name='div', attrs={'class': 'CbirSites-Item'}) + image_results = image_result_container.find_all(name='li', attrs={'class': 'CbirSites-Item'}) result = [] for item in image_results: try: From 77473cacec713bedfe5d325f11eca5cc5dfacc40 Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Tue, 29 Nov 2022 22:23:53 +0800 Subject: [PATCH 17/26] Add: Omega API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 Omega API --- omega_miya/service/omega_api/__init__.py | 67 ++++++++++++++++++++++++ omega_miya/service/omega_api/model.py | 33 ++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 omega_miya/service/omega_api/__init__.py create mode 100644 omega_miya/service/omega_api/model.py diff --git a/omega_miya/service/omega_api/__init__.py b/omega_miya/service/omega_api/__init__.py new file mode 100644 index 00000000..491f35ca --- /dev/null +++ b/omega_miya/service/omega_api/__init__.py @@ -0,0 +1,67 @@ +""" +@Author : Ailitonia +@Date : 2022/11/28 21:24 +@FileName : omega_api.py +@Project : nonebot2_miya +@Description : 自定义 api +@GitHub : https://github.com/Ailitonia +@Software : PyCharm +""" + +# TODO #1 规范 api 请求及响应模型 +# TODO #2 规范 api 依赖注入 +# TODO #3 添加 api 认证 + +import inspect +from typing import TypeVar, ParamSpec, Callable, Coroutine +from nonebot import get_driver, get_app +from nonebot.log import logger + +from .model import BaseApiModel, BaseApiReturn + +P = ParamSpec("P") +R = TypeVar("R") + + +def register_get_route(path: str, *, enabled: bool = True): + """包装 async function 并注册为服务 + + :param path: 请求路径 + :param enabled: 启用开关, 用于插件配置控制 + """ + if not path.startswith('/'): + path = '/' + path + + def decorator(func: Callable[P, Coroutine[None, None, R]]) -> Callable[P, Coroutine[None, None, R]]: + if not inspect.iscoroutinefunction(func): + raise ValueError('The decorated function must be coroutine function') + + if not enabled: + return func + + driver = get_driver() + if 'fastapi' not in driver.type: + raise RuntimeError('fastapi driver not enabled') + + app = get_app() + app.get(path)(func) + + host = str(driver.config.host) + port = driver.config.port + if host in ["0.0.0.0", "127.0.0.1"]: + host = "localhost" + logger.opt(colors=True).info( + f"Service {inspect.getmodule(func).__name__} running at: " + f"http://{host}:{port}/{path.removeprefix('/')}" + ) + + return func + + return decorator + + +__all__ = [ + 'register_get_route', + 'BaseApiModel', + 'BaseApiReturn' +] diff --git a/omega_miya/service/omega_api/model.py b/omega_miya/service/omega_api/model.py new file mode 100644 index 00000000..6f3febe5 --- /dev/null +++ b/omega_miya/service/omega_api/model.py @@ -0,0 +1,33 @@ +""" +@Author : Ailitonia +@Date : 2022/11/29 19:49 +@FileName : model.py +@Project : nonebot2_miya +@Description : api model +@GitHub : https://github.com/Ailitonia +@Software : PyCharm +""" + +from typing import Optional +from pydantic import BaseModel + + +class BaseApiModel(BaseModel): + """api 基类""" + class Config: + extra = 'ignore' + allow_mutation = False + + +class BaseApiReturn(BaseApiModel): + """api 返回值基类""" + error: bool + body: BaseApiModel + message: str + exception: Optional[str] + + +__all = [ + 'BaseApiModel', + 'BaseApiReturn' +] From 620194cc9f078c47e4e8ada8dacb392f9ffb2bfb Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Tue, 29 Nov 2022 22:24:18 +0800 Subject: [PATCH 18/26] =?UTF-8?q?Upgrade:=20LocalResource=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LocalResource 添加属性 --- omega_miya/local_resource/__init__.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/omega_miya/local_resource/__init__.py b/omega_miya/local_resource/__init__.py index 4894e84d..413c3507 100644 --- a/omega_miya/local_resource/__init__.py +++ b/omega_miya/local_resource/__init__.py @@ -56,6 +56,18 @@ def __call__(self, *args) -> "LocalResource": new_obj.path = self.path.joinpath(*[str(x) for x in args]) return new_obj + @property + def is_exist(self) -> bool: + return self.path.exists() + + @property + def is_file(self) -> bool: + return self.is_exist and self.path.is_file() + + @property + def is_dir(self) -> bool: + return self.is_exist and self.path.is_dir() + @staticmethod def check_directory(func: Callable[P, R]) -> Callable[P, R]: """装饰一个方法, 需要实例 path 为文件夹时才能运行""" From 86e6d5ddb29e15cf8166aff26b4a71ade017061b Mon Sep 17 00:00:00 2001 From: ShianoQwQ Date: Tue, 29 Nov 2022 22:33:00 +0800 Subject: [PATCH 19/26] =?UTF-8?q?=E5=B0=86=E6=B1=82=E7=AD=BE=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=E5=88=86=E7=A6=BB=E5=87=BA=E6=9D=A5=EF=BC=8C=E4=BB=A5?= =?UTF-8?q?=E4=BE=BF=E4=BF=AE=E6=94=B9=E4=B8=BA=E4=B8=8D=E5=90=8C=E4=B8=BB?= =?UTF-8?q?=E9=A2=98=E7=9A=84=E6=B1=82=E7=AD=BE=20(#116)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 将求签结果分离出来,以便修改为不同主题的求签 增加从xlsx生成求签结果工具,方便使用在线文档收集求签结果 * 打错字修复 * fix * Upgrade: 签到插件求签事件导入功能 - 新增签到插件求签事件导入功能 * Upgrade: Dependencies - Upgrade Dependencies * Upgrade: Dependencies - Upgrade Dependencies Co-authored-by: Ailitonia Co-authored-by: Ailitonia <41713304+Ailitonia@users.noreply.github.com> --- omega_miya/plugins/omega_sign_in/README.md | 48 ++++ omega_miya/plugins/omega_sign_in/config.py | 8 + omega_miya/plugins/omega_sign_in/fortune.py | 249 +++++++++++--------- requirements.txt | 3 +- 4 files changed, 197 insertions(+), 111 deletions(-) create mode 100644 omega_miya/plugins/omega_sign_in/README.md diff --git a/omega_miya/plugins/omega_sign_in/README.md b/omega_miya/plugins/omega_sign_in/README.md new file mode 100644 index 00000000..a0a3a543 --- /dev/null +++ b/omega_miya/plugins/omega_sign_in/README.md @@ -0,0 +1,48 @@ +# 签到插件 + +可以通过 `戳一戳` 或者 `\签到` 命令进行签到并生成签到图 + +## 关于求签相关功能的补充说明 + +求签事件及结果可以通过模板导入并与内置事件进行合并 + +### 启用方法 + +使用求签事件导入 API 进行操作: `/fortune/import-fortune-event` + +若要启用导入功能, 请先在配置文件 `.env.*` 中加入 `SIGNIN_ENABLE_FORTUNE_IMPORT_API=true` + +重启后在加载插件时日志中有如下提示即为启用成功 + +```angular2html +11-29 21:54:51 [INFO] omega_api | Service omega_miya.plugins.omega_sign_in.fortune running at: http://localhost:port/fortune/import-fortune-event +11-29 21:54:51 [SUCCESS] nonebot | Succeeded to import "omega_sign_in" +``` + +直接访问日志中该链接即可触发导入: `http://localhost:port/fortune/import-fortune-event` + +### 导入模板 + +导入模板默认路径为 `项目路径\tmp\fortune\fortune_event_import.xlsx` + +若不知道模板具体路径或者没有模板文件, 可直接访问上述 API 地址, 若返回: + +```json +{"error":false,"body":{"events":[]},"message":"未找到导入文件, 已生成导入模板: C:\\ProjectsPath\\tmp\\fortune\\fortune_event_import.xlsx, 请在模板上直接修改或覆盖后重新导入","exception":null} +``` + +则已生成导入模板, 在该模板上直接修改或覆盖后即可重新访问 API 地址进行导入 + +若导入成功, 则会返回: + +```json +{"error":false,"body":{"events":[]},"message":"求签事件导入并更新成功","exception":null} +``` + +注意 `message` 提示 "求签事件导入并更新成功" 即为成功, `body` 中会有具体的事件 + +### 重置与调试 + +手动删除 `项目路径\tmp\fortune\` 中 `event.json` 和 `fortune_event_import.xlsx` 两个文件并重启即可将求签事件恢复为初始值 + +调试时可在 API 请求中添加参数: `?reset=true` 即可暂时重置缓存中的事件 diff --git a/omega_miya/plugins/omega_sign_in/config.py b/omega_miya/plugins/omega_sign_in/config.py index fc41ed10..775ef753 100644 --- a/omega_miya/plugins/omega_sign_in/config.py +++ b/omega_miya/plugins/omega_sign_in/config.py @@ -35,6 +35,9 @@ class SignInConfig(BaseModel): # 每日首次签到获取的基础硬币数 同时也是补签所需硬币的倍率基数 signin_base_currency: int = 5 + # 是否启用求签事件导入 api + signin_enable_fortune_import_api: bool = False + class Config: extra = "ignore" @@ -51,6 +54,11 @@ class SignLocalResourceConfig: # 默认的缓存资源保存路径 default_save_folder: TmpResource = TmpResource('sign_in') + # 求签事件资源路径 + default_fortune_event: LocalResource = LocalResource('docs', 'fortune', 'event.json') + tmp_fortune_event: TmpResource = TmpResource('fortune', 'event.json') + fortune_event_import_file: TmpResource = TmpResource('fortune', 'fortune_event_import.xlsx') + try: sign_local_resource_config = SignLocalResourceConfig() diff --git a/omega_miya/plugins/omega_sign_in/fortune.py b/omega_miya/plugins/omega_sign_in/fortune.py index 1248e4d7..772161e1 100644 --- a/omega_miya/plugins/omega_sign_in/fortune.py +++ b/omega_miya/plugins/omega_sign_in/fortune.py @@ -1,8 +1,15 @@ import random import hashlib +import ujson as json from datetime import datetime from typing import Literal from pydantic import BaseModel, parse_obj_as +from openpyxl import load_workbook, Workbook + +from omega_miya.service.omega_api import register_get_route, BaseApiModel, BaseApiReturn +from omega_miya.utils.process_utils import run_sync + +from .config import sign_in_config, sign_local_resource_config class Fortune(BaseModel): @@ -15,121 +22,143 @@ class Fortune(BaseModel): bad_do_nd: str -class Something(BaseModel): +class FortuneEvent(BaseModel): """老黄历宜和不宜内容""" name: str good: str bad: str + def __eq__(self, other) -> bool: + if isinstance(other, FortuneEvent): + return self.name == other.name + else: + return False -do_something = [ - {'name': '看直播', 'good': '喜欢的V开歌回啦', 'bad': '喜欢的V咕了一整天'}, - {'name': '打轴', 'good': '一次性过', 'bad': '"谁说话这么难懂"'}, - {'name': '剪辑', 'good': '灵感爆发', 'bad': '一团乱麻'}, - {'name': '校对', 'good': '变成无情的审轴机器', 'bad': '被闪轴闪瞎眼'}, - {'name': '浏览Pixiv', 'good': '发现符合xp的涩图', 'bad': '找不到想要的涩图'}, - {'name': '打SC', 'good': '享受石油佬的乐趣', 'bad': '吃土中'}, - {'name': '吃人', 'good': '你面前这位有成为神龙的潜质', 'bad': '这人会用Aegisub吗?'}, - {'name': '背单词', 'good': '这次六级肯定过', 'bad': '背完50个忘了45个'}, - {'name': '翘课', 'good': '老师不会点名', 'bad': '老师准会抽到你来回答问题'}, - {'name': '做作业', 'good': '做的每个都对', 'bad': '做一个做错一个'}, - {'name': '锻炼一下身体', 'good': '身体健康, 更加性福', 'bad': '能量没消耗多少, 吃得却更多'}, - {'name': '浏览成人网站', 'good': '重拾对生活的信心', 'bad': '你会心神不宁'}, - {'name': '修复BUG', 'good': '你今天对BUG的嗅觉大大提高', 'bad': '新产生的BUG将比修复的更多'}, - {'name': '上AB站', 'good': '还需要理由吗?', 'bad': '满屏兄贵亮瞎你的眼'}, - {'name': '打LOL', 'good': '你将有如神助', 'bad': '你会被虐的很惨'}, - {'name': '打DOTA', 'good': '天梯5000分不是梦', 'bad': '你会遇到猪一样的队友'}, - {'name': '打DOTA2', 'good': 'Godlike', 'bad': '不怕神一样的对手,就怕猪一样的队友'}, - {'name': '穿女装', 'good': '你会得到很多炙热的目光', 'bad': '被父母看到'}, - {'name': '组模型', 'good': '今天的喷漆会很完美', 'bad': '精神不集中板件被剪断了'}, - {'name': '熬夜', 'good': '夜间的效率更高', 'bad': '明天有很重要的事'}, - {'name': '抚摸猫咪', 'good': '才不是特意蹭你的呢', 'bad': '死开! 愚蠢的人类'}, - {'name': '烹饪', 'good': '黑暗料理界就由我来打败', 'bad': '难道这就是……仰望星空派?'}, - {'name': '告白', 'good': '其实我也喜欢你好久了', 'bad': '对不起, 你是一个好人'}, - {'name': '追新番', 'good': '完结之前我绝不会死', 'bad': '会被剧透'}, - {'name': '日麻', 'good': '立直一发自摸!', 'bad': '碰喵吃喵杠喵荣喵!'}, - {'name': '音游', 'good': 'FCACFRPR不过如此', 'bad': '又双叒叕LOST了...'}, - {'name': '向大佬请教', 'good': '太棒了,学到许多', 'bad': '太棒了,什么都没学到'}, - {'name': '早起', 'good': '迎接第一缕阳光', 'bad': '才4点,再睡一会'}, - {'name': '早睡', 'good': '第二天精神饱满', 'bad': '失眠数羊画圈圈'}, - {'name': '入正版游戏', 'good': '买了痛三天,不买悔三年', 'bad': 'emmmm,汇率还是……'}, - {'name': '补旧作', 'good': '意外地对胃口', 'bad': '会踩雷'}, - {'name': '晾晒老婆(抱枕套)', 'good': '天気も晴れココロも晴れ', 'bad': '引发路人围观'}, - {'name': '不按攻略打', 'good': '居然是HAPPY END', 'bad': '碰到BAD END'}, - {'name': '观赏CG包', 'good': '社保。', 'bad': '还不去如看游戏剧情'}, - {'name': '研究黄油创作理论', 'good': '增进鉴赏水平', 'bad': '闲适玩家不需要这些'}, - {'name': '暴露性癖', 'good': '会引来很多趣味相同的变态', 'bad': '四斋蒸鹅心'}, - {'name': '施法', 'good': '传统手艺精进了', 'bad': '房间门关好了吗'}, - {'name': '刷新作动态', 'good': '喜欢的画师发了新图', 'bad': '发现游戏跳票'}, - {'name': '回味玩过的作品', 'good': '重温感动', 'bad': '还是先看看新作'}, - {'name': '出门走走', 'good': '宅久了要发霉', 'bad': '太陽が眩しすぎる'}, - {'name': '思考人生', 'good': '自己的幸福呢?', 'bad': '喵喵……喵?'}, - {'name': '撸猫', 'good': '啊……好爽', 'bad': '家里没有猫的洗洗睡吧'}, - {'name': '抽卡', 'good': '单抽出货', 'bad': '到井前一发出货'}, - {'name': '拼乐高', 'good': '顺利完工', 'bad': '发现少了一块零件'}, - {'name': '跳槽', 'good': '新工作待遇大幅提升', 'bad': '待遇还不如之前的'}, - {'name': '和女神聊天', 'good': '今天天气不错', 'bad': '我去洗澡了,呵呵'}, - {'name': '写开源库', 'good': '今天北斗七星汇聚,裤子造的又快又好', 'bad': '写好会发现github上已经有了更好的'}, - {'name': '给测试妹子埋个bug', 'good': '下辈子的幸福就靠这个bug了', 'bad': '妹子会认为你活和代码一样差'}, - {'name': '写单元测试', 'good': '写单元测试将减少出错', 'bad': '写单元测试会降低你的开发效率'}, - {'name': '洗澡', 'good': '你几天没洗澡了?', 'bad': '会把设计方面的灵感洗掉'}, - {'name': '白天上线', 'good': '今天白天上线是安全的', 'bad': '可能导致灾难性后果'}, - {'name': '重构', 'good': '代码质量得到提高', 'bad': '你很有可能会陷入泥潭'}, - {'name': '招人', 'good': '你面前这位有成为牛人的潜质', 'bad': '这人会写程序吗?'}, - {'name': '面试', 'good': '面试官今天心情很好', 'bad': '面试官不爽,会拿你出气'}, - {'name': '申请加薪', 'good': '老板今天心情很好', 'bad': '公司正在考虑裁员'}, - {'name': '提交代码', 'good': '遇到冲突的几率是最低的', 'bad': '会遇到的一大堆冲突'}, - {'name': '代码复审', 'good': '发现重要问题的几率大大增加', 'bad': '你什么问题都发现不了,白白浪费时间'}, - {'name': '晚上上线', 'good': '晚上是程序员精神最好的时候', 'bad': '你白天已经筋疲力尽了'}, - {'name': '乘电梯', 'good': '正好赶上打卡截止时间', 'bad': '电梯超载'}, - {'name': '复读', 'good': '有时候,人云亦云也是一种生存方式', 'bad': '你的对手是鸽子'}, - {'name': '肝爆', 'good': '努力使人进步,肝爆让人快乐', 'bad': '醒醒,限时活动没了'}, - {'name': '氪金', 'good': '早买早享受,晚买哭着求', 'bad': '第二天就 50% off'}, - {'name': '卖弱', 'good': '楚楚动人更容易打动群友', 'bad': 'Boy♂next♂door'}, - {'name': '唱脑力', 'good': '唱一次提神醒脑,唱两次精神百倍', 'bad': '会与复读机一起对群聊造成毁灭性打击'}, - {'name': '看手元', 'good': '从手元中获得一点音游经验', 'bad': '会被大佬闪瞎'}, - {'name': '录手元', 'good': '音游届的未来新星UP主就是你', 'bad': '打完歌才发现忘记开录像'}, - {'name': '挑战魔王曲', 'good': '一上来就是一个新纪录', 'bad': '有这点时间还不如干点别的'}, - {'name': '咕咕咕', 'good': '一时咕一时爽', 'bad': '会被抓起来,被群友强迫穿上女装'}, - {'name': '与群友水聊', 'good': '扶我起来我还能打字', 'bad': '一不小心就被大佬闪瞎'}, - {'name': '迫害大佬', 'good': '迫害是大佬进步的阶梯', 'bad': '亲爱的,你号没了'}, - {'name': '算命', 'good': '算啥都准', 'bad': '诸事不宜'}, - {'name': '成为魔法少女', 'good': '勇敢的烧酒啊快去拯救世界吧!', 'bad': '会掉头'}, - {'name': '沟通克苏鲁', 'good': '奇怪的知识增加了', 'bad': '&▓▓▓◆▓▓▓¥#▓@■.◆'}, - {'name': '看新番', 'good': '你看的这部新番有成为本季度霸权的可能', 'bad': '这周更新的是总集篇'}, - {'name': '看旧番', 'good': '在宅的道路上又前进了一步', 'bad': '被剧情喂屎'}, - {'name': '看里番', 'good': '传统手艺精进了', 'bad': '房间门关好了吗?'}, - {'name': '看漫画', 'good': '正在追的作品十话连发', 'bad': '刷到正在追的作品的腰斩停更通知'}, - {'name': '看轻小说', 'good': '插画很好舔,孩子很满意', 'bad': '买插画送的厕纸有啥好看的'}, - {'name': '看本子', 'good': '被精准戳中性癖', 'bad': '更新的全是你不喜欢的类型'}, - {'name': '前往女仆咖啡厅', 'good': '感受身心上的治愈', 'bad': '虚假的女仆只会让你内心更加空虚'}, - {'name': '女装Cosplay', 'good': '好评如潮', 'bad': '照片传到班级群还被认出来'}, - {'name': '修仙', 'good': '能突破到下一个境界', 'bad': '会在进阶中遭受心魔侵蚀'}, - {'name': '渡劫', 'good': '万事俱备,只待飞升', 'bad': '没能扛过去,寿元终'}, - {'name': '在妹子面前吹牛', 'good': '改善你矮穷挫的形象', 'bad': '会被识破'}, - {'name': '发超过10条的状态', 'good': '显得你很高产', 'bad': '会被人直接拉黑'}, - {'name': '在B站上传视频', 'good': '播放量爆炸', 'bad': '没人看'}, - {'name': '搬运视频', 'good': '会被硬币砸得很爽', 'bad': '不会有人看的'}, - {'name': '上微博', 'good': '今天的瓜不能错过', 'bad': '被智障发言糊一脸'}, - {'name': '作死', 'good': '节目效果一流', 'bad': '吾之旧友弔似汝,如今坟头草丈五'}, - {'name': '看老黄历', 'good': '反正你已经看了', 'bad': '反正你已经看了'}, - {'name': '学习一门新技能', 'good': '有会成为大神的资质', 'bad': '可能会误入歧途'}, - {'name': '睡懒觉', 'good': '避免内存不足', 'bad': '早上很早醒来睡不着了'}, - {'name': '睡懒觉', 'good': '你今天会更有精神', 'bad': '会错过重要的事情'}, - {'name': '上课玩手机', 'good': '会发现好玩的事情', 'bad': '会被老师教训'}, - {'name': '抄作业', 'good': '没有作业抄的学生生活是罪恶的!', 'bad': '老师会认真批改,你懂的……'}, - {'name': '学习', 'good': '你已经几天(月、年)没学习了?', 'bad': '会睡着'}, - {'name': '出门带伞', 'good': '今天下雨你信不信', 'bad': '好运气都被遮住了'}, - {'name': '走夜路', 'good': '偶尔也要一个人静一静', 'bad': '有坏人'}, - {'name': '补番', 'good': '你会后悔没早点看这部番', 'bad': '你会后悔看了这部番'}, - {'name': '玩Minecraft', 'good': '建筑灵感爆发', 'bad': '启动器都会崩溃'}, - {'name': '上Steam', 'good': '愿望单里全是90%off', 'bad': '钱包被G胖洗劫一空'}, - {'name': '修图', 'good': '原片直出毫无压力', 'bad': 'Photoshop未响应'}, - {'name': '赶稿', 'good': '完美守住deadline', 'bad': '终究还是超期了'}, - {'name': '摸鱼', 'good': '摸鱼一时爽,一直摸鱼一直爽', 'bad': '被老板当场抓获'}, - {'name': '入手新游戏', 'good': '你会玩的很开心', 'bad': '这游戏明天就99%off'}, - {'name': '出门', 'good': '今天会是个好天气', 'bad': '中途突降暴雨'} -] + def __hash__(self) -> int: + return hash(self.name) + + +__fortune_event: list[FortuneEvent] = [] + + +def _upgrade_fortune_event(*, enforce_origin: bool = False) -> None: + """更新求签事件缓存 + + :param enforce_origin: 强制使用原始资源解析事件 + """ + if sign_local_resource_config.tmp_fortune_event.is_file and not enforce_origin: + event_file = sign_local_resource_config.tmp_fortune_event + else: + event_file = sign_local_resource_config.default_fortune_event + + with event_file.open('r', encoding='utf8') as f: + fortune_event = json.loads(f.read()) + + global __fortune_event + __fortune_event = parse_obj_as(list[FortuneEvent], fortune_event) + + +def get_fortune_event() -> list[FortuneEvent]: + """获取所有求签事件""" + if not __fortune_event: + _upgrade_fortune_event() + + return __fortune_event + + +def random_fortune_event(num: int = 4) -> list[FortuneEvent]: + """随机获取求签事件""" + return random.sample(get_fortune_event(), k=num) + + +def _load_fortune_event_import_file() -> None: + """从 xlsx 导入文件中读取并转换格式""" + if not sign_local_resource_config.fortune_event_import_file.is_file: + raise FileNotFoundError('Fortune event import file not found') + + import_workbook = load_workbook(sign_local_resource_config.fortune_event_import_file.path) + import_sheet = import_workbook['fortune_event'] + fortune_event = [] + for row in import_sheet.iter_rows(min_row=2): + fortune_event.append( + FortuneEvent.parse_obj({ + 'name': row[0].value, + 'good': row[1].value, + 'bad': row[2].value + }) + ) + + fortune_event = set(fortune_event) + _upgrade_fortune_event(enforce_origin=True) + fortune_event.update(__fortune_event) + fortune_event = sorted([x.dict() for x in fortune_event], key=lambda x: x['name']) + + with sign_local_resource_config.tmp_fortune_event.open('w', encoding='utf8') as f: + json.dump(fortune_event, f, ensure_ascii=False) + + +def _create_fortune_event_import_file_template() -> None: + """生成空白的 xlsx 导入文件模板""" + template_workbook = Workbook() + template_sheet = template_workbook.active + template_sheet.title = 'fortune_event' + + template_sheet.cell(column=1, row=1, value='事项') + template_sheet.cell(column=2, row=1, value='吉') + template_sheet.cell(column=3, row=1, value='凶') + + with sign_local_resource_config.fortune_event_import_file.open('wb') as f: + template_workbook.save(f) + + +@register_get_route(path='/fortune/import-fortune-event', enabled=sign_in_config.signin_enable_fortune_import_api) +async def _handle_fortune_event_import(reset: bool = False) -> BaseApiReturn: + """api 导入求签事件 + + :param reset: 重置求签事件为默认值(调试用, 仅影响事件缓存) + """ + class _ReturnBody(BaseApiModel): + events: list[FortuneEvent] + + class _Return(BaseApiReturn): + body: _ReturnBody + + # 没有导入文件则生成导入文件模板 + if not sign_local_resource_config.fortune_event_import_file.is_file: + try: + await run_sync(_create_fortune_event_import_file_template)() + return _Return( + error=False, + body=_ReturnBody(events=[]), + message=f'未找到导入文件, ' + f'已生成导入模板: {sign_local_resource_config.fortune_event_import_file.resolve_path}, ' + f'请在模板上直接修改或覆盖后重新导入' + ) + except Exception as e: + return _Return( + error=True, + body=_ReturnBody(events=[]), + message=f'创建导入模板失败: {sign_local_resource_config.fortune_event_import_file.resolve_path}, ' + f'请检查文件路径是否被占用', + exception=str(e) + ) + + # 有导入文件则读取并解析导入文件 + try: + await run_sync(_load_fortune_event_import_file)() + await run_sync(_upgrade_fortune_event)(enforce_origin=reset) + return _Return( + error=False, + body=_ReturnBody(events=get_fortune_event()), + message=f'求签事件导入并更新成功' + ) + except Exception as e: + return _Return( + error=True, + body=_ReturnBody(events=[]), + message=f'求签事件导入或更新失败', + exception=str(e) + ) def get_fortune(user_id: int, *, date: datetime | None = None) -> Fortune: @@ -186,7 +215,7 @@ def get_fortune(user_id: int, *, date: datetime | None = None) -> Fortune: fortune_text = '大吉' # 宜做和不宜做 - do_and_not = random.sample(parse_obj_as(list[Something], do_something), k=4) + do_and_not = random_fortune_event(num=4) result = { 'star': fortune_star, diff --git a/requirements.txt b/requirements.txt index 0ec1c372..ca21e9b3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,4 +23,5 @@ py7zr~=0.20.2 zhconv~=1.4.3 rapidfuzz~=2.13.2 emoji~=2.2.0 -rich~=12.6.0 \ No newline at end of file +rich~=12.6.0 +openpyxl~=3.0.10 \ No newline at end of file From 20f549af88c5bad7bffec3316068df42909ae337 Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Tue, 29 Nov 2022 22:37:29 +0800 Subject: [PATCH 20/26] =?UTF-8?q?Fix:=20=E6=B1=82=E7=AD=BE=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E8=B5=84=E6=BA=90=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加求签默认资源文件 --- .../local_resource/docs/fortune/event.json | 532 ++++++++++++++++++ 1 file changed, 532 insertions(+) create mode 100644 omega_miya/local_resource/docs/fortune/event.json diff --git a/omega_miya/local_resource/docs/fortune/event.json b/omega_miya/local_resource/docs/fortune/event.json new file mode 100644 index 00000000..6f90570b --- /dev/null +++ b/omega_miya/local_resource/docs/fortune/event.json @@ -0,0 +1,532 @@ +[ + { + "name": "看直播", + "good": "喜欢的V开歌回啦", + "bad": "喜欢的V咕了一整天" + }, + { + "name": "打轴", + "good": "一次性过", + "bad": "\"谁说话这么难懂\"" + }, + { + "name": "剪辑", + "good": "灵感爆发", + "bad": "一团乱麻" + }, + { + "name": "校对", + "good": "变成无情的审轴机器", + "bad": "被闪轴闪瞎眼" + }, + { + "name": "浏览Pixiv", + "good": "发现符合xp的涩图", + "bad": "找不到想要的涩图" + }, + { + "name": "打SC", + "good": "享受石油佬的乐趣", + "bad": "吃土中" + }, + { + "name": "吃人", + "good": "你面前这位有成为神龙的潜质", + "bad": "这人会用Aegisub吗?" + }, + { + "name": "背单词", + "good": "这次六级肯定过", + "bad": "背完50个忘了45个" + }, + { + "name": "翘课", + "good": "老师不会点名", + "bad": "老师准会抽到你来回答问题" + }, + { + "name": "做作业", + "good": "做的每个都对", + "bad": "做一个做错一个" + }, + { + "name": "锻炼一下身体", + "good": "身体健康, 更加性福", + "bad": "能量没消耗多少, 吃得却更多" + }, + { + "name": "浏览成人网站", + "good": "重拾对生活的信心", + "bad": "你会心神不宁" + }, + { + "name": "修复BUG", + "good": "你今天对BUG的嗅觉大大提高", + "bad": "新产生的BUG将比修复的更多" + }, + { + "name": "上AB站", + "good": "还需要理由吗?", + "bad": "满屏兄贵亮瞎你的眼" + }, + { + "name": "打LOL", + "good": "你将有如神助", + "bad": "你会被虐的很惨" + }, + { + "name": "打DOTA", + "good": "天梯5000分不是梦", + "bad": "你会遇到猪一样的队友" + }, + { + "name": "打DOTA2", + "good": "Godlike", + "bad": "不怕神一样的对手,就怕猪一样的队友" + }, + { + "name": "穿女装", + "good": "你会得到很多炙热的目光", + "bad": "被父母看到" + }, + { + "name": "组模型", + "good": "今天的喷漆会很完美", + "bad": "精神不集中板件被剪断了" + }, + { + "name": "熬夜", + "good": "夜间的效率更高", + "bad": "明天有很重要的事" + }, + { + "name": "抚摸猫咪", + "good": "才不是特意蹭你的呢", + "bad": "死开! 愚蠢的人类" + }, + { + "name": "烹饪", + "good": "黑暗料理界就由我来打败", + "bad": "难道这就是……仰望星空派?" + }, + { + "name": "告白", + "good": "其实我也喜欢你好久了", + "bad": "对不起, 你是一个好人" + }, + { + "name": "追新番", + "good": "完结之前我绝不会死", + "bad": "会被剧透" + }, + { + "name": "日麻", + "good": "立直一发自摸!", + "bad": "碰喵吃喵杠喵荣喵!" + }, + { + "name": "音游", + "good": "FCACFRPR不过如此", + "bad": "又双叒叕LOST了..." + }, + { + "name": "向大佬请教", + "good": "太棒了,学到许多", + "bad": "太棒了,什么都没学到" + }, + { + "name": "早起", + "good": "迎接第一缕阳光", + "bad": "才4点,再睡一会" + }, + { + "name": "早睡", + "good": "第二天精神饱满", + "bad": "失眠数羊画圈圈" + }, + { + "name": "入正版游戏", + "good": "买了痛三天,不买悔三年", + "bad": "emmmm,汇率还是……" + }, + { + "name": "补旧作", + "good": "意外地对胃口", + "bad": "会踩雷" + }, + { + "name": "晾晒老婆(抱枕套)", + "good": "天気も晴れココロも晴れ", + "bad": "引发路人围观" + }, + { + "name": "不按攻略打", + "good": "居然是HAPPY END", + "bad": "碰到BAD END" + }, + { + "name": "观赏CG包", + "good": "社保。", + "bad": "还不去如看游戏剧情" + }, + { + "name": "研究黄油创作理论", + "good": "增进鉴赏水平", + "bad": "闲适玩家不需要这些" + }, + { + "name": "暴露性癖", + "good": "会引来很多趣味相同的变态", + "bad": "四斋蒸鹅心" + }, + { + "name": "施法", + "good": "传统手艺精进了", + "bad": "房间门关好了吗" + }, + { + "name": "刷新作动态", + "good": "喜欢的画师发了新图", + "bad": "发现游戏跳票" + }, + { + "name": "回味玩过的作品", + "good": "重温感动", + "bad": "还是先看看新作" + }, + { + "name": "出门走走", + "good": "宅久了要发霉", + "bad": "太陽が眩しすぎる" + }, + { + "name": "思考人生", + "good": "自己的幸福呢?", + "bad": "喵喵……喵?" + }, + { + "name": "撸猫", + "good": "啊……好爽", + "bad": "家里没有猫的洗洗睡吧" + }, + { + "name": "抽卡", + "good": "单抽出货", + "bad": "到井前一发出货" + }, + { + "name": "拼乐高", + "good": "顺利完工", + "bad": "发现少了一块零件" + }, + { + "name": "跳槽", + "good": "新工作待遇大幅提升", + "bad": "待遇还不如之前的" + }, + { + "name": "和女神聊天", + "good": "今天天气不错", + "bad": "我去洗澡了,呵呵" + }, + { + "name": "写开源库", + "good": "今天北斗七星汇聚,裤子造的又快又好", + "bad": "写好会发现github上已经有了更好的" + }, + { + "name": "给测试妹子埋个bug", + "good": "下辈子的幸福就靠这个bug了", + "bad": "妹子会认为你活和代码一样差" + }, + { + "name": "写单元测试", + "good": "写单元测试将减少出错", + "bad": "写单元测试会降低你的开发效率" + }, + { + "name": "洗澡", + "good": "你几天没洗澡了?", + "bad": "会把设计方面的灵感洗掉" + }, + { + "name": "白天上线", + "good": "今天白天上线是安全的", + "bad": "可能导致灾难性后果" + }, + { + "name": "重构", + "good": "代码质量得到提高", + "bad": "你很有可能会陷入泥潭" + }, + { + "name": "招人", + "good": "你面前这位有成为牛人的潜质", + "bad": "这人会写程序吗?" + }, + { + "name": "面试", + "good": "面试官今天心情很好", + "bad": "面试官不爽,会拿你出气" + }, + { + "name": "申请加薪", + "good": "老板今天心情很好", + "bad": "公司正在考虑裁员" + }, + { + "name": "提交代码", + "good": "遇到冲突的几率是最低的", + "bad": "会遇到的一大堆冲突" + }, + { + "name": "代码复审", + "good": "发现重要问题的几率大大增加", + "bad": "你什么问题都发现不了,白白浪费时间" + }, + { + "name": "晚上上线", + "good": "晚上是程序员精神最好的时候", + "bad": "你白天已经筋疲力尽了" + }, + { + "name": "乘电梯", + "good": "正好赶上打卡截止时间", + "bad": "电梯超载" + }, + { + "name": "复读", + "good": "有时候,人云亦云也是一种生存方式", + "bad": "你的对手是鸽子" + }, + { + "name": "肝爆", + "good": "努力使人进步,肝爆让人快乐", + "bad": "醒醒,限时活动没了" + }, + { + "name": "氪金", + "good": "早买早享受,晚买哭着求", + "bad": "第二天就 50% off" + }, + { + "name": "卖弱", + "good": "楚楚动人更容易打动群友", + "bad": "Boy♂next♂door" + }, + { + "name": "唱脑力", + "good": "唱一次提神醒脑,唱两次精神百倍", + "bad": "会与复读机一起对群聊造成毁灭性打击" + }, + { + "name": "看手元", + "good": "从手元中获得一点音游经验", + "bad": "会被大佬闪瞎" + }, + { + "name": "录手元", + "good": "音游届的未来新星UP主就是你", + "bad": "打完歌才发现忘记开录像" + }, + { + "name": "挑战魔王曲", + "good": "一上来就是一个新纪录", + "bad": "有这点时间还不如干点别的" + }, + { + "name": "咕咕咕", + "good": "一时咕一时爽", + "bad": "会被抓起来,被群友强迫穿上女装" + }, + { + "name": "与群友水聊", + "good": "扶我起来我还能打字", + "bad": "一不小心就被大佬闪瞎" + }, + { + "name": "迫害大佬", + "good": "迫害是大佬进步的阶梯", + "bad": "亲爱的,你号没了" + }, + { + "name": "算命", + "good": "算啥都准", + "bad": "诸事不宜" + }, + { + "name": "成为魔法少女", + "good": "勇敢的烧酒啊快去拯救世界吧!", + "bad": "会掉头" + }, + { + "name": "沟通克苏鲁", + "good": "奇怪的知识增加了", + "bad": "&▓▓▓◆▓▓▓¥#▓@■.◆" + }, + { + "name": "看新番", + "good": "你看的这部新番有成为本季度霸权的可能", + "bad": "这周更新的是总集篇" + }, + { + "name": "看旧番", + "good": "在宅的道路上又前进了一步", + "bad": "被剧情喂屎" + }, + { + "name": "看里番", + "good": "传统手艺精进了", + "bad": "房间门关好了吗?" + }, + { + "name": "看漫画", + "good": "正在追的作品十话连发", + "bad": "刷到正在追的作品的腰斩停更通知" + }, + { + "name": "看轻小说", + "good": "插画很好舔,孩子很满意", + "bad": "买插画送的厕纸有啥好看的" + }, + { + "name": "看本子", + "good": "被精准戳中性癖", + "bad": "更新的全是你不喜欢的类型" + }, + { + "name": "前往女仆咖啡厅", + "good": "感受身心上的治愈", + "bad": "虚假的女仆只会让你内心更加空虚" + }, + { + "name": "女装Cosplay", + "good": "好评如潮", + "bad": "照片传到班级群还被认出来" + }, + { + "name": "修仙", + "good": "能突破到下一个境界", + "bad": "会在进阶中遭受心魔侵蚀" + }, + { + "name": "渡劫", + "good": "万事俱备,只待飞升", + "bad": "没能扛过去,寿元终" + }, + { + "name": "在妹子面前吹牛", + "good": "改善你矮穷挫的形象", + "bad": "会被识破" + }, + { + "name": "发超过10条的状态", + "good": "显得你很高产", + "bad": "会被人直接拉黑" + }, + { + "name": "在B站上传视频", + "good": "播放量爆炸", + "bad": "没人看" + }, + { + "name": "搬运视频", + "good": "会被硬币砸得很爽", + "bad": "不会有人看的" + }, + { + "name": "上微博", + "good": "今天的瓜不能错过", + "bad": "被智障发言糊一脸" + }, + { + "name": "作死", + "good": "节目效果一流", + "bad": "吾之旧友弔似汝,如今坟头草丈五" + }, + { + "name": "看老黄历", + "good": "反正你已经看了", + "bad": "反正你已经看了" + }, + { + "name": "学习一门新技能", + "good": "有会成为大神的资质", + "bad": "可能会误入歧途" + }, + { + "name": "睡懒觉", + "good": "避免内存不足", + "bad": "早上很早醒来睡不着了" + }, + { + "name": "睡懒觉", + "good": "你今天会更有精神", + "bad": "会错过重要的事情" + }, + { + "name": "上课玩手机", + "good": "会发现好玩的事情", + "bad": "会被老师教训" + }, + { + "name": "抄作业", + "good": "没有作业抄的学生生活是罪恶的!", + "bad": "老师会认真批改,你懂的……" + }, + { + "name": "学习", + "good": "你已经几天(月、年)没学习了?", + "bad": "会睡着" + }, + { + "name": "出门带伞", + "good": "今天下雨你信不信", + "bad": "好运气都被遮住了" + }, + { + "name": "走夜路", + "good": "偶尔也要一个人静一静", + "bad": "有坏人" + }, + { + "name": "补番", + "good": "你会后悔没早点看这部番", + "bad": "你会后悔看了这部番" + }, + { + "name": "玩Minecraft", + "good": "建筑灵感爆发", + "bad": "启动器都会崩溃" + }, + { + "name": "上Steam", + "good": "愿望单里全是90%off", + "bad": "钱包被G胖洗劫一空" + }, + { + "name": "修图", + "good": "原片直出毫无压力", + "bad": "Photoshop未响应" + }, + { + "name": "赶稿", + "good": "完美守住deadline", + "bad": "终究还是超期了" + }, + { + "name": "摸鱼", + "good": "摸鱼一时爽,一直摸鱼一直爽", + "bad": "被老板当场抓获" + }, + { + "name": "入手新游戏", + "good": "你会玩的很开心", + "bad": "这游戏明天就99%off" + }, + { + "name": "出门", + "good": "今天会是个好天气", + "bad": "中途突降暴雨" + } +] \ No newline at end of file From 59a0de7d834f8ac6ac67a13fd449c62942746695 Mon Sep 17 00:00:00 2001 From: Ailitonia Date: Wed, 30 Nov 2022 20:23:25 +0800 Subject: [PATCH 21/26] =?UTF-8?q?Fix:=20=E7=AD=BE=E5=88=B0=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E6=B1=82=E7=AD=BE=E4=BA=8B=E4=BB=B6=20FortuneEvent=20?= =?UTF-8?q?=E5=AF=B9=20eq=20=E5=92=8C=20hash=20=E7=9A=84=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复签到插件求签事件 FortuneEvent 对 eq 和 hash 的判断 --- omega_miya/plugins/omega_sign_in/fortune.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/omega_miya/plugins/omega_sign_in/fortune.py b/omega_miya/plugins/omega_sign_in/fortune.py index 772161e1..7b6d202d 100644 --- a/omega_miya/plugins/omega_sign_in/fortune.py +++ b/omega_miya/plugins/omega_sign_in/fortune.py @@ -30,12 +30,12 @@ class FortuneEvent(BaseModel): def __eq__(self, other) -> bool: if isinstance(other, FortuneEvent): - return self.name == other.name + return self.name == other.name and self.good == other.good and self.bad == other.bad else: return False def __hash__(self) -> int: - return hash(self.name) + return hash(self.name + self.good + self.bad) __fortune_event: list[FortuneEvent] = [] From 491f08dac2818ed7e2a2047263db13dc53f69dc6 Mon Sep 17 00:00:00 2001 From: Ailitonia <41713304+Ailitonia@users.noreply.github.com> Date: Tue, 27 Jun 2023 20:47:26 +0800 Subject: [PATCH 22/26] =?UTF-8?q?=E6=9B=B4=E6=8D=A2B=E7=AB=99=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E7=94=A8=E6=88=B7=E4=BF=A1=E6=81=AF=E7=9A=84Api?= =?UTF-8?q?=EF=BC=8C=E9=99=8D=E4=BD=8E=E9=A3=8E=E6=8E=A7=E6=A6=82=E7=8E=87?= =?UTF-8?q?=20(#125)=20(#127)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 更换获取用户信息的Api,避免风控 * Fix: 调整 wbi 参数获取为异步方法 - 调整 wbi 参数获取为异步方法 --------- Co-authored-by: 星ノ谷绫沫 <30362789+yokinanya@users.noreply.github.com> --- omega_miya/web_resource/bilibili/bilibili.py | 6 +- omega_miya/web_resource/bilibili/wbi.py | 68 ++++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 omega_miya/web_resource/bilibili/wbi.py diff --git a/omega_miya/web_resource/bilibili/bilibili.py b/omega_miya/web_resource/bilibili/bilibili.py index 936d6c88..0909dd7a 100644 --- a/omega_miya/web_resource/bilibili/bilibili.py +++ b/omega_miya/web_resource/bilibili/bilibili.py @@ -16,6 +16,7 @@ from omega_miya.web_resource import HttpFetcher from .config import bilibili_config, bilibili_resource_config +from .wbi import transform_params from .exception import BilibiliApiError, BilibiliNetworkError from .model.search import BaseBilibiliSearchingModel, UserSearchingModel from .model import (BilibiliUserModel, BilibiliUserDynamicModel, BilibiliDynamicModel, @@ -126,7 +127,7 @@ async def _global_search( class BilibiliUser(Bilibili): - _data_api_url = 'https://api.bilibili.com/x/space/acc/info' + _data_api_url = 'https://api.bilibili.com/x/space/wbi/acc/info' _dynamic_api_url = 'https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/space_history' def __init__(self, uid: int): @@ -164,7 +165,8 @@ async def get_user_model(self) -> BilibiliUserModel: """获取并初始化用户对应 BilibiliUserModel""" if not isinstance(self.user_model, BilibiliUserModel): params = {'mid': self.mid} - user_result = await self._fetcher.get_json_dict(url=self._data_api_url, params=params) + signed_params = await transform_params(params) + user_result = await self._fetcher.get_json_dict(url=self._data_api_url, params=signed_params) if user_result.status != 200: raise BilibiliApiError(f'BilibiliApiError, {user_result.result}') self.user_model = BilibiliUserModel.parse_obj(user_result.result) diff --git a/omega_miya/web_resource/bilibili/wbi.py b/omega_miya/web_resource/bilibili/wbi.py new file mode 100644 index 00000000..ce3cd928 --- /dev/null +++ b/omega_miya/web_resource/bilibili/wbi.py @@ -0,0 +1,68 @@ +from functools import reduce +from hashlib import md5 +import urllib.parse +import time + +from omega_miya.web_resource import HttpFetcher + +from .exception import BilibiliApiError + + +mixinKeyEncTab = [ + 46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49, + 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40, + 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11, + 36, 20, 34, 44, 52 +] + + +def get_mixin_key(orig: str): + """对 imgKey 和 subKey 进行字符顺序打乱编码""" + return reduce(lambda s, i: s + orig[i], mixinKeyEncTab, '')[:32] + + +def enc_wbi(params: dict, img_key: str, sub_key: str): + """为请求参数进行 wbi 签名""" + mixin_key = get_mixin_key(img_key + sub_key) + curr_time = round(time.time()) + params['wts'] = curr_time # 添加 wts 字段 + params = dict(sorted(params.items())) # 按照 key 重排参数 + # 过滤 value 中的 "!'()*" 字符 + params = { + k: ''.join(filter(lambda x: x not in "!'()*", str(v))) + for k, v + in params.items() + } + query = urllib.parse.urlencode(params) # 序列化参数 + wbi_sign = md5((query + mixin_key).encode()).hexdigest() # 计算 w_rid + params['w_rid'] = wbi_sign + return params + + +async def get_wbi_keys() -> tuple[str, str]: + """获取最新的 img_key 和 sub_key""" + resp = await HttpFetcher().get_json_dict('https://api.bilibili.com/x/web-interface/nav') + if resp.status != 200: + raise BilibiliApiError(f'BilibiliApiError, {resp.result}') + + json_content = resp.result + img_url: str = json_content['data']['wbi_img']['img_url'] + sub_url: str = json_content['data']['wbi_img']['sub_url'] + img_key = img_url.rsplit('/', 1)[1].split('.')[0] + sub_key = sub_url.rsplit('/', 1)[1].split('.')[0] + return img_key, sub_key + + +async def transform_params(params: dict): + img_key, sub_key = await get_wbi_keys() + signed_params = enc_wbi( + params=params, + img_key=img_key, + sub_key=sub_key + ) + return signed_params + + +__all__ = [ + 'transform_params' +] From e840813c81d787d695aceaa54e7096045ae80f27 Mon Sep 17 00:00:00 2001 From: Ailitonia <41713304+Ailitonia@users.noreply.github.com> Date: Tue, 27 Jun 2023 20:52:24 +0800 Subject: [PATCH 23/26] =?UTF-8?q?Fix:=20Pixivision=20=E6=96=87=E7=AB=A0?= =?UTF-8?q?=E9=A2=84=E8=A7=88=E5=9B=BE=E8=A7=A3=E6=9E=90=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 Pixivision 文章预览图解析问题 --- omega_miya/web_resource/pixiv/helper.py | 120 ++++++++++++------------ 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/omega_miya/web_resource/pixiv/helper.py b/omega_miya/web_resource/pixiv/helper.py index 1e152c91..60aefd67 100644 --- a/omega_miya/web_resource/pixiv/helper.py +++ b/omega_miya/web_resource/pixiv/helper.py @@ -14,6 +14,8 @@ from PIL import Image, ImageDraw, ImageFont from bs4 import BeautifulSoup +from lxml import etree + from omega_miya.local_resource import TmpResource from omega_miya.web_resource import HttpFetcher from omega_miya.utils.process_utils import semaphore_gather, run_sync @@ -117,22 +119,26 @@ def parse_pixivision_show_page(content: str, root_url: str) -> PixivisionIllustr :param content: 网页 html :param root_url: pixivision 主域名 """ - page_bs = BeautifulSoup(content, 'lxml') - # 获取所有 illustration 内容 - illustration_cards = page_bs.find_all(name='li', attrs={'class': 'article-card-container'}) + html = etree.HTML(content) + illustration_cards = html.xpath('/html/body//li[@class="article-card-container"]') + result_list = [] for card in illustration_cards: # 解析每篇文章对应 card 的内容 - aid = card.find(name='h2', attrs={'class': 'arc__title'}).find(name='a').attrs.get('data-gtm-label') - thumbnail = card.find(name='div', attrs={'class': '_thumbnail'}).attrs.get('style') + title_href = card.xpath('article//h2[@class="arc__title"]/a[1]').pop(0) + title = title_href.text.strip() + aid = title_href.attrib.get('data-gtm-label') + url = root_url + title_href.attrib.get('href') + + thumbnail = card.xpath('article//div[@class="_thumbnail"]').pop(0).attrib.get('style') thumbnail = re.search(r'^background-image:\s\surl\((.+)\)$', thumbnail).group(1) - url = root_url + card.find(name='h2', attrs={'class': 'arc__title'}).find(name='a').attrs.get('href') - title = card.find(name='h2', attrs={'class': 'arc__title'}).get_text(strip=True) - tag_tag = card.find(name='ul', attrs={'class': '_tag-list'}).find_all(name='li') + + tag_container = card.xpath('article//ul[@class="_tag-list"]/li[@class="tls__list-item-container"]') tag_list = [] - for tag in tag_tag: - tag_name = tag.get_text(strip=True) - tag_rela_url = tag.find(name='a').attrs.get('href') + for tag in tag_container: + tag_href = tag.xpath('a[1]').pop(0) + tag_name = tag_href.attrib.get('data-gtm-label').strip() + tag_rela_url = tag_href.attrib.get('href') tag_id = re.sub(r'^/zh/t/(?=\d+)', '', tag_rela_url) tag_url = root_url + tag_rela_url tag_list.append({'tag_id': tag_id, 'tag_name': tag_name, 'tag_url': tag_url}) @@ -146,66 +152,60 @@ def parse_pixivision_article_page(content: str, root_url: str) -> PixivisionArti :param content: 网页 html :param root_url: pixivision 主域名 """ - page_bs = BeautifulSoup(content, 'lxml') + html = etree.HTML(content) + article_main = html.xpath('/html/body//div[@class="_article-main"]').pop(0) # 解析 article 描述部分 - article_main = page_bs.find(name='div', attrs={'class': '_article-main'}) - article_title = article_main.find(name='h1', attrs={'class': 'am__title'}).get_text(strip=True) - article_eyecatch = article_main.find(name='div', attrs={'class': '_article-illust-eyecatch'}) - article_eyecatch_image = None if article_eyecatch is None else article_eyecatch.find('img').attrs.get('src') - - # 解析tag - tags_list = [] - tag_tag = page_bs.find(name='ul', attrs={'class': '_tag-list'}) - tag_tag = [] if tag_tag is None else tag_tag.find_all(name='a') - for tag in tag_tag: - tag_name = tag.attrs.get('data-gtm-label', None) - tag_rela_url = tag.attrs.get('href', None) - tag_id = re.sub(r'^/zh/t/(?=\d+)', '', tag_rela_url) - tag_url = root_url + tag_rela_url - tags_list.append({'tag_id': tag_id, 'tag_name': tag_name, 'tag_url': tag_url}) + article = article_main.xpath('article[@class="am__article-body-container"]').pop(0) + article_title = article.xpath('header[1]//h1[@class="am__title"]').pop(0).text.strip() + eyecatch = article.xpath('div//div[@class="_article-illust-eyecatch"]') + eyecatch_image = eyecatch.pop(0).xpath('img[1]').pop(0).attrib.get('src') if eyecatch else None # 解析 article 主体部分 - article_body = article_main.find(name='div', attrs={'class': 'am__body'}) - - # 注意 pixivision illustration 的文章有两种页面样式, 要分开解析 - # 这个是某些特殊特辑的样式, 一般出现在各种特辑TOP排行榜或者专题特辑上 - feature_article_body = article_body.find(name='div', attrs={'class': '_feature-article-body'}) - # 重新解析description - if feature_article_body is not None: - article_description = article_main.find( - name='div', attrs={'class': 'fab__paragraph _medium-editor-text'}).get_text(strip=True) - else: - article_description = article_main.find( - name='div', attrs={'class': 'am__description _medium-editor-text'}).get_text(strip=True) + article_body = article_main.xpath('article//div[@class="am__body"]').pop(0) + + # 获取文章描述 + # 注意 pixivision illustration 的文章有两种页面样式 + article_description = article_body.xpath( + 'div//div[@class="fab__paragraph _medium-editor-text" or @class="am__description _medium-editor-text"]' + ).pop(0).xpath('p') + description = '\n'.join(x.text.strip() for x in article_description if x.text) # 获取所有作品内容 - article_illusts = article_body.find_all(name='div', attrs={'class': 'am__work__main'}) - # 分别解析两种样式中的图片列表 artwork_list = [] - # 一般样式 - if article_illusts: - for item in article_illusts: - # 解析作品信息 - artwork_info = item.previous_sibling - artwork_user_name = artwork_info.find(name='p', attrs={'class': 'am__work__user-name'}) - artwork_user_name = artwork_user_name.find(name='a', attrs={ - 'class': 'author-img-container inner-link', - 'target': '_blank' - }).get_text() - artwork_title = artwork_info.find(name='h3', attrs={'class': 'am__work__title'}).get_text() - artwork_url = item.find(name='a', attrs={'class': 'inner-link'}).attrs.get('href') - artwork_id = parse_pid_from_url(text=artwork_url) - image_url = item.find(name='img', attrs={'class': 'am__work__illust'}).attrs.get('src') - artwork_list.append({'artwork_id': artwork_id, 'artwork_user': artwork_user_name, - 'artwork_title': artwork_title, 'artwork_url': artwork_url, 'image_url': image_url}) + artworks = article_body.xpath('div//div[@class="am__work"]') + for artwork in artworks: + # 解析作品信息 + artwork_info = artwork.xpath('div[@class="am__work__info"]').pop(0) + artwork_title = artwork_info.xpath('div//h3[@class="am__work__title"]/a[1]').pop(0).text.strip() + artwork_user_name = artwork_info.xpath( + 'div//p[@class="am__work__user-name"]/a[@class="author-img-container inner-link"]' + ).pop(0).text.strip() + + artwork_main = artwork.xpath('div[@class="am__work__main"]').pop(0) + artwork_url = artwork_main.xpath('a[@class="inner-link"]').pop(0).attrib.get('href') + artwork_id = parse_pid_from_url(text=artwork_url, url_mode=False) + image_url = artwork_main.xpath('a//img[contains(@class, "am__work__illust")]').pop(0).attrib.get('src') + + artwork_list.append({'artwork_id': artwork_id, 'artwork_user': artwork_user_name, + 'artwork_title': artwork_title, 'artwork_url': artwork_url, 'image_url': image_url}) + + # 解析 tag + tag_list = [] + tag_hrefs = article_main.xpath('div//ul[@class="_tag-list"]/a') + for tag in tag_hrefs: + tag_name = tag.attrib.get('data-gtm-label') + tag_rela_url = tag.attrib.get('href') + tag_id = re.sub(r'^/zh/t/(?=\d+)', '', tag_rela_url) + tag_url = root_url + tag_rela_url + tag_list.append({'tag_id': tag_id, 'tag_name': tag_name, 'tag_url': tag_url}) result = { 'title': article_title, - 'description': article_description, - 'eyecatch_image': article_eyecatch_image, + 'description': description, + 'eyecatch_image': eyecatch_image, 'artwork_list': artwork_list, - 'tags_list': tags_list + 'tags_list': tag_list } return PixivisionArticle.parse_obj(result) From 5bd2a84146e4ba3de6cb48fd217770c292a61a85 Mon Sep 17 00:00:00 2001 From: Ailitonia <41713304+Ailitonia@users.noreply.github.com> Date: Tue, 27 Jun 2023 20:52:48 +0800 Subject: [PATCH 24/26] Upgrade: go-cqhttp patch - Upgrade go-cqhttp patch --- .../gocqhttp_addition_event_patch/__init__.py | 19 +++ .../gocqhttp_addition_event_patch/model.py | 150 ++++++++++++++++++ .../service/gocqhttp_guild_patch/README.md | 12 +- .../service/gocqhttp_guild_patch/__init__.py | 4 +- .../service/gocqhttp_guild_patch/models.py | 51 +----- .../gocqhttp_guild_patch/permission.py | 46 ++++-- .../service/gocqhttp_self_sent_patch/model.py | 10 +- 7 files changed, 216 insertions(+), 76 deletions(-) create mode 100644 omega_miya/service/gocqhttp_addition_event_patch/__init__.py create mode 100644 omega_miya/service/gocqhttp_addition_event_patch/model.py diff --git a/omega_miya/service/gocqhttp_addition_event_patch/__init__.py b/omega_miya/service/gocqhttp_addition_event_patch/__init__.py new file mode 100644 index 00000000..97e4d358 --- /dev/null +++ b/omega_miya/service/gocqhttp_addition_event_patch/__init__.py @@ -0,0 +1,19 @@ +""" +@Author : Ailitonia +@Date : 2023/3/18 3:15 +@FileName : gocqhttp_addition_event_patch +@Project : nonebot2_miya +@Description : Go-cqhttp 事件补丁 +@GitHub : https://github.com/Ailitonia +@Software : PyCharm +""" + +from .model import GroupCardNoticeEvent, OfflineFileNoticeEvent, ClientStatusNoticeEvent, EssenceNoticeEvent + + +__all__ = [ + 'GroupCardNoticeEvent', + 'OfflineFileNoticeEvent', + 'ClientStatusNoticeEvent', + 'EssenceNoticeEvent' +] diff --git a/omega_miya/service/gocqhttp_addition_event_patch/model.py b/omega_miya/service/gocqhttp_addition_event_patch/model.py new file mode 100644 index 00000000..1c40d8d9 --- /dev/null +++ b/omega_miya/service/gocqhttp_addition_event_patch/model.py @@ -0,0 +1,150 @@ +""" +@Author : Ailitonia +@Date : 2023/3/18 3:16 +@FileName : model +@Project : nonebot2_miya +@Description : Go-cqhttp addition event model +@GitHub : https://github.com/Ailitonia +@Software : PyCharm +""" + +from typing import Type, TypeVar, Literal + +from nonebot.adapters.onebot.v11.adapter import Adapter +from nonebot.adapters.onebot.v11.event import Event, NoticeEvent +from nonebot.log import logger +from nonebot.typing import overrides +from pydantic import BaseModel + + +Event_T = TypeVar("Event_T", bound=Type[Event]) + + +def register_event(event: Event_T) -> Event_T: + Adapter.add_custom_model(event) + logger.opt(colors=True).trace( + f"Custom event {event.__qualname__!r} registered to adapter {Adapter.get_name()!r} " + f"from module {event.__module__!r}" + ) + return event + + +@register_event +class GroupCardNoticeEvent(NoticeEvent): + """群成员名片更新提醒事件(此事件不保证时效性, 仅在收到消息时校验卡片)""" + + notice_type: Literal["group_card"] + group_id: int + user_id: int + card_new: str + card_old: str + + @overrides(Event) + def is_tome(self) -> bool: + return self.user_id == self.self_id + + @overrides(NoticeEvent) + def get_user_id(self) -> str: + return str(self.user_id) + + @overrides(NoticeEvent) + def get_session_id(self) -> str: + return f"group_{self.group_id}_{self.user_id}" + + +class OfflineFile(BaseModel): + name: str + size: int + url: str + + class Config: + extra = "allow" + + +@register_event +class OfflineFileNoticeEvent(NoticeEvent): + """接收到离线文件提醒事件""" + + notice_type: Literal["offline_file"] + user_id: int + file: OfflineFile + + @overrides(Event) + def is_tome(self) -> bool: + return True + + @overrides(NoticeEvent) + def get_user_id(self) -> str: + return str(self.user_id) + + @overrides(NoticeEvent) + def get_session_id(self) -> str: + return f"{self.user_id}_{self.file.name}" + + +class Device(BaseModel): + """在线客户端 + + - app_id: 客户端ID + - device_name: 设备名称 + - device_kind: 设备类型 + """ + app_id: int + device_name: str + device_kind: str + + class Config: + extra = "allow" + + +@register_event +class ClientStatusNoticeEvent(NoticeEvent): + """其他客户端在线状态变更""" + + notice_type: Literal["client_status"] + client: Device + online: bool + + @overrides(Event) + def is_tome(self) -> bool: + return True + + @overrides(NoticeEvent) + def get_user_id(self) -> str: + return str(self.self_id) + + @overrides(NoticeEvent) + def get_session_id(self) -> str: + return f"{self.self_id}_{self.client.app_id}" + + +@register_event +class EssenceNoticeEvent(NoticeEvent): + """精华消息变更""" + + notice_type: Literal["essence"] + sub_type: Literal["add", "delete"] + group_id: int + sender_id: int + operator_id: int + message_id: int + + @overrides(Event) + def is_tome(self) -> bool: + return self.self_id == self.sender_id or self.self_id == self.operator_id + + @overrides(NoticeEvent) + def get_user_id(self) -> str: + return str(self.sender_id) + + @overrides(NoticeEvent) + def get_session_id(self) -> str: + return f"group_{self.group_id}_{self.sender_id}" + + +__all__ = [ + 'GroupCardNoticeEvent', + 'OfflineFileNoticeEvent', + 'ClientStatusNoticeEvent', + 'EssenceNoticeEvent' +] diff --git a/omega_miya/service/gocqhttp_guild_patch/README.md b/omega_miya/service/gocqhttp_guild_patch/README.md index c8878da6..865bc339 100644 --- a/omega_miya/service/gocqhttp_guild_patch/README.md +++ b/omega_miya/service/gocqhttp_guild_patch/README.md @@ -1,8 +1,8 @@ # nonebot-plugin-guild-patch -*Patch plugin for NoneBot2 QQ guild (go-cqhttp) support.* +_Patch plugin for NoneBot2 QQ guild (go-cqhttp) support._ -*NoneBot2 QQ 频道 (go-cqhttp) 支持适配补丁插件.* +_NoneBot2 QQ 频道 (go-cqhttp) 支持适配补丁插件._ ![PyPI](https://img.shields.io/pypi/v/nonebot-plugin-guild-patch?style=for-the-badge) @@ -11,7 +11,7 @@ [![GitHub stars](https://img.shields.io/github/stars/mnixry/nonebot-plugin-guild-patch)](https://github.com/mnixry/nonebot-plugin-guild-patch/stargazers) [![GitHub license](https://img.shields.io/github/license/mnixry/nonebot-plugin-guild-patch)](https://github.com/mnixry/nonebot-plugin-guild-patch/blob/main/LICENSE) -> **注: 本补丁没有经过充分测试, 不建议在生产环境使用, 如果发现任何问题请[Issue反馈](https://github.com/mnixry/nonebot-plugin-guild-patch/issues/new/choose)** +> **注: 本补丁没有经过充分测试, 不建议在生产环境使用, 如果发现任何问题请[Issue 反馈](https://github.com/mnixry/nonebot-plugin-guild-patch/issues/new/choose)** ## 适用版本 @@ -24,7 +24,7 @@ - [x] 支持字符串形式消息上报 - [x] 支持数组形式消息上报 - [x] 支持`bot.send`和`matcher.send`直接向频道发送消息 -- [ ] 支持`event.to_me`以支持`to_me`规则 +- [x] 支持`event.to_me`以支持`to_me`规则 - [ ] 可选的事件转换器, 将频道消息事件转换为群消息 ## 安装 @@ -34,7 +34,7 @@ 如果它被成功加载, 你在调试模式下应该看到这样的日志: ```diff -11-13 09:14:52 [DEBUG] nonebot | Succeeded to load adapter "cqhttp" +11-13 09:14:52 [DEBUG] nonebot | Succeeded to load adapter "onebot" 11-13 09:14:52 [SUCCESS] nonebot | Succeeded to import "nonebot.plugins.echo" + 11-13 09:14:52 [SUCCESS] nonebot | Succeeded to import "nonebot_plugin_guild_patch" 11-13 09:14:52 [SUCCESS] nonebot | Running NoneBot... @@ -50,7 +50,7 @@ ```python from nonebot.plugin import on_command -from nonebot.adapters.cqhttp import Bot, MessageSegment +from nonebot.adapters.onebot import Bot, MessageSegment from nonebot_plugin_guild_patch import GuildMessageEvent diff --git a/omega_miya/service/gocqhttp_guild_patch/__init__.py b/omega_miya/service/gocqhttp_guild_patch/__init__.py index 99905023..130f93d5 100644 --- a/omega_miya/service/gocqhttp_guild_patch/__init__.py +++ b/omega_miya/service/gocqhttp_guild_patch/__init__.py @@ -12,7 +12,7 @@ GuildMessageEvent, MessageReactionsUpdatedNoticeEvent, ) -from .permission import GUILD, GUILD_SUPERUSER +from .permission import GUILD, GUILD_ADMIN, GUILD_OWNER, GUILD_SUPERUSER original_send = Bot.send @@ -55,6 +55,8 @@ async def patched_send( __all__ = [ "GUILD", + "GUILD_OWNER", + "GUILD_ADMIN", "GUILD_SUPERUSER", "GuildMessageEvent", "ChannelNoticeEvent", diff --git a/omega_miya/service/gocqhttp_guild_patch/models.py b/omega_miya/service/gocqhttp_guild_patch/models.py index 9acc2a88..1bcc132f 100644 --- a/omega_miya/service/gocqhttp_guild_patch/models.py +++ b/omega_miya/service/gocqhttp_guild_patch/models.py @@ -1,4 +1,4 @@ -from typing import List, Optional, Type, TypeVar +from typing import List, Optional, Tuple, Type, TypeVar from nonebot.adapters.onebot.v11 import ( Adapter, @@ -8,21 +8,18 @@ MessageSegment, NoticeEvent, ) -from nonebot.exception import NoLogException from nonebot.log import logger from nonebot.typing import overrides from nonebot.utils import escape_tag from pydantic import BaseModel, Field, parse_obj_as, root_validator, validator from typing_extensions import Literal -from .config import guild_patch_config - Event_T = TypeVar("Event_T", bound=Type[Event]) def register_event(event: Event_T) -> Event_T: Adapter.add_custom_model(event) - logger.opt(colors=True).debug( + logger.opt(colors=True).trace( f"Custom event {event.__qualname__!r} registered " f"from module {event.__class__.__module__!r}" ) @@ -83,18 +80,12 @@ def get_event_description(self) -> str: ) ) - def get_log_string(self) -> str: - if not guild_patch_config.enable_guild_event_log: - raise NoLogException(adapter_name='nonebot-adapter-onebot') - else: - return super().get_log_string() - @overrides(MessageEvent) def get_session_id(self) -> str: return f"guild_{self.guild_id}_channel_{self.channel_id}_{self.user_id}" @staticmethod - def _check_at_me(message: Message, self_tiny_id: int) -> tuple[Message, bool]: + def _check_at_me(message: Message, self_tiny_id: int) -> Tuple[Message, bool]: """检查消息开头或结尾是否存在 @机器人,去除并赋值 event.to_me""" is_tome = False # ensure message not empty @@ -166,12 +157,6 @@ class ChannelNoticeEvent(NoticeEvent): user_id: int sub_type: None = None - def get_log_string(self) -> str: - if not guild_patch_config.enable_guild_event_log: - raise NoLogException(adapter_name='nonebot-adapter-onebot') - else: - return super().get_log_string() - @register_event class GuildChannelRecallNoticeEvent(ChannelNoticeEvent): @@ -181,12 +166,6 @@ class GuildChannelRecallNoticeEvent(ChannelNoticeEvent): operator_id: int message_id: str - def get_log_string(self) -> str: - if not guild_patch_config.enable_guild_event_log: - raise NoLogException(adapter_name='nonebot-adapter-onebot') - else: - return super().get_log_string() - @register_event class MessageReactionsUpdatedNoticeEvent(ChannelNoticeEvent): @@ -196,12 +175,6 @@ class MessageReactionsUpdatedNoticeEvent(ChannelNoticeEvent): message_id: str current_reactions: Optional[List[ReactionInfo]] = None - def get_log_string(self) -> str: - if not guild_patch_config.enable_guild_event_log: - raise NoLogException(adapter_name='nonebot-adapter-onebot') - else: - return super().get_log_string() - class SlowModeInfo(BaseModel): slow_mode_key: int @@ -239,12 +212,6 @@ class ChannelUpdatedNoticeEvent(ChannelNoticeEvent): old_info: ChannelInfo new_info: ChannelInfo - def get_log_string(self) -> str: - if not guild_patch_config.enable_guild_event_log: - raise NoLogException(adapter_name='nonebot-adapter-onebot') - else: - return super().get_log_string() - @register_event class ChannelCreatedNoticeEvent(ChannelNoticeEvent): @@ -254,12 +221,6 @@ class ChannelCreatedNoticeEvent(ChannelNoticeEvent): operator_id: int channel_info: ChannelInfo - def get_log_string(self) -> str: - if not guild_patch_config.enable_guild_event_log: - raise NoLogException(adapter_name='nonebot-adapter-onebot') - else: - return super().get_log_string() - @register_event class ChannelDestroyedNoticeEvent(ChannelNoticeEvent): @@ -269,12 +230,6 @@ class ChannelDestroyedNoticeEvent(ChannelNoticeEvent): operator_id: int channel_info: ChannelInfo - def get_log_string(self) -> str: - if not guild_patch_config.enable_guild_event_log: - raise NoLogException(adapter_name='nonebot-adapter-onebot') - else: - return super().get_log_string() - __all__ = [ "GuildMessageEvent", diff --git a/omega_miya/service/gocqhttp_guild_patch/permission.py b/omega_miya/service/gocqhttp_guild_patch/permission.py index 67a84059..ca5bf97f 100644 --- a/omega_miya/service/gocqhttp_guild_patch/permission.py +++ b/omega_miya/service/gocqhttp_guild_patch/permission.py @@ -1,13 +1,3 @@ -""" -@Author : Ailitonia -@Date : 2022/05/22 1:17 -@FileName : permission.py -@Project : nonebot2_miya -@Description : Guild Permission -@GitHub : https://github.com/Ailitonia -@Software : PyCharm -""" - from nonebot.adapters.onebot.v11.bot import Bot from nonebot.permission import Permission @@ -18,18 +8,44 @@ async def _guild(event: GuildMessageEvent) -> bool: return True +async def _guild_admin(bot: Bot, event: GuildMessageEvent): + roles = { + role["role_name"] + for role in ( + await bot.get_guild_member_profile( + guild_id=event.guild_id, user_id=event.user_id + ) + )["roles"] + } + return "管理员" in roles + + +async def _guild_owner(bot: Bot, event: GuildMessageEvent): + roles = { + role["role_name"] + for role in ( + await bot.get_guild_member_profile( + guild_id=event.guild_id, user_id=event.user_id + ) + )["roles"] + } + return "频道主" in roles + + async def _guild_superuser(bot: Bot, event: GuildMessageEvent) -> bool: return ( - f"{bot.adapter.get_name().split(maxsplit=1)[0].lower()}:{event.get_user_id()}" + f"{bot.adapter.get_name().lower()}:{event.get_user_id()}" in bot.config.superusers - or event.get_user_id() in bot.config.superusers - ) # 兼容旧配置 + ) or (event.get_user_id() in bot.config.superusers) GUILD: Permission = Permission(_guild) """匹配任意频道消息类型事件""" GUILD_SUPERUSER: Permission = Permission(_guild_superuser) """匹配任意超级用户频道消息类型事件""" +GUILD_ADMIN: Permission = Permission(_guild_admin) +"""匹配任意频道管理员消息类型事件""" +GUILD_OWNER: Permission = Permission(_guild_owner) +"""匹配任意频道频道主消息类型事件""" - -__all__ = ["GUILD", "GUILD_SUPERUSER"] \ No newline at end of file +__all__ = ["GUILD", "GUILD_OWNER", "GUILD_ADMIN", "GUILD_SUPERUSER"] diff --git a/omega_miya/service/gocqhttp_self_sent_patch/model.py b/omega_miya/service/gocqhttp_self_sent_patch/model.py index 5d20c4c6..780819db 100644 --- a/omega_miya/service/gocqhttp_self_sent_patch/model.py +++ b/omega_miya/service/gocqhttp_self_sent_patch/model.py @@ -8,8 +8,7 @@ @Software : PyCharm """ -import inspect -from typing import Optional, Type, TypeVar +from typing import Optional, Type, TypeVar, Literal from nonebot.adapters.onebot.v11.adapter import Adapter from nonebot.adapters.onebot.v11.event import Event, MessageEvent, Anonymous, Sender @@ -17,7 +16,6 @@ from nonebot.log import logger from nonebot.typing import overrides from nonebot.utils import escape_tag -from typing_extensions import Literal Event_T = TypeVar("Event_T", bound=Type[Event]) @@ -25,9 +23,9 @@ def register_event(event: Event_T) -> Event_T: Adapter.add_custom_model(event) - logger.opt(colors=True).debug( - f"Custom event {event.__qualname__!r} registered " - f"from module {inspect.getmodule(event).__name__!r}" + logger.opt(colors=True).trace( + f"Custom event {event.__qualname__!r} registered to adapter {Adapter.get_name()!r} " + f"from module {event.__module__!r}" ) return event From 42cd726378f0ea44511b848a6bce65083c3c59b2 Mon Sep 17 00:00:00 2001 From: Ailitonia <41713304+Ailitonia@users.noreply.github.com> Date: Tue, 27 Jun 2023 23:45:22 +0800 Subject: [PATCH 25/26] =?UTF-8?q?Upgrade:=20=E6=98=8E=E6=97=A5=E6=96=B9?= =?UTF-8?q?=E8=88=9F=E5=8D=A1=E6=B1=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新明日方舟卡池 --- omega_miya/plugins/draw/deck/arknights.py | 46 ++++++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/omega_miya/plugins/draw/deck/arknights.py b/omega_miya/plugins/draw/deck/arknights.py index f689b930..da6edbd4 100644 --- a/omega_miya/plugins/draw/deck/arknights.py +++ b/omega_miya/plugins/draw/deck/arknights.py @@ -26,27 +26,61 @@ class UpEvent(BaseModel): UpEvent( star=6, operator=[ - Operator(name='焰尾/Flametail', star=6, limited=False, recruit_only=False, event_only=False, + Operator(name='菲亚梅塔/Fiammetta', star=6, limited=False, recruit_only=False, event_only=False, + special_only=False), + Operator(name='棘刺/Thorns', star=6, limited=False, recruit_only=False, event_only=False, special_only=False), - Operator(name='傀影/Phantom', star=6, limited=False, recruit_only=False, event_only=False, - special_only=False) ], zoom=0.5 ), UpEvent( star=5, operator=[ - Operator(name='赫默/Silence', star=5, limited=False, recruit_only=False, event_only=False, + Operator(name='夜半/Blacknight', star=5, limited=False, recruit_only=False, event_only=False, + special_only=False), + Operator(name='安哲拉/Andreana', star=5, limited=False, recruit_only=False, event_only=False, special_only=False), - Operator(name='守林人/Firewatch', star=5, limited=False, recruit_only=False, event_only=False, + Operator(name='极境/Elysium', star=5, limited=False, recruit_only=False, event_only=False, special_only=False), - Operator(name='吽/Hung', star=5, limited=False, recruit_only=False, event_only=False, special_only=False) ], zoom=0.5 ) ] ALL_OPERATOR: list[Operator] = [ + Operator(name='圣约送葬人/Executor the Ex Foedere', star=6, limited=False, recruit_only=False, event_only=False, + special_only=False), + Operator(name='空构/Spuria', star=5, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='隐现/Insider', star=5, limited=False, recruit_only=False, event_only=True, special_only=False), + Operator(name='Friston-3/Friston-3', star=1, limited=False, recruit_only=True, event_only=False, + special_only=False), + Operator(name='缪尔赛思/Muelsyse', star=6, limited=True, recruit_only=False, event_only=False, special_only=False), + Operator(name="霍尔海雅/Ho'olheyak", star=6, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='淬羽赫默/Silence the Paradigmatic', star=6, limited=False, recruit_only=False, event_only=True, + special_only=False), + Operator(name='玫拉/Melanite', star=5, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='伊内丝/Ines', star=6, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='洋灰/Cement', star=5, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='摩根/Morgan', star=5, limited=False, recruit_only=False, event_only=True, special_only=False), + Operator(name='休谟斯/Humus', star=4, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='U-Official/U-Official', star=1, limited=False, recruit_only=False, event_only=True, + special_only=False), + Operator(name='麒麟X夜刀/Kirin X Yato', star=6, limited=True, recruit_only=False, event_only=True, + special_only=False), + Operator(name='火龙S黑角/Rathalos S Noir Corne', star=5, limited=True, recruit_only=False, event_only=True, + special_only=False), + Operator(name='泰拉大陆调查团/Terra Research Commission', star=1, limited=False, recruit_only=False, event_only=True, + special_only=False), + Operator(name='仇白/Qiubai', star=6, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='铎铃/Wind Chimes', star=5, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='重岳/Chongyue', star=6, limited=True, recruit_only=False, event_only=False, special_only=False), + Operator(name='林/Lin', star=6, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='火哨/Firewhistle', star=5, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='截云/Jieyun', star=5, limited=False, recruit_only=False, event_only=True, special_only=False), + Operator(name='焰影苇草/Reed The Flame Shadow', star=6, limited=False, recruit_only=False, event_only=False, + special_only=False), + Operator(name='和弦/Harmonie', star=5, limited=False, recruit_only=False, event_only=False, special_only=False), + Operator(name='谜图/Puzzle', star=5, limited=False, recruit_only=False, event_only=True, special_only=False), Operator(name='缄默德克萨斯/Texas the Omertosa', star=6, limited=True, recruit_only=False, event_only=False, special_only=False), Operator(name='斥罪/Penance', star=6, limited=False, recruit_only=False, event_only=False, special_only=False), From d890d25d07b6677263cb2072121559205fc71e25 Mon Sep 17 00:00:00 2001 From: Ailitonia <41713304+Ailitonia@users.noreply.github.com> Date: Tue, 27 Jun 2023 23:54:41 +0800 Subject: [PATCH 26/26] Upgrade: README.md - Upgrade README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4c2bc7bc..c640e7e8 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ _基于 [Nonebot2](https://github.com/nonebot/nonebot2) 和 [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) 的 qq 机器人_ -![Nonebot2](https://img.shields.io/badge/Nonebot2-Release_v2.0.0_beta.5-brightgreen) -![go-cqhttp](https://img.shields.io/badge/go--cqhttp-v1.0.0_rc3-brightgreen) +![Nonebot2](https://img.shields.io/badge/Nonebot2-Release_v2.0.0rc2-brightgreen) +![go-cqhttp](https://img.shields.io/badge/go--cqhttp-v1.0.0_rc5-brightgreen)
![GitHub](https://img.shields.io/github/license/Ailitonia/omega-miya) ![Python](https://img.shields.io/badge/Python-3.10+-blue) @@ -18,11 +18,11 @@ _基于 [Nonebot2](https://github.com/nonebot/nonebot2) 和 [go-cqhttp](https:// ## 当前适配 nonebot2 版本 -[Nonebot2 Release v2.0.0-beta.5](https://github.com/nonebot/nonebot2/releases/tag/v2.0.0-beta.5) +[Nonebot2 Release v2.0.0rc2](https://github.com/nonebot/nonebot2/releases/tag/v2.0.0rc2) ## 当前适配 go-cqhttp 版本 -[go-cqhttp v1.0.0-rc3](https://github.com/Mrs4s/go-cqhttp/releases/tag/v1.0.0-rc3) +[go-cqhttp v1.0.0-rc5](https://github.com/Mrs4s/go-cqhttp/releases/tag/v1.0.0-rc5) ## 功能 & 特点