diff --git a/.vscode/settings.json b/.vscode/settings.json index 69e2b4b..8c89b82 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,7 @@ "apscheduler", "arclet", "Arparma", + "bnum", "cesaa", "chatdatanum", "chatrecorder", @@ -15,6 +16,7 @@ "pygal", "sqlalchemy", "userinfo", + "whereclause", "xaxis", "yaxis" ] diff --git a/nonebot_plugin_dialectlist/__init__.py b/nonebot_plugin_dialectlist/__init__.py index 2ce7ad9..41af1f3 100644 --- a/nonebot_plugin_dialectlist/__init__.py +++ b/nonebot_plugin_dialectlist/__init__.py @@ -8,12 +8,9 @@ require("nonebot_plugin_alconna") require("nonebot_plugin_cesaa") import re +import os import nonebot_plugin_saa as saa -from pyecharts.charts import Bar -from pyecharts import options as opts -from pyecharts.globals import ThemeType - from typing import Union, Optional, List from datetime import datetime, timedelta from arclet.alconna import ArparmaBehavior @@ -50,13 +47,13 @@ from nonebot_plugin_session import Session, SessionIdType, extract_session # from .function import * from .config import Config, plugin_config from .usage import __usage__ +from .time import get_datetime_fromisoformat_with_timezone, get_datetime_now_with_timezone,parse_datetime +from .model import UserRankInfo from .utils import ( - get_datetime_fromisoformat_with_timezone, - get_datetime_now_with_timezone, got_rank, msg_counter, persist_id2user_id, - parse_datetime, + get_rank_image ) __plugin_meta__ = PluginMetadata( @@ -92,9 +89,9 @@ rank_cmd = on_alconna( Alconna( "B话榜", Args["type?", ["今日", "昨日", "本周", "上周", "本月", "上月", "年度", "历史"]][ - "time?", - str, - ]["group_id?", str], + "time?",str,][ + "group_id?", str + ], behaviors=[SameTime()], ), aliases={"废话榜"}, @@ -223,42 +220,45 @@ async def handle_rank( time_stop=stop, exclude_id1s=plugin_config.excluded_people, ) - rank = got_rank(msg_counter(messages)) + rank2: List[UserRankInfo] = [] ids = await persist_id2user_id([int(i[0]) for i in rank]) for i in range(len(rank)): rank[i][0] = str(ids[i]) + logger.debug(rank[i]) + + total = sum([i[1] for i in rank]) - string: str = "" - nicknames: List = [] for i in rank: if user_info := await get_user_info(bot, event, user_id=str(i[0])): - ( - nicknames.append(user_info.user_displayname) - if user_info.user_displayname - else ( - nicknames.append(user_info.user_name) - if user_info.user_name - else nicknames.append(user_info.user_id) - ) + user_nickname = user_info.user_displayname\ + if user_info.user_displayname\ + else user_info.user_name\ + if user_info.user_name\ + else\ + user_info.user_id + user_avatar = await user_info.user_avatar.get_image()\ + if user_info.user_avatar\ + else open(os.path.dirname(os.path.abspath(__file__))+"/template/avatar/default.jpg", "rb").read() + user = UserRankInfo(**user_info.model_dump(), + user_bnum=i[1], + user_proportion= round(i[1] / total * 100, 2), + user_index= rank.index(i) + 1, + user_nickname=user_nickname, + user_avatar_bytes= user_avatar, ) - else: - nicknames.append(None) - logger.debug(nicknames) + user.user_gender="她" if user_info.user_gender == "female" else "他" if user_info.user_gender == "male" else "ta" + rank2.append(user) + + string: str = "" for i in range(len(rank)): - index = i + 1 - nickname, chatdatanum = nicknames[i], rank[i][1] str_example = plugin_config.string_format.format( - index=index, nickname=nickname, chatdatanum=chatdatanum + index=rank2[i].user_index, + nickname=rank2[i].user_nickname, + chatdatanum=rank2[i].user_bnum ) string += str_example - - bar = Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT)) - bar.add_xaxis(nicknames) - bar.add_yaxis("B话数量", [i[1] for i in rank]) # type: ignore - bar.render(str(get_cache_file("nonebot_plugin_dialectlist", "cache.html"))) - with open(get_cache_file("nonebot_plugin_dialectlist", "cache.html")) as f: - a = f.read() - image = await html_to_pic(a, device_scale_factor=3.2) + + image = await get_rank_image(rank2) await (saa.Text(string) + saa.Image(image)).finish(reply=True) diff --git a/nonebot_plugin_dialectlist/config.py b/nonebot_plugin_dialectlist/config.py index 770d6af..ee2baab 100644 --- a/nonebot_plugin_dialectlist/config.py +++ b/nonebot_plugin_dialectlist/config.py @@ -9,6 +9,7 @@ class ScopedConfig(BaseModel): timezone: Optional[str] = "Asia/Shanghai" excluded_self: bool = True string_format: str = "第{index}名:\n{nickname},{chatdatanum}条消息\n" # 消息格式 + template_path: str = "./template/rank_template.j2" # 模板路径 visualization: bool = True # 是否可视化 excluded_people: List[str] = [] # 排除的人的QQ号 visualization_type: Literal["饼图", "圆环图", "柱状图"] = "圆环图" # 可视化方案 diff --git a/nonebot_plugin_dialectlist/model.py b/nonebot_plugin_dialectlist/model.py new file mode 100644 index 0000000..ce8ca37 --- /dev/null +++ b/nonebot_plugin_dialectlist/model.py @@ -0,0 +1,11 @@ +from typing import Optional, Literal, List, Union +from pydantic import BaseModel +from nonebot_plugin_userinfo import get_user_info, UserInfo + + +class UserRankInfo(UserInfo): + user_bnum: int + user_proportion: float + user_nickname: str + user_index: Union[int,str] + user_avatar_bytes: bytes \ No newline at end of file diff --git a/nonebot_plugin_dialectlist/render.py b/nonebot_plugin_dialectlist/render.py deleted file mode 100644 index 70214e1..0000000 --- a/nonebot_plugin_dialectlist/render.py +++ /dev/null @@ -1 +0,0 @@ -# TODO 更好的图片渲染,支持自定义模板渲染。 diff --git a/nonebot_plugin_dialectlist/template/avatar/default.jpg b/nonebot_plugin_dialectlist/template/avatar/default.jpg new file mode 100644 index 0000000..39f103a Binary files /dev/null and b/nonebot_plugin_dialectlist/template/avatar/default.jpg differ diff --git a/nonebot_plugin_dialectlist/temple/cache/头像.jpg b/nonebot_plugin_dialectlist/template/avatar/master.jpg similarity index 97% rename from nonebot_plugin_dialectlist/temple/cache/头像.jpg rename to nonebot_plugin_dialectlist/template/avatar/master.jpg index 014c344..c909a22 100644 Binary files a/nonebot_plugin_dialectlist/temple/cache/头像.jpg and b/nonebot_plugin_dialectlist/template/avatar/master.jpg differ diff --git a/nonebot_plugin_dialectlist/temple/css/style.css b/nonebot_plugin_dialectlist/template/css/style.css similarity index 92% rename from nonebot_plugin_dialectlist/temple/css/style.css rename to nonebot_plugin_dialectlist/template/css/style.css index 9326d3b..206c320 100644 --- a/nonebot_plugin_dialectlist/temple/css/style.css +++ b/nonebot_plugin_dialectlist/template/css/style.css @@ -1,10 +1,11 @@ body { - background-image: url('../img/background.jpg'); + background-image: url('https://image.anosu.top/pixiv/direct?size=regular'); background-attachment: scroll; margin: 0; background-size: cover; background-position: center; background-repeat: no-repeat; + width: 1024; } .container { @@ -74,8 +75,8 @@ body { border-radius: 2rem; /* padding: 20px 40px; */ align-content: center; - backdrop-filter: blur(30px); - -webkit-backdrop-filter: blur(30px); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); } .profile{ @@ -88,8 +89,8 @@ body { } .profile2{ - width: 4%; - height: 4%; + width: 8%; + height: 8%; border: 3px solid rgba(255, 255, 255, 0.61); border-radius: 2000px; background-size: cover; diff --git a/nonebot_plugin_dialectlist/template/rank_template.j2 b/nonebot_plugin_dialectlist/template/rank_template.j2 new file mode 100644 index 0000000..0c23982 --- /dev/null +++ b/nonebot_plugin_dialectlist/template/rank_template.j2 @@ -0,0 +1,39 @@ + + + + + + 在 title 里夹一点私货应该不会被发现吧?——ddl 真是太厉害啦! + + + + +
+
+

