From 30f5f80fd8fc42cbe40cc7d2da93a9121e63e24d Mon Sep 17 00:00:00 2001 From: student_2333 Date: Thu, 3 Aug 2023 18:48:25 +0800 Subject: [PATCH] 0.2.0 --- README.md | 5 ++ nonebot_plugin_nonememe/__init__.py | 3 +- nonebot_plugin_nonememe/__main__.py | 11 +-- nonebot_plugin_nonememe/config.py | 15 +++- nonebot_plugin_nonememe/data_source.py | 66 +++++++++++++--- pdm.lock | 105 ++++++++++++++++++++++++- pyproject.toml | 7 +- 7 files changed, 188 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index e83ea72..ab0a743 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,11 @@ Telegram:[@lgc2333](https://t.me/lgc2333) ## 📝 更新日志 +### 0.2.0 + +- 自动更新图片列表 +- 缓存获取到的图片和图片列表 + ### 0.1.1 - 发送梗图会回复指令消息 diff --git a/nonebot_plugin_nonememe/__init__.py b/nonebot_plugin_nonememe/__init__.py index 61db7b0..1c4dc67 100644 --- a/nonebot_plugin_nonememe/__init__.py +++ b/nonebot_plugin_nonememe/__init__.py @@ -1,12 +1,13 @@ from nonebot import require from nonebot.plugin import PluginMetadata +require("nonebot_plugin_apscheduler") require("nonebot_plugin_saa") from . import __main__ as __main__ # noqa: E402 from .config import ConfigModel # noqa: E402 -__version__ = "0.1.1" +__version__ = "0.2.0" __plugin_meta__ = PluginMetadata( name="NoneMeme", description="NoneBot 群大佬的日常", diff --git a/nonebot_plugin_nonememe/__main__.py b/nonebot_plugin_nonememe/__main__.py index aed6e79..1bb4c11 100644 --- a/nonebot_plugin_nonememe/__main__.py +++ b/nonebot_plugin_nonememe/__main__.py @@ -10,16 +10,13 @@ from nonebot_plugin_saa import Image, MessageFactory, Text from .config import config -from .data_source import MemeItem, fetch_meme, meme_list, search_meme_items +from .data_source import MemeItem, get_meme, meme_list, search_meme_items async def finish_with_meme(meme_item: MemeItem) -> NoReturn: - image_bytes = await fetch_meme(meme_item.path) + image_bytes = await get_meme(meme_item) await MessageFactory( - [ - Text(f"# {meme_item.name}"), - Image(image_bytes), - ], + [Text(f"# {meme_item.name}"), Image(image_bytes)], ).finish(reply=True) @@ -38,7 +35,7 @@ async def _(matcher: Matcher, state: T_State, arg_msg: Message = CommandArg()): searched = search_meme_items(arg, use_regex=use_regex) if not searched: - await matcher.finish("没有找到相关 NoneMeme") + await matcher.finish("没有找到相关图片") if len(searched) == 1: await finish_with_meme(searched[0]) diff --git a/nonebot_plugin_nonememe/config.py b/nonebot_plugin_nonememe/config.py index d65fc12..6478e4d 100644 --- a/nonebot_plugin_nonememe/config.py +++ b/nonebot_plugin_nonememe/config.py @@ -1,15 +1,28 @@ -from typing import Optional +from typing import Optional, TypedDict, Union +from typing_extensions import NotRequired from nonebot import get_driver from pydantic import BaseModel +class CronDict(TypedDict): + year: NotRequired[Union[str, int]] + month: NotRequired[Union[str, int]] + day: NotRequired[Union[str, int]] + week: NotRequired[Union[str, int]] + day_of_week: NotRequired[Union[str, int]] + hour: NotRequired[Union[str, int]] + minute: NotRequired[Union[str, int]] + second: NotRequired[Union[str, int]] + + class ConfigModel(BaseModel): nonememe_proxy: Optional[str] = None nonememe_repo_prefix: str = ( "https://raw.githubusercontent.com/NoneMeme/NoneMeme/main" ) + nonememe_update_cron: CronDict = {"hour": 1} nonememe_search_limit: int = 5 diff --git a/nonebot_plugin_nonememe/data_source.py b/nonebot_plugin_nonememe/data_source.py index 910a874..97bd980 100644 --- a/nonebot_plugin_nonememe/data_source.py +++ b/nonebot_plugin_nonememe/data_source.py @@ -1,19 +1,32 @@ +import json import re import urllib.parse -from dataclasses import dataclass from pathlib import Path from typing import List, cast +import anyio import json5 from httpx import AsyncClient from nonebot import get_driver, logger +from nonebot_plugin_apscheduler import scheduler +from pydantic import BaseModel, parse_raw_as from .config import config +DATA_DIR = Path.cwd() / "data" / "nonememe" +LIST_CACHE_PATH = DATA_DIR / "cached_list.json" +MEME_CACHE_DIR = DATA_DIR / "cache" -@dataclass -class MemeItem: +if not DATA_DIR.exists(): + DATA_DIR.mkdir(parents=True) +# if MEME_CACHE_DIR.exists(): +# shutil.rmtree(MEME_CACHE_DIR) +# MEME_CACHE_DIR.mkdir(parents=True) + + +class MemeItem(BaseModel): name: str + suffix: str path: str @@ -43,9 +56,19 @@ async def fetch_meme(path: str) -> bytes: return resp.content +async def get_meme(meme: MemeItem) -> bytes: + cache_path = anyio.Path(MEME_CACHE_DIR / f"{meme.name}{meme.suffix}") + if await cache_path.exists(): + return await cache_path.read_bytes() + + data = await fetch_meme(meme.path) + await cache_path.write_bytes(data) + return data + + def build_meme_item(meme_path: str) -> MemeItem: - name = Path(urllib.parse.unquote(meme_path)).stem - return MemeItem(name=name, path=meme_path) + path_obj = Path(urllib.parse.unquote(meme_path)) + return MemeItem(name=path_obj.stem, suffix=path_obj.suffix, path=meme_path) async def fetch_meme_list() -> List[MemeItem]: @@ -58,13 +81,38 @@ async def fetch_meme_list() -> List[MemeItem]: return [build_meme_item(item) for item in items] -async def init_meme_list(): +async def update_meme_list(): + logger.info("Updating meme list") + + cache_json_path = anyio.Path(LIST_CACHE_PATH) + + try: + got_meme_list = await fetch_meme_list() + await cache_json_path.write_text( + json.dumps( + [x.dict() for x in got_meme_list], + indent=2, + ensure_ascii=False, + ), + ) + + except Exception: + if not await cache_json_path.exists(): + raise + + logger.warning("Failed to fetch meme list, use cache instead") + got_meme_list = parse_raw_as( + List[MemeItem], + await cache_json_path.read_text(encoding="u8"), + ) + meme_list.clear() - meme_list.extend(await fetch_meme_list()) + meme_list.extend(got_meme_list) logger.opt(colors=True).success( - f"Succeed to init meme list, Loaded {len(meme_list)} memes", + f"Succeed to update meme list, Loaded {len(meme_list)} memes", ) driver = get_driver() -driver.on_startup(init_meme_list) +driver.on_startup(update_meme_list) +scheduler.add_job(update_meme_list, "cron", **config.nonememe_update_cron) diff --git a/pdm.lock b/pdm.lock index d82cdbc..cc3f504 100644 --- a/pdm.lock +++ b/pdm.lock @@ -6,7 +6,7 @@ groups = ["default"] cross_platform = true static_urls = false lock_version = "4.3" -content_hash = "sha256:a8a3e00ee2ef5c8484b0938e14f86cf6cdf054f6f8e3c4ad0c76d192df2e6076" +content_hash = "sha256:83ffe5532b4f5d6cfd96124a4b08bbdf5042a2192464bce19119c4aa6acd2354" [[package]] name = "anyio" @@ -23,6 +23,36 @@ files = [ {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, ] +[[package]] +name = "apscheduler" +version = "3.10.1" +requires_python = ">=3.6" +summary = "In-process task scheduler with Cron-like capabilities" +dependencies = [ + "pytz", + "setuptools>=0.7", + "six>=1.4.0", + "tzlocal!=3.*,>=2.0", +] +files = [ + {file = "APScheduler-3.10.1-py3-none-any.whl", hash = "sha256:e813ad5ada7aff36fb08cdda746b520531eaac7757832abc204868ba78e0c8f6"}, + {file = "APScheduler-3.10.1.tar.gz", hash = "sha256:0293937d8f6051a0f493359440c1a1b93e882c57daf0197afeff0e727777b96e"}, +] + +[[package]] +name = "backports-zoneinfo" +version = "0.2.1" +requires_python = ">=3.6" +summary = "Backport of the standard library zoneinfo module" +files = [ + {file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"}, + {file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"}, +] + [[package]] name = "certifi" version = "2023.7.22" @@ -210,6 +240,20 @@ files = [ {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, ] +[[package]] +name = "nonebot-plugin-apscheduler" +version = "0.3.0" +requires_python = ">=3.8,<4.0" +summary = "APScheduler Support for NoneBot2" +dependencies = [ + "apscheduler<4.0.0,>=3.7.0", + "nonebot2<3.0.0,>=2.0.0", +] +files = [ + {file = "nonebot_plugin_apscheduler-0.3.0-py3-none-any.whl", hash = "sha256:ec5e0267293fc9803e543c6086d3e109ac87bf6dccea5473d219cad826238aae"}, + {file = "nonebot_plugin_apscheduler-0.3.0.tar.gz", hash = "sha256:7c41cc1d49ea6af7c4518c72cd15f8c2f549071b8bc8bfc4b21fbdd0a4875cfd"}, +] + [[package]] name = "nonebot-plugin-send-anything-anywhere" version = "0.2.7" @@ -359,6 +403,35 @@ files = [ {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, ] +[[package]] +name = "pytz" +version = "2023.3" +summary = "World timezone definitions, modern and historical" +files = [ + {file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"}, + {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"}, +] + +[[package]] +name = "setuptools" +version = "68.0.0" +requires_python = ">=3.7" +summary = "Easily download, build, install, upgrade, and uninstall Python packages" +files = [ + {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, + {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, +] + +[[package]] +name = "six" +version = "1.16.0" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +summary = "Python 2 and 3 compatibility utilities" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + [[package]] name = "sniffio" version = "1.3.0" @@ -390,12 +463,36 @@ files = [ [[package]] name = "typing-extensions" -version = "4.6.3" +version = "4.7.1" requires_python = ">=3.7" summary = "Backported and Experimental Type Hints for Python 3.7+" files = [ - {file = "typing_extensions-4.6.3-py3-none-any.whl", hash = "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26"}, - {file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"}, + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, +] + +[[package]] +name = "tzdata" +version = "2023.3" +requires_python = ">=2" +summary = "Provider of IANA time zone data" +files = [ + {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, + {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, +] + +[[package]] +name = "tzlocal" +version = "5.0.1" +requires_python = ">=3.7" +summary = "tzinfo object for the local timezone" +dependencies = [ + "backports-zoneinfo; python_version < \"3.9\"", + "tzdata; platform_system == \"Windows\"", +] +files = [ + {file = "tzlocal-5.0.1-py3-none-any.whl", hash = "sha256:f3596e180296aaf2dbd97d124fe76ae3a0e3d32b258447de7b939b3fd4be992f"}, + {file = "tzlocal-5.0.1.tar.gz", hash = "sha256:46eb99ad4bdb71f3f72b7d24f4267753e240944ecfc16f25d2719ba89827a803"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index 0151bda..6148319 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,14 +1,17 @@ [project] name = "nonebot-plugin-nonememe" -version = "0.1.1.post1" +version = "0.2.0" description = "The daily life of the NoneBot group members" authors = [{ name = "student_2333", email = "lgc2333@126.com" }] dependencies = [ "nonebot2>=2.0.0", - "pydantic>=1.10.4,<2", "nonebot-plugin-send-anything-anywhere>=0.2.7", + "nonebot-plugin-apscheduler>=0.3.0", + "pydantic>=1.10.4,<2", "httpx>=0.24.1", "json5>=0.9.14", + "anyio>=3.7.1", + "typing-extensions>=4.7.1", ] requires-python = ">=3.8,<4.0" readme = "README.md"