├── .env.example ├── .github ├── FUNDING.yml └── workflows │ └── pypi-publish.yml ├── .gitignore ├── LICENSE ├── README.md ├── examples └── external_example │ ├── README.md │ ├── __init__.py │ ├── bg_provider.py │ ├── collectors.py │ └── templates │ ├── __init__.py │ └── example_template │ ├── __init__.py │ └── res │ └── index.html.jinja ├── nonebot_plugin_picstatus ├── __init__.py ├── __main__.py ├── bg_provider.py ├── collectors │ ├── __init__.py │ ├── bot.py │ ├── cpu.py │ ├── disk.py │ ├── mem.py │ ├── misc.py │ ├── network.py │ └── process.py ├── config.py ├── debug.py ├── misc_statistics.py ├── res │ ├── assets │ │ ├── default_avatar.webp │ │ └── default_bg.webp │ └── js │ │ ├── globalThis.d.ts │ │ ├── init-global.js │ │ ├── lazy-load.js │ │ └── load-plugin.js ├── templates │ ├── __init__.py │ ├── default │ │ ├── __init__.py │ │ └── res │ │ │ ├── css │ │ │ ├── index.css │ │ │ ├── no-blur.css │ │ │ ├── no-radius.css │ │ │ ├── no-shadow.css │ │ │ └── theme-dark.css │ │ │ └── templates │ │ │ ├── index.html.jinja │ │ │ └── macros.html.jinja │ └── pw_render.py └── util.py ├── pdm.lock └── pyproject.toml /.env.example: -------------------------------------------------------------------------------- 1 | # ==================== 2 | # 全局设置 3 | 4 | # 用来测试访问网址时的代理(默认为空) 5 | PROXY="http://127.0.0.1:7890" 6 | 7 | # ==================== 8 | # 行为设置 9 | 10 | # 要使用的图片模板 11 | # 目前只有 default 可用 12 | # 关于模板的特定配置请见下方 13 | PS_TEMPLATE=default 14 | 15 | # 触发插件功能的指令列表(可不填) 16 | PS_COMMAND=["运行状态", "状态", "zt", "yxzt", "status"] 17 | 18 | # 是否只能由 SUPERUSER 触发指令(可不填) 19 | PS_ONLY_SU=False 20 | 21 | # 触发指令是否需要 @Bot(可不填) 22 | PS_NEED_AT=False 23 | 24 | # 是否回复目标用户(可不填) 25 | # 使用 QQ 官方机器人时需要关闭此项(官方尚未支持回复),否则可能导致报错! 26 | PS_REPLY_TARGET=True 27 | 28 | # 请求头像等其他 URL 时的超时时间(秒)(可不填) 29 | PS_REQ_TIMEOUT=10 30 | 31 | # ==================== 32 | # 全局个性化设置 33 | 34 | # 图片背景图来源(可不填) 35 | # 当背景图获取失败时,会自动 fallback 到 "local" 来源 36 | # 图片来源列表: 37 | # - "loli": LoliApi (https://docs.loliapi.com/api-shi-yong-wen-dang/sui-ji-er-ci-yuan-tu-pian/shou-ji-duan-sui-ji-tu-pian) 38 | # - "lolicon": Lolicon API (https://api.lolicon.app/#/setu) 39 | # > 注: 此来源会直接从 Pixiv 而不是反代站获取图片,如遇网络问题请自备代理 40 | # - "local": 本地图片 41 | # - "none": 无背景图 42 | PS_BG_PROVIDER=loli 43 | 44 | # 背景图预载数量(可不填) 45 | PS_BG_PRELOAD_COUNT=1 46 | 47 | # Lolicon API 背景图来源获取图片的 R18 类型(可不填) 48 | # 可用值:0 (哒咩 R18!)、1 (就要 R18!)、2 (U18 / R18 混合) 49 | PS_BG_LOLICON_R18_TYPE=0 50 | 51 | # 本地背景图来源 ("local") 使用的图片文件 / 文件夹路径(默认为插件自带背景图) 52 | # 如路径不存在,会 fallback 到插件自带默认背景图 53 | PS_BG_LOCAL_PATH= 54 | 55 | # 当获取 Bot 头像失败时使用的默认头像路径(可不填) 56 | PS_DEFAULT_AVATAR= 57 | 58 | # ==================== 59 | # 数据收集设置 60 | 61 | # == 基础设置 == 62 | 63 | # PeriodicCollector 的调用间隔,单位秒 64 | PS_COLLECT_INTERVAL=2 65 | 66 | # PeriodicCollector 中 deque 的默认大小 67 | PS_DEFAULT_COLLECT_CACHE_SIZE=1 68 | 69 | # 设置特定 PeriodicCollector 中 deque 的大小,{ [name: string]: number } 70 | PS_COLLECT_CACHE_SIZE={} 71 | 72 | # == header == 73 | 74 | # 使用 .env 中配置的 NICKNAME 作为图片上的 Bot 昵称(可不填) 75 | PS_USE_ENV_NICK=False 76 | 77 | # 仅显示当前 Bot(可不填) 78 | PS_SHOW_CURRENT_BOT_ONLY=False 79 | 80 | # 是否对适配器为 OneBot V11 的 Bot 调用 get_status 获取收发消息数 81 | PS_OB_V11_USE_GET_STATUS=True 82 | 83 | # 是否使用 message_sent 事件(OneBot V11),或 user_id 为自身的消息事件统计发送消息数 84 | # 为 False 时全局禁用,为 True 时全局启用, 85 | # 为适配器名称列表(如 ["OneBot V11", "Telegram"])仅对指定的适配器启用 86 | PS_COUNT_MESSAGE_SENT_EVENT=False 87 | 88 | # 是否在 Bot 断开链接时清空收发消息计数 89 | PS_DISCONNECT_RESET_COUNTER=True 90 | 91 | # == disk == 92 | 93 | # 分区列表里忽略的盘符(挂载点)(可不填) 94 | # 使用正则表达式匹配 95 | # 由于配置项使用JSON解析,所以需要使用双反斜杠转义, 96 | # 如:"sda\\d" 解析为 sda\d(代表 sda<一位阿拉伯数字>); 97 | # "C:\\\\Windows" 解析为 C:\\Windows(代表 C:\Windows) 98 | PS_IGNORE_PARTS=[] 99 | 100 | # 忽略获取容量状态失败的磁盘分区(可不填) 101 | PS_IGNORE_BAD_PARTS=False 102 | 103 | # 是否排序分区列表(按照已用大小比例倒序)(可不填) 104 | PS_SORT_PARTS=True 105 | 106 | # 是否反转分区列表排序(可不填) 107 | PS_SORT_PARTS_REVERSE=False 108 | 109 | # 磁盘 IO 统计列表中忽略的磁盘名(可不填) 110 | # 使用正则表达式匹配(注意事项同上) 111 | PS_IGNORE_DISK_IOS=[] 112 | 113 | # 是否忽略 IO 都为 0B/s 的磁盘(可不填) 114 | PS_IGNORE_NO_IO_DISK=False 115 | 116 | # 是否排序磁盘 IO 统计列表(按照读写速度总和倒序)(可不填) 117 | PS_SORT_DISK_IOS=True 118 | 119 | # == network == 120 | 121 | # 网速列表中忽略的网络名称(可不填) 122 | # 使用正则表达式匹配(注意事项同上) 123 | PS_IGNORE_NETS=["^lo$", "^Loopback"] 124 | 125 | # 是否忽略上下行都为 0B/s 的网卡(可不填) 126 | PS_IGNORE_0B_NET=False 127 | 128 | # 是否排序网速列表(按照上下行速度总和倒序)(可不填) 129 | PS_SORT_NETS=True 130 | 131 | # 需要进行测试响应速度的网址列表(可不填) 132 | # 字段说明: 133 | # - name: 显示名称 134 | # - url: 测试网址 135 | # - use_proxy: 是否使用插件配置中的代理访问(可不填,默认为 false) 136 | PS_TEST_SITES=' 137 | [ 138 | {"name": "百度", "url": "https://www.baidu.com/"}, 139 | {"name": "谷歌", "url": "https://www.google.com/", "use_proxy": true} 140 | ] 141 | ' 142 | 143 | # 是否将测试网址的结果排序(按照响应时间正序)(可不填) 144 | PS_SORT_SITES=True 145 | 146 | # 网址测试访问时的超时时间(秒)(可不填) 147 | PS_TEST_TIMEOUT=5 148 | 149 | # == process == 150 | 151 | # 进程列表的最大项目数量(可不填) 152 | PS_PROC_LEN=5 153 | 154 | # 要忽略的进程名(可不填) 155 | # 使用正则表达式匹配(注意事项同上) 156 | PS_IGNORE_PROCS=[] 157 | 158 | # 进程列表的排序方式(可不填) 159 | # 可选:cpu、mem 160 | PS_PROC_SORT_BY=cpu 161 | 162 | # 是否将进程 CPU 占用率显示为类似 Windows 任务管理器的百分比(最高 100%)(可不填) 163 | # 例:当你的 CPU 总共有 4 线程时,如果该进程吃满了两个线程, 164 | # Linux 会显示为 200%(每个线程算 100%),而 Windows 会显示为 50%(总占用率算 100%) 165 | PS_PROC_CPU_MAX_100P=False 166 | 167 | # ==================== 168 | # default 模板特定配置 169 | 170 | # 图片中渲染的组件列表及其排列顺序(可不填) 171 | # 默认启用全部组件 172 | # 组件介绍: 173 | # - "header": 已连接的 Bot 信息、NoneBot 运行时间、系统运行时间 174 | # - "cpu_mem": CPU、MEM、SWAP 使用率圆环图 175 | # - "disk": 分区占用情况、磁盘 IO 情况 176 | # - "network": 网络 IO 情况、网络响应速度测试 177 | # - "process": 进程 CPU、MEM 占用情况 178 | # - "footer": NoneBot 与 PicStatus 版本、当前时间、Python 实现及版本、系统名称及架构 179 | PS_DEFAULT_COMPONENTS=["header", "cpu_mem", "disk", "network", "process", "footer"] 180 | 181 | # 向模板中附加的 CSS 路径列表(默认为空) 182 | # 如要使用插件内置 CSS,请使用 res: 前缀 183 | # 内置 CSS 列表: 184 | # - "res:theme-dark.css": 深色主题 185 | # - "res:no-blur.css": 禁用卡片毛玻璃效果 186 | # - “res:no-radius.css”: 禁用圆角 187 | # - “res:no-shadow.css”: 禁用阴影 188 | # Tip: 189 | # 可以在 Bot 工作目录下新建一个名为 debug 的文件夹, 190 | # 当检测到存在此文件夹时,渲染出来的 HTML 会被写进 debug/picstatus 文件夹中,方便调试 191 | PS_DEFAULT_ADDITIONAL_CSS=[] 192 | 193 | # 向模板中附加的 JS 脚本路径列表(默认为空) 194 | # 暂无内置 JS 脚本可供使用 195 | # 编写方式请参考 res/js/index.js 196 | PS_DEFAULT_ADDITIONAL_SCRIPT=[] 197 | 198 | # 输出的图片格式(可不填) 199 | # 可选:jpeg、png 200 | PS_DEFAULT_PIC_FORMAT=jpeg 201 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: ["https://afdian.net/@lgc2333/"] 4 | -------------------------------------------------------------------------------- /.github/workflows/pypi-publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Python 🐍 distributions 📦 to PyPI 2 | 3 | on: 4 | release: 5 | types: [published] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build-n-publish: 10 | name: Use PDM to Build and publish Python 🐍 distributions 📦 to PyPI 11 | runs-on: ubuntu-latest 12 | 13 | permissions: 14 | # IMPORTANT: this permission is mandatory for trusted publishing 15 | id-token: write 16 | 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@master 20 | with: 21 | submodules: true 22 | 23 | - name: Setup PDM 24 | uses: pdm-project/setup-pdm@v3 25 | 26 | - name: Build and Publish distribution 📦 to PyPI 27 | run: pdm publish 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode/ 2 | /.idea/ 3 | /.run/ 4 | /venv/ 5 | /testnb2/ 6 | .pdm-python 7 | build/ 8 | __pycache__ 9 | /nonebot_plugin_picstatus/res/picstatus-debug.html 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 LgCookie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | NoneBotPluginLogo 7 | 8 | 9 |

10 | NoneBotPluginText 11 |