话痨排行榜

+
+ +
+ + {% for i in users %} +
+ +
+

第{{ i.user_index }}名:{{ i.user_name }}

+
+
+
+

{{ i.user_gender }}一共废话了{{ i.user_bnum }}句,占比{{ i.user_proportion }}%

+
+
+ {% endfor %} + +
+ +
+ +

Designed By ChenXu233

+
+
+ + \ No newline at end of file diff --git a/nonebot_plugin_dialectlist/temple/img/background.jpg b/nonebot_plugin_dialectlist/temple/img/background.jpg deleted file mode 100644 index c6b9ca0..0000000 Binary files a/nonebot_plugin_dialectlist/temple/img/background.jpg and /dev/null differ diff --git a/nonebot_plugin_dialectlist/temple/img/头像.jpg b/nonebot_plugin_dialectlist/temple/img/头像.jpg deleted file mode 100644 index 014c344..0000000 Binary files a/nonebot_plugin_dialectlist/temple/img/头像.jpg and /dev/null differ diff --git a/nonebot_plugin_dialectlist/temple/rank_temple.html b/nonebot_plugin_dialectlist/temple/rank_temple.html deleted file mode 100644 index a121011..0000000 --- a/nonebot_plugin_dialectlist/temple/rank_temple.html +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - 在 title 里夹一点私货应该不会被发现吧?——ddl 真是太厉害啦! - - - - -
-
-