12 | 13 | # NoneBot-Plugin-PicStatus 14 | 15 | _✨ 运行状态图片版 for NoneBot2 ✨_ 16 | 17 | python 18 | 19 | pdm-managed 20 | 21 | 22 | wakatime 23 | 24 | 25 |
26 | 27 | 28 | Pydantic Version 1 Or 2 29 | 30 | 31 | license 32 | 33 | 34 | pypi 35 | 36 | 37 | pypi download 38 | 39 | 40 |
41 | 42 | 43 | NoneBot Registry 44 | 45 | 46 | Supported Adapters 47 | 48 | 49 |
50 | 51 | ## 📖 介绍 52 | 53 | 不多说,直接看图! 54 | 55 | ### 效果图 56 | 57 |
58 | 点击展开 59 | 60 | ![example](https://raw.githubusercontent.com/lgc-NB2Dev/readme/main/picstatus/example1.jpg) 61 | ![example](https://raw.githubusercontent.com/lgc-NB2Dev/readme/main/picstatus/example2.jpg) 62 | 63 |
64 | 65 | ## 💿 安装 66 | 67 | 以下提到的方法 任选**其一** 即可 68 | 69 |
70 | [推荐] 使用 nb-cli 安装 71 | 在 nonebot2 项目的根目录下打开命令行, 输入以下指令即可安装 72 | 73 | ```bash 74 | nb plugin install nonebot-plugin-picstatus 75 | ``` 76 | 77 |
78 | 79 |
80 | 使用包管理器安装 81 | 在 nonebot2 项目的插件目录下, 打开命令行, 根据你使用的包管理器, 输入相应的安装命令 82 | 83 |
84 | pip 85 | 86 | ```bash 87 | pip install nonebot-plugin-picstatus 88 | ``` 89 | 90 |
91 |
92 | pdm 93 | 94 | ```bash 95 | pdm add nonebot-plugin-picstatus 96 | ``` 97 | 98 |
99 |
100 | poetry 101 | 102 | ```bash 103 | poetry add nonebot-plugin-picstatus 104 | ``` 105 | 106 |
107 |
108 | conda 109 | 110 | ```bash 111 | conda install nonebot-plugin-picstatus 112 | ``` 113 | 114 |
115 | 116 | 打开 nonebot2 项目根目录下的 `pyproject.toml` 文件, 在 `[tool.nonebot]` 部分的 `plugins` 项里追加写入 117 | 118 | ```toml 119 | [tool.nonebot] 120 | plugins = [ 121 | # ... 122 | "nonebot_plugin_picstatus" 123 | ] 124 | ``` 125 | 126 |
127 | 128 | ## ⚙️ 配置 129 | 130 | ### 见 [.env.example](https://github.com/lgc2333/nonebot-plugin-picstatus/blob/master/.env.example) 131 | 132 | ## 🎨 扩展 133 | 134 | 想知道如何为插件新增数据源、图片模板与背景图来源的话,请参考下方示例 135 | 136 | ### 见 [examples/external_example](https://github.com/lgc-NB2Dev/nonebot-plugin-picstatus/tree/master/examples/external_example) 137 | 138 | ## 🎉 使用 139 | 140 | 使用指令 `运行状态`(或者 `状态` / `zt` / `yxzt` / `status`,可修改)来触发插件功能 141 | 可以在消息后面跟一张图片或者回复一张图片来自定义背景图,默认为随机背景图 142 | 更多自定义项参见 [配置](#️-配置) 143 | 144 | ## 📞 联系 145 | 146 | QQ:3076823485 147 | Telegram:[@lgc2333](https://t.me/lgc2333) 148 | 吹水群:[1105946125](https://jq.qq.com/?_wv=1027&k=Z3n1MpEp) 149 | 邮箱: 150 | 151 | ## 💡 鸣谢 152 | 153 | ### [nonebot/plugin-alconna](https://github.com/nonebot/plugin-alconna) 154 | 155 | - 强大的命令解析库,和多平台适配方案 156 | 157 | ### [noneplugin/nonebot-plugin-userinfo](https://github.com/noneplugin/nonebot-plugin-userinfo) 158 | 159 | - 多平台用户信息获取方案 160 | 161 | ### [kexue-z/nonebot-plugin-htmlrender](https://github.com/kexue-z/nonebot-plugin-htmlrender) 162 | 163 | - HTML 渲染方案 164 | 165 | ### [LoliApi](https://docs.loliapi.com/) & [Lolicon API](https://api.lolicon.app/) 166 | 167 | - 背景图来源 168 | 169 | ## 💰 赞助 170 | 171 | **[赞助我](https://blog.lgc2333.top/donate)** 172 | 173 | 感谢大家的赞助!你们的赞助将是我继续创作的动力! 174 | 175 | ## 📝 更新日志 176 | 177 | ### 2.1.3 178 | 179 | - 兼容 HTTPX 0.28 180 | 181 | ### 2.1.2 182 | 183 | - fix [#49](https://github.com/lgc-NB2Dev/nonebot-plugin-picstatus/issues/49) 184 | 185 | ### 2.1.1 186 | 187 | - 重新加入指令带图/回复图自定义背景功能 188 | 189 | ### 2.1.0 190 | 191 | - 优化代码结构,现在理论上可以使用其他插件往本插件内添加数据源、图片模板与背景图来源了 192 | - 添加背景图预载功能,理论可以加快出图速度 193 | - 删除 `gm` 背景图源,默认图源改为 `loli` 194 | - 配置项变动: 195 | - 新增 `PS_BG_PRELOAD_COUNT` 196 | - 新增 `PS_DEFAULT_PIC_FORMAT` 197 | - `PS_BG_PROVIDER` 默认值变动 198 | - `PS_COUNT_MESSAGE_SENT_EVENT` 接受类型变动 199 | 200 | ### 2.0.0 201 | 202 | - 代码重构,现在开发者可以更自由灵活的添加新状态图片样式 203 | - 配置项变动: 204 | - 新增 `PS_TEMPLATE` 205 | - 新增 `PS_COLLECT_INTERVAL` 206 | - 新增 `PS_DEFAULT_COLLECT_CACHE_SIZE` 207 | - 新增 `PS_COLLECT_CACHE_SIZE` 208 | - 新增 `PS_COUNT_MESSAGE_SENT_EVENT` 209 | - 新增 `PS_DISCONNECT_RESET_COUNTER` 210 | - 重命名 `PS_COMPONENTS` -> `PS_DEFAULT_COMPONENTS` 211 | - 重命名 `PS_ADDITIONAL_CSS` -> `PS_DEFAULT_ADDITIONAL_CSS` 212 | - 重命名 `PS_ADDITIONAL_SCRIPT` -> `PS_DEFAULT_ADDITIONAL_SCRIPT` 213 | 214 |
215 | v1 更新日志(点击展开) 216 | 217 | ### 1.1.1 218 | 219 | - 新增内置 CSS `theme-vanilla.css` 220 | - 微调默认 CSS 221 | 222 | ### 1.1.0 223 | 224 | - 支持 Pydantic V2 225 | 226 | ### 1.0.3 227 | 228 | - 修复了当有读取数据出错的分区时图片无法正常渲染的 Bug 229 | 230 | ### 1.0.2 231 | 232 | - 修复了背景图还没加载就出图的 Bug(希望),顺带调整了一下附加 JS 的写法 233 | 234 | ### 1.0.1 235 | 236 | - impl [#38](https://github.com/lgc-NB2Dev/nonebot-plugin-picstatus/issues/38),新增配置 `PS_OB_V11_USE_GET_STATUS` 237 | 238 | ### 1.0.0 239 | 240 | 重构项目: 241 | 242 | - 换用 alconna 与 userinfo 适配多平台 243 | - 换用 htmlrender 渲染图片 244 | - 删除消息附带图片作为自定义背景图功能 245 | - 配置项改动: 246 | - 添加 `PS_COMPONENTS` 247 | - 添加 `PS_ADDITIONAL_CSS` 248 | - 添加 `PS_ADDITIONAL_SCRIPT` 249 | - 添加 `PS_BG_PROVIDER` 250 | - 添加 `PS_BG_LOLICON_R18_TYPE` 251 | - 添加 `PS_BG_LOCAL_PATH` 252 | - 添加 `PS_SHOW_CURRENT_BOT_ONLY` 253 | - 删除 `PS_FONT` 254 | - 删除 `PS_CUSTOM_BG` 255 | - 删除 `PS_BG_COLOR` 256 | - 删除 `PS_MASK_COLOR` 257 | - 删除 `PS_BLUR_RADIUS` 258 | - 删除 `PS_FOOTER_SIZE` 259 | - 删除 `PS_MAX_TEXT_LEN` 260 | - 删除 `PS_DEFAULT_BG` 261 | 262 |
263 | 264 |
265 | v0 更新日志(点击展开) 266 | 267 | ### 0.5.7 268 | 269 | - 修复 Bot 刚连接时收发数为未知的问题 270 | 271 | ### 0.5.6 272 | 273 | - 修复 Bot 连接时间与收发数显示不正确的问题 274 | 275 | ### 0.5.5 276 | 277 | - 一些不影响使用的小更改 278 | - 添加配置项 `PS_DEFAULT_AVATAR`、`PS_DEFAULT_BG`、`PS_COMMAND` 279 | 280 | ### 0.5.4 281 | 282 | - 针对性修复 Shamrock 获取状态信息报错的问题 ([#34](https://github.com/lgc-NB2Dev/nonebot-plugin-picstatus/issues/34)) 283 | 284 | ### 0.5.3 285 | 286 | - 修改了读取 Linux 发行版名称与版本的方式 287 | 288 | ### 0.5.2 289 | 290 | - 修正读取分区信息错误时的提示信息 \([#33](https://github.com/lgc-NB2Dev/nonebot-plugin-picstatus/issues/33)\) 291 | 292 | ### 0.5.1 293 | 294 | - 使用 SAA 向 Telegram 平台发送消息 295 | 296 | ### 0.5.0 297 | 298 | - 先获取状态信息再进行画图,可以获取到更精准的状态信息 299 | - 添加进程占用信息的展示 300 | - 测试网站结果状态码后面会带上 `reason`,如 `200 OK` / `404 Not Found` 301 | - 添加了一些配置项(`PS_SORT_PARTS`, `PS_SORT_PARTS_REVERSE`, `PS_SORT_DISK_IOS`, `PS_SORT_NETS`, `PS_SORT_SITES`, `PS_PROC_LEN`, `PS_IGNORE_PROCS`, `PS_PROC_SORT_BY`, `PS_PROC_CPU_MAX_100P`, `PS_REPLY_TARGET`, `PS_TG_MAX_FILE_SIZE`) 302 | 303 | ### 0.4.2 304 | 305 | - 添加配置项 `PS_REQ_TIMEOUT` ([#25](https://github.com/lgc-NB2Dev/nonebot-plugin-picstatus/issues/25)) 306 | 307 | ### 0.4.1 308 | 309 | - 现在默认使用 `pil_utils` 自动选择系统内支持中文的字体,删除插件内置字体 310 | - 使用 `pil_utils` 写 Bot 昵称,可以显示 Emoji 等特殊字符 311 | - 测试网站出错时不会往日志里甩错误堆栈了 312 | 313 | ### 0.4.0 314 | 315 | - 使用 [nonebot-plugin-send-anything-anywhere](https://github.com/felinae98/nonebot-plugin-send-anything-anywhere) 兼容多平台发送,并对 OneBot V11 和 Telegram 做了特殊兼容 316 | - 将状态图片保存为 `jpg` 格式,缩减体积 317 | - 测试网站现在按照配置文件中的顺序排序 318 | - 随机图来源换回 [故梦 API](https://api.gumengya.com) 319 | - `aiohttp` 与 `aiofiles` 换成了 `httpx` 与 `anyio` 320 | 321 | ### 0.3.3 322 | 323 | - 修了点 bug 324 | - 新配置 `PS_MAX_TEXT_LEN` 325 | 326 | ### 0.3.2 327 | 328 | - 只有当 `nickname` 配置项填写后,插件才会使用该项作为图片中 Bot 的显示名称 329 | 330 | ### 0.3.1 331 | 332 | - 修复一处 Py 3.10 以下无法正常运行的代码 333 | 334 | ### 0.3.0 335 | 336 | 配置项更新详见 [配置](#️-配置) 337 | 338 | - 更新配置项 `PS_TEST_SITES` `PS_TEST_TIMEOUT` 339 | - 修复`PS_NEED_AT`配置无效的 bug 340 | - 现在只有命令完全匹配时才会触发 341 | 342 | ### 0.2.5 343 | 344 | - 更新配置项 `PS_FOOTER_SIZE` 345 | 346 | ### 0.2.4 347 | 348 | - 支持自定义默认背景图 349 | - 一些配置项类型更改(不影响原先配置) 350 | 351 | ### 0.2.3 352 | 353 | - 尝试修复磁盘列表的潜在 bug 354 | 355 | ### 0.2.2 356 | 357 | 此版本在图片脚注中显示的版本还是`0.2.1`,抱歉,我大意了没有改版本号 358 | 359 | - 添加配置项`PS_IGNORE_NO_IO_DISK`用于忽略 IO 为 0B/s 的磁盘 360 | - 添加配置项`PS_IGNORE_0B_NET`用于忽略上下行都为 0B/s 的网卡 361 | - 添加触发指令`zt` `yxzt` `status` 362 | - 获取信息收发量兼容旧版 GoCQ ,即使获取失败也不会报错而显示`未知` 363 | - 将忽略 IO 统计磁盘名独立出一个配置项`PS_IGNORE_DISK_IOS` 364 | - 忽略 磁盘容量盘符/IO 统计磁盘名/网卡名称 改为匹配正则表达式 365 | - 配置项`PS_IGNORE_NETS`添加默认值`["^lo$", "^Loopback"]` 366 | - 修复空闲内存显示错误的问题 367 | 368 | ### 0.2.1 369 | 370 | - 尝试修复`type object is not subscriptable`报错 371 | 372 | ### 0.2.0 373 | 374 | - 新增磁盘 IO、网络 IO 状态显示 375 | - SWAP 大小为 0 时占用率将会显示`未部署`而不是`0%` 376 | - CPU 等占用下方灰色字排板更改 377 | - 获取失败的磁盘分区占用率修改为`未知%` 378 | - 图片下方脚注修改为居中文本,字号调小,优化显示的系统信息 379 | - 修改随机背景图 API 为[故梦 API 随机二次元壁纸](https://api.gmit.vip) 380 | - 现在会分 QQ 记录 Bot 连接时间(不同的 QQ 连接同一个 NoneBot 显示的连接时间将不同) 381 | - 背景图增加遮罩,颜色可配置 382 | - 可以配置各模块的背景底色 383 | - 可以配置分区列表中忽略的盘符(挂载点) 384 | - 可以忽略获取容量状态失败的分区 385 | - 可以使用`.env.*`文件中配置的`NICKNAME`作为图片中的 Bot 昵称 386 | - 添加必须 @Bot 才能触发指令的配置 387 | - 其他小优化/更改 388 | 389 |
390 | -------------------------------------------------------------------------------- /examples/external_example/README.md: -------------------------------------------------------------------------------- 1 | # External Example 2 | 3 | 这是一个如何为本插件增加自定义内容的插件示例 4 | 5 | 你可以直接把此文件夹当做插件包扔给 nonebot2 加载,并按照下方说明更改配置项查看效果 6 | 7 | ```properties 8 | PS_TEMPLATE=example_template 9 | PS_BG_PROVIDER=lgc_icon 10 | ``` 11 | -------------------------------------------------------------------------------- /examples/external_example/__init__.py: -------------------------------------------------------------------------------- 1 | # ruff: noqa: E402 2 | 3 | from nonebot import require 4 | 5 | require("nonebot_plugin_picstatus") # 别忘 require 6 | 7 | from . import ( 8 | bg_provider as bg_provider, 9 | collectors as collectors, 10 | templates as templates, 11 | ) 12 | -------------------------------------------------------------------------------- /examples/external_example/bg_provider.py: -------------------------------------------------------------------------------- 1 | from httpx import AsyncClient 2 | from nonebot_plugin_picstatus.bg_provider import BgData, bg_provider, resp_to_bg_data 3 | from nonebot_plugin_picstatus.config import config 4 | 5 | # 添加自定义背景源演示 6 | # 实际应用例请见 https://github.com/lgc-NB2Dev/nonebot-plugin-picstatus/blob/master/nonebot_plugin_picstatus/bg_provider.py 7 | 8 | 9 | # 需要用 bg_provider 装饰器注册函数为背景源 10 | # 背景源名称默认为函数名,当然你也可以手动指定名称,比如 11 | # @bg_provider("lgc_icon") 12 | @bg_provider() 13 | async def lgc_icon() -> BgData: 14 | async with AsyncClient( 15 | follow_redirects=True, 16 | proxy=config.proxy, 17 | timeout=config.ps_req_timeout, 18 | ) as cli: 19 | return resp_to_bg_data( 20 | ( 21 | await cli.get("https://blog.lgc2333.top/assets/favicon.png") 22 | ).raise_for_status(), 23 | ) 24 | -------------------------------------------------------------------------------- /examples/external_example/collectors.py: -------------------------------------------------------------------------------- 1 | import random 2 | from typing_extensions import override 3 | 4 | from nonebot_plugin_picstatus.collectors import ( 5 | TimeBasedCounterCollector, 6 | collector, 7 | first_time_collector, 8 | normal_collector, 9 | periodic_collector, 10 | ) 11 | 12 | # 添加自定义数据源展示 13 | # 实际应用例请见 https://github.com/lgc-NB2Dev/nonebot-plugin-picstatus/tree/master/nonebot_plugin_picstatus/collectors 14 | 15 | # PicStatus 内置了三种可以直接用装饰器注册的 collector 16 | 17 | # region 1. normal_collector 18 | 19 | counter_count = 0 20 | 21 | 22 | # 该 collector 在每次生成状态图片时都会被调用 23 | # 使用该装饰器时,默认使用函数名作为数据源名称,你也可以手动指定,例如 24 | # @normal_collector("counter") 25 | @normal_collector() 26 | async def counter(): 27 | global counter_count 28 | counter_count += 1 29 | return counter_count 30 | 31 | 32 | # endregion 33 | 34 | # region 2. first_time_collector 35 | 36 | first_time_counter_count = 0 37 | 38 | 39 | # 该 collector 只会在 nonebot 启动时被调用一次并缓存结果 40 | # 下面的例子中,该 collector 的结果将会始终为 1 41 | # 装饰器使用教程同上 42 | @first_time_collector() 43 | async def first_time_counter(): 44 | global first_time_counter_count 45 | first_time_counter_count += 1 46 | return first_time_counter_count 47 | 48 | 49 | # endregion 50 | 51 | # region 3. periodic_collector 52 | 53 | periodic_counter_count = 0 54 | 55 | 56 | # 该 collector 每隔一定时间被调用一次,并保存一定数量的结果在 deque 中 57 | # 调用的间隔与保留的结果数量可以在 PicStatus 的配置中找到 58 | # 装饰器使用教程同上 59 | @periodic_collector() 60 | async def periodic_counter(): 61 | global periodic_counter_count 62 | periodic_counter_count += 1 63 | return periodic_counter_count 64 | 65 | 66 | # endregion 67 | 68 | 69 | # 此外 PicStatus 内置了另外一种较特殊的 collector 70 | # 它不可以直接用装饰器装饰一个函数使用 71 | 72 | 73 | # region 4. TimeBasedCounterCollector 74 | 75 | 76 | # 该 collector 基于 PeriodicCollector 77 | # 它会记录该次调用与上次调用的时间间隔 78 | # 你可以根据这段时间间隔来计算你希望展示并缓存的值 79 | # 这对展示 IO 速度等数据可能会很有帮助 80 | 81 | 82 | # 使用此 collector 你需要继承两个 abstract method 83 | # 并使用 collector 装饰器注册,注意在这里 collector 名称必须手动指定 84 | # 你也可以使用 collector 装饰器来注册上述 collector,不过具体操作请自行看插件源码理解 85 | @collector("time_counter") 86 | class TestTimeBasedCounter(TimeBasedCounterCollector[int, str]): 87 | @override 88 | async def _calc(self, past: int, now: int, time_passed: float) -> str: 89 | """ 90 | 此方法计算你最终想要返回的结果 91 | 92 | Args: 93 | past: 上次调用时返回的原始结果 94 | now: 本次调用时返回的原始结果 95 | time_passed: 从上次调用到本次调用的时间间隔(秒) 96 | 97 | Returns: 98 | 处理后的结果,和 PeriodicCollector 一样保存在 deque 中 99 | """ 100 | return f"{(now - past) / time_passed:.2f}/s" 101 | 102 | @override 103 | async def _get_obj(self) -> int: 104 | """此方法返回传入 _calc 方法中的原始结果""" 105 | return random.randint(0, 100) 106 | 107 | 108 | # endregion 109 | -------------------------------------------------------------------------------- /examples/external_example/templates/__init__.py: -------------------------------------------------------------------------------- 1 | def _import(): 2 | from pathlib import Path 3 | 4 | from cookit import auto_import 5 | 6 | auto_import(Path(__file__).parent, __package__) 7 | 8 | 9 | _import() 10 | del _import 11 | -------------------------------------------------------------------------------- /examples/external_example/templates/example_template/__init__.py: -------------------------------------------------------------------------------- 1 | # ruff: noqa: E402 2 | 3 | import json 4 | from collections import deque 5 | from pathlib import Path 6 | from typing import TYPE_CHECKING, Any, Optional 7 | 8 | import jinja2 as jj 9 | from cookit.jinja import make_register_jinja_filter_deco 10 | from nonebot import get_plugin_config, require 11 | from nonebot_plugin_picstatus.templates import pic_template 12 | from nonebot_plugin_picstatus.templates.pw_render import ( 13 | ROUTE_URL, 14 | add_background_router, 15 | add_root_router, 16 | base_router_group, 17 | register_global_filter_to, 18 | ) 19 | from pydantic import BaseModel 20 | 21 | # 添加自定义图片模板示例 22 | # 示例应用例请见 https://github.com/lgc-NB2Dev/nonebot-plugin-picstatus/blob/master/nonebot_plugin_picstatus/templates/default/__init__.py 23 | 24 | # 可以使用你喜欢的任意库来绘图 25 | # 示例使用 playwright,你也可以使用 Pillow 等 26 | # 本插件中自带一些使用 playwright 时绘图有帮助的工具函数 27 | # 详见 https://github.com/lgc-NB2Dev/nonebot-plugin-picstatus/blob/master/nonebot_plugin_picstatus/templates/pw_render.py 28 | 29 | require("nonebot_plugin_htmlrender") 30 | 31 | from nonebot_plugin_htmlrender import get_new_page 32 | 33 | if TYPE_CHECKING: 34 | from nonebot_plugin_picstatus.bg_provider import BgData 35 | 36 | RES_DIR = Path(__file__).parent / "res" 37 | 38 | template_env = jj.Environment( 39 | loader=jj.FileSystemLoader(RES_DIR), 40 | autoescape=True, 41 | enable_async=True, 42 | ) 43 | # 将插件提供的实用模板 filter 注册到模板环境中 44 | register_global_filter_to(template_env) 45 | 46 | 47 | jinja_filter = make_register_jinja_filter_deco(template_env) 48 | 49 | 50 | @jinja_filter 51 | def json_dumps(obj: Any, **kwargs) -> str: 52 | if isinstance(obj, dict): 53 | obj = {k: list(v) if isinstance(v, deque) else v for k, v in obj.items()} 54 | return json.dumps(obj, **kwargs) 55 | 56 | 57 | # 复制一份插件自带的路由组以便在该份代码范围中增删 58 | template_router_group = base_router_group.copy() 59 | 60 | 61 | # 可以把模板特定配置项声明在这里 62 | # 你也可以不在这里写,换成写到插件的 config.py 文件中,看个人喜好了 63 | class TemplateConfig(BaseModel): 64 | ps_example_template_example_config: Optional[str] = "example" 65 | 66 | 67 | template_config = get_plugin_config(TemplateConfig) 68 | 69 | 70 | # 注册模板渲染函数 71 | # name 参数为模板名称,不提供时使用函数名 72 | # collecting 传入需要启用的 collector 名称集合,如不提供则启用所有 collector 73 | @pic_template( 74 | collecting={ 75 | "counter", 76 | "first_time_counter", 77 | "periodic_counter", 78 | "time_counter", 79 | }, 80 | ) 81 | async def example_template(collected: dict[str, Any], bg: "BgData", **_): 82 | template = template_env.get_template("index.html.jinja") 83 | html = await template.render_async(d=collected, config=template_config) 84 | 85 | # copy 一份以添加这次图片渲染中特定的 router 86 | router_group = template_router_group.copy() 87 | add_root_router(router_group, html) # 注册根路由,访问返回渲好的 html 88 | add_background_router(router_group, bg) # 注册背景图片路由 89 | 90 | async with get_new_page() as page: 91 | await router_group.apply(page) 92 | await page.goto(f"{ROUTE_URL}/", wait_until="load") 93 | elem = await page.wait_for_selector("main") 94 | assert elem 95 | return await elem.screenshot(type="jpeg") 96 | -------------------------------------------------------------------------------- /examples/external_example/templates/example_template/res/index.html.jinja: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 23 | 24 | 25 |
26 |
27 |
28 |
29 |

Example Template

30 |

This is an example status picture template.

31 |
32 |
33 |
d = {{ d | json_dumps(indent=2) }}
34 |
config = {{ config.model_dump_json(indent=2) }}
35 |
36 |
37 | 38 | 39 |
40 |
41 |
42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/__init__.py: -------------------------------------------------------------------------------- 1 | # ruff: noqa: E402 2 | 3 | from nonebot import get_driver, require 4 | from nonebot.plugin import PluginMetadata, inherit_supported_adapters 5 | 6 | require("nonebot_plugin_apscheduler") 7 | require("nonebot_plugin_alconna") 8 | require("nonebot_plugin_userinfo") 9 | 10 | from . import __main__ as __main__, misc_statistics as misc_statistics 11 | from .bg_provider import bg_preloader 12 | from .collectors import ( 13 | enable_collectors, 14 | load_builtin_collectors, 15 | registered_collectors, 16 | ) 17 | from .config import ConfigModel, config 18 | from .templates import load_builtin_templates, loaded_templates 19 | 20 | driver = get_driver() 21 | 22 | 23 | # lazy load builtin templates and collectors 24 | @driver.on_startup 25 | async def _(): 26 | if config.ps_template not in loaded_templates: 27 | load_builtin_templates() 28 | current_template = loaded_templates.get(config.ps_template) 29 | if current_template is None: 30 | raise ValueError(f"Template {config.ps_template} not found") 31 | 32 | if (current_template.collectors is None) or any( 33 | (x not in registered_collectors) for x in current_template.collectors 34 | ): 35 | load_builtin_collectors() 36 | 37 | collectors = ( 38 | set(registered_collectors) 39 | if current_template.collectors is None 40 | else current_template.collectors 41 | ) 42 | await enable_collectors(*collectors) 43 | 44 | bg_preloader.start_preload() 45 | 46 | 47 | usage = f"指令:{' / '.join(config.ps_command)}" 48 | if config.ps_need_at: 49 | usage += "\n注意:使用指令时需要@机器人" 50 | if config.ps_only_su: 51 | usage += "\n注意:仅SuperUser可以使用此指令" 52 | 53 | __version__ = "2.1.3.post1" 54 | __plugin_meta__ = PluginMetadata( 55 | name="PicStatus", 56 | description="以图片形式显示当前设备的运行状态", 57 | usage=usage, 58 | type="application", 59 | homepage="https://github.com/lgc-NB2Dev/nonebot-plugin-picstatus", 60 | config=ConfigModel, 61 | supported_adapters=inherit_supported_adapters( 62 | "nonebot_plugin_alconna", 63 | "nonebot_plugin_userinfo", 64 | ), 65 | extra={"License": "MIT", "Author": "LgCookie"}, 66 | ) 67 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/__main__.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from contextlib import suppress 3 | from typing import Optional 4 | 5 | from nonebot import logger, on_command 6 | from nonebot.adapters import Bot as BaseBot, Event as BaseEvent, Message as BaseMessage 7 | from nonebot.matcher import current_bot, current_event, current_matcher 8 | from nonebot.params import CommandArg, Depends 9 | from nonebot.permission import SUPERUSER 10 | from nonebot.rule import Rule, to_me 11 | from nonebot_plugin_alconna.uniseg import Image, Reply, UniMessage, UniMsg, image_fetch 12 | 13 | from .bg_provider import BgData, bg_preloader 14 | from .collectors import collect_all 15 | from .config import config 16 | from .misc_statistics import cache_bot_info 17 | from .templates import render_current_template 18 | 19 | 20 | def check_empty_arg_rule(arg: BaseMessage = CommandArg()): 21 | return not arg.extract_plain_text() 22 | 23 | 24 | def trigger_rule(): 25 | rule = Rule(check_empty_arg_rule) 26 | if config.ps_need_at: 27 | rule &= to_me() 28 | return rule 29 | 30 | 31 | _cmd, *_alias = config.ps_command 32 | stat_matcher = on_command( 33 | _cmd, 34 | aliases=set(_alias), 35 | rule=trigger_rule(), 36 | permission=SUPERUSER if config.ps_only_su else None, 37 | ) 38 | 39 | 40 | async def _msg_pic(msg: UniMsg) -> Optional[BgData]: 41 | msg = ( 42 | r 43 | if ( 44 | Reply in msg 45 | and isinstance((r_org := msg[Reply, 0].msg), BaseMessage) 46 | and Image in (r := await UniMessage.generate(message=r_org)) 47 | ) 48 | else msg 49 | ) 50 | if Image not in msg: 51 | return None 52 | img = msg[Image, 0] 53 | data = await image_fetch( 54 | current_event.get(), 55 | current_bot.get(), 56 | current_matcher.get().state, 57 | img, 58 | ) 59 | if not data: 60 | return None 61 | return BgData(data=data, mime=img.mimetype or "image") 62 | 63 | 64 | def MsgPic(): # noqa: N802 65 | return Depends(_msg_pic) 66 | 67 | 68 | @stat_matcher.handle() 69 | async def _(bot: BaseBot, event: BaseEvent, msg_pic: Optional[BgData] = MsgPic()): 70 | with suppress(Exception): 71 | await cache_bot_info(bot, event) 72 | 73 | async def get_bg(): 74 | return msg_pic or (await bg_preloader.get()) 75 | 76 | try: 77 | bg, collected = await asyncio.gather(get_bg(), collect_all()) 78 | ret = await render_current_template(collected=collected, bg=bg) 79 | except Exception: 80 | logger.exception("获取运行状态图失败") 81 | await UniMessage("获取运行状态图片失败,请检查后台输出").send( 82 | reply_to=config.ps_reply_target, 83 | ) 84 | else: 85 | await UniMessage.image(raw=ret).send(reply_to=config.ps_reply_target) 86 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/bg_provider.py: -------------------------------------------------------------------------------- 1 | import asyncio as aio 2 | import mimetypes 3 | import random 4 | from collections.abc import Awaitable 5 | from typing import TYPE_CHECKING, Callable, NamedTuple, Optional, TypeVar 6 | 7 | import anyio 8 | from httpx import AsyncClient, Response 9 | from nonebot import logger 10 | 11 | from .config import DEFAULT_BG_PATH, config 12 | 13 | if TYPE_CHECKING: 14 | from pathlib import Path 15 | 16 | 17 | class BgData(NamedTuple): 18 | data: bytes 19 | mime: str 20 | 21 | 22 | BGProviderType = Callable[[], Awaitable[BgData]] 23 | TBP = TypeVar("TBP", bound=BGProviderType) 24 | 25 | registered_bg_providers: dict[str, BGProviderType] = {} 26 | 27 | 28 | def get_bg_files() -> list["Path"]: 29 | if not config.ps_bg_local_path.exists(): 30 | logger.warning("Custom background path does not exist, fallback to default") 31 | return [DEFAULT_BG_PATH] 32 | if config.ps_bg_local_path.is_file(): 33 | return [config.ps_bg_local_path] 34 | 35 | files = [x for x in config.ps_bg_local_path.glob("*") if x.is_file()] 36 | if not files: 37 | logger.warning("Custom background dir has no file in it, fallback to default") 38 | return [DEFAULT_BG_PATH] 39 | return files 40 | 41 | 42 | BG_FILES = get_bg_files() 43 | 44 | 45 | def bg_provider(name: Optional[str] = None): 46 | def deco(func: TBP) -> TBP: 47 | provider_name = name or func.__name__ 48 | if provider_name in registered_bg_providers: 49 | raise ValueError(f"Duplicate bg provider name `{provider_name}`") 50 | registered_bg_providers[provider_name] = func 51 | return func 52 | 53 | return deco 54 | 55 | 56 | def resp_to_bg_data(resp: Response): 57 | return BgData( 58 | resp.content, 59 | (resp.headers.get("Content-Type") or "application/octet-stream"), 60 | ) 61 | 62 | 63 | @bg_provider() 64 | async def loli(): 65 | async with AsyncClient( 66 | follow_redirects=True, 67 | proxy=config.proxy, 68 | timeout=config.ps_req_timeout, 69 | ) as cli: 70 | return resp_to_bg_data( 71 | (await cli.get("https://www.loliapi.com/acg/pe/")).raise_for_status(), 72 | ) 73 | 74 | 75 | @bg_provider() 76 | async def lolicon(): 77 | async with AsyncClient( 78 | follow_redirects=True, 79 | proxy=config.proxy, 80 | timeout=config.ps_req_timeout, 81 | ) as cli: 82 | resp = await cli.get( 83 | "https://api.lolicon.app/setu/v2", 84 | params={ 85 | "r18": config.ps_bg_lolicon_r18_type, 86 | "proxy": "false", 87 | "excludeAI": "true", 88 | }, 89 | ) 90 | url = resp.raise_for_status().json()["data"][0]["urls"]["original"] 91 | resp = await cli.get( 92 | url, 93 | headers={ 94 | "User-Agent": ( 95 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " 96 | "AppleWebKit/537.36 (KHTML, like Gecko) " 97 | "Chrome/119.0.0.0 " 98 | "Safari/537.36" 99 | ), 100 | "Referer": "https://www.pixiv.net/", 101 | }, 102 | ) 103 | return resp_to_bg_data(resp) 104 | 105 | 106 | @bg_provider() 107 | async def local(): 108 | file = random.choice(BG_FILES) 109 | logger.debug(f"Choice background file `{file}`") 110 | return BgData( 111 | await anyio.Path(file).read_bytes(), 112 | mimetypes.guess_type(file)[0] or "application/octet-stream", 113 | ) 114 | 115 | 116 | @bg_provider() 117 | async def none(): 118 | return BgData(b"", "application/octet-stream") 119 | 120 | 121 | async def fetch_bg() -> BgData: 122 | if config.ps_bg_provider in registered_bg_providers: 123 | try: 124 | return await registered_bg_providers[config.ps_bg_provider]() 125 | except Exception: 126 | logger.exception("Error when getting background, fallback to local") 127 | else: 128 | logger.warning( 129 | f"Unknown background provider `{config.ps_bg_provider}`, fallback to local", 130 | ) 131 | return await local() 132 | 133 | 134 | class BgPreloader: 135 | def __init__(self, preload_count: int): 136 | if preload_count < 1: 137 | raise ValueError("preload_count must be greater than 0") 138 | self.preload_count = preload_count 139 | self.backgrounds: list[BgData] = [] 140 | self.tasks: list[aio.Task[None]] = [] 141 | self.task_signal: Optional[aio.Future[None]] = None 142 | self.signal_wait_lock = aio.Lock() 143 | 144 | def _get_signal(self) -> aio.Future[None]: 145 | if (not self.task_signal) or self.task_signal.done(): 146 | self.task_signal = aio.Future() 147 | return self.task_signal 148 | 149 | def _wait_signal(self): 150 | async def inner(): 151 | async with self.signal_wait_lock: 152 | await self._get_signal() 153 | 154 | return aio.wait_for(inner(), timeout=15) 155 | 156 | def create_preload_task(self): 157 | async def task_func(): 158 | logger.debug("Started a preload background task") 159 | try: 160 | bg = await fetch_bg() 161 | except Exception as e: 162 | # fetch_bg has fallback so it should ensure we can get a bg 163 | # if error occurred this should be an unexpected error 164 | # need to let this error raise 165 | logger.opt(exception=e).debug("Exception when preloading") 166 | if not (s := self._get_signal()).done(): 167 | s.set_exception(e) 168 | else: 169 | logger.debug("A preload task done") 170 | self.backgrounds.append(bg) 171 | if not (s := self._get_signal()).done(): 172 | s.set_result(None) 173 | finally: 174 | self.tasks.remove(task) 175 | 176 | task = aio.create_task(task_func()) 177 | self.tasks.append(task) 178 | 179 | def start_preload(self, create_when_full: bool = False): 180 | task_count = self.preload_count - len(self.backgrounds) - len(self.tasks) 181 | if task_count <= 0: 182 | if not create_when_full: 183 | return 184 | task_count = 1 185 | logger.debug(f"Will preload {task_count} backgrounds") 186 | for _ in range(task_count): 187 | self.create_preload_task() 188 | 189 | async def get(self) -> BgData: 190 | if not self.backgrounds: 191 | self.start_preload(create_when_full=True) 192 | if self.tasks: 193 | await self._wait_signal() 194 | if not self.backgrounds: 195 | raise RuntimeError("Failed to wait background") 196 | bg = self.backgrounds.pop(0) 197 | self.start_preload() 198 | return bg 199 | 200 | 201 | bg_preloader = BgPreloader(config.ps_bg_preload_count) 202 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/collectors/__init__.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import importlib 3 | import time 4 | from abc import abstractmethod 5 | from collections import deque 6 | from collections.abc import Awaitable 7 | from pathlib import Path 8 | from typing import Any, Callable, Generic, Optional, TypeVar, Union 9 | from typing_extensions import override 10 | 11 | from nonebot import logger 12 | from nonebot_plugin_apscheduler import scheduler 13 | 14 | from ..config import config 15 | 16 | T = TypeVar("T") 17 | TI = TypeVar("TI") 18 | TR = TypeVar("TR") 19 | TC = TypeVar("TC", bound="Collector") 20 | TCF = TypeVar("TCF", bound=Callable[[], Awaitable[Any]]) 21 | R = TypeVar("R") 22 | 23 | Undefined = type("Undefined", (), {}) 24 | 25 | 26 | class SkipCollectError(Exception): 27 | pass 28 | 29 | 30 | class Collector(Generic[TI, TR]): 31 | @abstractmethod 32 | async def _get(self) -> TI: ... 33 | 34 | @abstractmethod 35 | async def get(self) -> TR: ... 36 | 37 | 38 | class BaseNormalCollector(Collector[T, T], Generic[T]): 39 | def __init__(self) -> None: 40 | super().__init__() 41 | 42 | @override 43 | async def get(self) -> T: 44 | return await self._get() 45 | 46 | 47 | class BaseFirstTimeCollector(Collector[T, T], Generic[T]): 48 | def __init__(self) -> None: 49 | super().__init__() 50 | self._cached: Union[T, Undefined] = Undefined() 51 | 52 | @override 53 | async def get(self) -> T: 54 | if not isinstance(self._cached, Undefined): 55 | return self._cached 56 | data = await self._get() 57 | self._cached = data 58 | return data 59 | 60 | 61 | class BasePeriodicCollector(Collector[T, deque[T]], Generic[T]): 62 | def __init__(self, size: int = config.ps_default_collect_cache_size) -> None: 63 | super().__init__() 64 | self.data = deque(maxlen=size) 65 | 66 | @override 67 | async def get(self) -> deque[T]: 68 | return self.data 69 | 70 | async def collect(self): 71 | try: 72 | data = await self._get() 73 | except SkipCollectError: 74 | return 75 | except Exception: 76 | logger.exception("Error occurred while collecting data") 77 | else: 78 | self.data.append(data) 79 | 80 | 81 | registered_collectors: dict[str, type[Collector]] = {} 82 | enabled_collectors: dict[str, Collector] = {} 83 | 84 | 85 | def collector(name: str): 86 | def deco(cls: type[TC]) -> type[TC]: 87 | if name in registered_collectors: 88 | raise ValueError(f"Collector {name} already exists") 89 | registered_collectors[name] = cls 90 | logger.debug(f"Registered collector {name}") 91 | return cls 92 | 93 | return deco 94 | 95 | 96 | def _enable_collector(name: str): 97 | if name not in registered_collectors: 98 | raise ValueError(f"Collector {name} not found") 99 | cls = registered_collectors[name] 100 | if issubclass(cls, BasePeriodicCollector) and name in config.ps_collect_cache_size: 101 | instance = cls(size=config.ps_collect_cache_size[name]) 102 | else: 103 | instance = cls() 104 | enabled_collectors[name] = instance 105 | 106 | 107 | async def enable_collectors(*names: str): 108 | for name in names: 109 | _enable_collector(name) 110 | await init_first_time_collectors() 111 | await collect_perodic_collectors() 112 | 113 | 114 | def functional_collector(cls: type[Collector], name: Optional[str] = None): 115 | def deco(func: TCF) -> TCF: 116 | collector_name = name or func.__name__ 117 | if not collector_name: 118 | raise ValueError("name must be provided") 119 | 120 | class Collector(cls): 121 | async def _get(self) -> Any: 122 | return await func() 123 | 124 | collector(collector_name)(Collector) 125 | return func 126 | 127 | return deco 128 | 129 | 130 | def normal_collector(name: Optional[str] = None): 131 | return functional_collector(BaseNormalCollector, name) 132 | 133 | 134 | def first_time_collector(name: Optional[str] = None): 135 | return functional_collector(BaseFirstTimeCollector, name) 136 | 137 | 138 | def periodic_collector(name: Optional[str] = None): 139 | return functional_collector(BasePeriodicCollector, name) 140 | 141 | 142 | class TimeBasedCounterCollector(BasePeriodicCollector[R], Generic[T, R]): 143 | def __init__(self, size: int = config.ps_default_collect_cache_size) -> None: 144 | super().__init__(size) 145 | self.last_obj: Union[Undefined, T] = Undefined() 146 | self.last_time: float = 0 147 | 148 | @abstractmethod 149 | async def _calc(self, past: T, now: T, time_passed: float) -> R: ... 150 | 151 | @abstractmethod 152 | async def _get_obj(self) -> T: ... 153 | 154 | @override 155 | async def _get(self) -> R: 156 | past = self.last_obj 157 | past_time = self.last_time 158 | time_now = time.time() 159 | time_passed = time_now - past_time 160 | 161 | self.last_time = time_now 162 | self.last_obj = await self._get_obj() 163 | if isinstance(past, Undefined): 164 | raise SkipCollectError 165 | return await self._calc(past, self.last_obj, time_passed) 166 | 167 | 168 | async def collect_all() -> dict[str, Any]: 169 | async def get(name: str): 170 | return name, await enabled_collectors[name].get() 171 | 172 | res = await asyncio.gather(*(get(name) for name in enabled_collectors)) 173 | return dict(res) 174 | 175 | 176 | def load_builtin_collectors(): 177 | for module in Path(__file__).parent.iterdir(): 178 | if not module.name.startswith("_"): 179 | importlib.import_module(f".{module.stem}", __package__) 180 | 181 | 182 | async def init_first_time_collectors(): 183 | await asyncio.gather( 184 | *( 185 | x.get() 186 | for x in enabled_collectors.values() 187 | if isinstance(x, BaseFirstTimeCollector) 188 | ), 189 | ) 190 | 191 | 192 | @scheduler.scheduled_job("interval", seconds=config.ps_collect_interval) 193 | async def collect_perodic_collectors(): 194 | await asyncio.gather( 195 | *( 196 | x.collect() 197 | for x in enabled_collectors.values() 198 | if isinstance(x, BasePeriodicCollector) 199 | ), 200 | ) 201 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/collectors/bot.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from dataclasses import dataclass 3 | from datetime import datetime 4 | from typing import TYPE_CHECKING, Optional 5 | 6 | from nonebot import get_bots, logger 7 | from nonebot.matcher import current_bot 8 | 9 | from ..config import config 10 | from ..misc_statistics import ( 11 | bot_connect_time, 12 | bot_info_cache, 13 | recv_num, 14 | send_num, 15 | ) 16 | from ..util import format_timedelta 17 | from . import normal_collector 18 | 19 | if TYPE_CHECKING: 20 | from nonebot.adapters import Bot as BaseBot 21 | 22 | try: 23 | from nonebot.adapters.onebot.v11 import Bot as OBV11Bot 24 | except ImportError: 25 | OBV11Bot = None 26 | 27 | 28 | @dataclass 29 | class BotStatus: 30 | self_id: str 31 | adapter: str 32 | nick: str 33 | bot_connected: str 34 | msg_rec: str 35 | msg_sent: str 36 | 37 | 38 | async def get_ob11_msg_num(bot: "BaseBot") -> tuple[Optional[int], Optional[int]]: 39 | if not (config.ps_ob_v11_use_get_status and OBV11Bot and isinstance(bot, OBV11Bot)): 40 | return None, None 41 | 42 | try: 43 | bot_stat = (await bot.get_status()).get("stat") 44 | except Exception as e: 45 | logger.warning( 46 | f"Error when getting bot status: {e.__class__.__name__}: {e}", 47 | ) 48 | return None, None 49 | if not bot_stat: 50 | return None, None 51 | 52 | msg_rec = bot_stat.get("message_received") or bot_stat.get( 53 | "MessageReceived", 54 | ) 55 | msg_sent = bot_stat.get("message_sent") or bot_stat.get("MessageSent") 56 | return msg_rec, msg_sent 57 | 58 | 59 | async def get_bot_status(bot: "BaseBot", now_time: datetime) -> BotStatus: 60 | nick = ( 61 | ((info := bot_info_cache[bot.self_id]).user_displayname or info.user_name) 62 | if (not config.ps_use_env_nick) and (bot.self_id in bot_info_cache) 63 | else next(iter(config.nickname), None) 64 | ) or "Bot" 65 | bot_connected = ( 66 | format_timedelta(now_time - t) 67 | if (t := bot_connect_time.get(bot.self_id)) 68 | else "未知" 69 | ) 70 | 71 | msg_rec, msg_sent = await get_ob11_msg_num(bot) 72 | if msg_rec is None: 73 | msg_rec = recv_num.get(bot.self_id) 74 | if msg_sent is None: 75 | msg_sent = send_num.get(bot.self_id) 76 | msg_rec = "未知" if (msg_rec is None) else str(msg_rec) 77 | msg_sent = "未知" if (msg_sent is None) else str(msg_sent) 78 | 79 | return BotStatus( 80 | self_id=bot.self_id, 81 | adapter=bot.adapter.get_name(), 82 | nick=nick, 83 | bot_connected=bot_connected, 84 | msg_rec=msg_rec, 85 | msg_sent=msg_sent, 86 | ) 87 | 88 | 89 | @normal_collector() 90 | async def bots() -> list[BotStatus]: 91 | now_time = datetime.now().astimezone() 92 | return ( 93 | [await get_bot_status(current_bot.get(), now_time)] 94 | if config.ps_show_current_bot_only 95 | else await asyncio.gather( 96 | *(get_bot_status(bot, now_time) for bot in get_bots().values()), 97 | ) 98 | ) 99 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/collectors/cpu.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Optional, cast 3 | 4 | import psutil 5 | from cpuinfo import get_cpu_info 6 | from nonebot import logger 7 | 8 | from . import first_time_collector, periodic_collector 9 | 10 | 11 | @dataclass 12 | class CpuFreq: 13 | current: Optional[float] 14 | min: Optional[float] # noqa: A003 15 | max: Optional[float] # noqa: A003 16 | 17 | 18 | @first_time_collector() 19 | async def cpu_brand() -> str: 20 | try: 21 | brand = ( 22 | cast("str", get_cpu_info().get("brand_raw", "")) 23 | .split("@", maxsplit=1)[0] 24 | .strip() 25 | ) 26 | if brand.lower().endswith(("cpu", "processor")): 27 | brand = brand.rsplit(maxsplit=1)[0].strip() 28 | except Exception: 29 | logger.exception("Error when getting CPU brand") 30 | return "未知型号" 31 | else: 32 | return brand 33 | 34 | 35 | @first_time_collector() 36 | async def cpu_count_logical() -> int: 37 | return psutil.cpu_count() 38 | 39 | 40 | @first_time_collector() 41 | async def cpu_count() -> int: 42 | return psutil.cpu_count(logical=False) 43 | 44 | 45 | @periodic_collector() 46 | async def cpu_percent() -> float: 47 | return psutil.cpu_percent() 48 | 49 | 50 | @periodic_collector() 51 | async def cpu_freq() -> CpuFreq: 52 | cpu_freq = psutil.cpu_freq() 53 | return CpuFreq( 54 | current=getattr(cpu_freq, "current", None), 55 | min=getattr(cpu_freq, "min", None), 56 | max=getattr(cpu_freq, "max", None), 57 | ) 58 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/collectors/disk.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Optional, Union 3 | 4 | import psutil 5 | from nonebot import logger 6 | from psutil._common import sdiskio, sdiskpart 7 | 8 | from ..config import config 9 | from ..util import match_list_regexp 10 | from . import TimeBasedCounterCollector, collector, periodic_collector 11 | 12 | 13 | @dataclass 14 | class DiskUsageNormal: 15 | name: str 16 | percent: float 17 | used: int 18 | total: int 19 | 20 | 21 | @dataclass 22 | class DiskUsageWithExc: 23 | name: str 24 | exception: str 25 | 26 | 27 | DiskUsageType = Union[DiskUsageNormal, DiskUsageWithExc] 28 | 29 | 30 | @dataclass 31 | class DiskIO: 32 | name: str 33 | read: float 34 | write: float 35 | 36 | 37 | @periodic_collector() 38 | async def disk_usage() -> list[DiskUsageType]: 39 | def get_one(disk: sdiskpart) -> Optional[DiskUsageType]: 40 | mountpoint = disk.mountpoint 41 | 42 | if match_list_regexp(config.ps_ignore_parts, mountpoint): 43 | # logger.info(f"空间读取 分区 {mountpoint} 匹配 {regex.re.pattern},忽略") 44 | return None 45 | 46 | try: 47 | usage = psutil.disk_usage(mountpoint) 48 | except Exception as e: 49 | logger.exception(f"读取 {mountpoint} 占用失败") 50 | return ( 51 | None 52 | if config.ps_ignore_bad_parts 53 | else DiskUsageWithExc(name=mountpoint, exception=str(e)) 54 | ) 55 | 56 | return DiskUsageNormal( 57 | name=mountpoint, 58 | percent=usage.percent, 59 | used=usage.used, 60 | total=usage.total, 61 | ) 62 | 63 | usage = [x for x in map(get_one, psutil.disk_partitions()) if x] 64 | if config.ps_sort_parts: 65 | usage.sort( 66 | key=lambda x: x.percent if isinstance(x, DiskUsageNormal) else -1, 67 | reverse=not config.ps_sort_parts_reverse, 68 | ) 69 | 70 | return usage 71 | 72 | 73 | @collector("disk_io") 74 | class DiskIOCollector(TimeBasedCounterCollector[dict[str, sdiskio], list[DiskIO]]): 75 | async def _calc( 76 | self, 77 | past: dict[str, sdiskio], 78 | now: dict[str, sdiskio], 79 | time_passed: float, 80 | ) -> list[DiskIO]: 81 | def calc_one(name: str, past_it: sdiskio, now_it: sdiskio) -> Optional[DiskIO]: 82 | if match_list_regexp(config.ps_ignore_disk_ios, name): 83 | # logger.info(f"IO统计 磁盘 {name} 匹配 {regex.re.pattern},忽略") 84 | return None 85 | 86 | read = (now_it.read_bytes - past_it.read_bytes) / time_passed 87 | write = (now_it.write_bytes - past_it.write_bytes) / time_passed 88 | 89 | if read == 0 and write == 0 and config.ps_ignore_no_io_disk: 90 | # logger.info(f"IO统计 忽略无IO磁盘 {name}") 91 | return None 92 | 93 | return DiskIO(name=name, read=read, write=write) 94 | 95 | res = [calc_one(name, past[name], now[name]) for name in past if name in now] 96 | res = [x for x in res if x] 97 | if config.ps_sort_disk_ios: 98 | res.sort(key=lambda x: x.read + x.write, reverse=True) 99 | return res 100 | 101 | async def _get_obj(self) -> dict[str, sdiskio]: 102 | return psutil.disk_io_counters(perdisk=True) 103 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/collectors/mem.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | import psutil 4 | 5 | from . import periodic_collector 6 | 7 | 8 | @dataclass 9 | class MemoryStat: 10 | percent: float 11 | used: int 12 | total: int 13 | 14 | 15 | @periodic_collector() 16 | async def memory_stat() -> MemoryStat: 17 | mem = psutil.virtual_memory() 18 | return MemoryStat(percent=mem.percent, used=mem.used, total=mem.total) 19 | 20 | 21 | @periodic_collector() 22 | async def swap_stat() -> MemoryStat: 23 | swap = psutil.swap_memory() 24 | return MemoryStat(percent=swap.percent, used=swap.used, total=swap.total) 25 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/collectors/misc.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import time 4 | from datetime import datetime 5 | from pathlib import Path 6 | from typing import Optional, Union 7 | 8 | import nonebot 9 | import psutil 10 | 11 | from ..misc_statistics import nonebot_run_time 12 | from ..util import format_time_delta_ps 13 | from . import first_time_collector, normal_collector, periodic_collector 14 | 15 | 16 | def parse_env(env: str) -> dict[str, Optional[str]]: 17 | env_lines = env.strip().splitlines() 18 | env_dict: dict[str, Optional[str]] = {} 19 | 20 | for line in env_lines: 21 | if "=" not in line: 22 | env_dict[line.upper()] = None 23 | continue 24 | 25 | key, value = line.split("=", 1) 26 | env_dict[key.upper()] = value.strip("\"'").strip() 27 | 28 | return env_dict 29 | 30 | 31 | def parse_env_file(env_file: Union[str, Path]) -> Optional[dict[str, Optional[str]]]: 32 | if not isinstance(env_file, Path): 33 | env_file = Path(env_file) 34 | if not env_file.exists(): 35 | return None 36 | content = env_file.read_text(encoding="u8") 37 | return parse_env(content) 38 | 39 | 40 | # Thanks to https://github.com/nonedesktop/nonebot-plugin-guestool/blob/main/nonebot_plugin_guestool/info.py 41 | def get_linux_name_version() -> Optional[tuple[str, str]]: 42 | env = parse_env_file("/etc/os-release") 43 | if env and (name := env.get("NAME")) and (version_id := env.get("VERSION_ID")): 44 | return name, version_id 45 | 46 | env = parse_env_file("/etc/lsb-release") 47 | if ( 48 | env 49 | and (name := env.get("DISTRIB_ID")) 50 | and (version_id := env.get("DISTRIB_RELEASE")) 51 | ): 52 | return name, version_id 53 | 54 | return None 55 | 56 | 57 | @normal_collector("nonebot_run_time") 58 | async def nonebot_run_time_str() -> str: 59 | now_time = datetime.now().astimezone() 60 | return ( 61 | format_time_delta_ps(now_time - nonebot_run_time) 62 | if nonebot_run_time 63 | else "未知" 64 | ) 65 | 66 | 67 | @normal_collector() 68 | async def system_run_time() -> str: 69 | now_time = datetime.now().astimezone() 70 | return format_time_delta_ps( 71 | now_time - datetime.fromtimestamp(psutil.boot_time()).astimezone(), 72 | ) 73 | 74 | 75 | @first_time_collector() 76 | async def nonebot_version() -> str: 77 | return nonebot.__version__ 78 | 79 | 80 | @first_time_collector() 81 | async def ps_version() -> str: 82 | from .. import __version__ 83 | 84 | return __version__ 85 | 86 | 87 | @periodic_collector("time") 88 | async def time_str() -> str: 89 | return time.strftime("%Y-%m-%d %H:%M:%S") 90 | 91 | 92 | @first_time_collector() 93 | async def python_version() -> str: 94 | return f"{platform.python_implementation()} {platform.python_version()}" 95 | 96 | 97 | @first_time_collector() 98 | async def system_name(): 99 | system, _, release, version, machine, _ = platform.uname() 100 | system, release, version = platform.system_alias(system, release, version) 101 | 102 | if system == "Java": 103 | _, _, _, (system, release, machine) = platform.java_ver() 104 | 105 | if system == "Darwin": 106 | return f"MacOS {platform.mac_ver()[0]} {machine}" 107 | 108 | if system == "Windows": 109 | return f"Windows {release} {platform.win32_edition()} {machine}" 110 | 111 | if system == "Linux": 112 | if (pfx := os.getenv("PREFIX")) and "termux" in pfx: 113 | system = f"Termux (Android) {release}" # a strange platform 114 | 115 | elif os.getenv("ANDROID_ROOT") == "/system": 116 | system = f"Linux (Android) {release}" 117 | 118 | elif ver := get_linux_name_version(): 119 | name, version_id = ver 120 | version = release if version_id.lower() == "rolling" else version_id 121 | system = f"{name} {version}" 122 | 123 | else: 124 | system = f"未知 Linux {release}" 125 | 126 | return f"{system} {machine}" 127 | 128 | return f"{system} {release}" 129 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/collectors/network.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import time 3 | from dataclasses import dataclass 4 | from typing import Optional, Union 5 | 6 | import psutil 7 | from httpx import AsyncClient, ReadTimeout 8 | from psutil._common import snetio 9 | 10 | from ..config import TestSiteCfg, config 11 | from ..util import match_list_regexp 12 | from . import TimeBasedCounterCollector, collector, normal_collector 13 | 14 | 15 | @dataclass 16 | class NetworkIO: 17 | name: str 18 | sent: float 19 | recv: float 20 | 21 | 22 | @dataclass 23 | class NetworkConnectionOK: 24 | name: str 25 | status: int 26 | reason: str 27 | delay: float 28 | 29 | 30 | @dataclass 31 | class NetworkConnectionError: 32 | name: str 33 | error: str 34 | 35 | 36 | NetworkConnectionType = Union[NetworkConnectionOK, NetworkConnectionError] 37 | 38 | 39 | @collector("network_io") 40 | class NetworkIOCollector(TimeBasedCounterCollector[dict[str, snetio], list[NetworkIO]]): 41 | async def _calc( 42 | self, 43 | past: dict[str, snetio], 44 | now: dict[str, snetio], 45 | time_passed: float, 46 | ) -> list[NetworkIO]: 47 | def calc_one(name: str, past_it: snetio, now_it: snetio) -> Optional[NetworkIO]: 48 | if match_list_regexp(config.ps_ignore_nets, name): 49 | # logger.info(f"网卡IO统计 {name} 匹配 {regex.re.pattern},忽略") 50 | return None 51 | 52 | sent = (now_it.bytes_sent - past_it.bytes_sent) / time_passed 53 | recv = (now_it.bytes_recv - past_it.bytes_recv) / time_passed 54 | 55 | if sent == 0 and recv == 0 and config.ps_ignore_0b_net: 56 | # logger.info(f"网卡IO统计 忽略无IO网卡 {name}") 57 | return None 58 | 59 | return NetworkIO(name=name, sent=sent, recv=recv) 60 | 61 | res = [calc_one(name, past[name], now[name]) for name in past if name in now] 62 | res = [x for x in res if x] 63 | if config.ps_sort_nets: 64 | res.sort(key=lambda x: x.sent + x.recv, reverse=True) 65 | return res 66 | 67 | async def _get_obj(self) -> dict[str, snetio]: 68 | return psutil.net_io_counters(pernic=True) 69 | 70 | 71 | @normal_collector() 72 | async def network_connection() -> list[NetworkConnectionType]: 73 | def format_conn_error(error: Exception) -> str: 74 | if isinstance(error, ReadTimeout): 75 | return "超时" 76 | return error.__class__.__name__ 77 | 78 | async def test_one(site: TestSiteCfg) -> NetworkConnectionType: 79 | try: 80 | async with AsyncClient( 81 | timeout=config.ps_test_timeout, 82 | proxy=config.proxy if site.use_proxy else None, 83 | follow_redirects=True, 84 | ) as client: 85 | start = time.time() 86 | resp = await client.get(str(site.url)) 87 | delay = (time.time() - start) * 1000 88 | 89 | except Exception as e: 90 | return NetworkConnectionError(name=site.name, error=format_conn_error(e)) 91 | 92 | return NetworkConnectionOK( 93 | name=site.name, 94 | status=resp.status_code, 95 | reason=resp.reason_phrase, 96 | delay=delay, 97 | ) 98 | 99 | res = await asyncio.gather(*map(test_one, config.ps_test_sites)) 100 | if config.ps_sort_sites: 101 | res.sort(key=lambda x: x.delay if isinstance(x, NetworkConnectionOK) else -1) 102 | 103 | return res 104 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/collectors/process.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from dataclasses import dataclass 3 | from typing import Optional, Union, cast 4 | 5 | import psutil 6 | 7 | from ..config import config 8 | from ..util import match_list_regexp 9 | from . import periodic_collector 10 | 11 | 12 | @dataclass 13 | class ProcessStatus: 14 | name: str 15 | cpu: float 16 | mem: int 17 | 18 | 19 | @periodic_collector() 20 | async def process_status() -> list[ProcessStatus]: 21 | if not config.ps_proc_len: 22 | return [] 23 | 24 | async def parse_one(proc: psutil.Process) -> Optional[ProcessStatus]: 25 | name = proc.name() 26 | if match_list_regexp(config.ps_ignore_procs, name): 27 | # logger.info(f"进程 {name} 匹配 {regex.re.pattern},忽略") 28 | return None 29 | 30 | # proc.cpu_percent() 31 | # await asyncio.sleep(1) 32 | with proc.oneshot(): 33 | cpu = proc.cpu_percent() 34 | cpu = cpu / psutil.cpu_count() if config.ps_proc_cpu_max_100p else cpu 35 | mem: int = proc.memory_info().rss 36 | 37 | return ProcessStatus(name=name, cpu=cpu, mem=mem) 38 | 39 | def sorter(x: ProcessStatus): 40 | sort_by = config.ps_proc_sort_by 41 | if sort_by == "mem": 42 | return x.mem 43 | # if sort_by == "cpu": 44 | return x.cpu 45 | 46 | proc_list = cast( 47 | "list[Union[Optional[ProcessStatus], Exception]]", 48 | await asyncio.gather( 49 | *(parse_one(proc) for proc in psutil.process_iter()), 50 | return_exceptions=True, 51 | ), 52 | ) 53 | proc_list = [x for x in proc_list if x and (not isinstance(x, Exception))] 54 | proc_list.sort(key=sorter, reverse=True) 55 | return proc_list[: config.ps_proc_len] 56 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/config.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from typing import Literal, Optional, Union 3 | 4 | from nonebot import get_plugin_config 5 | from nonebot.compat import type_validate_python 6 | from pydantic import AnyHttpUrl, BaseModel, Field 7 | 8 | RES_PATH = Path(__file__).parent / "res" 9 | ASSETS_PATH = RES_PATH / "assets" 10 | TEMPLATE_PATH = RES_PATH / "templates" 11 | DEFAULT_BG_PATH = ASSETS_PATH / "default_bg.webp" 12 | DEFAULT_AVATAR_PATH = ASSETS_PATH / "default_avatar.webp" 13 | 14 | ProcSortByType = Literal["cpu", "mem"] 15 | 16 | 17 | class TestSiteCfg(BaseModel): 18 | name: str 19 | url: AnyHttpUrl 20 | use_proxy: bool = False 21 | 22 | 23 | class ConfigModel(BaseModel): 24 | # region builtin 25 | superusers: set[str] 26 | nickname: set[str] 27 | # endregion 28 | 29 | # region global 30 | proxy: Optional[str] = None 31 | # endregion 32 | 33 | # region behavior 34 | ps_template: str = "default" 35 | ps_command: list[str] = ["运行状态", "状态", "zt", "yxzt", "status"] 36 | ps_only_su: bool = False 37 | ps_need_at: bool = False 38 | ps_reply_target: bool = True 39 | ps_req_timeout: Optional[int] = 10 40 | # endregion 41 | 42 | # region style 43 | ps_bg_provider: str = "loli" 44 | ps_bg_preload_count: int = 1 45 | ps_bg_lolicon_r18_type: Literal[0, 1, 2] = 0 46 | ps_bg_local_path: Path = DEFAULT_BG_PATH 47 | ps_default_avatar: Path = DEFAULT_AVATAR_PATH 48 | # endregion 49 | 50 | # region collectors 51 | # region base 52 | ps_collect_interval: int = 2 53 | ps_default_collect_cache_size: int = 1 54 | ps_collect_cache_size: dict[str, int] = Field(default_factory=dict) 55 | # endregion 56 | 57 | # region header 58 | ps_use_env_nick: bool = False 59 | ps_show_current_bot_only: bool = False 60 | ps_ob_v11_use_get_status: bool = True 61 | ps_count_message_sent_event: Union[bool, set[str]] = False 62 | ps_disconnect_reset_counter: bool = True 63 | # endregion 64 | 65 | # region disk 66 | # usage 67 | ps_ignore_parts: list[str] = [] 68 | ps_ignore_bad_parts: bool = False 69 | ps_sort_parts: bool = True 70 | ps_sort_parts_reverse: bool = False 71 | # io 72 | ps_ignore_disk_ios: list[str] = [] 73 | ps_ignore_no_io_disk: bool = False 74 | ps_sort_disk_ios: bool = True 75 | # endregion 76 | 77 | # region network 78 | # io 79 | ps_ignore_nets: list[str] = [r"^lo(op)?\d*$", "^Loopback"] 80 | ps_ignore_0b_net: bool = False 81 | ps_sort_nets: bool = True 82 | # connection_test 83 | ps_test_sites: list[TestSiteCfg] = [ 84 | type_validate_python( 85 | TestSiteCfg, 86 | {"name": "百度", "url": "https://www.baidu.com/"}, 87 | ), 88 | type_validate_python( 89 | TestSiteCfg, 90 | {"name": "Google", "url": "https://www.google.com/", "use_proxy": True}, 91 | ), 92 | ] 93 | ps_sort_sites: bool = True 94 | ps_test_timeout: int = 5 95 | # endregion 96 | 97 | # region process 98 | ps_proc_len: int = 5 99 | ps_ignore_procs: list[str] = ["^System Idle Process$"] 100 | ps_proc_sort_by: ProcSortByType = "cpu" 101 | ps_proc_cpu_max_100p: bool = False 102 | # endregion 103 | # endregion components 104 | 105 | 106 | config: ConfigModel = get_plugin_config(ConfigModel) 107 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/debug.py: -------------------------------------------------------------------------------- 1 | import json 2 | import time 3 | from pathlib import Path 4 | from typing import Any 5 | 6 | ROOT_DEBUG_DIR = Path.cwd() / "debug" 7 | DEBUG_DIR = ROOT_DEBUG_DIR / "picstatus" 8 | 9 | 10 | def is_debug_mode(): 11 | return ROOT_DEBUG_DIR.exists() 12 | 13 | 14 | def write_debug_file(filename: str, content: Any): 15 | if not DEBUG_DIR.exists(): 16 | DEBUG_DIR.mkdir(parents=True) 17 | filename = filename.format(time=round(time.time() * 1000)) 18 | path = DEBUG_DIR / filename 19 | if isinstance(content, (bytes, bytearray)): 20 | path.write_bytes(content) 21 | return 22 | path.write_text( 23 | ( 24 | content 25 | if isinstance(content, str) 26 | else json.dumps(content, ensure_ascii=False) 27 | ), 28 | "u8", 29 | ) 30 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/misc_statistics.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from typing import Any, Callable, Optional, Union 3 | 4 | from nonebot import get_driver, logger 5 | from nonebot.adapters import Bot as BaseBot, Event as BaseEvent 6 | from nonebot.message import event_preprocessor 7 | from nonebot_plugin_userinfo import UserInfo, get_user_info 8 | 9 | from .config import config 10 | 11 | nonebot_run_time: datetime = datetime.now().astimezone() 12 | bot_connect_time: dict[str, datetime] = {} 13 | recv_num: dict[str, int] = {} 14 | send_num: dict[str, int] = {} 15 | 16 | bot_info_cache: dict[str, UserInfo] = {} 17 | bot_avatar_cache: dict[str, bytes] = {} 18 | 19 | driver = get_driver() 20 | 21 | SEND_APIS: dict[str, Union[list[str], Callable[[str], bool]]] = { 22 | # "BilibiliLive": [], # 狗东西发消息不走 call_api 23 | "Console": ["send_msg"], 24 | "Ding": ["send"], 25 | "Discord": ["create_message"], 26 | "Feishu": ["im/v1/messages"], 27 | "Kaiheila": ["message_create", "directMessage_create"], 28 | "Minecraft": ["send_msg"], 29 | "mirai2": ["send_friend_message", "send_group_message", "send_temp_message"], 30 | "ntchat": lambda x: x.startswith("send_"), 31 | "OneBot V11": ["send_private_msg", "send_group_msg", "send_msg"], 32 | "OneBot V12": ["send_message"], 33 | "QQ": [ 34 | "post_dms_messages", 35 | "post_messages", 36 | "post_c2c_messages", 37 | "post_c2c_files", 38 | "post_group_messages", 39 | "post_group_files", 40 | ], 41 | "RedProtocol": ["send_message", "send_fake_forward"], 42 | "Satori": ["message_create"], 43 | "Telegram": lambda x: x.startswith("send_"), 44 | "大别野": ["send_message"], 45 | } 46 | 47 | 48 | def method_is_send_msg(platform: str, name: str) -> bool: 49 | return (platform in SEND_APIS) and ( 50 | (name in it) if isinstance((it := SEND_APIS[platform]), list) else it(name) 51 | ) 52 | 53 | 54 | if config.ps_count_message_sent_event: 55 | 56 | @event_preprocessor 57 | async def _(bot: BaseBot, event: BaseEvent): 58 | if ( 59 | config.ps_count_message_sent_event 60 | and ( 61 | (config.ps_count_message_sent_event is True) 62 | or bot.adapter.get_name() in config.ps_count_message_sent_event 63 | ) 64 | and ( 65 | (event.get_type() == "message_sent") 66 | or ( 67 | event.get_type() == "message" and event.get_user_id() == bot.self_id 68 | ) 69 | ) 70 | ): 71 | # logger.debug(f"Bot {bot.self_id} sent counter +1") 72 | send_num[bot.self_id] += 1 73 | 74 | 75 | if config.ps_count_message_sent_event is not True: 76 | 77 | @BaseBot.on_called_api 78 | async def called_api( 79 | bot: BaseBot, 80 | exc: Optional[Exception], 81 | api: str, 82 | _: dict[str, Any], 83 | __: Any, 84 | ): 85 | if ( 86 | (not exc) 87 | and (config.ps_count_message_sent_event is not True) 88 | and ( 89 | (config.ps_count_message_sent_event is False) 90 | or (bot.adapter.get_name() not in config.ps_count_message_sent_event) 91 | ) 92 | and method_is_send_msg(bot.adapter.get_name(), api) 93 | ): 94 | # logger.debug(f"Bot {bot.self_id} sent counter +1") 95 | send_num[bot.self_id] += 1 96 | 97 | 98 | @driver.on_bot_connect 99 | async def _(bot: BaseBot): 100 | bot_connect_time[bot.self_id] = datetime.now().astimezone() 101 | if bot.self_id not in recv_num: 102 | recv_num[bot.self_id] = 0 103 | if (bot.self_id not in send_num) and (bot.adapter.get_name() in SEND_APIS): 104 | send_num[bot.self_id] = 0 105 | 106 | 107 | @driver.on_bot_disconnect 108 | async def _(bot: BaseBot): 109 | bot_connect_time.pop(bot.self_id, None) 110 | if config.ps_disconnect_reset_counter: 111 | recv_num.pop(bot.self_id, None) 112 | send_num.pop(bot.self_id, None) 113 | 114 | 115 | @event_preprocessor 116 | async def _(bot: BaseBot, event: BaseEvent): 117 | if event.get_type() == "message": 118 | recv_num[bot.self_id] += 1 119 | 120 | 121 | async def cache_bot_info(bot: BaseBot, event: BaseEvent): 122 | try: 123 | info = await get_user_info(bot, event, bot.self_id) 124 | except ValueError as e: 125 | logger.debug(e) 126 | except Exception as e: 127 | logger.warning(f"Error when getting bot info: {e.__class__.__name__}: {e}") 128 | else: 129 | if info: 130 | bot_info_cache[bot.self_id] = info 131 | 132 | 133 | @event_preprocessor 134 | async def _(bot: BaseBot, event: BaseEvent): 135 | if bot.self_id in bot_info_cache: 136 | return 137 | await cache_bot_info(bot, event) 138 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/res/assets/default_avatar.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lgc-NB2Dev/nonebot-plugin-picstatus/e1706f6c29bf8980e7ea1b9bfa763b8087fcb2f3/nonebot_plugin_picstatus/res/assets/default_avatar.webp -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/res/assets/default_bg.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lgc-NB2Dev/nonebot-plugin-picstatus/e1706f6c29bf8980e7ea1b9bfa763b8087fcb2f3/nonebot_plugin_picstatus/res/assets/default_bg.webp -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/res/js/globalThis.d.ts: -------------------------------------------------------------------------------- 1 | declare module globalThis { 2 | var plugins: (() => Promise)[]; 3 | } 4 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/res/js/init-global.js: -------------------------------------------------------------------------------- 1 | globalThis.plugins = []; 2 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/res/js/lazy-load.js: -------------------------------------------------------------------------------- 1 | /// 2 | (() => { 3 | /** 4 | * @template {any[]} A 5 | * @template R 6 | * @param {(...args: A) => Promise} func 7 | * @returns {(...args: A) => Promise} 8 | */ 9 | function wrapErr(func) { 10 | return async (...args) => { 11 | try { 12 | return await func(...args); 13 | } catch (e) { 14 | console.error(e); 15 | } 16 | }; 17 | } 18 | 19 | /** 20 | * @param {string} url 21 | */ 22 | async function makeObjUrlFromUrl(url) { 23 | const res = await fetch(url); 24 | const blob = await res.blob(); 25 | return URL.createObjectURL(blob); 26 | } 27 | 28 | /** @typedef {(elem: HTMLElement, objUrl: string) => Promise} PropSetterType */ 29 | 30 | /** @type {Record} */ 31 | const propSetterMap = { 32 | 'data-background-image': async (elem, objUrl) => { 33 | elem.style.backgroundImage = `url(${objUrl})`; 34 | }, 35 | 'data-src': async (elem, objUrl) => { 36 | if (elem instanceof HTMLImageElement) elem.src = objUrl; 37 | }, 38 | }; 39 | 40 | /** 41 | * @param {HTMLElement} elem 42 | * @param {string} attr 43 | * @param {PropSetterType} setter 44 | */ 45 | async function lazyLoadOne(elem, attr, setter) { 46 | const url = elem.getAttribute(attr); 47 | if (!url) return; 48 | const objUrl = await makeObjUrlFromUrl(url); 49 | await setter(elem, objUrl); 50 | elem.removeAttribute(attr); 51 | } 52 | 53 | /** 54 | * @param {string} attr 55 | */ 56 | async function lazyLoad(attr) { 57 | /** @type {HTMLElement[]} */ 58 | // @ts-ignore 59 | const elements = [...document.body.querySelectorAll(`[${attr}]`)].filter( 60 | (v) => v instanceof HTMLElement 61 | ); 62 | const tasks = elements.map((v) => 63 | wrapErr(lazyLoadOne)(v, attr, propSetterMap[attr]) 64 | ); 65 | await Promise.all(tasks); 66 | } 67 | 68 | async function lazyLoadAll() { 69 | await Promise.all(Object.keys(propSetterMap).map(lazyLoad)); 70 | } 71 | 72 | // 使用 globalThis.plugins.push 注册插件 73 | // 只有这样才能保证你的插件运行完成之后才会网页截图 74 | globalThis.plugins.push(async () => { 75 | await lazyLoadAll(); 76 | }); 77 | })(); 78 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/res/js/load-plugin.js: -------------------------------------------------------------------------------- 1 | (async () => { 2 | for (const plugin of globalThis.plugins) { 3 | try { 4 | await plugin(); 5 | } catch (e) { 6 | console.error(e); 7 | } 8 | } 9 | document.body.classList.add('done'); 10 | })(); 11 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/templates/__init__.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | from collections.abc import Awaitable 3 | from dataclasses import dataclass 4 | from pathlib import Path 5 | from typing import TYPE_CHECKING, Any, Optional, TypedDict 6 | from typing_extensions import Protocol, Unpack 7 | 8 | from nonebot import logger 9 | 10 | from ..config import config 11 | 12 | if TYPE_CHECKING: 13 | from ..bg_provider import BgData 14 | 15 | 16 | class TemplateRendererKwargs(TypedDict): 17 | collected: dict[str, Any] 18 | bg: "BgData" 19 | 20 | 21 | class TemplateRenderer(Protocol): 22 | __name__: str 23 | 24 | def __call__( 25 | self, 26 | **kwargs: Unpack[TemplateRendererKwargs], 27 | ) -> Awaitable[bytes]: ... 28 | 29 | 30 | @dataclass() 31 | class TemplateInfo: 32 | renderer: TemplateRenderer 33 | collectors: Optional[set[str]] = None 34 | 35 | 36 | loaded_templates: dict[str, TemplateInfo] = {} 37 | 38 | 39 | def pic_template( 40 | name: Optional[str] = None, 41 | collecting: Optional[set[str]] = None, 42 | ): 43 | def deco(func: TemplateRenderer): 44 | template_name = name or func.__name__ 45 | if template_name in loaded_templates: 46 | raise ValueError(f"Template {template_name} already exists") 47 | loaded_templates[template_name] = TemplateInfo( 48 | renderer=func, 49 | collectors=collecting, 50 | ) 51 | logger.debug(f"Registered template {template_name}") 52 | 53 | return deco 54 | 55 | 56 | def load_builtin_templates(): 57 | for module in Path(__file__).parent.iterdir(): 58 | name = module.name 59 | if (not module.is_dir()) or name.startswith("_"): 60 | continue 61 | assert importlib.import_module(f".{name}", __package__) 62 | 63 | 64 | async def render_current_template(**kwargs: Unpack[TemplateRendererKwargs]): 65 | return await loaded_templates[config.ps_template].renderer(**kwargs) 66 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/templates/default/__init__.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | from pathlib import Path 3 | from typing import TYPE_CHECKING, Any, Literal 4 | 5 | import jinja2 6 | from cookit import flatten 7 | from cookit.pyd import field_validator 8 | from nonebot import get_plugin_config, require 9 | from pydantic import BaseModel 10 | 11 | from ...debug import is_debug_mode, write_debug_file 12 | from .. import pic_template 13 | from ..pw_render import ( 14 | ROUTE_URL, 15 | add_background_router, 16 | add_root_router, 17 | base_router_group, 18 | make_file_router, 19 | register_global_filter_to, 20 | resolve_file_url, 21 | ) 22 | 23 | require("nonebot_plugin_htmlrender") 24 | 25 | from nonebot_plugin_htmlrender import get_new_page # noqa: E402 26 | 27 | if TYPE_CHECKING: 28 | from ...bg_provider import BgData 29 | 30 | RES_PATH = Path(__file__).parent / "res" 31 | TEMPLATE_PATH = RES_PATH / "templates" 32 | CSS_PATH = RES_PATH / "css" 33 | ENVIRONMENT = jinja2.Environment( 34 | loader=jinja2.FileSystemLoader(str(TEMPLATE_PATH)), 35 | autoescape=jinja2.select_autoescape(["html", "xml"]), 36 | enable_async=True, 37 | ) 38 | register_global_filter_to(ENVIRONMENT) 39 | 40 | template_router_group = base_router_group.copy() 41 | template_router_group.router(f"{ROUTE_URL}/default/res/**/*", priority=99)( 42 | make_file_router(query_name=None, base_path=RES_PATH, prefix_omit="default/res/"), 43 | ) 44 | 45 | COMPONENT_COLLECTORS = { 46 | "header": {"bots", "nonebot_run_time", "system_run_time"}, 47 | "cpu_mem": { 48 | "cpu_percent", 49 | "cpu_count", 50 | "cpu_count_logical", 51 | "cpu_freq", 52 | "cpu_brand", 53 | "memory_stat", 54 | "swap_stat", 55 | }, 56 | "disk": {"disk_usage", "disk_io"}, 57 | "network": {"network_io", "network_connection"}, 58 | "process": {"process_status"}, 59 | "footer": { 60 | "nonebot_version", 61 | "ps_version", 62 | "time", 63 | "python_version", 64 | "system_name", 65 | }, 66 | } 67 | 68 | 69 | class TemplateConfig(BaseModel): 70 | ps_default_components: list[str] = [ 71 | "header", 72 | "cpu_mem", 73 | "disk", 74 | "network", 75 | "process", 76 | "footer", 77 | ] 78 | ps_default_additional_css: list[str] = [] 79 | ps_default_additional_script: list[str] = [] 80 | ps_default_pic_format: Literal["jpeg", "png"] = "jpeg" 81 | 82 | @field_validator("ps_default_additional_css") 83 | def resolve_css_url(cls, v: list[str]): # noqa: N805 84 | return [resolve_file_url(x, {"default/res/css": CSS_PATH}) for x in v] 85 | 86 | @field_validator("ps_default_additional_script") 87 | def resolve_script_url(cls, v: list[str]): # noqa: N805 88 | return [resolve_file_url(x) for x in v] 89 | 90 | 91 | template_config = get_plugin_config(TemplateConfig) 92 | collecting = set( 93 | flatten(COMPONENT_COLLECTORS[k] for k in template_config.ps_default_components), 94 | ) 95 | 96 | 97 | @pic_template(collecting=collecting) 98 | async def default(collected: dict[str, Any], bg: "BgData", **_) -> bytes: 99 | collected = {k: v[0] if isinstance(v, deque) else v for k, v in collected.items()} 100 | template = ENVIRONMENT.get_template("index.html.jinja") 101 | html = await template.render_async(d=collected, config=template_config) 102 | 103 | if is_debug_mode(): 104 | write_debug_file("default_{time}.html", html) 105 | 106 | router_group = template_router_group.copy() 107 | add_root_router(router_group, html) 108 | add_background_router(router_group, bg) 109 | 110 | async with get_new_page() as page: 111 | await router_group.apply(page) 112 | await page.goto(f"{ROUTE_URL}/") 113 | await page.wait_for_selector("body.done") 114 | elem = await page.query_selector(".main-background") 115 | assert elem 116 | return await elem.screenshot(type="jpeg") 117 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/templates/default/res/css/index.css: -------------------------------------------------------------------------------- 1 | /* Base */ 2 | 3 | * { 4 | --font-family: 'HarmonyOS Sans SC', 'Source Han Sans SC', 'Source Han Sans', 5 | sans-serif; 6 | --monospace-font-family: 'JetBrains Mono', 'Cascadia Code', 'Consolas', 7 | monospace; 8 | 9 | --primary-text-color: #3a3a3a; 10 | --secondary-text-color: #6a6a6a; 11 | 12 | --default-background: #3a3a3aaa; 13 | --background-mask-color: #fafafa66; 14 | --card-background-color: #fafafaaa; 15 | --default-box-shadow: 2px 2px 6px #6a6a6a66; 16 | 17 | --label-red-text-color: var(--secondary-text-color); 18 | --label-orange-text-color: var(--secondary-text-color); 19 | --label-yellow-text-color: var(--secondary-text-color); 20 | --label-green-text-color: var(--secondary-text-color); 21 | --label-cyan-text-color: var(--secondary-text-color); 22 | --label-blue-text-color: var(--secondary-text-color); 23 | --label-purple-text-color: var(--secondary-text-color); 24 | --label-gray-text-color: var(--secondary-text-color); 25 | --label-black-text-color: #fafafa; 26 | 27 | --background-color: var(--card-background-color); 28 | --label-red-bg-color: #e05661aa; 29 | --label-orange-bg-color: #ee9025aa; 30 | --label-yellow-bg-color: #eea825aa; 31 | --label-green-bg-color: #1da912aa; 32 | --label-cyan-bg-color: #56b6c2aa; 33 | --label-blue-bg-color: #118dc3aa; 34 | --label-purple-bg-color: #9a77cfaa; 35 | --label-gray-bg-color: #bebebeaa; 36 | --label-black-bg-color: #3a3a3aaa; 37 | 38 | --prog-low-text-color: var(--secondary-text-color); 39 | --prog-medium-text-color: var(--secondary-text-color); 40 | --prog-high-text-color: var(--secondary-text-color); 41 | --prog-low-bg-color: var(--label-green-bg-color); 42 | --prog-medium-bg-color: var(--label-orange-bg-color); 43 | --prog-high-bg-color: var(--label-red-bg-color); 44 | 45 | --segment-color: #bebebe; 46 | } 47 | 48 | .red { 49 | --background-color: var(--label-red-bg-color); 50 | --secondary-text-color: var(--label-red-text-color); 51 | } 52 | 53 | .orange { 54 | --background-color: var(--label-orange-bg-color); 55 | --secondary-text-color: var(--label-orange-text-color); 56 | } 57 | 58 | .yellow { 59 | --background-color: var(--label-yellow-bg-color); 60 | --secondary-text-color: var(--label-yellow-text-color); 61 | } 62 | 63 | .green { 64 | --background-color: var(--label-green-bg-color); 65 | --secondary-text-color: var(--label-green-text-color); 66 | } 67 | 68 | .cyan { 69 | --background-color: var(--label-cyan-bg-color); 70 | --secondary-text-color: var(--label-cyan-text-color); 71 | } 72 | 73 | .blue { 74 | --background-color: var(--label-blue-bg-color); 75 | --secondary-text-color: var(--label-blue-text-color); 76 | } 77 | 78 | .purple { 79 | --background-color: var(--label-purple-bg-color); 80 | --secondary-text-color: var(--label-purple-text-color); 81 | } 82 | 83 | .gray { 84 | --background-color: var(--label-gray-bg-color); 85 | --secondary-text-color: var(--label-gray-text-color); 86 | } 87 | 88 | .black { 89 | --background-color: var(--label-black-bg-color); 90 | --secondary-text-color: var(--label-black-text-color); 91 | } 92 | 93 | .prog-low { 94 | --background-color: var(--prog-low-bg-color); 95 | --secondary-text-color: var(--prog-low-text-color); 96 | } 97 | 98 | .prog-medium { 99 | --background-color: var(--prog-medium-bg-color); 100 | --secondary-text-color: var(--prog-medium-text-color); 101 | } 102 | 103 | .prog-high { 104 | --background-color: var(--prog-high-bg-color); 105 | --secondary-text-color: var(--prog-high-text-color); 106 | } 107 | 108 | .monospace { 109 | font-family: var(--monospace-font-family); 110 | } 111 | 112 | body { 113 | font-family: var(--font-family); 114 | color: var(--primary-text-color); 115 | font-size: 20px; 116 | } 117 | 118 | .main-background { 119 | width: 650px; 120 | background: var(--default-background); 121 | background-repeat: no-repeat; 122 | background-position: center; 123 | background-size: cover; 124 | } 125 | 126 | .main-background-mask { 127 | padding: 16px; 128 | background-color: var(--background-mask-color); 129 | } 130 | 131 | .main { 132 | display: grid; 133 | grid-template-columns: 1fr; 134 | gap: 16px; 135 | } 136 | 137 | .card { 138 | border-radius: 8px; 139 | padding: 16px; 140 | background: var(--card-background-color); 141 | box-shadow: var(--default-box-shadow); 142 | backdrop-filter: blur(2px); 143 | overflow: hidden; 144 | } 145 | 146 | .splitter > *:not(:first-child) { 147 | margin-top: 8px; 148 | padding-top: 8px; 149 | border-top: 2px solid var(--segment-color); 150 | } 151 | 152 | .align-right { 153 | text-align: right; 154 | } 155 | 156 | /* Span Label */ 157 | 158 | span.label { 159 | padding: 2px 4px; 160 | border-radius: 4px; 161 | box-shadow: var(--default-box-shadow); 162 | background-color: var(--background-color); 163 | color: var(--secondary-text-color); 164 | } 165 | 166 | .label-container { 167 | display: flex; 168 | flex-direction: row; 169 | flex-wrap: wrap; 170 | align-items: center; 171 | } 172 | 173 | .label-container > * { 174 | margin-right: 2px; 175 | margin-bottom: 2px; 176 | } 177 | 178 | /* Account */ 179 | 180 | .account { 181 | display: flex; 182 | flex-direction: row; 183 | } 184 | 185 | .account .avatar { 186 | width: 125px; 187 | height: 125px; 188 | border-radius: 50%; 189 | box-shadow: var(--default-box-shadow); 190 | background-color: var(--card-background-color); 191 | } 192 | 193 | .account .description { 194 | margin-left: 16px; 195 | display: flex; 196 | flex-direction: column; 197 | justify-content: center; 198 | } 199 | 200 | .account .description .nickname { 201 | font-size: 36px; 202 | font-weight: bold; 203 | margin-bottom: 8px; 204 | word-break: break-word; 205 | line-height: 1.1; 206 | } 207 | 208 | /* Donut Chart */ 209 | 210 | .donut-chart { 211 | display: flex; 212 | flex-direction: column; 213 | justify-content: center; 214 | align-items: center; 215 | text-align: center; 216 | } 217 | 218 | .donut-chart .chart-wrapper, 219 | .donut-chart .chart, 220 | .donut-chart .shadow { 221 | width: 150px; 222 | height: 150px; 223 | } 224 | 225 | .donut-chart .chart-wrapper { 226 | position: relative; 227 | } 228 | 229 | .donut-chart .chart { 230 | transform: rotate(-90deg); 231 | } 232 | 233 | .donut-chart .chart .empty, 234 | .donut-chart .chart .slice { 235 | fill: transparent; 236 | stroke-width: 15px; 237 | } 238 | 239 | .donut-chart .chart .empty { 240 | stroke: var(--label-gray-bg-color); 241 | } 242 | 243 | .donut-chart .chart .slice { 244 | stroke: var(--background-color); 245 | stroke-dasharray: calc((67.5px * 2) * 3.1415926); 246 | stroke-dashoffset: calc( 247 | (67.5px * 2) * 3.1415926 / 360 * (360 - (360 * var(--percent))) 248 | ); 249 | } 250 | 251 | .donut-chart .shadow { 252 | position: absolute; 253 | top: 0; 254 | border-radius: 50%; 255 | box-shadow: var(--default-box-shadow); 256 | } 257 | 258 | .donut-chart .label { 259 | position: absolute; 260 | left: 50%; 261 | top: 50%; 262 | transform: translate(-50%, -50%); 263 | } 264 | 265 | .donut-chart .label, 266 | .donut-chart .title { 267 | font-size: 32px; 268 | font-weight: bold; 269 | text-wrap: nowrap; 270 | white-space: nowrap; 271 | } 272 | 273 | .donut-chart .desc { 274 | font-size: 12px; 275 | word-break: break-word; 276 | color: var(--secondary-text-color); 277 | } 278 | 279 | /* Progress Bar */ 280 | 281 | .progress-bar { 282 | position: relative; 283 | border-radius: 4px; 284 | overflow: hidden; 285 | box-shadow: var(--default-box-shadow); 286 | } 287 | 288 | .progress-bar .background { 289 | position: absolute; 290 | width: 100%; 291 | height: 100%; 292 | background-color: var(--label-gray-bg-color); 293 | } 294 | 295 | .progress-bar .progress { 296 | position: absolute; 297 | height: 100%; 298 | background-color: var(--background-color); 299 | } 300 | 301 | .progress-bar .label { 302 | position: relative; 303 | z-index: 1; 304 | text-align: center; 305 | } 306 | 307 | /* #### Split Line #### */ 308 | 309 | /* Card Header */ 310 | 311 | .card.header { 312 | display: flex; 313 | flex-direction: column; 314 | } 315 | 316 | .card.header .label-container { 317 | font-size: 16px; 318 | } 319 | 320 | .card.header .extra > * { 321 | flex-grow: 1; 322 | text-align: center; 323 | } 324 | 325 | /* Donut Chart Line */ 326 | 327 | .donut-chart-line { 328 | display: grid; 329 | gap: 8px; 330 | grid-template-columns: repeat(3, 1fr); 331 | align-items: start; 332 | } 333 | 334 | /* List Grid */ 335 | 336 | .list-grid { 337 | display: grid; 338 | gap: 4px; 339 | align-items: center; 340 | } 341 | 342 | .list-grid.disk-usage { 343 | grid-template-columns: auto minmax(180px, 100%) auto; 344 | } 345 | 346 | .list-grid.disk-io, 347 | .list-grid.network-io, 348 | .list-grid.network-connection-test, 349 | .list-grid.process-usage { 350 | grid-template-columns: minmax(0, 100%) auto auto auto auto auto; 351 | } 352 | 353 | .list-grid.network-connection-test { 354 | grid-template-columns: minmax(0, 100%) auto auto auto; 355 | } 356 | 357 | .list-grid.network-connection-test .error { 358 | grid-column-end: span 3; 359 | text-align: right; 360 | } 361 | 362 | /* Footer */ 363 | 364 | .footer { 365 | font-size: 14px; 366 | text-align: center; 367 | color: var(--primary-text-color); 368 | text-shadow: var(--default-box-shadow); 369 | } 370 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/templates/default/res/css/no-blur.css: -------------------------------------------------------------------------------- 1 | .card { 2 | backdrop-filter: none; 3 | } 4 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/templates/default/res/css/no-radius.css: -------------------------------------------------------------------------------- 1 | .card, 2 | span.label, 3 | .progress-bar { 4 | border-radius: 0; 5 | } 6 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/templates/default/res/css/no-shadow.css: -------------------------------------------------------------------------------- 1 | * { 2 | --default-box-shadow: none; 3 | } 4 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/templates/default/res/css/theme-dark.css: -------------------------------------------------------------------------------- 1 | * { 2 | --primary-text-color: #d0d6e2; 3 | --secondary-text-color: #abb2bf; 4 | 5 | --default-background: #d0d6e2aa; 6 | --background-mask-color: #282c3455; 7 | --card-background-color: #282c3488; 8 | --default-box-shadow: 2px 2px 6px #abb2bf66; 9 | 10 | --label-default-bg-color: #282c34aa; 11 | --label-red-bg-color: #e06c75aa; 12 | --label-orange-bg-color: #d19a66aa; 13 | --label-yellow-bg-color: #e5c07baa; 14 | --label-green-bg-color: #98c379aa; 15 | --label-cyan-bg-color: #56b6c2aa; 16 | --label-blue-bg-color: #61afefaa; 17 | --label-purple-bg-color: #c678ddaa; 18 | --label-gray-bg-color: #737c8caa; 19 | --label-black-bg-color: #d0d6e2aa; 20 | --label-black-text-color: #282c34; 21 | 22 | --segment-color: #5c6370; 23 | } 24 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/templates/default/res/templates/index.html.jinja: -------------------------------------------------------------------------------- 1 | {% from 'macros.html.jinja' import header, cpu_mem, disk, network, process, footer %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {% for css in config.ps_default_additional_css -%} 11 | {% endfor %} 12 | 13 | 14 | 15 |
16 |
17 |
18 | {% for name in config.ps_default_components %} 19 | {% if name == "header" %} 20 | {{ header(d) }} 21 | {% elif name == "cpu_mem" %} 22 | {{ cpu_mem(d) }} 23 | {% elif name == "disk" %} 24 | {{ disk(d) }} 25 | {% elif name == "network" %} 26 | {{ network(d) }} 27 | {% elif name == "process" %} 28 | {{ process(d) }} 29 | {% elif name == "footer" %} 30 | {{ footer(d) }} 31 | {% endif %} 32 | {% endfor %} 33 |
34 |
35 |
36 | 37 | 38 | 39 | 40 | {% for script in config.ps_default_additional_script -%} 41 | {% endfor %} 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/templates/default/res/templates/macros.html.jinja: -------------------------------------------------------------------------------- 1 | {% macro donut_chart(percent, title, caption) %} 2 |
3 |
4 | 5 | 6 | {% if percent != None %} 7 | 8 | {% endif %} 9 | 10 |
11 |
12 | {%- if percent == None %}未部署 13 | {%- else %}{{ '{0:.0f}%'.format(percent) }}{% endif -%} 14 |
15 |
16 |
{{ title }}
17 |
{{ caption | br }}
18 |
19 | {% endmacro %} 20 | 21 | {% macro header(d) %} 22 |
23 | {% for info in d.bots %} 24 | 36 | {% endfor %} 37 |
38 | NoneBot运行 {{ d.nonebot_run_time }} 39 | 系统运行 {{ d.system_run_time }} 40 |
41 |
42 | {% endmacro %} 43 | 44 | {% macro cpu_mem(d) %} 45 | {% set freq = d.cpu_freq | format_cpu_freq %} 46 | {% set ram_used = d.memory_stat.used | auto_convert_unit %} 47 | {% set ram_total = d.memory_stat.total | auto_convert_unit %} 48 | {% set swap_used = d.swap_stat.used | auto_convert_unit %} 49 | {% set swap_total = d.swap_stat.total | auto_convert_unit %} 50 |
51 | {{ donut_chart(d.cpu_percent, "CPU", "{}核 {}线程 {}\n{}".format(d.cpu_count, d.cpu_count_logical, freq, d.cpu_brand)) }} 52 | {{ donut_chart(d.memory_stat.percent, "RAM", "{} / {}").format(ram_used, ram_total) }} 53 | {{ donut_chart(d.swap_stat.percent, "SWAP", "{} / {}").format(swap_used, swap_total) }} 54 |
55 | {% endmacro %} 56 | 57 | {% macro disk(d) %} 58 |
59 |
60 | {% for it in d.disk_usage %} 61 |
{{ it.name }}
62 |
63 |
64 | {% if it.exception %} 65 |
{{ it.exception }}
66 | {% else %} 67 |
68 |
{{ it.used | auto_convert_unit }} / {{ it.total | auto_convert_unit }}
69 | {% endif %} 70 |
71 |
72 | {%- if it.percent %}{{ '{0:.1f}%'.format(it.percent) }} 73 | {%- else %}??.?%{% endif -%} 74 |
75 | {% endfor %} 76 |
77 | 78 | {% if d.disk_io -%} 79 |
80 | {% for it in d.disk_io %} 81 |
{{ it.name }}
82 |
83 |
{{ it.read | auto_convert_unit(suffix='/s') }}
84 |
|
85 |
86 |
{{ it.write | auto_convert_unit(suffix='/s') }}
87 | {% endfor %} 88 |
89 | {%- endif %} 90 |
91 | {% endmacro %} 92 | 93 | {% macro network(d) %} 94 |
95 |
96 | {% for it in d.network_io %} 97 |
{{ it.name }}
98 |
99 |
{{ it.sent | auto_convert_unit(suffix='/s') }}
100 |
|
101 |
102 |
{{ it.recv | auto_convert_unit(suffix='/s') }}
103 | {% endfor %} 104 |
105 |
106 | {% for it in d.network_connection %} 107 |
{{ it.name }}
108 | {% if it.error %} 109 |
{{ it.error }}
110 | {% else %} 111 |
{{ it.status }} {{ it.reason }}
112 |
|
113 |
{{ '{0:.2f}ms'.format(it.delay) }}
114 | {% endif %} 115 | {% endfor %} 116 |
117 |
118 | {% endmacro %} 119 | 120 | {% macro process(d) %} 121 |
122 |
123 | {% for it in d.process_status %} 124 |
{{ it.name }}
125 |
CPU
126 |
{{ '{0:.1f}%'.format(it.cpu) }}
127 |
|
128 |
MEM
129 |
{{ it.mem | auto_convert_unit }}
130 | {% endfor %} 131 |
132 |
133 | {% endmacro %} 134 | 135 | {% macro footer(d) %} 136 | 140 | {% endmacro %} 141 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/templates/pw_render.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar 3 | from urllib.parse import urlencode 4 | 5 | from cookit import auto_convert_byte, make_append_obj_to_dict_deco 6 | from cookit.jinja import all_filters 7 | from cookit.pw import CKRouterFunc, RouterGroup, make_real_path_router 8 | from cookit.pw.loguru import log_router_err 9 | from nonebot import logger 10 | from yarl import URL 11 | 12 | from ..config import DEFAULT_AVATAR_PATH, config 13 | from ..misc_statistics import bot_avatar_cache, bot_info_cache 14 | from ..util import format_cpu_freq 15 | 16 | if TYPE_CHECKING: 17 | import jinja2 18 | from playwright.async_api import Request, Route 19 | 20 | from ..bg_provider import BgData 21 | 22 | TC = TypeVar("TC", bound=Callable[..., Any]) 23 | 24 | ROOT_PATH = Path(__file__).parent.parent 25 | RES_PATH = ROOT_PATH / "res" 26 | ROUTE_URL = "http://picstatus.nonebot" 27 | RES_LOCATION_MAP = { 28 | "": RES_PATH, 29 | } 30 | 31 | # region pw 32 | 33 | base_router_group = RouterGroup() 34 | 35 | 36 | def resolve_file_url( 37 | path: str, 38 | additional_locations: Optional[dict[str, Path]] = None, 39 | ) -> str: 40 | if path.startswith("res:"): 41 | path = path[4:].lstrip("/") 42 | locations = {**RES_LOCATION_MAP, **(additional_locations or {})} 43 | for pfx, loc in locations.items(): 44 | if (loc / path).exists(): 45 | return f"/{pfx}/{path}" 46 | raise ValueError(f"Cannot resolve builtin resource `{path}`") 47 | params = urlencode({"path": path}) 48 | return f"/api/local_file?{params}" 49 | 50 | 51 | def make_file_router( 52 | query_name: Optional[str] = None, 53 | base_path: Optional[Path] = None, 54 | prefix_omit: str = "", 55 | ) -> CKRouterFunc: 56 | @log_router_err() 57 | @make_real_path_router 58 | async def router(request: "Request", **_): 59 | url = URL(request.url) 60 | query_path = url.query.get(query_name, "") if query_name else url.path[1:] 61 | if prefix_omit and query_path.startswith(prefix_omit): 62 | query_path = query_path[len(prefix_omit) :] 63 | path = Path((base_path / query_path) if base_path else query_path) 64 | logger.debug(f"Associated file `{path}`") 65 | return path 66 | 67 | return router 68 | 69 | 70 | @base_router_group.router(f"{ROUTE_URL}/api/bot_avatar/*") 71 | @log_router_err() 72 | async def _(route: "Route", request: "Request", **_): 73 | url = URL(request.url) 74 | self_id = url.parts[-1] 75 | 76 | if self_id in bot_avatar_cache: 77 | await route.fulfill(body=bot_avatar_cache[self_id]) 78 | return 79 | 80 | if (self_id in bot_info_cache) and (avatar := bot_info_cache[self_id].user_avatar): 81 | try: 82 | img = await avatar.get_image() 83 | except Exception as e: 84 | logger.warning( 85 | f"Error when getting bot avatar, fallback to default: " 86 | f"{e.__class__.__name__}: {e}", 87 | ) 88 | else: 89 | bot_avatar_cache[self_id] = img 90 | await route.fulfill(body=img) 91 | return 92 | 93 | data = ( 94 | config.ps_default_avatar 95 | if config.ps_default_avatar.is_file() 96 | else DEFAULT_AVATAR_PATH 97 | ).read_bytes() 98 | await route.fulfill(body=data) 99 | 100 | 101 | base_router_group.router(f"{ROUTE_URL}/api/local_file*")( 102 | make_file_router(query_name="path", base_path=None), 103 | ) 104 | 105 | base_router_group.router(f"{ROUTE_URL}/**/*", priority=100)( 106 | make_file_router(query_name=None, base_path=RES_PATH), 107 | ) 108 | 109 | 110 | def add_root_router(router_group: RouterGroup, html: str): 111 | @router_group.router(f"{ROUTE_URL}/") 112 | @log_router_err() 113 | async def _(route: "Route", **_): 114 | await route.fulfill(content_type="text/html", body=html) 115 | 116 | 117 | def add_background_router(router_group: RouterGroup, bg: "BgData"): 118 | @router_group.router(f"{ROUTE_URL}/api/background") 119 | @log_router_err() 120 | async def _(route: "Route", **_): 121 | await route.fulfill(content_type=bg.mime, body=bg.data) 122 | 123 | 124 | # endregion 125 | 126 | # region jinja 127 | 128 | global_jinja_filters: dict[str, Callable] = all_filters.copy() 129 | 130 | 131 | jinja_filter = make_append_obj_to_dict_deco(global_jinja_filters) 132 | 133 | 134 | def register_global_filter_to(env: "jinja2.Environment"): 135 | env.filters.update(global_jinja_filters) 136 | 137 | 138 | jinja_filter(format_cpu_freq) 139 | 140 | 141 | @jinja_filter 142 | def percent_to_color(percent: float) -> str: 143 | if percent < 70: 144 | return "prog-low" 145 | if percent < 90: 146 | return "prog-medium" 147 | return "prog-high" 148 | 149 | 150 | @jinja_filter 151 | def auto_convert_unit(value: float, **kw) -> str: 152 | return auto_convert_byte(value=value, with_space=False, **kw) 153 | 154 | 155 | # endregion 156 | -------------------------------------------------------------------------------- /nonebot_plugin_picstatus/util.py: -------------------------------------------------------------------------------- 1 | import re 2 | from functools import partial 3 | from typing import TYPE_CHECKING, Optional 4 | 5 | from cookit import auto_convert_byte, format_timedelta 6 | 7 | if TYPE_CHECKING: 8 | from .collectors.cpu import CpuFreq 9 | 10 | format_time_delta_ps = partial(format_timedelta, day_divider=" ", day_suffix="天") 11 | 12 | 13 | def match_list_regexp(reg_list: list[str], txt: str) -> Optional[re.Match]: 14 | return next((match for r in reg_list if (match := re.search(r, txt))), None) 15 | 16 | 17 | def format_cpu_freq(freq: "CpuFreq") -> str: 18 | cu = partial(auto_convert_byte, suffix="Hz", unit_index=2, with_space=False) 19 | if not freq.current: 20 | return "主频未知" 21 | if not freq.max: 22 | return cu(value=freq.current) 23 | if freq.max == freq.current: 24 | return cu(value=freq.max) 25 | return f"{cu(value=freq.current)} / {cu(value=freq.max)}" 26 | -------------------------------------------------------------------------------- /pdm.lock: -------------------------------------------------------------------------------- 1 | # This file is @generated by PDM. 2 | # It is not intended for manual editing. 3 | 4 | [metadata] 5 | groups = ["default"] 6 | strategy = ["inherit_metadata"] 7 | lock_version = "4.5.0" 8 | content_hash = "sha256:891c0bb94eb7c754c70bedc507acd8483e545fd929369c0963562e46ddca7891" 9 | 10 | [[metadata.targets]] 11 | requires_python = "~=3.9" 12 | 13 | [[package]] 14 | name = "aiofiles" 15 | version = "24.1.0" 16 | requires_python = ">=3.8" 17 | summary = "File support for asyncio." 18 | groups = ["default"] 19 | files = [ 20 | {file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"}, 21 | {file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"}, 22 | ] 23 | 24 | [[package]] 25 | name = "annotated-types" 26 | version = "0.7.0" 27 | requires_python = ">=3.8" 28 | summary = "Reusable constraint types to use with typing.Annotated" 29 | groups = ["default"] 30 | dependencies = [ 31 | "typing-extensions>=4.0.0; python_version < \"3.9\"", 32 | ] 33 | files = [ 34 | {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, 35 | {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, 36 | ] 37 | 38 | [[package]] 39 | name = "anyio" 40 | version = "4.8.0" 41 | requires_python = ">=3.9" 42 | summary = "High level compatibility layer for multiple asynchronous event loop implementations" 43 | groups = ["default"] 44 | dependencies = [ 45 | "exceptiongroup>=1.0.2; python_version < \"3.11\"", 46 | "idna>=2.8", 47 | "sniffio>=1.1", 48 | "typing-extensions>=4.5; python_version < \"3.13\"", 49 | ] 50 | files = [ 51 | {file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"}, 52 | {file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"}, 53 | ] 54 | 55 | [[package]] 56 | name = "apscheduler" 57 | version = "3.11.0" 58 | requires_python = ">=3.8" 59 | summary = "In-process task scheduler with Cron-like capabilities" 60 | groups = ["default"] 61 | dependencies = [ 62 | "backports-zoneinfo; python_version < \"3.9\"", 63 | "tzlocal>=3.0", 64 | ] 65 | files = [ 66 | {file = "APScheduler-3.11.0-py3-none-any.whl", hash = "sha256:fc134ca32e50f5eadcc4938e3a4545ab19131435e851abb40b34d63d5141c6da"}, 67 | {file = "apscheduler-3.11.0.tar.gz", hash = "sha256:4c622d250b0955a65d5d0eb91c33e6d43fd879834bf541e0a18661ae60460133"}, 68 | ] 69 | 70 | [[package]] 71 | name = "arclet-alconna" 72 | version = "1.8.35" 73 | requires_python = ">=3.9" 74 | summary = "A High-performance, Generality, Humane Command Line Arguments Parser Library." 75 | groups = ["default"] 76 | dependencies = [ 77 | "nepattern<1.0.0,>=0.7.7", 78 | "tarina<0.7.0,>=0.6.1", 79 | "typing-extensions>=4.5.0", 80 | ] 81 | files = [ 82 | {file = "arclet_alconna-1.8.35-py3-none-any.whl", hash = "sha256:95d8aaf079167b24e158a0c5125dc17c671da129969dcc5f8b79a9cc72b6389c"}, 83 | {file = "arclet_alconna-1.8.35.tar.gz", hash = "sha256:0cdb7fbdd154110ed7fb79e2b281df6c5fc87861770301f1c0cf8af594ee95f3"}, 84 | ] 85 | 86 | [[package]] 87 | name = "arclet-alconna-tools" 88 | version = "0.7.10" 89 | requires_python = ">=3.9" 90 | summary = "Builtin Tools for Alconna" 91 | groups = ["default"] 92 | dependencies = [ 93 | "arclet-alconna>=1.8.31", 94 | "nepattern<1.0.0,>=0.7.3", 95 | ] 96 | files = [ 97 | {file = "arclet_alconna_tools-0.7.10-py3-none-any.whl", hash = "sha256:50e8b2f433fbc612dc8b99f4f5410006dcb1ef406c971c795071117a4eab8e20"}, 98 | {file = "arclet_alconna_tools-0.7.10.tar.gz", hash = "sha256:446a63a9c56886c23fb44548bb9a18655e0ba5b5dd80cc87915b858dfb02554c"}, 99 | ] 100 | 101 | [[package]] 102 | name = "cachetools" 103 | version = "5.5.1" 104 | requires_python = ">=3.7" 105 | summary = "Extensible memoizing collections and decorators" 106 | groups = ["default"] 107 | files = [ 108 | {file = "cachetools-5.5.1-py3-none-any.whl", hash = "sha256:b76651fdc3b24ead3c648bbdeeb940c1b04d365b38b4af66788f9ec4a81d42bb"}, 109 | {file = "cachetools-5.5.1.tar.gz", hash = "sha256:70f238fbba50383ef62e55c6aff6d9673175fe59f7c6782c7a0b9e38f4a9df95"}, 110 | ] 111 | 112 | [[package]] 113 | name = "certifi" 114 | version = "2025.1.31" 115 | requires_python = ">=3.6" 116 | summary = "Python package for providing Mozilla's CA Bundle." 117 | groups = ["default"] 118 | files = [ 119 | {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, 120 | {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, 121 | ] 122 | 123 | [[package]] 124 | name = "colorama" 125 | version = "0.4.6" 126 | requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 127 | summary = "Cross-platform colored terminal text." 128 | groups = ["default"] 129 | marker = "sys_platform == \"win32\"" 130 | files = [ 131 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 132 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 133 | ] 134 | 135 | [[package]] 136 | name = "cookit" 137 | version = "0.9.3" 138 | requires_python = "<4.0,>=3.9" 139 | summary = "A toolkit for self use." 140 | groups = ["default"] 141 | dependencies = [ 142 | "typing-extensions>=4.12.2", 143 | ] 144 | files = [ 145 | {file = "cookit-0.9.3-py3-none-any.whl", hash = "sha256:bcda202f9b6bbc7739ee613a5742b2189cb134d73e54dd61178881676b431dbf"}, 146 | {file = "cookit-0.9.3.tar.gz", hash = "sha256:68e6fce24bc7c0481b0aefb41e517b5c8744e371d87fe2a89c627c006a7b1ea8"}, 147 | ] 148 | 149 | [[package]] 150 | name = "cookit" 151 | version = "0.9.3" 152 | extras = ["jinja", "loguru", "playwright", "pydantic"] 153 | requires_python = "<4.0,>=3.9" 154 | summary = "A toolkit for self use." 155 | groups = ["default"] 156 | dependencies = [ 157 | "anyio>=4.6.2.post1", 158 | "cookit==0.9.3", 159 | "jinja2>=3.1.4", 160 | "loguru>=0.7.2", 161 | "playwright>=1.49.0", 162 | "pydantic!=2.5.0,!=2.5.1,<3.0.0,>=1.10.0", 163 | "yarl>=1.18.3", 164 | ] 165 | files = [ 166 | {file = "cookit-0.9.3-py3-none-any.whl", hash = "sha256:bcda202f9b6bbc7739ee613a5742b2189cb134d73e54dd61178881676b431dbf"}, 167 | {file = "cookit-0.9.3.tar.gz", hash = "sha256:68e6fce24bc7c0481b0aefb41e517b5c8744e371d87fe2a89c627c006a7b1ea8"}, 168 | ] 169 | 170 | [[package]] 171 | name = "emoji" 172 | version = "2.14.1" 173 | requires_python = ">=3.7" 174 | summary = "Emoji for Python" 175 | groups = ["default"] 176 | dependencies = [ 177 | "typing-extensions>=4.7.0; python_version < \"3.9\"", 178 | ] 179 | files = [ 180 | {file = "emoji-2.14.1-py3-none-any.whl", hash = "sha256:35a8a486c1460addb1499e3bf7929d3889b2e2841a57401903699fef595e942b"}, 181 | {file = "emoji-2.14.1.tar.gz", hash = "sha256:f8c50043d79a2c1410ebfae833ae1868d5941a67a6cd4d18377e2eb0bd79346b"}, 182 | ] 183 | 184 | [[package]] 185 | name = "exceptiongroup" 186 | version = "1.2.2" 187 | requires_python = ">=3.7" 188 | summary = "Backport of PEP 654 (exception groups)" 189 | groups = ["default"] 190 | files = [ 191 | {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, 192 | {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, 193 | ] 194 | 195 | [[package]] 196 | name = "greenlet" 197 | version = "3.1.1" 198 | requires_python = ">=3.7" 199 | summary = "Lightweight in-process concurrent programming" 200 | groups = ["default"] 201 | files = [ 202 | {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, 203 | {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, 204 | {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0"}, 205 | {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120"}, 206 | {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc"}, 207 | {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617"}, 208 | {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7"}, 209 | {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6"}, 210 | {file = "greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80"}, 211 | {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"}, 212 | {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"}, 213 | {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e"}, 214 | {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1"}, 215 | {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383"}, 216 | {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a"}, 217 | {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511"}, 218 | {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395"}, 219 | {file = "greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39"}, 220 | {file = "greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d"}, 221 | {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79"}, 222 | {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa"}, 223 | {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441"}, 224 | {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36"}, 225 | {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9"}, 226 | {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0"}, 227 | {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942"}, 228 | {file = "greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01"}, 229 | {file = "greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1"}, 230 | {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff"}, 231 | {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a"}, 232 | {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e"}, 233 | {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4"}, 234 | {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e"}, 235 | {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1"}, 236 | {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c"}, 237 | {file = "greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761"}, 238 | {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011"}, 239 | {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13"}, 240 | {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475"}, 241 | {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b"}, 242 | {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822"}, 243 | {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01"}, 244 | {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6"}, 245 | {file = "greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3"}, 246 | {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42"}, 247 | {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"}, 248 | {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437"}, 249 | {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145"}, 250 | {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c"}, 251 | {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e"}, 252 | {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e"}, 253 | {file = "greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c"}, 254 | {file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"}, 255 | {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"}, 256 | ] 257 | 258 | [[package]] 259 | name = "h11" 260 | version = "0.14.0" 261 | requires_python = ">=3.7" 262 | summary = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" 263 | groups = ["default"] 264 | dependencies = [ 265 | "typing-extensions; python_version < \"3.8\"", 266 | ] 267 | files = [ 268 | {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, 269 | {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, 270 | ] 271 | 272 | [[package]] 273 | name = "httpcore" 274 | version = "1.0.7" 275 | requires_python = ">=3.8" 276 | summary = "A minimal low-level HTTP client." 277 | groups = ["default"] 278 | dependencies = [ 279 | "certifi", 280 | "h11<0.15,>=0.13", 281 | ] 282 | files = [ 283 | {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, 284 | {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, 285 | ] 286 | 287 | [[package]] 288 | name = "httpx" 289 | version = "0.28.1" 290 | requires_python = ">=3.8" 291 | summary = "The next generation HTTP client." 292 | groups = ["default"] 293 | dependencies = [ 294 | "anyio", 295 | "certifi", 296 | "httpcore==1.*", 297 | "idna", 298 | ] 299 | files = [ 300 | {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, 301 | {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, 302 | ] 303 | 304 | [[package]] 305 | name = "idna" 306 | version = "3.10" 307 | requires_python = ">=3.6" 308 | summary = "Internationalized Domain Names in Applications (IDNA)" 309 | groups = ["default"] 310 | files = [ 311 | {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, 312 | {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, 313 | ] 314 | 315 | [[package]] 316 | name = "importlib-metadata" 317 | version = "8.6.1" 318 | requires_python = ">=3.9" 319 | summary = "Read metadata from Python packages" 320 | groups = ["default"] 321 | dependencies = [ 322 | "typing-extensions>=3.6.4; python_version < \"3.8\"", 323 | "zipp>=3.20", 324 | ] 325 | files = [ 326 | {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, 327 | {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, 328 | ] 329 | 330 | [[package]] 331 | name = "jinja2" 332 | version = "3.1.5" 333 | requires_python = ">=3.7" 334 | summary = "A very fast and expressive template engine." 335 | groups = ["default"] 336 | dependencies = [ 337 | "MarkupSafe>=2.0", 338 | ] 339 | files = [ 340 | {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, 341 | {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, 342 | ] 343 | 344 | [[package]] 345 | name = "loguru" 346 | version = "0.7.3" 347 | requires_python = "<4.0,>=3.5" 348 | summary = "Python logging made (stupidly) simple" 349 | groups = ["default"] 350 | dependencies = [ 351 | "aiocontextvars>=0.2.0; python_version < \"3.7\"", 352 | "colorama>=0.3.4; sys_platform == \"win32\"", 353 | "win32-setctime>=1.0.0; sys_platform == \"win32\"", 354 | ] 355 | files = [ 356 | {file = "loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c"}, 357 | {file = "loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6"}, 358 | ] 359 | 360 | [[package]] 361 | name = "markdown" 362 | version = "3.7" 363 | requires_python = ">=3.8" 364 | summary = "Python implementation of John Gruber's Markdown." 365 | groups = ["default"] 366 | dependencies = [ 367 | "importlib-metadata>=4.4; python_version < \"3.10\"", 368 | ] 369 | files = [ 370 | {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, 371 | {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, 372 | ] 373 | 374 | [[package]] 375 | name = "markupsafe" 376 | version = "3.0.2" 377 | requires_python = ">=3.9" 378 | summary = "Safely add untrusted strings to HTML/XML markup." 379 | groups = ["default"] 380 | files = [ 381 | {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, 382 | {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, 383 | {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, 384 | {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, 385 | {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, 386 | {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, 387 | {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, 388 | {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, 389 | {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, 390 | {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, 391 | {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, 392 | {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, 393 | {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, 394 | {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, 395 | {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, 396 | {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, 397 | {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, 398 | {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, 399 | {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, 400 | {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, 401 | {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, 402 | {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, 403 | {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, 404 | {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, 405 | {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, 406 | {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, 407 | {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, 408 | {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, 409 | {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, 410 | {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, 411 | {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, 412 | {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, 413 | {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, 414 | {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, 415 | {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, 416 | {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, 417 | {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, 418 | {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, 419 | {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, 420 | {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, 421 | {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, 422 | {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, 423 | {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, 424 | {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, 425 | {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, 426 | {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, 427 | {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, 428 | {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, 429 | {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, 430 | {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, 431 | {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, 432 | {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, 433 | {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, 434 | {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, 435 | {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, 436 | {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, 437 | {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, 438 | {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, 439 | {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, 440 | {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, 441 | {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, 442 | ] 443 | 444 | [[package]] 445 | name = "multidict" 446 | version = "6.1.0" 447 | requires_python = ">=3.8" 448 | summary = "multidict implementation" 449 | groups = ["default"] 450 | dependencies = [ 451 | "typing-extensions>=4.1.0; python_version < \"3.11\"", 452 | ] 453 | files = [ 454 | {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, 455 | {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, 456 | {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, 457 | {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, 458 | {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, 459 | {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, 460 | {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, 461 | {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, 462 | {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, 463 | {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, 464 | {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, 465 | {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, 466 | {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, 467 | {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, 468 | {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, 469 | {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, 470 | {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, 471 | {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, 472 | {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, 473 | {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, 474 | {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, 475 | {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, 476 | {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, 477 | {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, 478 | {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, 479 | {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, 480 | {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, 481 | {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, 482 | {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, 483 | {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, 484 | {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, 485 | {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, 486 | {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, 487 | {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, 488 | {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, 489 | {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, 490 | {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, 491 | {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, 492 | {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, 493 | {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, 494 | {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, 495 | {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, 496 | {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, 497 | {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, 498 | {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, 499 | {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, 500 | {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, 501 | {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, 502 | {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, 503 | {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, 504 | {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, 505 | {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, 506 | {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, 507 | {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, 508 | {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, 509 | {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, 510 | {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, 511 | {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, 512 | {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, 513 | {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, 514 | {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, 515 | {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, 516 | {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, 517 | {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, 518 | {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, 519 | {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, 520 | {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, 521 | {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, 522 | {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, 523 | {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, 524 | {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, 525 | {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, 526 | {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, 527 | {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, 528 | {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, 529 | {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, 530 | {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, 531 | ] 532 | 533 | [[package]] 534 | name = "nepattern" 535 | version = "0.7.7" 536 | requires_python = ">=3.8" 537 | summary = "a complex pattern, support typing" 538 | groups = ["default"] 539 | dependencies = [ 540 | "tarina>=0.5.1", 541 | "typing-extensions>=4.5.0", 542 | ] 543 | files = [ 544 | {file = "nepattern-0.7.7-py3-none-any.whl", hash = "sha256:2d66f964333f42df7971390da4fb98dfed1e8b769236f305c28a83c0bcda849a"}, 545 | {file = "nepattern-0.7.7.tar.gz", hash = "sha256:6667f888457e78937998f9412eb70ad16d220464d2d77850dd2b05e9ecfb3207"}, 546 | ] 547 | 548 | [[package]] 549 | name = "nonebot-plugin-alconna" 550 | version = "0.54.2" 551 | requires_python = ">=3.9" 552 | summary = "Alconna Adapter for Nonebot" 553 | groups = ["default"] 554 | dependencies = [ 555 | "arclet-alconna-tools>=0.7.10", 556 | "arclet-alconna<2.0,>=1.8.35", 557 | "importlib-metadata>=4.13.0", 558 | "nepattern<1.0,>=0.7.7", 559 | "nonebot-plugin-waiter>=0.6.0", 560 | "nonebot2>=2.3.0", 561 | "tarina<0.7,>=0.6.8", 562 | ] 563 | files = [ 564 | {file = "nonebot_plugin_alconna-0.54.2-py3-none-any.whl", hash = "sha256:ab9a1a5f0f8c9a30ba57a49bed5d3e9c3f761ea5954cbafb15bcd2aa9c7d5507"}, 565 | {file = "nonebot_plugin_alconna-0.54.2.tar.gz", hash = "sha256:0216da3bc2e5f8b4c4c44c2701f8f0a536d35ea0db79e708cc2ecd002b57ace6"}, 566 | ] 567 | 568 | [[package]] 569 | name = "nonebot-plugin-apscheduler" 570 | version = "0.5.0" 571 | requires_python = "<4.0,>=3.9" 572 | summary = "APScheduler Support for NoneBot2" 573 | groups = ["default"] 574 | dependencies = [ 575 | "apscheduler<4.0.0,>=3.7.0", 576 | "nonebot2<3.0.0,>=2.2.0", 577 | "pydantic!=2.5.0,!=2.5.1,<3.0.0,>=1.10.0", 578 | ] 579 | files = [ 580 | {file = "nonebot_plugin_apscheduler-0.5.0-py3-none-any.whl", hash = "sha256:8b99b5ee60c4bc195d4df2fd27dab3d6963691e3332f6cee31a06eb4277c307f"}, 581 | {file = "nonebot_plugin_apscheduler-0.5.0.tar.gz", hash = "sha256:6c0230e99765f275dc83d6639ff33bd6f71203fa10cd1b8a204b0f95530cda86"}, 582 | ] 583 | 584 | [[package]] 585 | name = "nonebot-plugin-htmlrender" 586 | version = "0.5.1" 587 | requires_python = "<4.0,>=3.9" 588 | summary = "通过浏览器渲染图片" 589 | groups = ["default"] 590 | dependencies = [ 591 | "aiofiles>=0.8.0", 592 | "jinja2>=3.0.3", 593 | "markdown>=3.3.6", 594 | "nonebot2>=2.2.0", 595 | "playwright>=1.48.0", 596 | "pygments>=2.10.0", 597 | "pymdown-extensions>=9.1", 598 | "python-markdown-math>=0.8", 599 | ] 600 | files = [ 601 | {file = "nonebot_plugin_htmlrender-0.5.1-py3-none-any.whl", hash = "sha256:5c6395202bf437bb111edc0c761a201fdeaeb12114275d723ca9f33fd8b7dca7"}, 602 | {file = "nonebot_plugin_htmlrender-0.5.1.tar.gz", hash = "sha256:8dc3fedf12ae7a2b9d0f41af81cfa31b07373b50c362ddfb5f1ff1b436a56889"}, 603 | ] 604 | 605 | [[package]] 606 | name = "nonebot-plugin-userinfo" 607 | version = "0.2.6" 608 | requires_python = "<4.0,>=3.9" 609 | summary = "Nonebot2 用户信息获取插件" 610 | groups = ["default"] 611 | dependencies = [ 612 | "cachetools<6.0.0,>=5.0.0", 613 | "emoji<3.0.0,>=2.0.0", 614 | "httpx<1.0.0,>=0.20.0", 615 | "nonebot2<3.0.0,>=2.3.0", 616 | "strenum<0.5.0,>=0.4.15", 617 | ] 618 | files = [ 619 | {file = "nonebot_plugin_userinfo-0.2.6-py3-none-any.whl", hash = "sha256:79d2481af08a5ec77cf171c685eecd14eaf1ce4d5a1ec1fd22fbb0b85e06c260"}, 620 | {file = "nonebot_plugin_userinfo-0.2.6.tar.gz", hash = "sha256:0d1ce897e94a9d4c0b5300bc8f239a4676f9bb62c78a14e0f0527e5398ffc840"}, 621 | ] 622 | 623 | [[package]] 624 | name = "nonebot-plugin-waiter" 625 | version = "0.8.1" 626 | requires_python = ">=3.9" 627 | summary = "An alternative for got-and-reject in Nonebot" 628 | groups = ["default"] 629 | dependencies = [ 630 | "nonebot2>=2.3.0", 631 | ] 632 | files = [ 633 | {file = "nonebot_plugin_waiter-0.8.1-py3-none-any.whl", hash = "sha256:3e1afc8f134496d3a4ecefd9c3a2a98d6ef28a5318268cb22b99a0ef61a44080"}, 634 | {file = "nonebot_plugin_waiter-0.8.1.tar.gz", hash = "sha256:5e54213dfea1fd8a1e20dbe6d93b7881f35cbeedf80005148cdc39c1fd2ccc0f"}, 635 | ] 636 | 637 | [[package]] 638 | name = "nonebot2" 639 | version = "2.4.1" 640 | requires_python = "<4.0,>=3.9" 641 | summary = "An asynchronous python bot framework." 642 | groups = ["default"] 643 | dependencies = [ 644 | "anyio<5.0.0,>=4.4.0", 645 | "exceptiongroup<2.0.0,>=1.2.2", 646 | "loguru<1.0.0,>=0.6.0", 647 | "pydantic!=2.10.0,!=2.10.1,!=2.5.0,!=2.5.1,<3.0.0,>=1.10.0", 648 | "pygtrie<3.0.0,>=2.4.1", 649 | "python-dotenv<2.0.0,>=0.21.0", 650 | "tomli<3.0.0,>=2.0.1; python_version < \"3.11\"", 651 | "typing-extensions<5.0.0,>=4.4.0", 652 | "yarl<2.0.0,>=1.7.2", 653 | ] 654 | files = [ 655 | {file = "nonebot2-2.4.1-py3-none-any.whl", hash = "sha256:fec95f075efc89dbe9ce148618b413b02f46ba284200367749b035e794695111"}, 656 | {file = "nonebot2-2.4.1.tar.gz", hash = "sha256:8fea364318501ed79721403a8ecd76587bc884d58c356260f691a8bbda9b05e6"}, 657 | ] 658 | 659 | [[package]] 660 | name = "playwright" 661 | version = "1.50.0" 662 | requires_python = ">=3.9" 663 | summary = "A high-level API to automate web browsers" 664 | groups = ["default"] 665 | dependencies = [ 666 | "greenlet<4.0.0,>=3.1.1", 667 | "pyee<13,>=12", 668 | ] 669 | files = [ 670 | {file = "playwright-1.50.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:f36d754a6c5bd9bf7f14e8f57a2aea6fd08f39ca4c8476481b9c83e299531148"}, 671 | {file = "playwright-1.50.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:40f274384591dfd27f2b014596250b2250c843ed1f7f4ef5d2960ecb91b4961e"}, 672 | {file = "playwright-1.50.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:9922ef9bcd316995f01e220acffd2d37a463b4ad10fd73e388add03841dfa230"}, 673 | {file = "playwright-1.50.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:8fc628c492d12b13d1f347137b2ac6c04f98197ff0985ef0403a9a9ee0d39131"}, 674 | {file = "playwright-1.50.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcff35f72db2689a79007aee78f1b0621a22e6e3d6c1f58aaa9ac805bf4497c"}, 675 | {file = "playwright-1.50.0-py3-none-win32.whl", hash = "sha256:3b906f4d351260016a8c5cc1e003bb341651ae682f62213b50168ed581c7558a"}, 676 | {file = "playwright-1.50.0-py3-none-win_amd64.whl", hash = "sha256:1859423da82de631704d5e3d88602d755462b0906824c1debe140979397d2e8d"}, 677 | ] 678 | 679 | [[package]] 680 | name = "propcache" 681 | version = "0.2.1" 682 | requires_python = ">=3.9" 683 | summary = "Accelerated property cache" 684 | groups = ["default"] 685 | files = [ 686 | {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, 687 | {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, 688 | {file = "propcache-0.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea"}, 689 | {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212"}, 690 | {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3"}, 691 | {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d"}, 692 | {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634"}, 693 | {file = "propcache-0.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2"}, 694 | {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958"}, 695 | {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c"}, 696 | {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583"}, 697 | {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf"}, 698 | {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034"}, 699 | {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b"}, 700 | {file = "propcache-0.2.1-cp310-cp310-win32.whl", hash = "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4"}, 701 | {file = "propcache-0.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba"}, 702 | {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16"}, 703 | {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717"}, 704 | {file = "propcache-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3"}, 705 | {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9"}, 706 | {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787"}, 707 | {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465"}, 708 | {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af"}, 709 | {file = "propcache-0.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7"}, 710 | {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f"}, 711 | {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54"}, 712 | {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505"}, 713 | {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82"}, 714 | {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca"}, 715 | {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e"}, 716 | {file = "propcache-0.2.1-cp311-cp311-win32.whl", hash = "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034"}, 717 | {file = "propcache-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3"}, 718 | {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a"}, 719 | {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0"}, 720 | {file = "propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d"}, 721 | {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4"}, 722 | {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d"}, 723 | {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5"}, 724 | {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24"}, 725 | {file = "propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff"}, 726 | {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f"}, 727 | {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec"}, 728 | {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348"}, 729 | {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6"}, 730 | {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6"}, 731 | {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518"}, 732 | {file = "propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246"}, 733 | {file = "propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1"}, 734 | {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc"}, 735 | {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9"}, 736 | {file = "propcache-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439"}, 737 | {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536"}, 738 | {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629"}, 739 | {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b"}, 740 | {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052"}, 741 | {file = "propcache-0.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce"}, 742 | {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d"}, 743 | {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce"}, 744 | {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95"}, 745 | {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf"}, 746 | {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f"}, 747 | {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30"}, 748 | {file = "propcache-0.2.1-cp313-cp313-win32.whl", hash = "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6"}, 749 | {file = "propcache-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1"}, 750 | {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6a9a8c34fb7bb609419a211e59da8887eeca40d300b5ea8e56af98f6fbbb1541"}, 751 | {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae1aa1cd222c6d205853b3013c69cd04515f9d6ab6de4b0603e2e1c33221303e"}, 752 | {file = "propcache-0.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:accb6150ce61c9c4b7738d45550806aa2b71c7668c6942f17b0ac182b6142fd4"}, 753 | {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eee736daafa7af6d0a2dc15cc75e05c64f37fc37bafef2e00d77c14171c2097"}, 754 | {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7a31fc1e1bd362874863fdeed71aed92d348f5336fd84f2197ba40c59f061bd"}, 755 | {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba4cfa1052819d16699e1d55d18c92b6e094d4517c41dd231a8b9f87b6fa681"}, 756 | {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f089118d584e859c62b3da0892b88a83d611c2033ac410e929cb6754eec0ed16"}, 757 | {file = "propcache-0.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:781e65134efaf88feb447e8c97a51772aa75e48b794352f94cb7ea717dedda0d"}, 758 | {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31f5af773530fd3c658b32b6bdc2d0838543de70eb9a2156c03e410f7b0d3aae"}, 759 | {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:a7a078f5d37bee6690959c813977da5291b24286e7b962e62a94cec31aa5188b"}, 760 | {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cea7daf9fc7ae6687cf1e2c049752f19f146fdc37c2cc376e7d0032cf4f25347"}, 761 | {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:8b3489ff1ed1e8315674d0775dc7d2195fb13ca17b3808721b54dbe9fd020faf"}, 762 | {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9403db39be1393618dd80c746cb22ccda168efce239c73af13c3763ef56ffc04"}, 763 | {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5d97151bc92d2b2578ff7ce779cdb9174337390a535953cbb9452fb65164c587"}, 764 | {file = "propcache-0.2.1-cp39-cp39-win32.whl", hash = "sha256:9caac6b54914bdf41bcc91e7eb9147d331d29235a7c967c150ef5df6464fd1bb"}, 765 | {file = "propcache-0.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:92fc4500fcb33899b05ba73276dfb684a20d31caa567b7cb5252d48f896a91b1"}, 766 | {file = "propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54"}, 767 | {file = "propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64"}, 768 | ] 769 | 770 | [[package]] 771 | name = "psutil" 772 | version = "6.1.1" 773 | requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" 774 | summary = "Cross-platform lib for process and system monitoring in Python." 775 | groups = ["default"] 776 | files = [ 777 | {file = "psutil-6.1.1-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed7fe2231a444fc219b9c42d0376e0a9a1a72f16c5cfa0f68d19f1a0663e8"}, 778 | {file = "psutil-6.1.1-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0bdd4eab935276290ad3cb718e9809412895ca6b5b334f5a9111ee6d9aff9377"}, 779 | {file = "psutil-6.1.1-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6e06c20c05fe95a3d7302d74e7097756d4ba1247975ad6905441ae1b5b66003"}, 780 | {file = "psutil-6.1.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97f7cb9921fbec4904f522d972f0c0e1f4fabbdd4e0287813b21215074a0f160"}, 781 | {file = "psutil-6.1.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33431e84fee02bc84ea36d9e2c4a6d395d479c9dd9bba2376c1f6ee8f3a4e0b3"}, 782 | {file = "psutil-6.1.1-cp37-abi3-win32.whl", hash = "sha256:eaa912e0b11848c4d9279a93d7e2783df352b082f40111e078388701fd479e53"}, 783 | {file = "psutil-6.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:f35cfccb065fff93529d2afb4a2e89e363fe63ca1e4a5da22b603a85833c2649"}, 784 | {file = "psutil-6.1.1.tar.gz", hash = "sha256:cf8496728c18f2d0b45198f06895be52f36611711746b7f30c464b422b50e2f5"}, 785 | ] 786 | 787 | [[package]] 788 | name = "py-cpuinfo" 789 | version = "9.0.0" 790 | summary = "Get CPU info with pure Python" 791 | groups = ["default"] 792 | files = [ 793 | {file = "py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690"}, 794 | {file = "py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5"}, 795 | ] 796 | 797 | [[package]] 798 | name = "pydantic" 799 | version = "2.10.6" 800 | requires_python = ">=3.8" 801 | summary = "Data validation using Python type hints" 802 | groups = ["default"] 803 | dependencies = [ 804 | "annotated-types>=0.6.0", 805 | "pydantic-core==2.27.2", 806 | "typing-extensions>=4.12.2", 807 | ] 808 | files = [ 809 | {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, 810 | {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, 811 | ] 812 | 813 | [[package]] 814 | name = "pydantic-core" 815 | version = "2.27.2" 816 | requires_python = ">=3.8" 817 | summary = "Core functionality for Pydantic validation and serialization" 818 | groups = ["default"] 819 | dependencies = [ 820 | "typing-extensions!=4.7.0,>=4.6.0", 821 | ] 822 | files = [ 823 | {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, 824 | {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, 825 | {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, 826 | {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, 827 | {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, 828 | {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, 829 | {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, 830 | {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, 831 | {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, 832 | {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, 833 | {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, 834 | {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, 835 | {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, 836 | {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, 837 | {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, 838 | {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, 839 | {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, 840 | {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, 841 | {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, 842 | {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, 843 | {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, 844 | {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, 845 | {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, 846 | {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, 847 | {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, 848 | {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, 849 | {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, 850 | {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, 851 | {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, 852 | {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, 853 | {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, 854 | {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, 855 | {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, 856 | {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, 857 | {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, 858 | {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, 859 | {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, 860 | {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, 861 | {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, 862 | {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, 863 | {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, 864 | {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, 865 | {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, 866 | {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, 867 | {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, 868 | {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, 869 | {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, 870 | {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, 871 | {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, 872 | {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, 873 | {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, 874 | {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, 875 | {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, 876 | {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, 877 | {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, 878 | {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, 879 | {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, 880 | {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, 881 | {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, 882 | {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, 883 | {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, 884 | {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, 885 | {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, 886 | {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, 887 | {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, 888 | {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, 889 | {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, 890 | {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, 891 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, 892 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, 893 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, 894 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, 895 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, 896 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, 897 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, 898 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, 899 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, 900 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, 901 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, 902 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, 903 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, 904 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, 905 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, 906 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, 907 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, 908 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, 909 | {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, 910 | ] 911 | 912 | [[package]] 913 | name = "pyee" 914 | version = "12.1.1" 915 | requires_python = ">=3.8" 916 | summary = "A rough port of Node.js's EventEmitter to Python with a few tricks of its own" 917 | groups = ["default"] 918 | dependencies = [ 919 | "typing-extensions", 920 | ] 921 | files = [ 922 | {file = "pyee-12.1.1-py3-none-any.whl", hash = "sha256:18a19c650556bb6b32b406d7f017c8f513aceed1ef7ca618fb65de7bd2d347ef"}, 923 | {file = "pyee-12.1.1.tar.gz", hash = "sha256:bbc33c09e2ff827f74191e3e5bbc6be7da02f627b7ec30d86f5ce1a6fb2424a3"}, 924 | ] 925 | 926 | [[package]] 927 | name = "pygments" 928 | version = "2.19.1" 929 | requires_python = ">=3.8" 930 | summary = "Pygments is a syntax highlighting package written in Python." 931 | groups = ["default"] 932 | files = [ 933 | {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, 934 | {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, 935 | ] 936 | 937 | [[package]] 938 | name = "pygtrie" 939 | version = "2.5.0" 940 | summary = "A pure Python trie data structure implementation." 941 | groups = ["default"] 942 | files = [ 943 | {file = "pygtrie-2.5.0-py3-none-any.whl", hash = "sha256:8795cda8105493d5ae159a5bef313ff13156c5d4d72feddefacaad59f8c8ce16"}, 944 | {file = "pygtrie-2.5.0.tar.gz", hash = "sha256:203514ad826eb403dab1d2e2ddd034e0d1534bbe4dbe0213bb0593f66beba4e2"}, 945 | ] 946 | 947 | [[package]] 948 | name = "pymdown-extensions" 949 | version = "10.14.3" 950 | requires_python = ">=3.8" 951 | summary = "Extension pack for Python Markdown." 952 | groups = ["default"] 953 | dependencies = [ 954 | "markdown>=3.6", 955 | "pyyaml", 956 | ] 957 | files = [ 958 | {file = "pymdown_extensions-10.14.3-py3-none-any.whl", hash = "sha256:05e0bee73d64b9c71a4ae17c72abc2f700e8bc8403755a00580b49a4e9f189e9"}, 959 | {file = "pymdown_extensions-10.14.3.tar.gz", hash = "sha256:41e576ce3f5d650be59e900e4ceff231e0aed2a88cf30acaee41e02f063a061b"}, 960 | ] 961 | 962 | [[package]] 963 | name = "python-dotenv" 964 | version = "1.0.1" 965 | requires_python = ">=3.8" 966 | summary = "Read key-value pairs from a .env file and set them as environment variables" 967 | groups = ["default"] 968 | files = [ 969 | {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, 970 | {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, 971 | ] 972 | 973 | [[package]] 974 | name = "python-markdown-math" 975 | version = "0.8" 976 | requires_python = ">=3.6" 977 | summary = "Math extension for Python-Markdown" 978 | groups = ["default"] 979 | dependencies = [ 980 | "Markdown>=3.0", 981 | ] 982 | files = [ 983 | {file = "python-markdown-math-0.8.tar.gz", hash = "sha256:8564212af679fc18d53f38681f16080fcd3d186073f23825c7ce86fadd3e3635"}, 984 | {file = "python_markdown_math-0.8-py3-none-any.whl", hash = "sha256:c685249d84b5b697e9114d7beb352bd8ca2e07fd268fd4057ffca888c14641e5"}, 985 | ] 986 | 987 | [[package]] 988 | name = "pyyaml" 989 | version = "6.0.2" 990 | requires_python = ">=3.8" 991 | summary = "YAML parser and emitter for Python" 992 | groups = ["default"] 993 | files = [ 994 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, 995 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, 996 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, 997 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, 998 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, 999 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, 1000 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, 1001 | {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, 1002 | {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, 1003 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, 1004 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, 1005 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, 1006 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, 1007 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, 1008 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, 1009 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, 1010 | {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, 1011 | {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, 1012 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, 1013 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, 1014 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, 1015 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, 1016 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, 1017 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, 1018 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, 1019 | {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, 1020 | {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, 1021 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, 1022 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, 1023 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, 1024 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, 1025 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, 1026 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, 1027 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, 1028 | {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, 1029 | {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, 1030 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, 1031 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, 1032 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, 1033 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, 1034 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, 1035 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, 1036 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, 1037 | {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, 1038 | {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, 1039 | {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, 1040 | ] 1041 | 1042 | [[package]] 1043 | name = "sniffio" 1044 | version = "1.3.1" 1045 | requires_python = ">=3.7" 1046 | summary = "Sniff out which async library your code is running under" 1047 | groups = ["default"] 1048 | files = [ 1049 | {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, 1050 | {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, 1051 | ] 1052 | 1053 | [[package]] 1054 | name = "strenum" 1055 | version = "0.4.15" 1056 | summary = "An Enum that inherits from str." 1057 | groups = ["default"] 1058 | files = [ 1059 | {file = "StrEnum-0.4.15-py3-none-any.whl", hash = "sha256:a30cda4af7cc6b5bf52c8055bc4bf4b2b6b14a93b574626da33df53cf7740659"}, 1060 | {file = "StrEnum-0.4.15.tar.gz", hash = "sha256:878fb5ab705442070e4dd1929bb5e2249511c0bcf2b0eeacf3bcd80875c82eff"}, 1061 | ] 1062 | 1063 | [[package]] 1064 | name = "tarina" 1065 | version = "0.6.8" 1066 | requires_python = ">=3.9" 1067 | summary = "A collection of common utils for Arclet" 1068 | groups = ["default"] 1069 | dependencies = [ 1070 | "typing-extensions>=4.4.0", 1071 | ] 1072 | files = [ 1073 | {file = "tarina-0.6.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a2f7b7e61912a020d6ba3c591c4edbc31bb468544640bd814470c69a07dcc4cd"}, 1074 | {file = "tarina-0.6.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1cac7cbd49317b8e63eba7d0ce0ba11e1218ab51c9d6ee9df8404b5e226db15b"}, 1075 | {file = "tarina-0.6.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:69d9923d888948ccc9d20a191ab1f6ff2bc097f4154b6a3780c45dc78ae047cd"}, 1076 | {file = "tarina-0.6.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b0f12395301584e0d56c7edd6118c5b5732b456067735ee7f0800575d5d490f"}, 1077 | {file = "tarina-0.6.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84bd95b18c7f6a55606b0071654137b98f70faf5d12343d361377cf0d81ecde6"}, 1078 | {file = "tarina-0.6.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94e6ee4df273e1fd3eaf36de7c78a93dde08bf2ad67526122afab1b077e344d9"}, 1079 | {file = "tarina-0.6.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7afc1a8a11983f39cce18ad7f8d27601c4d34b1ffe1a1aca2a59b4111d181d01"}, 1080 | {file = "tarina-0.6.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0f74dc96979b774d7e23b964886137f7cf5f58cb2d652c23c3e2d42c1ad39a9"}, 1081 | {file = "tarina-0.6.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3493d59ef19d1ded232e7a33e52c7d6ebfff66f30ac4c3649ae765b14dacc393"}, 1082 | {file = "tarina-0.6.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:323351247817dac174f609104c54fcdc4923aaf3ac5e4a4a81c3efaa007d5778"}, 1083 | {file = "tarina-0.6.8-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:eb7407046d8051b062f71440b90a11be3ef1a64e99b1c6be157312fe2889e4f4"}, 1084 | {file = "tarina-0.6.8-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:50bbbb249d0849855cf43f4fa09c4eecac53e76a54c876ef1a7fb967f17d35b6"}, 1085 | {file = "tarina-0.6.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8a05063e41f01f64c3c6e87b740c1e2cebffdcae20b9d89014a934bb2707a2b7"}, 1086 | {file = "tarina-0.6.8-cp310-cp310-win32.whl", hash = "sha256:162756b5c0872856c11cc97ed1a618a9118fef3fb6999edc917578b56f3eb67f"}, 1087 | {file = "tarina-0.6.8-cp310-cp310-win_amd64.whl", hash = "sha256:a58a8a6e36e43de8f3a0561cb264abe300ec6bb4a4215f16c316f1db564de6fb"}, 1088 | {file = "tarina-0.6.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c05d38546712014aafcf123e89d90ad75f08ebd40c5a7e4c76db40d2ae11b387"}, 1089 | {file = "tarina-0.6.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b036d53d2f8ec46fff9f649b5ec70431cdee3a2b99c8706f67d8d55ea4d4e493"}, 1090 | {file = "tarina-0.6.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:195d2b4a8a8ec2e946ecd8abab70cf3065fbba67e18e971e0412a74a8417a656"}, 1091 | {file = "tarina-0.6.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9bbe85a83904eb29143a43511c04a3fea9a7392c70a6216fcec3ba06cf1b83a"}, 1092 | {file = "tarina-0.6.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d6b5893b735d24d2a57f6dbb26de92bb5e09e476077a16408e2054f06263530"}, 1093 | {file = "tarina-0.6.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a74cc68f4678d37d517a2d661184712f15da608510de6b1bb9103d85d6014409"}, 1094 | {file = "tarina-0.6.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dd0f0d769e503fb81d07bd2d342585347f5149803bd7b4b233deaff1833451f"}, 1095 | {file = "tarina-0.6.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3b7c91a488aa616e141c337cddce1ba1e0bdac6b98362bc15ce76bd3cd71e17"}, 1096 | {file = "tarina-0.6.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4f0b91da85a816e34a3eb4670d21c26a3909872b1f5525797bedc32a45efd98d"}, 1097 | {file = "tarina-0.6.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bf3a5a8fe912d5984627f64cdd187c8a0801c6f3b2f7f12c68cdb5babf691155"}, 1098 | {file = "tarina-0.6.8-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:622e226553453a9a873fca5a2d495d2d28003a23834503147954d651186146c4"}, 1099 | {file = "tarina-0.6.8-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:198102e526d096f37bfb868f815cb3bfc864e136aa8b70e90a107a38ffb82c15"}, 1100 | {file = "tarina-0.6.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:07b252f686410fbf1fe64b3b6705dd40ff218bc1d6a67efac640a0a246f66da3"}, 1101 | {file = "tarina-0.6.8-cp311-cp311-win32.whl", hash = "sha256:9e4f56478c58706548e0f08bd277666bac783d918257950be49c4a5d8a35e7b0"}, 1102 | {file = "tarina-0.6.8-cp311-cp311-win_amd64.whl", hash = "sha256:d0a92b5368a8a1700f0482d0bc3891dccdfc6dc61f7e66101f68544fca10f096"}, 1103 | {file = "tarina-0.6.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e8e4bf8ed36c92af7b2a973905f5f4f58dabf7490c5dda695534bf5e3b20d92e"}, 1104 | {file = "tarina-0.6.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:175754132a107c2830a341d3efcae60f44bfd8e0eab56d92537cc6f849ad44e3"}, 1105 | {file = "tarina-0.6.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c5f983f0c84959589f884777c7ac0b22786d6d183ae367e53265f09e9ed7dc9f"}, 1106 | {file = "tarina-0.6.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c8cc5f1bf3c2099377b5de76b0e192bb4fae3dcfcc245be9ed14315b29c8c75"}, 1107 | {file = "tarina-0.6.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7067131af4081d8ca3d4e70c96e8903c9e483e4d5af4910b93051602f7e8b34"}, 1108 | {file = "tarina-0.6.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a66ea0778dcbd9e9b9b226529ed1822261722956468de026655e09b41feee42b"}, 1109 | {file = "tarina-0.6.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd8501b0cd51ad8c0e294b46614ef27ef6773324099b5a24711534db55ded44d"}, 1110 | {file = "tarina-0.6.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5812e903c79c02029bfe61fd0ff30bf2a5ac58e7c5b1ab3abfb0f0b57260c5b8"}, 1111 | {file = "tarina-0.6.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:60ea05cb8e07a629096d9abbd11258df2bcea03168bc8ef8f369ca0dddfc504a"}, 1112 | {file = "tarina-0.6.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b2635d27c34353e9431cb995acece3a0b607cde0934ebf8df35f0e4ae70a0335"}, 1113 | {file = "tarina-0.6.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:542b5359d23f969f275f9e80960ce7f19a2684e93cd3ab659afaeb7ee06de038"}, 1114 | {file = "tarina-0.6.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4ba2018a855f59c28f087ec1ec1d892c6afccdd0da8f4347fbf8e3219145d66c"}, 1115 | {file = "tarina-0.6.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fdf9c5c44d402041df79e747ff9f82c6794875febb053376acdb724043770975"}, 1116 | {file = "tarina-0.6.8-cp312-cp312-win32.whl", hash = "sha256:cf44ca009d79d0ecc8541b9067a3b7d086d1939de3bc1b04c164286b0fcf2112"}, 1117 | {file = "tarina-0.6.8-cp312-cp312-win_amd64.whl", hash = "sha256:8cd9ff00e2eb9ed6c42b80504005c9b6de4a9afe9ef36771f63b8f9492e38a90"}, 1118 | {file = "tarina-0.6.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:af3ff4742b0324d590298812997d028a6d6892bfab5680cd266bed0c1bbf90b0"}, 1119 | {file = "tarina-0.6.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e7919d53f20e7d780b44e1804f7c9006bcf5832f87cfeb8d5432f1c832953ed4"}, 1120 | {file = "tarina-0.6.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:878f8d9411ed36cf4408751e209a09c5e2363fc06b6074e966995cdbdc365a9d"}, 1121 | {file = "tarina-0.6.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa166fe58b65be6aed113fca54851b236cfd5c23e33f5e61c403f7b71eda17a"}, 1122 | {file = "tarina-0.6.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04ea6b8f4568fe5b4f167d002fbe05879bfa04a4121b4ca3d294580cfc86b85d"}, 1123 | {file = "tarina-0.6.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cbdf37600789f34c1eba6bf05e246f8e9a5b46cf93f9652e92d355e767bbc430"}, 1124 | {file = "tarina-0.6.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb748ad914898c293771ccdb32c73b3117a15a3234c9d5eb001f834533cf67ef"}, 1125 | {file = "tarina-0.6.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:55ca473eb8f3bbd70af71a29d59c6c0951e19ae24f3be86259b884fc097028a0"}, 1126 | {file = "tarina-0.6.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4b124eee1ce8e25f1c1aba0464c95226ce5ed1b35f079f0fce0f993f38d3393b"}, 1127 | {file = "tarina-0.6.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4295448f4dd9f54703adde2683cf6a515f0944687868da4ec500d595e6a9122d"}, 1128 | {file = "tarina-0.6.8-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:77392b3e53e9e1069b9131b1eb2037c53b360e4d7f3cfa8ae04277fe4e785eb1"}, 1129 | {file = "tarina-0.6.8-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:da9771f40ddcd9da087a4fc2d472b548c0da2bb61ad642b7e186ea7098120be6"}, 1130 | {file = "tarina-0.6.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fac01686a3f9d4461b8ce6ba3afadb5b24138e27f725ee11efc4b057d98d4e4a"}, 1131 | {file = "tarina-0.6.8-cp313-cp313-win32.whl", hash = "sha256:a977e3e38535455b38e018ec345f9982c4d2b40945b330aa5c70c1a24a2f8f5b"}, 1132 | {file = "tarina-0.6.8-cp313-cp313-win_amd64.whl", hash = "sha256:62445e9a7c0ab7ac8f9c316836ade382f4b9780492f0a8a35a658f827bcb1d7f"}, 1133 | {file = "tarina-0.6.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:491b46bfd030f64edc8daac99051e9e1a82d4592aaa9d91c86d18da7f1816a8d"}, 1134 | {file = "tarina-0.6.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8dab1c7ff584f427ae671ecd0379b5795f3ddfee867c5c8997fb110c35b72901"}, 1135 | {file = "tarina-0.6.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9f998c32df568da30f236876118f5c359caf00d4935249c78c2e6e15caa1a72f"}, 1136 | {file = "tarina-0.6.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b427b03be5e9d19882558dafd8cae6decc4406f1511741409f1b75a5ce0bf447"}, 1137 | {file = "tarina-0.6.8-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ec0bb5da7bbea3df20750acab45403c5fbd5a0b5c4dd7267e6d0cb3669935db4"}, 1138 | {file = "tarina-0.6.8-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7acbc4b547fd623beb174431e2d01b2bc2a5d7451fe3d0e9ae3e0118d862d375"}, 1139 | {file = "tarina-0.6.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed03a67986b00411aa5d3cd02896e5daf0e5af44247fd0ef79484b06780a2462"}, 1140 | {file = "tarina-0.6.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:637740055c0e7da77793e0f2d56189f4620e9db31578c590234f85240364e80d"}, 1141 | {file = "tarina-0.6.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9df3d4441b36b7e6ac290b05dd9a1e6193ff0acc2d970be2b855283b76d0ad40"}, 1142 | {file = "tarina-0.6.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ff583c3579e5ea939da67c6a32ebced76b270c884d9f686a003f66f32a0cecdc"}, 1143 | {file = "tarina-0.6.8-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:91589c96904f6a07ccf7fec977c403bab52aad63bd3a801a32e11d2ee85e1d46"}, 1144 | {file = "tarina-0.6.8-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4a7448cacff18d536c3cf4453282ee29cf401442cba1a50f02981e5d4c374d7f"}, 1145 | {file = "tarina-0.6.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:96fe4a3a33fa20cc3bd996ef9e4a909b00c37d89bd24d594a6024ca59a26c6c4"}, 1146 | {file = "tarina-0.6.8-cp39-cp39-win32.whl", hash = "sha256:cbff457659568f46fbbb8e594644278b51efddb532890117b4965b8c059d4d4a"}, 1147 | {file = "tarina-0.6.8-cp39-cp39-win_amd64.whl", hash = "sha256:bace5eeaed47048e9a8e9dd7953aadc11aace8c8a5db8457906d8f9c52604fc5"}, 1148 | {file = "tarina-0.6.8-py3-none-any.whl", hash = "sha256:0e8a04eecb741937244d97da54f8cd36995819fc12a6b60c9b3f3f3d2860ebe6"}, 1149 | {file = "tarina-0.6.8.tar.gz", hash = "sha256:d77d68948967a130066b61e85b21189259030f80ad19295bd81ff6b52f348316"}, 1150 | ] 1151 | 1152 | [[package]] 1153 | name = "tomli" 1154 | version = "2.2.1" 1155 | requires_python = ">=3.8" 1156 | summary = "A lil' TOML parser" 1157 | groups = ["default"] 1158 | marker = "python_version < \"3.11\"" 1159 | files = [ 1160 | {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, 1161 | {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, 1162 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, 1163 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, 1164 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, 1165 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, 1166 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, 1167 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, 1168 | {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, 1169 | {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, 1170 | {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, 1171 | {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, 1172 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, 1173 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, 1174 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, 1175 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, 1176 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, 1177 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, 1178 | {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, 1179 | {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, 1180 | {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, 1181 | {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, 1182 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, 1183 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, 1184 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, 1185 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, 1186 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, 1187 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, 1188 | {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, 1189 | {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, 1190 | {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, 1191 | {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, 1192 | ] 1193 | 1194 | [[package]] 1195 | name = "typing-extensions" 1196 | version = "4.12.2" 1197 | requires_python = ">=3.8" 1198 | summary = "Backported and Experimental Type Hints for Python 3.8+" 1199 | groups = ["default"] 1200 | files = [ 1201 | {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, 1202 | {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, 1203 | ] 1204 | 1205 | [[package]] 1206 | name = "tzdata" 1207 | version = "2025.1" 1208 | requires_python = ">=2" 1209 | summary = "Provider of IANA time zone data" 1210 | groups = ["default"] 1211 | marker = "platform_system == \"Windows\"" 1212 | files = [ 1213 | {file = "tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639"}, 1214 | {file = "tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694"}, 1215 | ] 1216 | 1217 | [[package]] 1218 | name = "tzlocal" 1219 | version = "5.2" 1220 | requires_python = ">=3.8" 1221 | summary = "tzinfo object for the local timezone" 1222 | groups = ["default"] 1223 | dependencies = [ 1224 | "backports-zoneinfo; python_version < \"3.9\"", 1225 | "tzdata; platform_system == \"Windows\"", 1226 | ] 1227 | files = [ 1228 | {file = "tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8"}, 1229 | {file = "tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"}, 1230 | ] 1231 | 1232 | [[package]] 1233 | name = "win32-setctime" 1234 | version = "1.2.0" 1235 | requires_python = ">=3.5" 1236 | summary = "A small Python utility to set file creation time on Windows" 1237 | groups = ["default"] 1238 | marker = "sys_platform == \"win32\"" 1239 | files = [ 1240 | {file = "win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390"}, 1241 | {file = "win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0"}, 1242 | ] 1243 | 1244 | [[package]] 1245 | name = "yarl" 1246 | version = "1.18.3" 1247 | requires_python = ">=3.9" 1248 | summary = "Yet another URL library" 1249 | groups = ["default"] 1250 | dependencies = [ 1251 | "idna>=2.0", 1252 | "multidict>=4.0", 1253 | "propcache>=0.2.0", 1254 | ] 1255 | files = [ 1256 | {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, 1257 | {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, 1258 | {file = "yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed"}, 1259 | {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde"}, 1260 | {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b"}, 1261 | {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5"}, 1262 | {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc"}, 1263 | {file = "yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd"}, 1264 | {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990"}, 1265 | {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db"}, 1266 | {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62"}, 1267 | {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760"}, 1268 | {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b"}, 1269 | {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690"}, 1270 | {file = "yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6"}, 1271 | {file = "yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8"}, 1272 | {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069"}, 1273 | {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193"}, 1274 | {file = "yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889"}, 1275 | {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8"}, 1276 | {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca"}, 1277 | {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8"}, 1278 | {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae"}, 1279 | {file = "yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3"}, 1280 | {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb"}, 1281 | {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e"}, 1282 | {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59"}, 1283 | {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d"}, 1284 | {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e"}, 1285 | {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a"}, 1286 | {file = "yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1"}, 1287 | {file = "yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5"}, 1288 | {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50"}, 1289 | {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576"}, 1290 | {file = "yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640"}, 1291 | {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2"}, 1292 | {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75"}, 1293 | {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512"}, 1294 | {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba"}, 1295 | {file = "yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb"}, 1296 | {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272"}, 1297 | {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6"}, 1298 | {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e"}, 1299 | {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb"}, 1300 | {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393"}, 1301 | {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285"}, 1302 | {file = "yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2"}, 1303 | {file = "yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477"}, 1304 | {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb"}, 1305 | {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa"}, 1306 | {file = "yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782"}, 1307 | {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0"}, 1308 | {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482"}, 1309 | {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186"}, 1310 | {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58"}, 1311 | {file = "yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53"}, 1312 | {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2"}, 1313 | {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8"}, 1314 | {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1"}, 1315 | {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a"}, 1316 | {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10"}, 1317 | {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8"}, 1318 | {file = "yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d"}, 1319 | {file = "yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c"}, 1320 | {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04"}, 1321 | {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719"}, 1322 | {file = "yarl-1.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e"}, 1323 | {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee"}, 1324 | {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789"}, 1325 | {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8"}, 1326 | {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c"}, 1327 | {file = "yarl-1.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5"}, 1328 | {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1"}, 1329 | {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24"}, 1330 | {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318"}, 1331 | {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985"}, 1332 | {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910"}, 1333 | {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1"}, 1334 | {file = "yarl-1.18.3-cp39-cp39-win32.whl", hash = "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5"}, 1335 | {file = "yarl-1.18.3-cp39-cp39-win_amd64.whl", hash = "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9"}, 1336 | {file = "yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b"}, 1337 | {file = "yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1"}, 1338 | ] 1339 | 1340 | [[package]] 1341 | name = "zipp" 1342 | version = "3.21.0" 1343 | requires_python = ">=3.9" 1344 | summary = "Backport of pathlib-compatible object wrapper for zip files" 1345 | groups = ["default"] 1346 | files = [ 1347 | {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, 1348 | {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, 1349 | ] 1350 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "nonebot-plugin-picstatus" 3 | dynamic = ["version"] 4 | description = "A NoneBot2 plugin generates a picture which shows the status of current device" 5 | authors = [{ name = "LgCookie", email = "lgc2333@126.com" }] 6 | dependencies = [ 7 | "nonebot2>=2.4.1", 8 | "nonebot-plugin-apscheduler>=0.5.0", 9 | "nonebot-plugin-alconna>=0.54.2", 10 | "nonebot-plugin-userinfo>=0.2.6", 11 | "nonebot-plugin-htmlrender>=0.5.1", 12 | "cookit[jinja,loguru,playwright,pydantic]>=0.9.3", 13 | "psutil>=6.1.1", 14 | "py-cpuinfo>=9.0.0", 15 | "anyio>=4.8.0", 16 | "httpx>=0.27.2", 17 | "jinja2>=3.1.5", 18 | "yarl>=1.18.3", 19 | ] 20 | requires-python = ">=3.9,<4.0" 21 | readme = "README.md" 22 | license = { text = "MIT" } 23 | 24 | [project.urls] 25 | homepage = "https://github.com/lgc-NB2Dev/nonebot-plugin-picstatus" 26 | 27 | [tool.pdm] 28 | 29 | [tool.pdm.version] 30 | source = "file" 31 | path = "nonebot_plugin_picstatus/__init__.py" 32 | 33 | [tool.pdm.build] 34 | includes = [] 35 | 36 | [build-system] 37 | requires = ["pdm-backend"] 38 | build-backend = "pdm.backend" 39 | --------------------------------------------------------------------------------