话痨排行榜

-
-
-
- -
-

第一名:ChenXu233

-
-
-
-

ta一共废话了12131231句,占比100%

-
-
-
- -
-

第一名:ChenXu233

-
-
-
-

ta一共废话了12131231句,占比100%

-
-
-
- -
-

第一名:ChenXu233

-
-
-
-

ta一共废话了12131231句,占比100%

-
-
-
- -
-

第一名:ChenXu233

-
-
-
-

ta一共废话了12131231句,占比100%

-
-
-
- -
-

第一名:ChenXu233

-
-
-
-

ta一共废话了12131231句,占比100%

-
-
-
- -
-

第一名:ChenXu233

-
-
-
-

ta一共废话了12131231句,占比100%

-
-
-
- -
-

第一名:ChenXu233

-
-
-
-

ta一共废话了12131231句,占比100%

-
-
-
- -
-

第一名:ChenXu233

-
-
-
-

ta一共废话了12131231句,占比100%

-
-
- -
-
- -

Designed By ChenXu233

-
-
- - \ No newline at end of file diff --git a/nonebot_plugin_dialectlist/time.py b/nonebot_plugin_dialectlist/time.py index 148e153..5cd482b 100644 --- a/nonebot_plugin_dialectlist/time.py +++ b/nonebot_plugin_dialectlist/time.py @@ -1 +1,80 @@ # TODO 时间处理模块,用于处理时间相关操作。 +from datetime import datetime, time, tzinfo +from typing import Optional, Union +from zoneinfo import ZoneInfo + +from nonebot.typing import T_State +from nonebot.params import Arg +from nonebot.adapters import Message + +from nonebot_plugin_apscheduler import scheduler +from nonebot_plugin_alconna import AlconnaMatcher + + +from .config import plugin_config + + +def parse_datetime(key: str): + """解析数字,并将结果存入 state 中""" + + async def _key_parser( + matcher: AlconnaMatcher, + state: T_State, + input: Union[datetime, Message] = Arg(key), + ): + if isinstance(input, datetime): + return + + plaintext = input.extract_plain_text() + try: + state[key] = get_datetime_fromisoformat_with_timezone(plaintext) + except ValueError: + await matcher.reject_arg(key, "请输入正确的日期,不然我没法理解呢!") + + return _key_parser + + +def get_datetime_now_with_timezone() -> datetime: + """获取当前时间,并包含时区信息""" + if plugin_config.timezone: + return datetime.now(ZoneInfo(plugin_config.timezone)) + else: + return datetime.now().astimezone() + + +def get_datetime_fromisoformat_with_timezone(date_string: str) -> datetime: + """从 ISO-8601 格式字符串中获取时间,并包含时区信息""" + if not plugin_config.timezone: + return datetime.fromisoformat(date_string).astimezone() + raw = datetime.fromisoformat(date_string) + return ( + raw.astimezone(ZoneInfo(plugin_config.timezone)) + if raw.tzinfo + else raw.replace(tzinfo=ZoneInfo(plugin_config.timezone)) + ) + + +def time_astimezone(time: time, tz: Optional[tzinfo] = None) -> time: + """将 time 对象转换为指定时区的 time 对象 + + 如果 tz 为 None,则转换为本地时区 + """ + local_time = datetime.combine(datetime.today(), time) + return local_time.astimezone(tz).timetz() + + +def get_time_fromisoformat_with_timezone(time_string: str) -> time: + """从 iso8601 格式字符串中获取时间,并包含时区信息""" + if not plugin_config.timezone: + return time_astimezone(time.fromisoformat(time_string)) + raw = time.fromisoformat(time_string) + return ( + time_astimezone(raw, ZoneInfo(plugin_config.timezone)) + if raw.tzinfo + else raw.replace(tzinfo=ZoneInfo(plugin_config.timezone)) + ) + + +def get_time_with_scheduler_timezone(time: time) -> time: + """获取转换到 APScheduler 时区的时间""" + return time_astimezone(time, scheduler.timezone) diff --git a/nonebot_plugin_dialectlist/utils.py b/nonebot_plugin_dialectlist/utils.py index baa4567..af9775d 100644 --- a/nonebot_plugin_dialectlist/utils.py +++ b/nonebot_plugin_dialectlist/utils.py @@ -1,6 +1,8 @@ -from datetime import datetime, time, tzinfo +import os +import unicodedata + from typing import Optional, Dict, List, Union -from zoneinfo import ZoneInfo +from pathlib import Path from sqlalchemy import or_, select from sqlalchemy.sql import ColumnElement @@ -14,79 +16,17 @@ from nonebot_plugin_orm import get_session from nonebot_plugin_session import Session, SessionLevel, extract_session from nonebot_plugin_session_orm import SessionModel from nonebot_plugin_userinfo import EventUserInfo, UserInfo +from nonebot_plugin_htmlrender import html_to_pic,template_to_pic from nonebot_plugin_apscheduler import scheduler from nonebot_plugin_chatrecorder import MessageRecord +from nonebot_plugin_localstore import get_cache_dir from nonebot_plugin_alconna import AlconnaMatcher from .config import plugin_config +from .model import UserRankInfo - -def parse_datetime(key: str): - """解析数字,并将结果存入 state 中""" - - async def _key_parser( - matcher: AlconnaMatcher, - state: T_State, - input: Union[datetime, Message] = Arg(key), - ): - if isinstance(input, datetime): - return - - plaintext = input.extract_plain_text() - try: - state[key] = get_datetime_fromisoformat_with_timezone(plaintext) - except ValueError: - await matcher.reject_arg(key, "请输入正确的日期,不然我没法理解呢!") - - return _key_parser - - -def get_datetime_now_with_timezone() -> datetime: - """获取当前时间,并包含时区信息""" - if plugin_config.timezone: - return datetime.now(ZoneInfo(plugin_config.timezone)) - else: - return datetime.now().astimezone() - - -def get_datetime_fromisoformat_with_timezone(date_string: str) -> datetime: - """从 ISO-8601 格式字符串中获取时间,并包含时区信息""" - if not plugin_config.timezone: - return datetime.fromisoformat(date_string).astimezone() - raw = datetime.fromisoformat(date_string) - return ( - raw.astimezone(ZoneInfo(plugin_config.timezone)) - if raw.tzinfo - else raw.replace(tzinfo=ZoneInfo(plugin_config.timezone)) - ) - - -def time_astimezone(time: time, tz: Optional[tzinfo] = None) -> time: - """将 time 对象转换为指定时区的 time 对象 - - 如果 tz 为 None,则转换为本地时区 - """ - local_time = datetime.combine(datetime.today(), time) - return local_time.astimezone(tz).timetz() - - -def get_time_fromisoformat_with_timezone(time_string: str) -> time: - """从 iso8601 格式字符串中获取时间,并包含时区信息""" - if not plugin_config.timezone: - return time_astimezone(time.fromisoformat(time_string)) - raw = time.fromisoformat(time_string) - return ( - time_astimezone(raw, ZoneInfo(plugin_config.timezone)) - if raw.tzinfo - else raw.replace(tzinfo=ZoneInfo(plugin_config.timezone)) - ) - - -def get_time_with_scheduler_timezone(time: time) -> time: - """获取转换到 APScheduler 时区的时间""" - return time_astimezone(time, scheduler.timezone) - +cache_path = get_cache_dir("nonebot_plugin_dialectlist") # 暂时不做考虑 # def admin_permission(): @@ -154,7 +94,7 @@ def msg_counter(msg_list: List[MessageRecord]) -> Dict[str, int]: return lst -def got_rank(msg_dict: Dict[str, int]) -> List[List[Union[str, int]]]: +def got_rank(msg_dict: Dict[str, int]) -> List: """### 获得排行榜 Args: @@ -175,34 +115,37 @@ def got_rank(msg_dict: Dict[str, int]) -> List[List[Union[str, int]]]: plugin_config.get_num, len(rank) ) ) - break + break return rank -# def remove_control_characters(string: str) -> str: -# """### 将字符串中的控制符去除 +def remove_control_characters(string: str) -> str: + """### 将字符串中的控制符去除 -# Args: -# string (str): 需要去除的字符串 + Args: + string (str): 需要去除的字符串 -# Returns: -# (str): 经过处理的字符串 -# """ -# return "".join(ch for ch in string if unicodedata.category(ch)[0] != "C") + Returns: + (str): 经过处理的字符串 + """ + return "".join(ch for ch in string if unicodedata.category(ch)[0] != "C") - -# async def render_template_pic(self) -> bytes: -# if plugin_config.dialectlist_visualization_type == "圆环图": -# view = pygal.Pie(inner_radius=0.6, style=style) -# elif plugin_config.dialectlist_visualization_type == "饼图": -# view = pygal.Pie(style=style) -# else: -# view = pygal.Bar(style=style) - -# view.title = "消息可视化" -# for i, j in zip(self.rank, await self.get_nickname_list()): # type: ignore -# view.add(str(j), int(i[1])) - -# png: bytes = view.render_to_png() # type: ignore -# self.img = png -# return png \ No newline at end of file +async def get_rank_image(rank: List[UserRankInfo]) -> bytes: + for i in rank: + if i.user_avatar: + try: + user_avatar = i.user_avatar_bytes + except NotImplementedError: + user_avatar = open(os.path.dirname(os.path.abspath(__file__))+"/template/avatar/default.jpg", "rb").read() + # if not os.path.exists(cache_path / str(i.user_id)): + with open(cache_path / (str(i.user_id) + ".jpg"), "wb") as f: + f.write(user_avatar) + + if plugin_config.template_path[:2] == './': + path = os.path.dirname(os.path.abspath(__file__)) + plugin_config.template_path[1:] + else: + path = plugin_config.template_path + + path_dir, filename = os.path.split(path) + logger.debug(os.path.dirname(os.path.abspath(__file__)) + plugin_config.template_path[1:]) + return await template_to_pic(path_dir,filename,{'users': rank, 'cache_path': cache_path, 'file_path': os.path.dirname(os.path.abspath(__file__))}) \ No newline at end of file diff --git a/pdm.lock b/pdm.lock index 2cc3ce2..8ea5983 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.1" -content_hash = "sha256:fdb46c5e66cf97af4806bb28cc369a5cfee536532eb9a1b05d581d5856ffcf6d" +content_hash = "sha256:74fd9abb0888f1ded1c2cef549dd45e68a73c42bab5d7e37c1d3f2e5880a4cdb" [[package]] name = "aiofiles" @@ -39,7 +39,7 @@ name = "annotated-types" version = "0.7.0" requires_python = ">=3.8" summary = "Reusable constraint types to use with typing.Annotated" -groups = ["default"] +groups = ["default", "dev"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -50,7 +50,7 @@ name = "anyio" version = "4.4.0" requires_python = ">=3.8" summary = "High level compatibility layer for multiple asynchronous event loop implementations" -groups = ["default"] +groups = ["default", "dev"] dependencies = [ "exceptiongroup>=1.0.2; python_version < \"3.11\"", "idna>=2.8", @@ -109,6 +109,21 @@ files = [ {file = "arclet_alconna_tools-0.7.6.tar.gz", hash = "sha256:7cb7dc54c1c2198529c63227739423401051b8489374f1a7a3efa0c4e70b2a22"}, ] +[[package]] +name = "arrow" +version = "1.3.0" +requires_python = ">=3.8" +summary = "Better dates & times for Python" +groups = ["dev"] +dependencies = [ + "python-dateutil>=2.7.0", + "types-python-dateutil>=2.8.10", +] +files = [ + {file = "arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80"}, + {file = "arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85"}, +] + [[package]] name = "backports-tarfile" version = "1.2.0" @@ -121,6 +136,19 @@ files = [ {file = "backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991"}, ] +[[package]] +name = "binaryornot" +version = "0.4.4" +summary = "Ultra-lightweight pure Python package to check if a file is binary or text." +groups = ["dev"] +dependencies = [ + "chardet>=3.0.2", +] +files = [ + {file = "binaryornot-0.4.4-py2.py3-none-any.whl", hash = "sha256:b8b71173c917bddcd2c16070412e369c3ed7f0528926f70cac18a6c97fd563e4"}, + {file = "binaryornot-0.4.4.tar.gz", hash = "sha256:359501dfc9d40632edc9fac890e19542db1a287bbcfa58175b66658392018061"}, +] + [[package]] name = "cachetools" version = "5.3.3" @@ -132,6 +160,17 @@ files = [ {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, ] +[[package]] +name = "cashews" +version = "7.1.0" +requires_python = ">=3.8" +summary = "cache tools with async power" +groups = ["dev"] +files = [ + {file = "cashews-7.1.0-py3-none-any.whl", hash = "sha256:b7c1ae4d49df6fdbff88e5025d3c1156515f58724c5b96fc9a9d081afada82a8"}, + {file = "cashews-7.1.0.tar.gz", hash = "sha256:058df55a39cb15697d331e7e41c2882b58d0d323f5671316105cc78668af7705"}, +] + [[package]] name = "certifi" version = "2024.6.2" @@ -189,6 +228,17 @@ files = [ {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, ] +[[package]] +name = "chardet" +version = "5.2.0" +requires_python = ">=3.7" +summary = "Universal encoding detector for Python 3" +groups = ["dev"] +files = [ + {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, + {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, +] + [[package]] name = "charset-normalizer" version = "3.3.2" @@ -250,7 +300,7 @@ name = "click" version = "8.1.7" requires_python = ">=3.7" summary = "Composable command line interface toolkit" -groups = ["default"] +groups = ["default", "dev"] dependencies = [ "colorama; platform_system == \"Windows\"", ] @@ -264,13 +314,34 @@ name = "colorama" version = "0.4.6" requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" summary = "Cross-platform colored terminal text." -groups = ["default"] +groups = ["default", "dev"] marker = "sys_platform == \"win32\" or platform_system == \"Windows\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "cookiecutter" +version = "2.6.0" +requires_python = ">=3.7" +summary = "A command-line utility that creates projects from project templates, e.g. creating a Python package project from a Python package project template." +groups = ["dev"] +dependencies = [ + "Jinja2<4.0.0,>=2.7", + "arrow", + "binaryornot>=0.4.4", + "click<9.0.0,>=7.0", + "python-slugify>=4.0.0", + "pyyaml>=5.3.1", + "requests>=2.23.0", + "rich", +] +files = [ + {file = "cookiecutter-2.6.0-py3-none-any.whl", hash = "sha256:a54a8e37995e4ed963b3e82831072d1ad4b005af736bb17b99c2cbd9d41b6e2d"}, + {file = "cookiecutter-2.6.0.tar.gz", hash = "sha256:db21f8169ea4f4fdc2408d48ca44859349de2647fbe494a9d6c3edfc0542c21c"}, +] + [[package]] name = "cryptography" version = "42.0.8" @@ -316,6 +387,16 @@ files = [ {file = "cryptography-42.0.8.tar.gz", hash = "sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2"}, ] +[[package]] +name = "distlib" +version = "0.3.8" +summary = "Distribution utilities" +groups = ["dev"] +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + [[package]] name = "dnspython" version = "2.6.1" @@ -372,7 +453,7 @@ name = "exceptiongroup" version = "1.2.1" requires_python = ">=3.7" summary = "Backport of PEP 654 (exception groups)" -groups = ["default"] +groups = ["default", "dev"] marker = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, @@ -417,6 +498,17 @@ files = [ {file = "fastapi_cli-0.0.4.tar.gz", hash = "sha256:e2e9ffaffc1f7767f488d6da34b6f5a377751c996f397902eb6abb99a67bde32"}, ] +[[package]] +name = "filelock" +version = "3.15.4" +requires_python = ">=3.8" +summary = "A platform independent file lock." +groups = ["dev"] +files = [ + {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, + {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, +] + [[package]] name = "filetype" version = "1.2.0" @@ -469,7 +561,7 @@ name = "h11" version = "0.14.0" requires_python = ">=3.7" summary = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -groups = ["default"] +groups = ["default", "dev"] files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, @@ -480,7 +572,7 @@ name = "httpcore" version = "1.0.5" requires_python = ">=3.8" summary = "A minimal low-level HTTP client." -groups = ["default"] +groups = ["default", "dev"] dependencies = [ "certifi", "h11<0.15,>=0.13", @@ -526,7 +618,7 @@ name = "httpx" version = "0.27.0" requires_python = ">=3.8" summary = "The next generation HTTP client." -groups = ["default"] +groups = ["default", "dev"] dependencies = [ "anyio", "certifi", @@ -635,7 +727,7 @@ name = "jinja2" version = "3.1.4" requires_python = ">=3.7" summary = "A very fast and expressive template engine." -groups = ["default"] +groups = ["default", "dev"] dependencies = [ "MarkupSafe>=2.0", ] @@ -723,7 +815,7 @@ name = "markupsafe" version = "2.1.5" requires_python = ">=3.7" summary = "Safely add untrusted strings to HTML/XML markup." -groups = ["default"] +groups = ["default", "dev"] files = [ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, @@ -836,6 +928,33 @@ files = [ {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, ] +[[package]] +name = "nb-cli" +version = "1.4.1" +requires_python = "<4.0,>=3.9" +summary = "CLI for nonebot2" +groups = ["dev"] +dependencies = [ + "anyio<5.0,>=3.6", + "cashews<8.0,>=6.0", + "click~=8.1", + "cookiecutter~=2.2", + "httpx~=0.18", + "jinja2~=3.0", + "noneprompt<1.0.0,>=0.1.9", + "pydantic!=2.5.0,!=2.5.1,<3.0.0,>=1.10.0", + "pyfiglet<2.0.0,>=1.0.1", + "tomlkit~=0.10", + "typing-extensions~=4.4", + "virtualenv~=20.21", + "watchfiles~=0.16", + "wcwidth~=0.2", +] +files = [ + {file = "nb_cli-1.4.1-py3-none-any.whl", hash = "sha256:57b6111773202bce29c0520f4a281edb8a7643fa33692d4afc70ca5b51b10f70"}, + {file = "nb_cli-1.4.1.tar.gz", hash = "sha256:908dd4cbbf66bf46fe879c23ad1377332f63385cebca1912b627aa686d1816f3"}, +] + [[package]] name = "nepattern" version = "0.7.4" @@ -1119,6 +1238,20 @@ files = [ {file = "nonebot2-2.3.1.tar.gz", hash = "sha256:ac5a1a1759f15310e9183b606ce6bdbe52a90287bf36a69201be548e23d41e6c"}, ] +[[package]] +name = "noneprompt" +version = "0.1.9" +requires_python = ">=3.8,<4.0" +summary = "Prompt toolkit for console interaction" +groups = ["dev"] +dependencies = [ + "prompt-toolkit<4.0.0,>=3.0.19", +] +files = [ + {file = "noneprompt-0.1.9-py3-none-any.whl", hash = "sha256:a54f1e6a19a3da2dedf7f365f80420e9ae49326a0ffe60a8a9c7afdee6b6eeb3"}, + {file = "noneprompt-0.1.9.tar.gz", hash = "sha256:338b8bb89a8d22ef35f1dedb3aa7c1b228cf139973bdc43c5ffc3eef64457db9"}, +] + [[package]] name = "orjson" version = "3.10.5" @@ -1167,6 +1300,17 @@ files = [ {file = "pkginfo-1.11.1.tar.gz", hash = "sha256:2e0dca1cf4c8e39644eed32408ea9966ee15e0d324c62ba899a393b3c6b467aa"}, ] +[[package]] +name = "platformdirs" +version = "4.2.2" +requires_python = ">=3.8" +summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +groups = ["dev"] +files = [ + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, +] + [[package]] name = "playwright" version = "1.44.0" @@ -1201,6 +1345,20 @@ files = [ {file = "prettytable-3.10.0.tar.gz", hash = "sha256:9665594d137fb08a1117518c25551e0ede1687197cf353a4fdc78d27e1073568"}, ] +[[package]] +name = "prompt-toolkit" +version = "3.0.47" +requires_python = ">=3.7.0" +summary = "Library for building powerful interactive command lines in Python" +groups = ["dev"] +dependencies = [ + "wcwidth", +] +files = [ + {file = "prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10"}, + {file = "prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360"}, +] + [[package]] name = "pycparser" version = "2.22" @@ -1218,7 +1376,7 @@ name = "pydantic" version = "2.7.4" requires_python = ">=3.8" summary = "Data validation using Python type hints" -groups = ["default"] +groups = ["default", "dev"] dependencies = [ "annotated-types>=0.4.0", "pydantic-core==2.18.4", @@ -1234,7 +1392,7 @@ name = "pydantic-core" version = "2.18.4" requires_python = ">=3.8" summary = "Core functionality for Pydantic validation and serialization" -groups = ["default"] +groups = ["default", "dev"] dependencies = [ "typing-extensions!=4.7.0,>=4.6.0", ] @@ -1325,6 +1483,17 @@ files = [ {file = "pyee-11.1.0.tar.gz", hash = "sha256:b53af98f6990c810edd9b56b87791021a8f54fd13db4edd1142438d44ba2263f"}, ] +[[package]] +name = "pyfiglet" +version = "1.0.2" +requires_python = ">=3.9" +summary = "Pure-python FIGlet implementation" +groups = ["dev"] +files = [ + {file = "pyfiglet-1.0.2-py3-none-any.whl", hash = "sha256:889b351d79c99e50a3f619c8f8e6ffdb27fd8c939fc43ecbd7559bd57d5f93ea"}, + {file = "pyfiglet-1.0.2.tar.gz", hash = "sha256:758788018ab8faaddc0984e1ea05ff330d3c64be663c513cc1f105f6a3066dab"}, +] + [[package]] name = "pygal" version = "3.0.4" @@ -1375,6 +1544,20 @@ files = [ {file = "pymdown_extensions-10.8.1.tar.gz", hash = "sha256:3ab1db5c9e21728dabf75192d71471f8e50f216627e9a1fa9535ecb0231b9940"}, ] +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +summary = "Extensions to the standard Python datetime module" +groups = ["dev"] +dependencies = [ + "six>=1.5", +] +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + [[package]] name = "python-dotenv" version = "1.0.1" @@ -1411,6 +1594,20 @@ files = [ {file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"}, ] +[[package]] +name = "python-slugify" +version = "8.0.4" +requires_python = ">=3.7" +summary = "A Python slugify application that also handles Unicode" +groups = ["dev"] +dependencies = [ + "text-unidecode>=1.3", +] +files = [ + {file = "python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856"}, + {file = "python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8"}, +] + [[package]] name = "pytz" version = "2024.1" @@ -1438,7 +1635,7 @@ name = "pyyaml" version = "6.0.1" requires_python = ">=3.6" summary = "YAML parser and emitter for Python" -groups = ["default"] +groups = ["default", "dev"] files = [ {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, @@ -1658,7 +1855,7 @@ name = "six" version = "1.16.0" requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" summary = "Python 2 and 3 compatibility utilities" -groups = ["default"] +groups = ["default", "dev"] files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -1669,7 +1866,7 @@ name = "sniffio" version = "1.3.1" requires_python = ">=3.7" summary = "Sniff out which async library your code is running under" -groups = ["default"] +groups = ["default", "dev"] files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, @@ -1779,6 +1976,16 @@ files = [ {file = "tarina-0.5.2.tar.gz", hash = "sha256:7d5d93d73422e97b2409e6a43bf4d11296fe2dac90a5b4dcbc19e56bc1b55298"}, ] +[[package]] +name = "text-unidecode" +version = "1.3" +summary = "The most basic Text::Unidecode port" +groups = ["dev"] +files = [ + {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, + {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, +] + [[package]] name = "tomli" version = "2.0.1" @@ -1791,6 +1998,17 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] +[[package]] +name = "tomlkit" +version = "0.12.5" +requires_python = ">=3.7" +summary = "Style preserving TOML library" +groups = ["dev"] +files = [ + {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, + {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, +] + [[package]] name = "twine" version = "5.1.0" @@ -1830,12 +2048,23 @@ files = [ {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, ] +[[package]] +name = "types-python-dateutil" +version = "2.9.0.20240316" +requires_python = ">=3.8" +summary = "Typing stubs for python-dateutil" +groups = ["dev"] +files = [ + {file = "types-python-dateutil-2.9.0.20240316.tar.gz", hash = "sha256:5d2f2e240b86905e40944dd787db6da9263f0deabef1076ddaed797351ec0202"}, + {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, +] + [[package]] name = "typing-extensions" version = "4.12.2" requires_python = ">=3.8" summary = "Backported and Experimental Type Hints for Python 3.8+" -groups = ["default"] +groups = ["default", "dev"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -2012,12 +2241,28 @@ files = [ {file = "uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd"}, ] +[[package]] +name = "virtualenv" +version = "20.26.3" +requires_python = ">=3.7" +summary = "Virtual Python Environment builder" +groups = ["dev"] +dependencies = [ + "distlib<1,>=0.3.7", + "filelock<4,>=3.12.2", + "platformdirs<5,>=3.9.1", +] +files = [ + {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, + {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, +] + [[package]] name = "watchfiles" version = "0.22.0" requires_python = ">=3.8" summary = "Simple, modern and high performance file watching and code reload in python." -groups = ["default"] +groups = ["default", "dev"] dependencies = [ "anyio>=3.0.0", ] @@ -2079,7 +2324,7 @@ files = [ name = "wcwidth" version = "0.2.13" summary = "Measures the displayed width of unicode strings in a terminal" -groups = ["default"] +groups = ["default", "dev"] files = [ {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, diff --git a/pyproject.toml b/pyproject.toml index f8b2e0a..7a43d36 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "nonebot_plugin_dialectlist" -version = "2.0.4" -description = "Default template for PDM package" +version = "2.1.4" +description = "看看你群群友有多能说" authors = [ {name = "Chen_Xu233", email = "woyerpa@outlook.com"}, ] @@ -27,6 +27,7 @@ dev = [ "ruff>=0.4.9", "setuptools>=70.0.0", "twine>=5.1.0", + "nb-cli>=0.7.6" ] [tool.pdm] diff --git a/tests/temple_render.py b/tests/temple_render.py index 44a8114..cc9f3b7 100644 --- a/tests/temple_render.py +++ b/tests/temple_render.py @@ -4,7 +4,9 @@ with sync_playwright() as p: for browser_type in [p.chromium, p.firefox, p.webkit]: browser = browser_type.launch(headless=True) page = browser.new_page() - page.goto('file:///H:/Bot/%E7%8E%B0%E5%BD%B9Bot/mybot/src/nonebot_plugin_dialectlist/nonebot_plugin_dialectlist/temple/rank_temple.html') - page.screenshot(path=f'screenshot-{browser_type.name}.png',full_page=True) + page.goto( + "file:///H:/Bot/%E7%8E%B0%E5%BD%B9Bot/mybot/src/nonebot_plugin_dialectlist/nonebot_plugin_dialectlist/temple/rank_temple.html" + ) + page.screenshot(path=f"screenshot-{browser_type.name}.png", full_page=True) print(page.title()) - browser.close() \ No newline at end of file + browser.close()