141 Commits

Author SHA1 Message Date
suyiiyii 074fe3ff58 ♻️ _site_name 和 _default_cookie_cd 内置 CookieClientManager 2024-10-29 21:28:35 +08:00
suyiiyii 8a8a48aef6 ♻️ 移除CookieSite 2024-10-29 21:17:07 +08:00
suyiiyii c5dea7e252 ♻️ 初步移除CookieSite 2024-10-29 21:12:50 +08:00
suyiiyii 07190a7f64 ♻️ 改回 _site_name 2024-10-29 20:53:23 +08:00
suyiiyii 62386931d7 ♻️ 移除 bilibili 的 _site_name 属性 2024-10-29 14:43:16 +08:00
suyiiyii 32c237015f ♻️ 更新部分方法使用 scheduler_dict 进行cookie操作 2024-10-29 14:15:15 +08:00
suyiiyii 29c2eb456d ♻️ 更新部分方法使用 scheduler_dict 进行cookie操作 2024-10-29 12:24:31 +08:00
suyiiyii 9d985eb3c8 ♻️ bilibili 移除 _client 属性 2024-10-29 12:23:50 +08:00
suyiiyii 4805d0d77c ♻️ 在 CookieClientManager 中添加对对应的 Site 的引用 2024-10-29 11:33:28 +08:00
suyiiyii 81e53419a3 ♻️ 将 refresh_anonymous_cookie 改为内部方法, 同时外部使用 refresh_client 方法进行刷新匿名 cookie 2024-10-29 09:38:08 +08:00
suyiiyii b130627d7e ♻️ 还原对 bilibili platform 的修改 2024-10-29 00:48:35 +08:00
suyiiyii 4dd4555fc7 📝 更新文档 2024-10-29 00:44:58 +08:00
suyiiyii e6c45e5b1b ♻️ 为 row2dict 添加类型注解 2024-10-28 23:56:10 +08:00
suyiiyii b60ba566de ♻️ 将 default_cd 重命名为 default_cookie_cd 2024-10-28 23:54:12 +08:00
suyiiyii ac794efd18 :refactor: 将 cookie default_cd 移动到 CookieSite 内部 2024-10-28 23:50:04 +08:00
suyiiyii cdd671b15f 🐛 修复「管理后台」重复发送消息 2024-10-28 23:06:54 +08:00
suyiiyii 6e53c6f4b2 🐛 优化对 bilibili cookie 的 mock 2024-10-28 22:48:11 +08:00
suyiiyii 3a0f95b712 🐛 优化对 weibo get_cookie_name 的 mock 2024-10-28 22:47:31 +08:00
suyiiyii 641cc44a12 🔀 merge 2024-10-28 22:07:20 +08:00
suyiiyii 42cc56ac24 🐛 优化对 bilibili cookie 的 mock 2024-10-28 22:06:12 +08:00
suyiiyii 0deb406692 🐛 更新判断superuser的私聊消息的逻辑 2024-10-28 22:00:15 +08:00
suyiiyii eb64eab14a 添加cookie命令的无权限提示 2024-10-28 13:46:22 +08:00
suyiiyii 3043817de4 🐛 为cookie相关操作添加非私聊拒绝提示 2024-10-28 13:29:10 +08:00
suyiiyii 0985705c22 🐛 添加 Cookie 时,显示 「无法获取cookie_name」 的错误提示 2024-10-28 13:17:36 +08:00
suyiiyii 438b23a0b9 🐛 从 Site 中移除 「无效的 Cookie」 文本 2024-10-28 12:44:51 +08:00
suyiiyii dcd32f0662 📝 更新文档顶栏 2024-10-28 12:37:17 +08:00
suyiiyii 60cee1bcc1 🐛 修复B站获取匿名Cookie逻辑-ExClimbWuzhi 2024-10-24 21:40:32 +08:00
suyiiyii e64fb09145 🐛 修复B站获取匿名Cookie逻辑 2024-10-24 17:43:13 +08:00
suyiiyii d2c33feff8 mock生成匿名Cookie的逻辑 2024-10-23 10:53:11 +08:00
suyiiyii ef52339337 移除用于测试的 token 2024-10-22 21:54:04 +08:00
suyiiyii d614d8bf64 为 bilibili 初步适配Cookie功能 2024-10-22 21:15:37 +08:00
suyiiyii e84894cd9e CookieClientManager中提出_generate_anonymous_cookie方法 2024-10-22 21:13:31 +08:00
suyiiyii ff1c3c3159 WebUI 中,允许查看 Cookie 的 content 2024-10-21 20:58:21 +08:00
suyiiyii 8d32145ea8 ✏️ 改改变量名 2024-10-21 20:43:08 +08:00
suyiiyii f5d30b998c 📝 添加 获取Cookie 的文档 2024-10-21 13:44:46 +08:00
suyiiyii a090ff2127 Merge branch 'MountainDash:main' into cookie 2024-10-21 13:21:44 +08:00
suyiiyii 6474504c30 (admin) 添加验证Cookie有效性 2024-10-21 11:22:54 +08:00
suyiiyii a78bb73281 (admin) 添加验证Cookie有效性接口 2024-10-21 10:04:00 +08:00
suyiiyii c3289671d8 📝 关于 Cookie 的开发指南 2024-10-19 13:25:07 +08:00
suyiiyii 0f4b0aab86 📝 好像还是要加上sass-embedded这个库 2024-10-16 18:15:07 +08:00
suyiiyii 69ef94bcf5 📝 破防了,不整zenuml了 2024-10-16 17:14:59 +08:00
suyiiyii 3c090db0cb 📝 似乎可以了? 2024-10-16 10:41:36 +08:00
suyiiyii 56627753fc 📝 怎么前端编译报错了( 2024-10-16 09:35:59 +08:00
suyiiyii 7359b3ef9d 📝 怎么前端编译报错了( 2024-10-16 09:33:59 +08:00
suyiiyii 7622d3da1e 📝 好像可以用 zenuml 了 ) 2024-10-15 21:43:25 +08:00
suyiiyii 3773c77864 📝 试试zenuml ( 2024-10-15 18:12:53 +08:00
suyiiyii cdc8de9619 📝 试试流程图) 2024-10-14 15:35:29 +08:00
suyiiyii 7aac134d5d 📝 测试mermaid画图 2024-10-14 12:32:59 +08:00
suyiiyii b2b20ab7c5 📝 添加cookie模块使用文档 2024-10-14 01:14:03 +08:00
suyiiyii 2093622672 🐛 改改单测 2024-10-13 21:51:47 +08:00
suyiiyii b6ba904a68 🐛 又忘记改单测了( 2024-10-13 21:30:08 +08:00
suyiiyii f3d8b7d5bc 添加 cookie 时自动使用用户名命名(weibo) 2024-10-13 20:44:37 +08:00
suyiiyii b1b8d37171 (admin) console.log()忘记删了 ( 2024-10-12 13:41:28 +08:00
suyiiyii 271e3e7a72 🗑️ 删除旧的CookieTargetManager 2024-10-12 13:37:17 +08:00
suyiiyii a941d77257 (admin) 支持删除cookieTarget( 2024-10-12 13:35:53 +08:00
suyiiyii 6d8de1b59e (admin) 支持添加cookieTarget 2024-10-12 13:14:04 +08:00
suyiiyii 8174ec895d (admin) 支持查看cookie详情 2024-10-11 13:41:00 +08:00
suyiiyii 10bb6179ae (admin) 支持查看cookie详情 2024-10-11 13:40:31 +08:00
suyiiyii 7030758ac9 (admin) 支持删除cookie 2024-10-11 12:05:28 +08:00
suyiiyii a18b2e4f7b (admin) 支持添加cookie 2024-10-11 11:54:30 +08:00
suyiiyii 3f3cc2d25e 🐛 更改命名siteName以符合小驼峰规范 2024-10-11 11:37:18 +08:00
suyiiyii 5b8d0440ee (admin) 完善 将site选择移动到侧边栏 2024-10-11 11:12:15 +08:00
suyiiyii 24251c2728 (admin) 将site选择移动到侧边栏 2024-10-09 23:23:43 +08:00
suyiiyii 19345cf960 移动validate_cookie到CookieSite中 2024-10-09 23:18:43 +08:00
suyiiyii 1346d07982 修改cookie_site.get_cookie_name为异步 2024-10-09 23:14:17 +08:00
suyiiyii 6c0efdddfc 🔀 merge main 2024-10-08 17:24:34 +08:00
suyiiyii 1d1b9f6574 🐛 config 中添加 clear_db 方法,用于清空数据库内容;添加clear_db fixture,用于在单测前后清空数据库 2024-09-28 16:51:22 +08:00
suyiiyii aa897939d9 为什么 eslint 的warning都不让我过编译 ( 2024-09-27 09:51:19 +08:00
suyiiyii 7d3193d958 为什么前端 pnpm 和 yarn 的行为还不一样( 2024-09-27 01:18:59 +08:00
suyiiyii f31a798326 修修单测 2024-09-27 00:45:45 +08:00
pre-commit-ci[bot] 6b6bf9d8f8 💄 auto fix by pre-commit hooks 2024-09-26 11:41:12 +00:00
suyiiyii 54d37e254d 📝 尝试添加文档 2024-09-26 19:38:43 +08:00
suyiiyii 8fa456bc4e 📝 尝试添加文档 2024-09-26 19:37:06 +08:00
suyiiyii b04cbc2ced 导入导出 cookie 优化代码 2024-09-23 13:49:39 +08:00
suyiiyii 5d160dcadc 导入导出支持 cookie 2024-09-23 11:44:33 +08:00
suyiiyii 8742de6cd1 在 cookie 中添加 cookie_name 字段 2024-09-23 10:29:56 +08:00
suyiiyii 59d42531a3 听劝,修改部分样式 XD 2024-09-22 19:54:17 +08:00
suyiiyii cc31ef88ef 听劝,修改部分样式 XD 2024-09-22 19:49:22 +08:00
suyiiyii 0083f0311a 支持删除 CookieTarget 2024-09-22 17:59:01 +08:00
suyiiyii 6990f04a74 支持删除 CookieTarget 2024-09-22 17:22:14 +08:00
suyiiyii bc44e40f56 支持删除 CookieTarget 2024-09-22 17:12:52 +08:00
suyiiyii bb63529fe8 支持添加 CookieTarget 2024-09-22 17:02:10 +08:00
suyiiyii 75a55c009a 添加 CookieTarget 页面 2024-09-22 14:33:56 +08:00
suyiiyii 4c20e47399 添加 Cookie 增删查 功能 2024-09-22 13:52:51 +08:00
suyiiyii db4b848e3f 添加 Cookie 界面 2024-09-22 13:09:16 +08:00
suyiiyii 54c9020c36 添加cookie页面 2024-09-22 00:28:26 +08:00
suyiiyii 43fb5231b8 🔀 merge 2024-09-21 12:26:36 +08:00
suyiiyii 0aeeb28919 🐛 t 2024-09-21 12:24:45 +08:00
suyiiyii 94daf74359 web api 初步完工 2024-09-20 15:39:22 +08:00
suyiiyii c85e77c801 web api 更新 2024-09-20 00:25:40 +08:00
suyiiyii d890d32bba web api 初稿 2024-09-19 21:16:56 +08:00
suyiiyii 76f271584f web api 初稿 2024-09-19 20:28:16 +08:00
suyiiyii 6cbc6f7d4d 🔀 merge 2024-09-19 10:00:34 +08:00
suyiiyii c784417ecc 初步实现删除cookie的单元测试 2024-09-13 20:01:54 +08:00
suyiiyii dd802a9c17 初步实现添加cookie的单元测试 2024-09-13 19:12:19 +08:00
suyiiyii 2cfd58373f pytest db_config 2024-09-13 14:12:02 +08:00
suyiiyii 4791fb69e0 ♻️ 重构 get_cookie 方法 2024-09-13 11:34:45 +08:00
suyiiyii 4b8d6a9379 🐛 fix 2024-09-13 10:11:54 +08:00
suyiiyii b25fcd9ac2 尝试添加一种可以跳过当前请求的方式 2024-09-13 01:07:34 +08:00
suyiiyii 16331b50d5 根据数据库的修改对应的逻辑,同时移除多余的init_cookie和_check_cookie 2024-09-13 01:00:53 +08:00
suyiiyii af246df222 俺又来改数据库哩 2024-09-13 00:35:14 +08:00
suyiiyii 4f73f8a08c 🐛 弃用_cookie_client_manger_,改用issubclass判断是否为CookieClientManager 2024-09-13 00:26:28 +08:00
suyiiyii 5111baa89c 🐛 应用部分推荐的重命名 2024-09-13 00:11:49 +08:00
suyiiyii f865cef427 🐛 调整日志等级 2024-09-12 23:33:11 +08:00
suyiiyii 318ba8fb3c 🔀 merge 2024-09-09 18:41:01 +08:00
suyiiyii 2c2c9a091c ♻️ del_cookie 2024-09-09 18:36:18 +08:00
suyiiyii 4a5e00c094 :recycles: ddel_cookie_target 2024-09-09 18:32:53 +08:00
suyiiyii 404b1e445c :recycles: add_cookie_target 2024-09-09 18:23:23 +08:00
suyiiyii d43d042618 :recycles: add_cookie 2024-09-09 11:35:39 +08:00
suyiiyii 65a5976897 :recycles: CookieClientManager 2024-09-09 11:06:59 +08:00
suyiiyii f959e3ee08 :recycles: 仿照 platform_manager 添加 site_manager 2024-09-09 11:01:34 +08:00
suyiiyii 4db7e7b911 :recycles: DBConfig中 替换platform_name为site_name 2024-09-08 18:38:38 +08:00
suyiiyii 275bc0cb53 :recycles: 注释掉cookie相关代码,使得bison可以正常运行 2024-09-08 18:21:57 +08:00
suyiiyii ce1f1bbedb 又来改数据库了( 2024-09-08 18:17:06 +08:00
suyiiyii 7c9e191f40 删除cookie 对话 2024-09-08 15:56:44 +08:00
suyiiyii 940301a6fc 取消关联cookie 对话 2024-09-08 15:17:19 +08:00
suyiiyii 61dcf879ce ♻️ 整理代码 2024-09-08 13:03:47 +08:00
suyiiyii a6227828e3 ♻️ 整理代码 2024-09-08 12:59:44 +08:00
suyiiyii bbc5492193 为匿名cookie设置标签 2024-09-08 12:22:06 +08:00
suyiiyii eddd3e42a1 修改_choose_cookie的逻辑以支持no target的Platform 2024-09-06 11:33:39 +08:00
suyiiyii 06079b98f7 关联cookie是不显示匿名cookie 2024-09-06 10:16:24 +08:00
suyiiyii 418a941448 添加不合法cookie的提示 2024-09-06 10:08:08 +08:00
suyiiyii afd1bee762 将get_cookie_friendly_name和valid_cookie移动到ccm内部 2024-09-06 01:08:32 +08:00
suyiiyii b61bde6e3f 关联cookie时,只显示支持的订阅 2024-09-06 00:39:57 +08:00
suyiiyii 6537f01a34 集中判断是否为CookieClientManager 2024-09-06 00:34:52 +08:00
suyiiyii 0ce2893911 匿名cookie和用户cookie一起调度 2024-09-06 00:25:33 +08:00
suyiiyii cf3966e69b 添加cookie只显示支持的Platform 2024-09-06 00:14:35 +08:00
suyiiyii 370fc250f0 数据库Cookie表添加is_universal属性 2024-09-05 19:32:47 +08:00
suyiiyii 3bd0867f0e 数据库Cookie表添加cd属性 2024-09-05 16:07:59 +08:00
suyiiyii 01435eeded 提出assemble_client方法 2024-09-04 01:19:53 +08:00
suyiiyii 498e7d60d4 根据条件选择Cookie 和 状态回写数据库 2024-09-03 13:15:18 +08:00
suyiiyii 055ed6e02a 使用闭包实现client hook 2024-09-03 10:52:20 +08:00
suyiiyii 4ce6b85f79 weibo 带 cookie mvp 2024-09-03 10:00:53 +08:00
suyiiyii 7901b845ea 初步实现携带cookie请求 2024-09-02 23:13:29 +08:00
suyiiyii 1cd778c2e0 ♻️ cookie 组件不再与 user 关联 2024-08-31 23:07:43 +08:00
suyiiyii c828fd94e4 ♻️ cookie 组件不再与 user 关联 2024-08-31 23:04:49 +08:00
suyiiyii ffae6f2ec5 添加cookie的时候显示关联的cookie 2024-08-26 18:09:26 +08:00
suyiiyii 6f20dbf358 支持对话关联cookie到订阅目标 2024-08-26 17:32:36 +08:00
suyiiyii b655eff755 支持对话添加cookie 2024-08-26 10:37:03 +08:00
suyiiyii c264ad374b 🐛 stash 2024-08-22 21:50:25 +08:00
suyiiyii 7913f7485a 添加cookie相关的数据库表 2024-08-22 20:55:39 +08:00
43 changed files with 23329 additions and 754 deletions
+2 -2
View File
@@ -80,7 +80,7 @@ jobs:
run: poetry run pytest --cov-report xml --cov=./nonebot_bison -k 'not compare and not render' -n auto
- name: Upload coverage report
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: smoke-test
@@ -114,7 +114,7 @@ jobs:
run: poetry run pytest --cov-report xml --cov=./nonebot_bison -k 'not compare' -n auto
- name: Upload coverage report
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: all-test
+7 -7
View File
@@ -7,23 +7,23 @@ ci:
autoupdate_commit_msg: ":arrow_up: auto update by pre-commit hooks"
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.1
rev: v0.6.3
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
stages: [pre-commit]
stages: [commit]
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- id: isort
stages: [pre-commit]
stages: [commit]
- repo: https://github.com/psf/black
rev: 24.10.0
rev: 24.8.0
hooks:
- id: black
stages: [pre-commit]
stages: [commit]
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0-alpha.8
@@ -31,10 +31,10 @@ repos:
- id: prettier
types_or: [javascript, jsx, ts, tsx, markdown, yaml, json]
exclude: "admin-frontend/"
stages: [pre-commit]
stages: [commit]
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v9.16.0
rev: v9.9.1
hooks:
- id: eslint
additional_dependencies:
-18
View File
@@ -4,26 +4,8 @@
### 新功能
- :sparkles: B站获取OPUS格式动态 [@AzideCupric](https://github.com/AzideCupric) ([#651](https://github.com/MountainDash/nonebot-bison/pull/651))
- :sparkles: 添加 Cookie 组件 [@suyiiyii](https://github.com/suyiiyii) ([#633](https://github.com/MountainDash/nonebot-bison/pull/633))
### Bug 修复
- :bug: 修复 cookie 模块 type hint [@suyiiyii](https://github.com/suyiiyii) ([#658](https://github.com/MountainDash/nonebot-bison/pull/658))
- :bug: B站转发动态补充 DeletedItem 类型解析 [@AzideCupric](https://github.com/AzideCupric) ([#659](https://github.com/MountainDash/nonebot-bison/pull/659))
- :bug: 小刻食堂cdn使用https [@phidiaLam](https://github.com/phidiaLam) ([#650](https://github.com/MountainDash/nonebot-bison/pull/650))
## v0.9.5
### 新功能
- :sparkles: 更新默认UA为Windows平台 [@suyiiyii](https://github.com/suyiiyii) ([#643](https://github.com/MountainDash/nonebot-bison/pull/643))
### Bug 修复
- 🐛 修复微博更换长内容接口 [@phidiaLam](https://github.com/phidiaLam) ([#645](https://github.com/MountainDash/nonebot-bison/pull/645))
- :bug: 修复B站获取匿名Cookie逻辑 [@suyiiyii](https://github.com/suyiiyii) ([#644](https://github.com/MountainDash/nonebot-bison/pull/644))
### 文档
- 📝 小刻食堂剪彩文档 [@phidiaLam](https://github.com/phidiaLam) ([#636](https://github.com/MountainDash/nonebot-bison/pull/636))
+2 -2
View File
@@ -14,7 +14,7 @@
"@testing-library/react": "^16.0.1",
"@testing-library/user-event": "^14.5.2",
"@types/jest": "^29.5.13",
"@types/node": "^22.9.0",
"@types/node": "^20.16.5",
"@types/react": "^18.3.7",
"@types/react-dom": "^18.3.0",
"react": "^18.3.1",
@@ -25,7 +25,7 @@
"redux": "^5.0.1",
"redux-persist": "^6.0.0",
"typescript": "^5.6.2",
"web-vitals": "^4.2.4"
"web-vitals": "^3.5.2"
},
"scripts": {
"start": "react-scripts start",
+69 -70
View File
@@ -36,8 +36,8 @@ importers:
specifier: ^29.5.13
version: 29.5.13
'@types/node':
specifier: ^22.9.0
version: 22.9.0
specifier: ^20.16.5
version: 20.16.5
'@types/react':
specifier: ^18.3.7
version: 18.3.7
@@ -58,7 +58,7 @@ importers:
version: 6.26.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react-scripts:
specifier: 5.0.1
version: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@types/babel__core@7.1.20)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)(react@18.3.1)(type-fest@0.21.3)(typescript@5.6.2)
version: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@types/babel__core@7.1.20)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)(react@18.3.1)(type-fest@0.21.3)(typescript@5.6.2)
redux:
specifier: ^5.0.1
version: 5.0.1
@@ -69,8 +69,8 @@ importers:
specifier: ^5.6.2
version: 5.6.2
web-vitals:
specifier: ^4.2.4
version: 4.2.4
specifier: ^3.5.2
version: 3.5.2
devDependencies:
'@testing-library/jest-dom':
specifier: ^6.5.0
@@ -86,10 +86,10 @@ importers:
version: 8.57.1
eslint-config-airbnb:
specifier: ^19.0.4
version: 19.0.4(eslint-plugin-import@2.30.0)(eslint-plugin-jsx-a11y@6.10.0(eslint@8.57.1))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.1))(eslint-plugin-react@7.36.1(eslint@8.57.1))(eslint@8.57.1)
version: 19.0.4(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint-plugin-jsx-a11y@6.10.0(eslint@8.57.1))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.1))(eslint-plugin-react@7.36.1(eslint@8.57.1))(eslint@8.57.1)
eslint-config-airbnb-typescript:
specifier: ^18.0.0
version: 18.0.0(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1)
version: 18.0.0(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1)
eslint-import-resolver-typescript:
specifier: ^3.6.3
version: 3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1)
@@ -1362,8 +1362,8 @@ packages:
'@types/mime@3.0.1':
resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==}
'@types/node@22.9.0':
resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==}
'@types/node@20.16.5':
resolution: {integrity: sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==}
'@types/parse-json@4.0.0':
resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==}
@@ -2827,7 +2827,6 @@ packages:
eslint@8.57.1:
resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options.
hasBin: true
espree@9.6.1:
@@ -3209,8 +3208,8 @@ packages:
resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==}
engines: {node: '>= 6'}
http-proxy-middleware@2.0.7:
resolution: {integrity: sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==}
http-proxy-middleware@2.0.6:
resolution: {integrity: sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==}
engines: {node: '>=12.0.0'}
peerDependencies:
'@types/express': ^4.17.13
@@ -5668,8 +5667,8 @@ packages:
wbuf@1.7.3:
resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==}
web-vitals@4.2.4:
resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==}
web-vitals@3.5.2:
resolution: {integrity: sha512-c0rhqNcHXRkY/ogGDJQxZ9Im9D19hDihbzSQJrsioex+KnFgmMzBiy57Z1EjkhX/+OjyBpclDCzz2ITtjokFmg==}
webidl-conversions@4.0.2:
resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
@@ -6980,7 +6979,7 @@ snapshots:
'@jest/console@27.5.1':
dependencies:
'@jest/types': 27.5.1
'@types/node': 22.9.0
'@types/node': 20.16.5
chalk: 4.1.2
jest-message-util: 27.5.1
jest-util: 27.5.1
@@ -6989,7 +6988,7 @@ snapshots:
'@jest/console@28.1.3':
dependencies:
'@jest/types': 28.1.3
'@types/node': 22.9.0
'@types/node': 20.16.5
chalk: 4.1.2
jest-message-util: 28.1.3
jest-util: 28.1.3
@@ -7002,7 +7001,7 @@ snapshots:
'@jest/test-result': 27.5.1
'@jest/transform': 27.5.1
'@jest/types': 27.5.1
'@types/node': 22.9.0
'@types/node': 20.16.5
ansi-escapes: 4.3.2
chalk: 4.1.2
emittery: 0.8.1
@@ -7036,7 +7035,7 @@ snapshots:
dependencies:
'@jest/fake-timers': 27.5.1
'@jest/types': 27.5.1
'@types/node': 22.9.0
'@types/node': 20.16.5
jest-mock: 27.5.1
'@jest/expect-utils@29.7.0':
@@ -7047,7 +7046,7 @@ snapshots:
dependencies:
'@jest/types': 27.5.1
'@sinonjs/fake-timers': 8.1.0
'@types/node': 22.9.0
'@types/node': 20.16.5
jest-message-util: 27.5.1
jest-mock: 27.5.1
jest-util: 27.5.1
@@ -7065,7 +7064,7 @@ snapshots:
'@jest/test-result': 27.5.1
'@jest/transform': 27.5.1
'@jest/types': 27.5.1
'@types/node': 22.9.0
'@types/node': 20.16.5
chalk: 4.1.2
collect-v8-coverage: 1.0.1
exit: 0.1.2
@@ -7149,7 +7148,7 @@ snapshots:
dependencies:
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
'@types/node': 22.9.0
'@types/node': 20.16.5
'@types/yargs': 16.0.9
chalk: 4.1.2
@@ -7158,7 +7157,7 @@ snapshots:
'@jest/schemas': 28.1.3
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
'@types/node': 22.9.0
'@types/node': 20.16.5
'@types/yargs': 17.0.32
chalk: 4.1.2
@@ -7167,7 +7166,7 @@ snapshots:
'@jest/schemas': 29.6.3
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
'@types/node': 22.9.0
'@types/node': 20.16.5
'@types/yargs': 17.0.32
chalk: 4.1.2
@@ -7436,20 +7435,20 @@ snapshots:
'@types/body-parser@1.19.2':
dependencies:
'@types/connect': 3.4.35
'@types/node': 22.9.0
'@types/node': 20.16.5
'@types/bonjour@3.5.10':
dependencies:
'@types/node': 22.9.0
'@types/node': 20.16.5
'@types/connect-history-api-fallback@1.3.5':
dependencies:
'@types/express-serve-static-core': 4.17.31
'@types/node': 22.9.0
'@types/node': 20.16.5
'@types/connect@3.4.35':
dependencies:
'@types/node': 22.9.0
'@types/node': 20.16.5
'@types/eslint@8.4.10':
dependencies:
@@ -7464,7 +7463,7 @@ snapshots:
'@types/express-serve-static-core@4.17.31':
dependencies:
'@types/node': 22.9.0
'@types/node': 20.16.5
'@types/qs': 6.9.7
'@types/range-parser': 1.2.4
@@ -7477,13 +7476,13 @@ snapshots:
'@types/graceful-fs@4.1.5':
dependencies:
'@types/node': 22.9.0
'@types/node': 20.16.5
'@types/html-minifier-terser@6.1.0': {}
'@types/http-proxy@1.17.9':
dependencies:
'@types/node': 22.9.0
'@types/node': 20.16.5
'@types/istanbul-lib-coverage@2.0.6': {}
@@ -7506,7 +7505,7 @@ snapshots:
'@types/mime@3.0.1': {}
'@types/node@22.9.0':
'@types/node@20.16.5':
dependencies:
undici-types: 6.19.8
@@ -7533,7 +7532,7 @@ snapshots:
'@types/resolve@1.17.1':
dependencies:
'@types/node': 22.9.0
'@types/node': 20.16.5
'@types/retry@0.12.0': {}
@@ -7546,11 +7545,11 @@ snapshots:
'@types/serve-static@1.15.0':
dependencies:
'@types/mime': 3.0.1
'@types/node': 22.9.0
'@types/node': 20.16.5
'@types/sockjs@0.3.33':
dependencies:
'@types/node': 22.9.0
'@types/node': 20.16.5
'@types/stack-utils@2.0.3': {}
@@ -7560,7 +7559,7 @@ snapshots:
'@types/ws@8.5.3':
dependencies:
'@types/node': 22.9.0
'@types/node': 20.16.5
'@types/yargs-parser@21.0.3': {}
@@ -9098,7 +9097,7 @@ snapshots:
optionalDependencies:
source-map: 0.6.1
eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.30.0)(eslint@8.57.1):
eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1):
dependencies:
confusing-browser-globals: 1.0.11
eslint: 8.57.1
@@ -9107,19 +9106,19 @@ snapshots:
object.entries: 1.1.7
semver: 6.3.1
eslint-config-airbnb-typescript@18.0.0(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1):
eslint-config-airbnb-typescript@18.0.0(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1):
dependencies:
'@typescript-eslint/eslint-plugin': 8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2)
'@typescript-eslint/parser': 8.6.0(eslint@8.57.1)(typescript@5.6.2)
eslint: 8.57.1
eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.30.0)(eslint@8.57.1)
eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1)
transitivePeerDependencies:
- eslint-plugin-import
eslint-config-airbnb@19.0.4(eslint-plugin-import@2.30.0)(eslint-plugin-jsx-a11y@6.10.0(eslint@8.57.1))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.1))(eslint-plugin-react@7.36.1(eslint@8.57.1))(eslint@8.57.1):
eslint-config-airbnb@19.0.4(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint-plugin-jsx-a11y@6.10.0(eslint@8.57.1))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.1))(eslint-plugin-react@7.36.1(eslint@8.57.1))(eslint@8.57.1):
dependencies:
eslint: 8.57.1
eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.30.0)(eslint@8.57.1)
eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1)
eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.1)
eslint-plugin-react: 7.36.1(eslint@8.57.1)
@@ -9127,7 +9126,7 @@ snapshots:
object.assign: 4.1.4
object.entries: 1.1.6
eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)(jest@27.5.1)(typescript@5.6.2):
eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)(jest@27.5.1)(typescript@5.6.2):
dependencies:
'@babel/core': 7.25.2
'@babel/eslint-parser': 7.19.1(@babel/core@7.25.2)(eslint@8.57.1)
@@ -9138,7 +9137,7 @@ snapshots:
confusing-browser-globals: 1.0.11
eslint: 8.57.1
eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(eslint@8.57.1)
eslint-plugin-import: 2.30.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
eslint-plugin-import: 2.30.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)
eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(jest@27.5.1)(typescript@5.6.2)
eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.1)
eslint-plugin-react: 7.36.1(eslint@8.57.1)
@@ -9168,7 +9167,7 @@ snapshots:
debug: 4.3.5
enhanced-resolve: 5.17.1
eslint: 8.57.1
eslint-module-utils: 2.8.1(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
eslint-module-utils: 2.8.1(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)
fast-glob: 3.3.2
get-tsconfig: 4.8.0
is-bun-module: 1.1.0
@@ -9181,7 +9180,7 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
eslint-module-utils@2.11.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
eslint-module-utils@2.11.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1):
dependencies:
debug: 3.2.7
optionalDependencies:
@@ -9192,7 +9191,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.11.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
eslint-module-utils@2.11.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1):
dependencies:
debug: 3.2.7
optionalDependencies:
@@ -9203,7 +9202,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.8.1(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
eslint-module-utils@2.8.1(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1):
dependencies:
debug: 3.2.7
optionalDependencies:
@@ -9221,7 +9220,7 @@ snapshots:
lodash: 4.17.21
string-natural-compare: 3.0.1
eslint-plugin-import@2.30.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
eslint-plugin-import@2.30.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.8
@@ -9232,7 +9231,7 @@ snapshots:
doctrine: 2.1.0
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.11.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
eslint-module-utils: 2.11.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)
hasown: 2.0.2
is-core-module: 2.15.1
is-glob: 4.0.3
@@ -9260,7 +9259,7 @@ snapshots:
doctrine: 2.1.0
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)
hasown: 2.0.2
is-core-module: 2.15.1
is-glob: 4.0.3
@@ -9862,7 +9861,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
http-proxy-middleware@2.0.7(@types/express@4.17.14):
http-proxy-middleware@2.0.6(@types/express@4.17.14):
dependencies:
'@types/http-proxy': 1.17.9
http-proxy: 1.18.1
@@ -10146,7 +10145,7 @@ snapshots:
'@jest/environment': 27.5.1
'@jest/test-result': 27.5.1
'@jest/types': 27.5.1
'@types/node': 22.9.0
'@types/node': 20.16.5
chalk: 4.1.2
co: 4.6.0
dedent: 0.7.0
@@ -10249,7 +10248,7 @@ snapshots:
'@jest/environment': 27.5.1
'@jest/fake-timers': 27.5.1
'@jest/types': 27.5.1
'@types/node': 22.9.0
'@types/node': 20.16.5
jest-mock: 27.5.1
jest-util: 27.5.1
jsdom: 16.7.0
@@ -10264,7 +10263,7 @@ snapshots:
'@jest/environment': 27.5.1
'@jest/fake-timers': 27.5.1
'@jest/types': 27.5.1
'@types/node': 22.9.0
'@types/node': 20.16.5
jest-mock: 27.5.1
jest-util: 27.5.1
@@ -10276,7 +10275,7 @@ snapshots:
dependencies:
'@jest/types': 27.5.1
'@types/graceful-fs': 4.1.5
'@types/node': 22.9.0
'@types/node': 20.16.5
anymatch: 3.1.2
fb-watchman: 2.0.2
graceful-fs: 4.2.11
@@ -10295,7 +10294,7 @@ snapshots:
'@jest/source-map': 27.5.1
'@jest/test-result': 27.5.1
'@jest/types': 27.5.1
'@types/node': 22.9.0
'@types/node': 20.16.5
chalk: 4.1.2
co: 4.6.0
expect: 27.5.1
@@ -10369,7 +10368,7 @@ snapshots:
jest-mock@27.5.1:
dependencies:
'@jest/types': 27.5.1
'@types/node': 22.9.0
'@types/node': 20.16.5
jest-pnp-resolver@1.2.2(jest-resolve@27.5.1):
optionalDependencies:
@@ -10407,7 +10406,7 @@ snapshots:
'@jest/test-result': 27.5.1
'@jest/transform': 27.5.1
'@jest/types': 27.5.1
'@types/node': 22.9.0
'@types/node': 20.16.5
chalk: 4.1.2
emittery: 0.8.1
graceful-fs: 4.2.11
@@ -10458,7 +10457,7 @@ snapshots:
jest-serializer@27.5.1:
dependencies:
'@types/node': 22.9.0
'@types/node': 20.16.5
graceful-fs: 4.2.11
jest-snapshot@27.5.1:
@@ -10491,7 +10490,7 @@ snapshots:
jest-util@27.5.1:
dependencies:
'@jest/types': 27.5.1
'@types/node': 22.9.0
'@types/node': 20.16.5
chalk: 4.1.2
ci-info: 3.9.0
graceful-fs: 4.2.11
@@ -10500,7 +10499,7 @@ snapshots:
jest-util@28.1.3:
dependencies:
'@jest/types': 28.1.3
'@types/node': 22.9.0
'@types/node': 20.16.5
chalk: 4.1.2
ci-info: 3.9.0
graceful-fs: 4.2.11
@@ -10509,7 +10508,7 @@ snapshots:
jest-util@29.7.0:
dependencies:
'@jest/types': 29.6.3
'@types/node': 22.9.0
'@types/node': 20.16.5
chalk: 4.1.2
ci-info: 3.9.0
graceful-fs: 4.2.11
@@ -10539,7 +10538,7 @@ snapshots:
dependencies:
'@jest/test-result': 27.5.1
'@jest/types': 27.5.1
'@types/node': 22.9.0
'@types/node': 20.16.5
ansi-escapes: 4.3.2
chalk: 4.1.2
jest-util: 27.5.1
@@ -10549,7 +10548,7 @@ snapshots:
dependencies:
'@jest/test-result': 28.1.3
'@jest/types': 28.1.3
'@types/node': 22.9.0
'@types/node': 20.16.5
ansi-escapes: 4.3.2
chalk: 4.1.2
emittery: 0.10.2
@@ -10558,19 +10557,19 @@ snapshots:
jest-worker@26.6.2:
dependencies:
'@types/node': 22.9.0
'@types/node': 20.16.5
merge-stream: 2.0.0
supports-color: 7.2.0
jest-worker@27.5.1:
dependencies:
'@types/node': 22.9.0
'@types/node': 20.16.5
merge-stream: 2.0.0
supports-color: 8.1.1
jest-worker@28.1.3:
dependencies:
'@types/node': 22.9.0
'@types/node': 20.16.5
merge-stream: 2.0.0
supports-color: 8.1.1
@@ -11682,7 +11681,7 @@ snapshots:
'@remix-run/router': 1.19.2
react: 18.3.1
react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@types/babel__core@7.1.20)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)(react@18.3.1)(type-fest@0.21.3)(typescript@5.6.2):
react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@types/babel__core@7.1.20)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)(react@18.3.1)(type-fest@0.21.3)(typescript@5.6.2):
dependencies:
'@babel/core': 7.25.2
'@pmmmwh/react-refresh-webpack-plugin': 0.5.9(react-refresh@0.11.0)(type-fest@0.21.3)(webpack-dev-server@4.11.1(webpack@5.94.0))(webpack@5.94.0)
@@ -11700,7 +11699,7 @@ snapshots:
dotenv: 10.0.0
dotenv-expand: 5.1.0
eslint: 8.57.1
eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)(jest@27.5.1)(typescript@5.6.2)
eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)(jest@27.5.1)(typescript@5.6.2)
eslint-webpack-plugin: 3.2.0(eslint@8.57.1)(webpack@5.94.0)
file-loader: 6.2.0(webpack@5.94.0)
fs-extra: 10.1.0
@@ -12704,7 +12703,7 @@ snapshots:
dependencies:
minimalistic-assert: 1.0.1
web-vitals@4.2.4: {}
web-vitals@3.5.2: {}
webidl-conversions@4.0.2: {}
@@ -12740,7 +12739,7 @@ snapshots:
express: 4.19.2
graceful-fs: 4.2.11
html-entities: 2.3.3
http-proxy-middleware: 2.0.7(@types/express@4.17.14)
http-proxy-middleware: 2.0.6(@types/express@4.17.14)
ipaddr.js: 2.0.1
open: 8.4.0
p-retry: 4.6.2
+1 -1
View File
@@ -1,4 +1,4 @@
FROM node:22.11.0 as frontend
FROM node:20.17.0 as frontend
ADD . /app
WORKDIR /app/admin-frontend
RUN npm install -g pnpm
Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 74 KiB

+34 -23
View File
@@ -5,7 +5,7 @@ prev: /usage/
# Cookie 开发须知
本项目将大部分 Cookie 相关逻辑提出到了 Site 及 ClientManger 模块中,只需要继承相关类即可获得使用 Cookie 的能力。
本项目将大部分 Cookie 相关逻辑提出到了 Site 及 ClientManger 模块中,只需要继承相关类即可获得使用 Cookie 的能力。
::: tip
@@ -18,10 +18,11 @@ prev: /usage/
- `nonebot_bison.config.db_model.Cookie`: 用于存储 Cookie 的实体类,包含了 Cookie 的名称、内容、状态等信息
- `nonebot_bison.config.db_model.CookieTarget`: 用于存储 Cookie 与订阅的关联关系
- `nonebot_bison.utils.site.CookieClientManager`: 添加了 Cookie 功能的 ClientManager,是 Cookie 管理功能的核心,调度 Cookie 的功能就在这里实现
- `nonebot_bison.utils.site.CookieSite`: 添加了 Cookie 功能的 Site 类,根据需求添加了和 Site 强相关的 Cookie 功能实现方法
## 快速上手
例如,现在有一个这样子的 Site 类:
例如,现在有一个这样子的 Site 类:
```python
class WeiboSite(Site):
@@ -30,30 +31,39 @@ class WeiboSite(Site):
schedule_setting = {"seconds": 3}
```
简而言之,要让站点获得 Cookie 能力,只需要:
简而言之,要让你的站点获得 Cookie 能力,只需要:
为 Site 类添加一个`client_mgr`字段,值为`CookieClientManager.from_name(name)`,其中`name`为站点名称,这是默认的 Cookie 管理器。
1. 将父类从`Site`改为`CookieSite`
```python {5}
class WeiboSite(Site):
```python {1}
class WeiboSite(CookieSite):
name = "weibo.com"
schedule_type = "interval"
schedule_setting = {"seconds": 3}
client_mgr = CookieClientManager.from_name(name)
```
至此,站点就可以使用 Cookie 了!
2. 为你的 Site 类添加一个`client_mgr`属性,值为`create_cookie_client_manager(name)`,其中`name`为你的站点名称,这是默认的 Cookie 管理器。
```python {5}
class WeiboSite(CookieSite):
name = "weibo.com"
schedule_type = "interval"
schedule_setting = {"seconds": 3}
client_mgr = create_cookie_client_manager(name)
```
至此,你的站点就可以使用 Cookie 了!
## 更好的体验
为了给用户提供更好的体验,还可以创建自己的 `ClientManager`:继承 `CookieClientManager` 并重写`validate_cookie`和`get_target_name`方法。
为了给用户提供更好的体验,还可以为你的 Site 重写`validate_cookie`和`get_target_name`方法。
- `async def validate_cookie(cls, content: str) -> bool`该方法将会在 Cookie 添加时被调用,可以在这里验证 Cookie 的有效性
- `async def get_cookie_name(cls, content: str) -> str`该方法将会在验证 Cookie 成功后被调用,可以在这里设置 Cookie 的名字并展示给用户
- `async def validate_cookie(cls, content: str) -> bool`该方法将会在 Cookie 添加时被调用,可以在这里验证 Cookie 的有效性
- `async def get_cookie_name(cls, content: str) -> str`该方法将会在验证 Cookie 成功后被调用,可以在这里设置 Cookie 的名字并展示给用户
## 自定义 Cookie 调度策略
## 我要自己调度 Cookie
当默认的 Cookie 调度逻辑无法满足需求时,可以重写`CookieClientManager`的`_choose_cookie`方法。
当默认的 Cookie 调度逻辑无法满足你的需求时,可以重写`CookieClientManager`的`_choose_cookie`方法。
目前整体的调度逻辑是:
@@ -84,12 +94,14 @@ sequenceDiagram
- `refresh_anonymous_cookie(cls)` 移除已有的匿名 cookie,添加一个新的匿名 cookie,应该在 CCM 初始化时调用
- `add_user_cookie(cls, content: str)` 添加用户 cookie,在这里对 Cookie 进行检查并获取 cookie_name,写入数据库
- `_generate_hook(self, cookie: Cookie) -> Callable` hook 函数生成器,用于回写请求状态到数据库
- `_generate_hook(self, cookie: Cookie) -> callable` hook 函数生成器,用于回写请求状态到数据库
- `_choose_cookie(self, target: Target) -> Cookie` 选择 cookie 的具体算法
- `add_user_cookie(cls, content: str, cookie_name: str | None = None) -> Cookie` 对外的接口,添加用户 cookie,内部会调用 Site 的方法进行检查
- `get_client(self, target: Target | None) -> AsyncClient` 对外的接口,获取 client,根据 target 选择 cookie
- `_assemble_client(self, client, cookie) -> AsyncClient` 组装 client,可以自定义 cookie 对象的 content 装配到 client 中的方式
CookieSite 的方法见上文
::: details 大致流程
1. `Platfrom` 调用 `CookieClientManager.get_client` 方法,传入 `Target` 对象
@@ -100,19 +112,18 @@ sequenceDiagram
简单来说:
- 如果需要修改 Cookie 的默认参数,可以重写`add_user_cookie`方法,这里设置需要的字段
- 如果需要修改选择 Cookie 的逻辑,可以重写`_choose_cookie`方法,使用自己的算法选择合适的 Cookie 并返回
- 如果需要自定义 Cookie 的格式,可以重写`valid_cookie`方法,自定义验证 Cookie 的逻辑,并重写`_assemble_client`方法,自定义将 Cookie 装配到 Client 中的逻辑
- 如果要在请求结束后做一些操作(例如保存此次请求的结果/状态),可以重写`_response_hook`方法,自定义请求结束后的行为
- 如果需要跳过一次请求,可以在 `get_client` 方法中抛出 `SkipRequestException` 异常,调度器会捕获该异常并跳过此次请求
- 如果需要修改 Cookie 的默认参数,可以重写`add_user_cookie`方法,这里设置需要的属性
- 如果需要修改选择 Cookie 的逻辑,可以重写`_choose_cookie`方法,使用自己的算法选择合适的 Cookie 并返回
- 如果需要自定义 Cookie 的格式,可以重写`valid_cookie`方法,自定义验证 Cookie 的逻辑,并重写`_assemble_client`方法,自定义将 Cookie 装配到 Client 中的逻辑
- 如果要在请求结束后做一些操作(例如保存此次请求的结果/状态),可以重写`_response_hook`方法,自定义请求结束后的行为
## 实名 Cookie 和匿名 Cookie
部分站点所有接口都需要携带 Cookie,对于匿名用户(未登录)也会发放一个临时 Cookie,本项目称为匿名 Cookie。
部分站点所有接口都需要携带 Cookie,对于匿名用户(未登录)也会发放一个临时 Cookie,我们称为匿名 Cookie。
在此基础上,我们添加了用户上传 Cookie 的功能,这种 Cookie 本项目称为实名 Cookie。
在此基础上,我们添加了用户上传 Cookie 的功能,这种 Cookie 我们称为实名 Cookie。
匿名 Cookie 和实名 Cookie 在同一个框架下统一调度,实名 Cookie 优先级高于匿名 Cookie。为了调度,Cookie 对象有以下字段
匿名 Cookie 和实名 Cookie 在同一个框架下统一调度,实名 Cookie 优先级高于匿名 Cookie。为了调度,Cookie 对象有以下属性
```python
# 最后使用的时刻
@@ -137,7 +148,7 @@ sequenceDiagram
- **无 Target 平台的 Cookie 处理方式**
对于不存在 Target 的平台,如小刻食堂,可以重写 add_user_cookie 方法,为用户 Cookie 设置 is_universal 字段。这样,在获取 Client 时,由于传入的 Target 为空,就只会选择 is_universal 的 cookie。实现了无 Target 平台的用户 Cookie 调度。
对于不存在 Target 的平台,如小刻食堂,可以重写 add_user_cookie 方法,为用户 Cookie 设置 is_universal 属性。这样,在获取 Client 时,由于传入的 Target 为空,就只会选择 is_universal 的 cookie。实现了无 Target 平台的用户 Cookie 调度。
## 默认的调度策略
+8 -8
View File
@@ -40,15 +40,15 @@ Cookie 全局生效,这意味着,通过你的 Cookie 获取到的内容,
## :nerd_face: 如何获取 Cookie
对于大部分平台,Bison 支持 JSON 格式的 Cookie,你可以通过浏览器的开发者工具获取。
对于大部分平台,Bison支持JSON格式的Cookie,你可以通过浏览器的开发者工具获取。
- RSS: 对于各种 RSS 订阅,你需要自行准备需要的 Cookie,以 JSON 格式添加即可
- 微博Bison 兼容 RSSHubCookie,以下方法引用自[RSSHub 的文档](https://docs.rsshub.app/zh/deploy/config#%E5%BE%AE%E5%8D%9A)
> 1. 打开并登录 https://m.weibo.cn(确保打开页面为手机版,如果强制跳转电脑端可尝试使用可更改 UserAgent 的浏览器插件)
> 2. 按下 F12 打开控制台,切换至 Network(网络)面板
> 3. 在该网页切换至任意关注分组,并在面板打开最先捕获到的请求(该情形下捕获到的请求路径应包含/feed/group
> 4. 查看该请求的 Headers(请求头), 找到 Cookie 字段并复制内容
- Bilibili: Bison 兼容 RSSHubCookie,以下方法引用自[RSSHub 的文档](https://docs.rsshub.app/zh/deploy/config#bilibili)
- RSS: 对于各种RSS订阅,你需要自行准备需要的Cookie,以JSON格式添加即可
- 微博: Bison兼容RSSHubCookie,以下方法引用自[RSSHub的文档](https://docs.rsshub.app/zh/deploy/config#%E5%BE%AE%E5%8D%9A)
> 1. 打开并登录 https://m.weibo.cn (确保打开页面为手机版,如果强制跳转电脑端可尝试使用可更改 UserAgent 的浏览器插件)
> 2. 按下F12打开控制台,切换至Network(网络)面板
> 3. 在该网页切换至任意关注分组,并在面板打开最先捕获到的请求 (该情形下捕获到的请求路径应包含/feed/group
> 4. 查看该请求的Headers(请求头), 找到Cookie字段并复制内容
- Bilibili: Bison兼容RSSHubCookie,以下方法引用自[RSSHub的文档](https://docs.rsshub.app/zh/deploy/config#bilibili)
> 1. 打开 https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/dynamic_new?uid=0&type=8
> 2. 打开控制台,切换到 Network 面板,刷新
> 3. 点击 dynamic_new 请求,找到 Cookie
-2
View File
@@ -12,7 +12,6 @@ from nonebot.adapters.onebot.v11.event import PrivateMessageEvent
from .api import router as api_router
from ..plugin_config import plugin_config
from .token_manager import token_manager as tm
from ..metrics import metrics_router as metrics_router
if TYPE_CHECKING:
from nonebot.drivers.fastapi import Driver
@@ -47,7 +46,6 @@ def init_fastapi(driver: "Driver"):
description="nonebot-bison webui and api",
)
nonebot_app.include_router(api_router)
nonebot_app.include_router(metrics_router)
nonebot_app.mount("/", SinglePageApplication(directory=static_path), name="bison-frontend")
app = driver.server_app
+6 -12
View File
@@ -15,14 +15,13 @@ from .jwt import load_jwt, pack_jwt
from ..scheduler import scheduler_dict
from ..types import Target as T_Target
from ..utils.get_bot import get_groups
from ..platform import platform_manager
from .token_manager import token_manager
from ..config.db_config import SubscribeDupException
from ..utils.site import CookieClientManager, site_manager, is_cookie_client_manager
from ..platform import site_manager, platform_manager
from ..utils.site import CookieClientManager, is_cookie_client_manager
from ..config import NoSuchUserException, NoSuchTargetException, NoSuchSubscribeException, config
from .types import (
Cookie,
Target,
TokenResp,
GlobalConf,
SiteConfig,
@@ -212,7 +211,7 @@ async def update_weigth_config(platformName: str, target: str, weight_config: We
@router.get("/cookie", dependencies=[Depends(check_is_superuser)])
async def get_cookie(site_name: str | None = None, target: str | None = None) -> list[Cookie]:
async def get_cookie(site_name: str = None, target: str = None) -> list[Cookie]:
cookies_in_db = await config.get_cookie(site_name, is_anonymous=False)
return [
Cookie(
@@ -251,12 +250,7 @@ async def get_cookie_target(
cookie_targets = await config.get_cookie_target()
# TODO: filter in SQL
return [
CookieTarget(
target=Target(
platform_name=x.target.platform_name, target_name=x.target.target_name, target=x.target.target
),
cookie_id=x.cookie.id,
)
x
for x in cookie_targets
if (site_name is None or x.cookie.site_name == site_name)
and (target is None or x.target.target == target)
@@ -265,13 +259,13 @@ async def get_cookie_target(
@router.post("/cookie_target", dependencies=[Depends(check_is_superuser)])
async def add_cookie_target(platform_name: str, target: T_Target, cookie_id: int) -> StatusResp:
async def add_cookie_target(platform_name: str, target: str, cookie_id: int) -> StatusResp:
await config.add_cookie_target(target, platform_name, cookie_id)
return StatusResp(ok=True, msg="")
@router.delete("/cookie_target", dependencies=[Depends(check_is_superuser)])
async def del_cookie_target(platform_name: str, target: T_Target, cookie_id: int) -> StatusResp:
async def del_cookie_target(platform_name: str, target: str, cookie_id: int) -> StatusResp:
await config.delete_cookie_target(target, platform_name, cookie_id)
return StatusResp(ok=True, msg="")
-4
View File
@@ -288,8 +288,6 @@ class DBConfig:
async def get_cookie_by_id(self, cookie_id: int) -> Cookie:
async with create_session() as sess:
cookie = await sess.scalar(select(Cookie).where(Cookie.id == cookie_id))
if not cookie:
raise NoSuchTargetException(f"cookie {cookie_id} not found")
return cookie
async def add_cookie(self, cookie: Cookie) -> int:
@@ -319,8 +317,6 @@ class DBConfig:
.outerjoin(CookieTarget)
.options(selectinload(Cookie.targets))
)
if not cookie:
raise NoSuchTargetException(f"cookie {cookie_id} not found")
if len(cookie.targets) > 0:
raise Exception(f"cookie {cookie.id} in use")
await sess.execute(delete(Cookie).where(Cookie.id == cookie_id))
@@ -8,11 +8,8 @@ from pydantic import BaseModel
from nonebot_plugin_saa.registries import AllSupportedPlatformTarget
from nonebot.compat import PYDANTIC_V2, ConfigDict, model_dump, type_validate_json, type_validate_python
from nonebot_bison.types import Tag
from nonebot_bison.types import Category
from nonebot_bison.types import Target as T_Target
from ..utils import NBESFParseErr
from ....types import Tag, Category
from .base import NBESFBase, SubReceipt
from ...db_model import Cookie as DBCookie
from ...db_config import SubscribeDupException, config
@@ -117,7 +114,7 @@ async def magic_cookie_gen(nbesf_data: SubGroup):
new_cookie = DBCookie(**model_dump(cookie, exclude={"targets"}))
cookie_id = await config.add_cookie(new_cookie)
for target in cookie.targets:
await config.add_cookie_target(T_Target(target.target), target.platform_name, cookie_id)
await config.add_cookie_target(target.target, target.platform_name, cookie_id)
except Exception as e:
logger.error(f"!添加 Cookie 条目 {repr(cookie)} 失败: {repr(e)}")
else:
+1 -1
View File
@@ -65,7 +65,7 @@ async def subscribes_export(selector: Callable[[Select], Select]) -> v3.SubGroup
target_payload = type_validate_python(v3.Target, cookie_target.target)
cookie_target_dict[cookie_target.cookie].append(target_payload)
def cookie_transform(cookie: Cookie, targets: list[v3.Target]) -> v3.Cookie:
def cookie_transform(cookie: Cookie, targets: [Target]) -> v3.Cookie:
cookie_dict = row2dict(cookie)
cookie_dict["tags"] = cookie.tags
cookie_dict["targets"] = targets
+2 -4
View File
@@ -1,6 +1,4 @@
from typing import Any
from sqlalchemy.orm import DeclarativeBase
from ..db_model import Model
class NBESFVerMatchErr(Exception): ...
@@ -9,7 +7,7 @@ class NBESFVerMatchErr(Exception): ...
class NBESFParseErr(Exception): ...
def row2dict(row: DeclarativeBase) -> dict[str, Any]:
def row2dict(row: Model) -> dict:
d = {}
for column in row.__table__.columns:
d[column.name] = str(getattr(row, column.name))
-42
View File
@@ -1,42 +0,0 @@
import time
from fastapi import APIRouter
from starlette.responses import Response
from prometheus_client import CONTENT_TYPE_LATEST, Gauge, Counter, Histogram, generate_latest
# Request counter
request_counter = Counter(
"bison_request_counter", "The number of requests", ["site_name", "platform_name", "target", "success"]
)
# Sent counter
sent_counter = Counter("bison_sent_counter", "The number of sent messages", ["site_name", "platform_name", "target"])
cookie_choose_counter = Counter(
"bison_cookie_choose_counter", "The number of cookie choose", ["site_name", "target", "cookie_id"]
)
request_histogram = Histogram(
"bison_request_histogram",
"The time of platform used to request the source",
["site_name", "platform_name"],
buckets=[0.1, 0.5, 1, 2, 5, 10, 30, 60],
)
render_histogram = Histogram(
"bison_render_histogram",
"The time of theme used to render",
["site_name", "platform_name"],
buckets=[0.1, 0.5, 1, 2, 5, 10, 30, 60],
)
start_time = Gauge("bison_start_time", "The start time of the program")
start_time.set(time.time())
metrics_router = APIRouter(prefix="/api/metrics", tags=["metrics"])
@metrics_router.get("")
async def metrics():
return Response(media_type=CONTENT_TYPE_LATEST, content=generate_latest())
+8
View File
@@ -3,6 +3,7 @@ from pkgutil import iter_modules
from collections import defaultdict
from importlib import import_module
from ..utils import Site
from ..plugin_config import plugin_config
from .platform import Platform, make_no_target_group
@@ -35,3 +36,10 @@ def _get_unavailable_platforms() -> dict[str, str]:
# platform => reason for not available
unavailable_paltforms: dict[str, str] = _get_unavailable_platforms()
site_manager: dict[str, type[Site]] = {}
for site in Site.registry:
if not hasattr(site, "name"):
continue
site_manager[site.name] = site
+1 -9
View File
@@ -131,7 +131,7 @@ class PostAPI(APIBase):
basic: "PostAPI.Basic"
id_str: str
modules: "PostAPI.Modules"
orig: "PostAPI.Item | PostAPI.DeletedItem | None" = None
orig: "PostAPI.Item | None" = None
topic: "PostAPI.Topic | None" = None
type: DynamicType
@@ -141,14 +141,6 @@ class PostAPI(APIBase):
modules: "PostAPI.Modules"
type: Literal["DYNAMIC_TYPE_NONE"]
def to_item(self) -> "PostAPI.Item":
return PostAPI.Item(
basic=self.basic,
id_str="",
modules=self.modules,
type=self.type,
)
class Data(Base):
items: "list[PostAPI.Item | PostAPI.DeletedItem] | None" = None
+6 -23
View File
@@ -32,7 +32,6 @@ from .models import (
DynamicType,
ArticleMajor,
CoursesMajor,
DeletedMajor,
UnknownMajor,
LiveRecommendMajor,
)
@@ -94,7 +93,7 @@ class Bilibili(NewMessage):
@retry_for_352
async def get_sub_list(self, target: Target) -> list[DynRawPost]:
client = await self.ctx.get_client(target)
params = {"host_mid": target, "timezone_offset": -480, "offset": "", "features": "itemOpusStyle"}
params = {"host_mid": target, "timezone_offset": -480, "offset": ""}
res = await client.get(
"https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space",
params=params,
@@ -244,13 +243,6 @@ class Bilibili(NewMessage):
pics=[courses.cover],
url=URL(courses.jump_url).with_scheme("https").human_repr(),
)
case DeletedMajor(none=none):
return _ParsedMojarPost(
title="",
content=none.tips,
pics=[],
url=None,
)
case UnknownMajor(type=unknown_type):
raise CategoryNotSupport(unknown_type)
case None: # 没有major的情况
@@ -267,12 +259,9 @@ class Bilibili(NewMessage):
parsed_raw_post = self.pre_parse_by_mojar(raw_post)
parsed_raw_repost = None
if self._do_get_category(raw_post.type) == Category(5):
match raw_post.orig:
case PostAPI.Item() as orig:
parsed_raw_repost = self.pre_parse_by_mojar(orig)
case PostAPI.DeletedItem() as orig:
parsed_raw_repost = self.pre_parse_by_mojar(orig.to_item())
case None:
if raw_post.orig:
parsed_raw_repost = self.pre_parse_by_mojar(raw_post.orig)
else:
logger.warning(f"转发动态{raw_post.id_str}没有原动态")
post = Post(
@@ -286,14 +275,8 @@ class Bilibili(NewMessage):
nickname=raw_post.modules.module_author.name,
)
if parsed_raw_repost:
match raw_post.orig:
case PostAPI.Item() as orig:
orig = orig
case PostAPI.DeletedItem() as orig:
orig = orig.to_item()
case None:
raise ValueError("转发动态没有原动态")
orig = raw_post.orig
assert orig
post.repost = Post(
self,
content=decode_unicode_escapes(parsed_raw_repost.content),
+3 -1
View File
@@ -24,8 +24,9 @@ B = TypeVar("B", bound="Bilibili")
class BilibiliClientManager(CookieClientManager):
_default_cookie_cd = timedelta(seconds=120)
_default_cookie_cd: int = timedelta(seconds=120)
@classmethod
async def _get_cookies(self) -> list[Cookie]:
browser = await get_browser()
async with await browser.new_page() as page:
@@ -37,6 +38,7 @@ class BilibiliClientManager(CookieClientManager):
return cookies
@classmethod
def _gen_json_cookie(self, cookies: list[Cookie]):
cookie_dict = {}
for cookie in cookies:
+1 -1
View File
@@ -1,4 +1,4 @@
DATASOURCE_URL = "https://server.ceobecanteen.top/api/v1/canteen/config/datasource/list"
COMB_ID_URL = "https://server.ceobecanteen.top/api/v1/canteen/user/getDatasourceComb"
COOKIE_ID_URL = "https://cdn.ceobecanteen.top/datasource-comb"
COOKIE_ID_URL = "http://cdn.ceobecanteen.top/datasource-comb"
COOKIES_URL = "https://server-cdn.ceobecanteen.top/api/v1/cdn/cookie/mainList/cookieList"
+1 -17
View File
@@ -16,7 +16,7 @@ from nonebot_plugin_saa import PlatformTarget
from ..post import Post
from ..utils import Site, ProcessContext
from ..plugin_config import plugin_config
from ..types import Tag, Target, RawPost, SubUnit, Category
from ..types import Tag, Target, RawPost, SubUnit, Category, RegistryMeta
class CategoryNotSupport(Exception):
@@ -29,21 +29,6 @@ class CategoryNotRecognize(Exception):
"""raise in get_category, when you don't know the category of post"""
class RegistryMeta(type):
def __new__(cls, name, bases, namespace, **kwargs):
return super().__new__(cls, name, bases, namespace)
def __init__(cls, name, bases, namespace, **kwargs):
if kwargs.get("base"):
# this is the base class
cls.registry = []
elif not kwargs.get("abstract"):
# this is the subclass
cls.registry.append(cls)
super().__init__(name, bases, namespace, **kwargs)
P = ParamSpec("P")
R = TypeVar("R")
@@ -315,7 +300,6 @@ class NewMessage(MessageProcess, abstract=True):
res.append(raw_post)
store.exists_posts.add(post_id)
self.set_stored_data(target, store)
logger.trace(f"本次抓取 {len(raw_post_list)} 条,过滤后 {len(filtered_post)} 条,新消息 {len(res)}")
return res
async def _handle_new_post(
+2 -2
View File
@@ -10,14 +10,14 @@ from ..post import Post
from .platform import NewMessage
from ..types import Target, RawPost
from ..utils import text_similarity
from ..utils.site import Site, CookieClientManager
from ..utils.site import Site, create_cookie_client_manager
class RssSite(Site):
name = "rss"
schedule_type = "interval"
schedule_setting = {"seconds": 30}
client_mgr = CookieClientManager.from_name(name)
client_mgr = create_cookie_client_manager(name)
class RssPost(Post):
+3 -4
View File
@@ -1,9 +1,8 @@
import re
import json
from typing import Any
from datetime import datetime
from typing import Any, override
from urllib.parse import unquote
from typing_extensions import override
from yarl import URL
from lxml.etree import HTML
@@ -176,7 +175,7 @@ class Weibo(NewMessage):
try:
client = await self.ctx.get_client()
weibo_info = await client.get(
"https://m.weibo.cn/statuses/extend",
"https://m.weibo.cn/statuses/show",
params={"id": weibo_id},
headers=_HEADER,
)
@@ -190,7 +189,7 @@ class Weibo(NewMessage):
async def _parse_weibo(self, info: dict) -> Post:
if info["isLongText"] or info["pic_num"] > 9:
info["text"] = (await self._get_long_weibo(info["mid"]))["longTextContent"]
info["text"] = (await self._get_long_weibo(info["mid"]))["text"]
parsed_text = self._get_text(info["text"])
raw_pics_list = info.get("pics", [])
pic_urls = [img["large"]["url"] for img in raw_pics_list]
+1 -2
View File
@@ -2,7 +2,6 @@ import reprlib
from io import BytesIO
from pathlib import Path
from typing import TYPE_CHECKING
from collections.abc import Sequence
from dataclasses import fields, dataclass
from nonebot.log import logger
@@ -31,7 +30,7 @@ class Post(AbstractPost, PlainContentSupport):
"""文本内容"""
title: str | None = None
"""标题"""
images: Sequence[str | bytes | Path | BytesIO] | None = None
images: list[str | bytes | Path | BytesIO] | None = None
"""图片列表"""
timestamp: float | None = None
"""发布/获取时间戳, 秒"""
+3 -32
View File
@@ -1,14 +1,11 @@
from dataclasses import dataclass
from collections import defaultdict
from collections.abc import Callable
from nonebot.log import logger
from nonebot_plugin_apscheduler import scheduler
from apscheduler.events import EVENT_JOB_MAX_INSTANCES
from nonebot_plugin_saa.utils.exceptions import NoBotFound
from nonebot_bison.utils import ClientManager
from nonebot_bison.metrics import sent_counter, request_counter, render_histogram, request_histogram
from ..config import config
from ..send import send_msgs
@@ -26,15 +23,6 @@ class Schedulable:
use_batch: bool = False
def handle_time_exceeded(event):
# event.job_id 是该任务在 apscheduler 的 id, 进而可以获得该任务的函数,再获取该函数绑定的对象
logger.warning(f"{scheduler.get_job(event.job_id).func.__self__.name} 抓取执行超时")
scheduler.get_job(event.job_id).func.__self__.metrics_report(False)
scheduler.add_listener(handle_time_exceeded, EVENT_JOB_MAX_INSTANCES)
class Scheduler:
schedulable_list: list[Schedulable] # for load weigth from db
batch_api_target_cache: dict[str, dict[Target, list[Target]]] # platform_name -> (target -> [target])
@@ -52,8 +40,8 @@ class Scheduler:
logger.error(f"scheduler config [{self.name}] not found, exiting")
raise RuntimeError(f"{self.name} not found")
self.scheduler_config = scheduler_config
self.client_mgr = scheduler_config.client_mgr()
self.scheduler_config_obj = self.scheduler_config()
self.client_mgr = scheduler_config.client_mgr()
self.schedulable_list = []
self.batch_platform_name_targets_cache = defaultdict(list)
@@ -67,7 +55,6 @@ class Scheduler:
self.platform_name_list = platform_name_list
self.pre_weight_val = 0 # 轮调度中“本轮”增加权重和的初值
self.metrics_report: Callable[[bool], None] | None = None # 作为函数变量,允许外部调用来上报此次抓取是否成功
logger.info(
f"register scheduler for {self.name} with "
f"{self.scheduler_config.schedule_type} {self.scheduler_config.schedule_setting}"
@@ -107,19 +94,8 @@ class Scheduler:
context = ProcessContext(self.client_mgr)
success_flag = False
platform_obj = platform_manager[schedulable.platform_name](context)
# 通过闭包的形式,将此次抓取任务的信息保存为函数变量,允许在该任务无法正常结束时由外部上报
self.metrics_report = lambda x: request_counter.labels(
platform_name=schedulable.platform_name,
site_name=platform_obj.site.name,
target=schedulable.target,
success=x,
).inc()
try:
with request_histogram.labels(
platform_name=schedulable.platform_name, site_name=platform_obj.site.name
).time():
platform_obj = platform_manager[schedulable.platform_name](context)
if schedulable.use_batch:
batch_targets = self.batch_api_target_cache[schedulable.platform_name][schedulable.target]
sub_units = []
@@ -132,7 +108,6 @@ class Scheduler:
schedulable.platform_name, schedulable.target
)
to_send = await platform_obj.do_fetch_new_post(SubUnit(schedulable.target, send_userinfo_list))
success_flag = True
except SkipRequestException as err:
logger.debug(f"skip request: {err}")
except Exception as err:
@@ -142,13 +117,9 @@ class Scheduler:
err.args += (records,)
raise
self.metrics_report(success_flag)
if not to_send:
return
sent_counter.labels(
platform_name=schedulable.platform_name, site_name=platform_obj.site.name, target=schedulable.target
).inc()
with render_histogram.labels(platform_name=schedulable.platform_name, site_name=platform_obj.site.name).time():
for user, send_list in to_send:
for send_post in send_list:
logger.info(f"send to {user}: {send_post}")
+1 -1
View File
@@ -83,7 +83,7 @@ async def subs_export(path: Path, format: str):
export_file = path / f"bison_subscribes_export_{int(time.time())}.{format}"
logger.info("正在获取订阅信息...")
export_data: v3.SubGroup = await subscribes_export(lambda x: x)
export_data: v2.SubGroup = await subscribes_export(lambda x: x)
with export_file.open("w", encoding="utf-8") as f:
match format:
-2
View File
@@ -30,12 +30,10 @@ add_sub_matcher = on_command(
add_sub_matcher.handle()(set_target_user_info)
do_add_sub(add_sub_matcher)
query_sub_matcher = on_command("查询订阅", rule=configurable_to_me, priority=5, block=True)
query_sub_matcher.handle()(set_target_user_info)
do_query_sub(query_sub_matcher)
del_sub_matcher = on_command(
"删除订阅",
rule=configurable_to_me,
@@ -26,7 +26,8 @@ def do_add_cookie_target(add_cookie_target_matcher: type[Matcher]):
@add_cookie_target_matcher.got("target_idx", parameterless=[handle_cancel])
async def got_target_idx(state: T_State, target_idx: str = ArgPlainText()):
try:
state["target"] = state["sub_table"][int(target_idx)]
target_idx = int(target_idx)
state["target"] = state["sub_table"][target_idx]
state["site"] = platform_manager[state["target"]["platform_name"]].site
except Exception:
await add_cookie_target_matcher.reject("序号错误")
@@ -56,7 +57,8 @@ def do_add_cookie_target(add_cookie_target_matcher: type[Matcher]):
@add_cookie_target_matcher.got("cookie_idx", MessageTemplate("{_prompt}"), [handle_cancel])
async def got_cookie_idx(state: T_State, cookie_idx: str = ArgPlainText()):
try:
state["cookie"] = state["cookies"][int(cookie_idx) - 1]
cookie_idx = int(cookie_idx)
state["cookie"] = state["cookies"][cookie_idx - 1]
except Exception:
await add_cookie_target_matcher.reject("序号错误")
+5 -5
View File
@@ -13,7 +13,6 @@ from nonebot_plugin_saa import PlatformTarget, extract_target
from ..config import config
from ..types import Category
from ..types import Target as T_Target
from ..platform import platform_manager
from ..plugin_config import plugin_config
from ..utils.site import is_cookie_client_manager
@@ -71,7 +70,7 @@ def admin_permission():
async def generate_sub_list_text(
matcher: type[Matcher],
state: T_State,
user_info: PlatformTarget | None = None,
user_info: PlatformTarget = None,
is_index=False,
is_show_cookie=False,
is_hide_no_cookie_platfrom=False,
@@ -89,7 +88,7 @@ async def generate_sub_list_text(
sub_list = [
sub
for sub in sub_list
if is_cookie_client_manager(platform_manager[sub.target.platform_name].site.client_mgr)
if is_cookie_client_manager(platform_manager.get(sub.target.platform_name).site.client_mgr)
]
if not sub_list:
await matcher.finish("暂无已订阅账号\n请使用“添加订阅”命令添加订阅")
@@ -110,7 +109,7 @@ async def generate_sub_list_text(
res += " {}".format(", ".join(sub.tags)) + "\n"
if is_show_cookie:
target_cookies = await config.get_cookie(
target=T_Target(sub.target.target), site_name=platform.site.name, is_anonymous=False
target=sub.target.target, site_name=platform.site.name, is_anonymous=False
)
if target_cookies:
res += " 关联的 Cookie\n"
@@ -127,5 +126,6 @@ async def only_allow_private(
event: Event,
matcher: type[Matcher],
):
if not (hasattr(event, "message_type") and getattr(event, "message_type") == "private"):
# if not issubclass(PrivateMessageEvent, event.__class__):
if event.message_type != "private":
await matcher.finish("请在私聊中使用此命令")
+15
View File
@@ -58,3 +58,18 @@ class ApiError(Exception):
class SubUnit(NamedTuple):
sub_target: Target
user_sub_infos: list[UserSubInfo]
class RegistryMeta(type):
def __new__(cls, name, bases, namespace, **kwargs):
return super().__new__(cls, name, bases, namespace)
def __init__(cls, name, bases, namespace, **kwargs):
if kwargs.get("base"):
# this is the base class
cls.registry = []
elif not kwargs.get("abstract"):
# this is the subclass
cls.registry.append(cls)
super().__init__(name, bases, namespace, **kwargs)
+20 -43
View File
@@ -2,18 +2,16 @@ import json
from typing import Literal
from json import JSONDecodeError
from abc import ABC, abstractmethod
from collections.abc import Callable
from datetime import datetime, timedelta
import httpx
from httpx import AsyncClient
from nonebot.log import logger
from ..types import Target
from ..config import config
from .http import http_client
from ..config.db_model import Cookie
from ..metrics import cookie_choose_counter
from ..types import Target, RegistryMeta
class ClientManager(ABC):
@@ -44,14 +42,8 @@ class DefaultClientManager(ClientManager):
pass
class SkipRequestException(Exception):
"""跳过请求异常,如果需要在选择 Cookie 时跳过此次请求,可以抛出此异常"""
pass
class CookieClientManager(ClientManager):
_default_cookie_cd = timedelta(seconds=15)
_default_cookie_cd: int = timedelta(seconds=15)
_site_name: str = ""
async def _generate_anonymous_cookie(self) -> Cookie:
@@ -106,7 +98,7 @@ class CookieClientManager(ClientManager):
return False
return True
def _generate_hook(self, cookie: Cookie) -> Callable:
def _generate_hook(self, cookie: Cookie) -> callable:
"""hook 函数生成器,用于回写请求状态到数据库"""
async def _response_hook(resp: httpx.Response):
@@ -124,15 +116,14 @@ class CookieClientManager(ClientManager):
async def _choose_cookie(self, target: Target | None) -> Cookie:
"""选择 cookie 的具体算法"""
cookies = await config.get_cookie(self._site_name, target)
avaliable_cookies = (cookie for cookie in cookies if cookie.last_usage + cookie.cd < datetime.now())
cookie = min(avaliable_cookies, key=lambda x: x.last_usage)
cookies = (cookie for cookie in cookies if cookie.last_usage + cookie.cd < datetime.now())
cookie = min(cookies, key=lambda x: x.last_usage)
return cookie
async def get_client(self, target: Target | None) -> AsyncClient:
"""获取 client,根据 target 选择 cookie"""
client = http_client()
cookie = await self._choose_cookie(target)
cookie_choose_counter.labels(site_name=self._site_name, target=target, cookie_id=cookie.id).inc()
if cookie.is_universal:
logger.trace(f"平台 {self._site_name} 未获取到用户cookie, 使用匿名cookie")
else:
@@ -141,7 +132,7 @@ class CookieClientManager(ClientManager):
return await self._assemble_client(client, cookie)
async def _assemble_client(self, client, cookie) -> AsyncClient:
"""组装 client,可以自定义 cookie 对象装配到 client 中的方式"""
"""组装 client,可以自定义 cookie 对象的 content 装配到 client 中的方式"""
cookies = httpx.Cookies()
if cookie:
cookies.update(json.loads(cookie.content))
@@ -149,15 +140,6 @@ class CookieClientManager(ClientManager):
client.event_hooks = {"response": [self._generate_hook(cookie)]}
return client
@classmethod
def from_name(cls, site_name: str) -> type["CookieClientManager"]:
"""创建一个平台特化的 CookieClientManger"""
return type(
"CookieClientManager",
(CookieClientManager,),
{"_site_name": site_name},
)
async def get_client_for_static(self) -> AsyncClient:
return http_client()
@@ -172,25 +154,7 @@ def is_cookie_client_manager(manger: type[ClientManager]) -> bool:
return issubclass(manger, CookieClientManager)
site_manager: dict[str, type["Site"]] = {}
class SiteMeta(type):
def __new__(cls, name, bases, namespace, **kwargs):
return super().__new__(cls, name, bases, namespace)
def __init__(cls, name, bases, namespace, **kwargs):
if kwargs.get("base"):
# this is the base class
cls._key = kwargs.get("key")
elif not kwargs.get("abstract"):
# this is the subclass
if "name" in namespace:
site_manager[namespace["name"]] = cls
super().__init__(name, bases, namespace, **kwargs)
class Site(metaclass=SiteMeta):
class Site(metaclass=RegistryMeta, base=True):
schedule_type: Literal["date", "interval", "cron"]
schedule_setting: dict
name: str
@@ -202,6 +166,15 @@ class Site(metaclass=SiteMeta):
return f"[{self.name}]-{self.name}-{self.schedule_setting}"
def create_cookie_client_manager(site_name: str) -> type[CookieClientManager]:
"""创建一个平台特化的 CookieClientManger"""
return type(
"CookieClientManager",
(CookieClientManager,),
{"_site_name": site_name},
)
def anonymous_site(schedule_type: Literal["date", "interval", "cron"], schedule_setting: dict) -> type[Site]:
return type(
"AnonymousSite",
@@ -212,3 +185,7 @@ def anonymous_site(schedule_type: Literal["date", "interval", "cron"], schedule_
"client_mgr": DefaultClientManager,
},
)
class SkipRequestException(Exception):
pass
Generated
+90 -146
View File
@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]]
name = "aiodns"
@@ -538,10 +538,6 @@ files = [
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"},
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"},
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"},
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"},
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"},
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"},
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"},
{file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"},
{file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"},
{file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"},
@@ -554,14 +550,8 @@ files = [
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"},
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"},
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"},
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"},
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"},
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"},
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"},
{file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"},
{file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"},
{file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"},
{file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"},
{file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"},
{file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"},
{file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"},
@@ -572,24 +562,8 @@ files = [
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"},
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"},
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"},
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"},
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"},
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"},
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"},
{file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"},
{file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"},
{file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"},
{file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"},
{file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"},
{file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"},
{file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"},
{file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"},
{file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"},
{file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"},
{file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"},
{file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"},
{file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"},
{file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"},
{file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"},
{file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"},
{file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"},
@@ -599,10 +573,6 @@ files = [
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"},
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"},
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"},
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"},
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"},
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"},
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"},
{file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"},
{file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"},
{file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"},
@@ -614,10 +584,6 @@ files = [
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"},
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"},
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"},
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"},
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"},
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"},
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"},
{file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"},
{file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"},
{file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"},
@@ -630,10 +596,6 @@ files = [
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"},
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"},
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"},
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"},
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"},
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"},
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"},
{file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"},
{file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"},
{file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"},
@@ -646,10 +608,6 @@ files = [
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"},
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"},
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"},
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"},
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"},
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"},
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"},
{file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"},
{file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"},
{file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"},
@@ -3007,90 +2965,95 @@ reference = "offical-source"
[[package]]
name = "pillow"
version = "11.0.0"
version = "10.4.0"
description = "Python Imaging Library (Fork)"
optional = false
python-versions = ">=3.9"
python-versions = ">=3.8"
files = [
{file = "pillow-11.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6619654954dc4936fcff82db8eb6401d3159ec6be81e33c6000dfd76ae189947"},
{file = "pillow-11.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b3c5ac4bed7519088103d9450a1107f76308ecf91d6dabc8a33a2fcfb18d0fba"},
{file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a65149d8ada1055029fcb665452b2814fe7d7082fcb0c5bed6db851cb69b2086"},
{file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88a58d8ac0cc0e7f3a014509f0455248a76629ca9b604eca7dc5927cc593c5e9"},
{file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:c26845094b1af3c91852745ae78e3ea47abf3dbcd1cf962f16b9a5fbe3ee8488"},
{file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:1a61b54f87ab5786b8479f81c4b11f4d61702830354520837f8cc791ebba0f5f"},
{file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:674629ff60030d144b7bca2b8330225a9b11c482ed408813924619c6f302fdbb"},
{file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:598b4e238f13276e0008299bd2482003f48158e2b11826862b1eb2ad7c768b97"},
{file = "pillow-11.0.0-cp310-cp310-win32.whl", hash = "sha256:9a0f748eaa434a41fccf8e1ee7a3eed68af1b690e75328fd7a60af123c193b50"},
{file = "pillow-11.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:a5629742881bcbc1f42e840af185fd4d83a5edeb96475a575f4da50d6ede337c"},
{file = "pillow-11.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:ee217c198f2e41f184f3869f3e485557296d505b5195c513b2bfe0062dc537f1"},
{file = "pillow-11.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1c1d72714f429a521d8d2d018badc42414c3077eb187a59579f28e4270b4b0fc"},
{file = "pillow-11.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:499c3a1b0d6fc8213519e193796eb1a86a1be4b1877d678b30f83fd979811d1a"},
{file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8b2351c85d855293a299038e1f89db92a2f35e8d2f783489c6f0b2b5f3fe8a3"},
{file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f4dba50cfa56f910241eb7f883c20f1e7b1d8f7d91c750cd0b318bad443f4d5"},
{file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5ddbfd761ee00c12ee1be86c9c0683ecf5bb14c9772ddbd782085779a63dd55b"},
{file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:45c566eb10b8967d71bf1ab8e4a525e5a93519e29ea071459ce517f6b903d7fa"},
{file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b4fd7bd29610a83a8c9b564d457cf5bd92b4e11e79a4ee4716a63c959699b306"},
{file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cb929ca942d0ec4fac404cbf520ee6cac37bf35be479b970c4ffadf2b6a1cad9"},
{file = "pillow-11.0.0-cp311-cp311-win32.whl", hash = "sha256:006bcdd307cc47ba43e924099a038cbf9591062e6c50e570819743f5607404f5"},
{file = "pillow-11.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:52a2d8323a465f84faaba5236567d212c3668f2ab53e1c74c15583cf507a0291"},
{file = "pillow-11.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:16095692a253047fe3ec028e951fa4221a1f3ed3d80c397e83541a3037ff67c9"},
{file = "pillow-11.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2c0a187a92a1cb5ef2c8ed5412dd8d4334272617f532d4ad4de31e0495bd923"},
{file = "pillow-11.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:084a07ef0821cfe4858fe86652fffac8e187b6ae677e9906e192aafcc1b69903"},
{file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8069c5179902dcdce0be9bfc8235347fdbac249d23bd90514b7a47a72d9fecf4"},
{file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f02541ef64077f22bf4924f225c0fd1248c168f86e4b7abdedd87d6ebaceab0f"},
{file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:fcb4621042ac4b7865c179bb972ed0da0218a076dc1820ffc48b1d74c1e37fe9"},
{file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:00177a63030d612148e659b55ba99527803288cea7c75fb05766ab7981a8c1b7"},
{file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8853a3bf12afddfdf15f57c4b02d7ded92c7a75a5d7331d19f4f9572a89c17e6"},
{file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3107c66e43bda25359d5ef446f59c497de2b5ed4c7fdba0894f8d6cf3822dafc"},
{file = "pillow-11.0.0-cp312-cp312-win32.whl", hash = "sha256:86510e3f5eca0ab87429dd77fafc04693195eec7fd6a137c389c3eeb4cfb77c6"},
{file = "pillow-11.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:8ec4a89295cd6cd4d1058a5e6aec6bf51e0eaaf9714774e1bfac7cfc9051db47"},
{file = "pillow-11.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:27a7860107500d813fcd203b4ea19b04babe79448268403172782754870dac25"},
{file = "pillow-11.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcd1fb5bb7b07f64c15618c89efcc2cfa3e95f0e3bcdbaf4642509de1942a699"},
{file = "pillow-11.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e038b0745997c7dcaae350d35859c9715c71e92ffb7e0f4a8e8a16732150f38"},
{file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ae08bd8ffc41aebf578c2af2f9d8749d91f448b3bfd41d7d9ff573d74f2a6b2"},
{file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d69bfd8ec3219ae71bcde1f942b728903cad25fafe3100ba2258b973bd2bc1b2"},
{file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:61b887f9ddba63ddf62fd02a3ba7add935d053b6dd7d58998c630e6dbade8527"},
{file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c6a660307ca9d4867caa8d9ca2c2658ab685de83792d1876274991adec7b93fa"},
{file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:73e3a0200cdda995c7e43dd47436c1548f87a30bb27fb871f352a22ab8dcf45f"},
{file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fba162b8872d30fea8c52b258a542c5dfd7b235fb5cb352240c8d63b414013eb"},
{file = "pillow-11.0.0-cp313-cp313-win32.whl", hash = "sha256:f1b82c27e89fffc6da125d5eb0ca6e68017faf5efc078128cfaa42cf5cb38798"},
{file = "pillow-11.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ba470552b48e5835f1d23ecb936bb7f71d206f9dfeee64245f30c3270b994de"},
{file = "pillow-11.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:846e193e103b41e984ac921b335df59195356ce3f71dcfd155aa79c603873b84"},
{file = "pillow-11.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4ad70c4214f67d7466bea6a08061eba35c01b1b89eaa098040a35272a8efb22b"},
{file = "pillow-11.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ec0d5af64f2e3d64a165f490d96368bb5dea8b8f9ad04487f9ab60dc4bb6003"},
{file = "pillow-11.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c809a70e43c7977c4a42aefd62f0131823ebf7dd73556fa5d5950f5b354087e2"},
{file = "pillow-11.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4b60c9520f7207aaf2e1d94de026682fc227806c6e1f55bba7606d1c94dd623a"},
{file = "pillow-11.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1e2688958a840c822279fda0086fec1fdab2f95bf2b717b66871c4ad9859d7e8"},
{file = "pillow-11.0.0-cp313-cp313t-win32.whl", hash = "sha256:607bbe123c74e272e381a8d1957083a9463401f7bd01287f50521ecb05a313f8"},
{file = "pillow-11.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c39ed17edea3bc69c743a8dd3e9853b7509625c2462532e62baa0732163a904"},
{file = "pillow-11.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:75acbbeb05b86bc53cbe7b7e6fe00fbcf82ad7c684b3ad82e3d711da9ba287d3"},
{file = "pillow-11.0.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2e46773dc9f35a1dd28bd6981332fd7f27bec001a918a72a79b4133cf5291dba"},
{file = "pillow-11.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2679d2258b7f1192b378e2893a8a0a0ca472234d4c2c0e6bdd3380e8dfa21b6a"},
{file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda2616eb2313cbb3eebbe51f19362eb434b18e3bb599466a1ffa76a033fb916"},
{file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ec184af98a121fb2da42642dea8a29ec80fc3efbaefb86d8fdd2606619045d"},
{file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:8594f42df584e5b4bb9281799698403f7af489fba84c34d53d1c4bfb71b7c4e7"},
{file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:c12b5ae868897c7338519c03049a806af85b9b8c237b7d675b8c5e089e4a618e"},
{file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:70fbbdacd1d271b77b7721fe3cdd2d537bbbd75d29e6300c672ec6bb38d9672f"},
{file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5178952973e588b3f1360868847334e9e3bf49d19e169bbbdfaf8398002419ae"},
{file = "pillow-11.0.0-cp39-cp39-win32.whl", hash = "sha256:8c676b587da5673d3c75bd67dd2a8cdfeb282ca38a30f37950511766b26858c4"},
{file = "pillow-11.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:94f3e1780abb45062287b4614a5bc0874519c86a777d4a7ad34978e86428b8dd"},
{file = "pillow-11.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:290f2cc809f9da7d6d622550bbf4c1e57518212da51b6a30fe8e0a270a5b78bd"},
{file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1187739620f2b365de756ce086fdb3604573337cc28a0d3ac4a01ab6b2d2a6d2"},
{file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fbbcb7b57dc9c794843e3d1258c0fbf0f48656d46ffe9e09b63bbd6e8cd5d0a2"},
{file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d203af30149ae339ad1b4f710d9844ed8796e97fda23ffbc4cc472968a47d0b"},
{file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a0d3b115009ebb8ac3d2ebec5c2982cc693da935f4ab7bb5c8ebe2f47d36f2"},
{file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:73853108f56df97baf2bb8b522f3578221e56f646ba345a372c78326710d3830"},
{file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e58876c91f97b0952eb766123bfef372792ab3f4e3e1f1a2267834c2ab131734"},
{file = "pillow-11.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:224aaa38177597bb179f3ec87eeefcce8e4f85e608025e9cfac60de237ba6316"},
{file = "pillow-11.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5bd2d3bdb846d757055910f0a59792d33b555800813c3b39ada1829c372ccb06"},
{file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:375b8dd15a1f5d2feafff536d47e22f69625c1aa92f12b339ec0b2ca40263273"},
{file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:daffdf51ee5db69a82dd127eabecce20729e21f7a3680cf7cbb23f0829189790"},
{file = "pillow-11.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7326a1787e3c7b0429659e0a944725e1b03eeaa10edd945a86dead1913383944"},
{file = "pillow-11.0.0.tar.gz", hash = "sha256:72bacbaf24ac003fea9bff9837d1eedb6088758d41e100c1552930151f677739"},
{file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"},
{file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"},
{file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"},
{file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"},
{file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"},
{file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"},
{file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"},
{file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"},
{file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"},
{file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"},
{file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"},
{file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"},
{file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"},
{file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"},
{file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"},
{file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"},
{file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"},
{file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"},
{file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"},
{file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"},
{file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"},
{file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"},
{file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"},
{file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"},
{file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"},
{file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"},
{file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"},
{file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"},
{file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"},
{file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"},
{file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"},
{file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"},
{file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"},
{file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"},
{file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"},
{file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"},
{file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"},
{file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"},
{file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"},
{file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"},
{file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"},
{file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"},
{file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"},
{file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"},
{file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"},
{file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"},
{file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"},
{file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"},
{file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"},
{file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"},
{file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"},
{file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"},
{file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"},
{file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"},
{file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"},
{file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"},
{file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"},
{file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"},
{file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"},
{file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"},
{file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"},
{file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"},
{file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"},
{file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"},
{file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"},
{file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"},
{file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"},
{file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"},
{file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"},
{file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"},
{file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"},
{file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"},
{file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"},
{file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"},
{file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"},
{file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"},
{file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"},
{file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"},
{file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"},
{file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"},
]
[package.extras]
docs = ["furo", "olefile", "sphinx (>=8.1)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"]
docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"]
fpx = ["olefile"]
mic = ["olefile"]
tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
@@ -3207,25 +3170,6 @@ type = "legacy"
url = "https://pypi.org/simple"
reference = "offical-source"
[[package]]
name = "prometheus-client"
version = "0.21.0"
description = "Python client for the Prometheus monitoring system."
optional = false
python-versions = ">=3.8"
files = [
{file = "prometheus_client-0.21.0-py3-none-any.whl", hash = "sha256:4fa6b4dd0ac16d58bb587c04b1caae65b8c5043e85f778f42f5f632f6af2e166"},
{file = "prometheus_client-0.21.0.tar.gz", hash = "sha256:96c83c606b71ff2b0a433c98889d275f51ffec6c5e267de37c7a2b5c9aa9233e"},
]
[package.extras]
twisted = ["twisted"]
[package.source]
type = "legacy"
url = "https://pypi.org/simple"
reference = "offical-source"
[[package]]
name = "prompt-toolkit"
version = "3.0.47"
@@ -3721,17 +3665,17 @@ reference = "offical-source"
[[package]]
name = "pytest-cov"
version = "6.0.0"
version = "5.0.0"
description = "Pytest plugin for measuring coverage."
optional = false
python-versions = ">=3.9"
python-versions = ">=3.8"
files = [
{file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"},
{file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"},
{file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"},
{file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"},
]
[package.dependencies]
coverage = {version = ">=7.5", extras = ["toml"]}
coverage = {version = ">=5.2.1", extras = ["toml"]}
pytest = ">=4.6"
[package.extras]
@@ -5185,4 +5129,4 @@ yaml = []
[metadata]
lock-version = "2.0"
python-versions = ">=3.10,<4.0.0"
content-hash = "5e4ea27ea11e18451d1ad0d4bbf3b44da9334c20728889041a4c908addbfdda8"
content-hash = "49633514954cd83e3973b4539baa86ca5518d8582bfdb08df85247e5dcabc70f"
+3 -4
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "nonebot-bison"
version = "0.9.5"
version = "0.9.4"
description = "Subscribe message from social medias"
authors = ["felinae98 <felinae225@qq.com>"]
license = "MIT"
@@ -31,7 +31,7 @@ nonebot-plugin-htmlrender = ">=0.3.5"
nonebot-plugin-datastore = ">=1.3.0,<2.0.0"
nonebot-plugin-apscheduler = ">=0.5.0"
nonebot-plugin-send-anything-anywhere = ">=0.7.1,<0.7.2"
pillow = ">=11.0.0,<11.1"
pillow = ">=10.4.0,<11.0"
pyjwt = "^2.9.0"
python-socketio = "^5.11.4"
tinydb = "^4.8.0"
@@ -42,7 +42,6 @@ yarl = ">=1.11.1"
hishel = "^0.0.30"
expiringdictx = "^1.1.0"
rapidfuzz = "^3.9.7"
prometheus-client = "^0.21.0"
[tool.poetry.group.dev.dependencies]
black = ">=24.8.0,<25.0"
@@ -59,7 +58,7 @@ nonebug = "^0.3.7"
nonebug-saa = "^0.4.1"
pytest = ">=8.3.3,<9.0.0"
pytest-asyncio = ">=0.24.0,<0.24.1"
pytest-cov = ">=6.0.0,<7"
pytest-cov = ">=5.0.0,<6"
pytest-mock = "^3.14.0"
pytest-xdist = { extras = ["psutil"], version = "^3.6.1" }
respx = ">=0.21.1,<0.22"
+2 -1
View File
@@ -10,11 +10,12 @@ from nonebug import App
async def test_cookie(app: App, init_scheduler):
from nonebot_plugin_saa import TargetQQGroup
from nonebot_bison.platform import site_manager
from nonebot_bison.config.db_config import config
from nonebot_bison.scheduler import scheduler_dict
from nonebot_bison.types import Target as T_Target
from nonebot_bison.utils.site import CookieClientManager
from nonebot_bison.config.utils import DuplicateCookieTargetException
from nonebot_bison.utils.site import CookieClientManager, site_manager
target = T_Target("weibo_id")
platform_name = "weibo"
-204
View File
@@ -4053,210 +4053,6 @@
},
"type": "DYNAMIC_TYPE_DRAW",
"visible": true
},
{
"basic": {
"comment_id_str": "965806534205374473",
"comment_type": 17,
"like_icon": {
"action_url": "https://i0.hdslb.com/bfs/garb/item/8860c7c01179f9984f88fb61bc55cab9dc1d28cb.bin",
"end_url": "",
"id": 33772,
"start_url": ""
},
"rid_str": "965806534205374473"
},
"id_str": "965806534205374473",
"modules": {
"module_author": {
"avatar": {
"container_size": {
"height": 1.35,
"width": 1.35
},
"fallback_layers": {
"is_critical_group": true,
"layers": [
{
"general_spec": {
"pos_spec": {
"axis_x": 0.675,
"axis_y": 0.675,
"coordinate_pos": 2
},
"render_spec": {
"opacity": 1
},
"size_spec": {
"height": 1,
"width": 1
}
},
"layer_config": {
"is_critical": true,
"tags": {
"AVATAR_LAYER": {},
"GENERAL_CFG": {
"config_type": 1,
"general_config": {
"web_css_style": {
"borderRadius": "50%"
}
}
}
}
},
"resource": {
"res_image": {
"image_src": {
"placeholder": 6,
"remote": {
"bfs_style": "widget-layer-avatar",
"url": "https://i0.hdslb.com/bfs/face/5d255a47deae6c5214f93cdbbf3b01f23cac4a5e.jpg"
},
"src_type": 1
}
},
"res_type": 3
},
"visible": true
}
]
},
"mid": "6050499"
},
"face": "https://i0.hdslb.com/bfs/face/5d255a47deae6c5214f93cdbbf3b01f23cac4a5e.jpg",
"face_nft": false,
"following": null,
"jump_url": "//space.bilibili.com/6050499/dynamic",
"label": "",
"mid": 6050499,
"name": "血毒嘿咻",
"official_verify": {
"desc": "",
"type": -1
},
"pendant": {
"expire": 0,
"image": "",
"image_enhance": "",
"image_enhance_frame": "",
"n_pid": 0,
"name": "",
"pid": 0
},
"pub_action": "",
"pub_location_text": "",
"pub_time": "08月15日",
"pub_ts": 1723707757,
"type": "AUTHOR_TYPE_NORMAL",
"vip": {
"avatar_subscript": 0,
"avatar_subscript_url": "",
"due_date": 1648224000000,
"label": {
"bg_color": "",
"bg_style": 0,
"border_color": "",
"img_label_uri_hans": "",
"img_label_uri_hans_static": "https://i0.hdslb.com/bfs/vip/d7b702ef65a976b20ed854cbd04cb9e27341bb79.png",
"img_label_uri_hant": "",
"img_label_uri_hant_static": "https://i0.hdslb.com/bfs/activity-plat/static/20220614/e369244d0b14644f5e1a06431e22a4d5/KJunwh19T5.png",
"label_theme": "",
"path": "",
"text": "",
"text_color": "",
"use_img_label": true
},
"nickname_color": "",
"status": 0,
"theme_type": 0,
"type": 1
}
},
"module_dynamic": {
"additional": null,
"desc": {
"rich_text_nodes": [
{
"orig_text": "转发动态",
"text": "转发动态",
"type": "RICH_TEXT_NODE_TYPE_TEXT"
}
],
"text": "转发动态"
},
"major": null,
"topic": null
},
"module_more": {
"three_point_items": [
{
"label": "举报",
"type": "THREE_POINT_REPORT"
}
]
},
"module_stat": {
"comment": {
"count": 0,
"forbidden": false
},
"forward": {
"count": 0,
"forbidden": false
},
"like": {
"count": 0,
"forbidden": false,
"status": false
}
}
},
"orig": {
"basic": {
"comment_id_str": "",
"comment_type": 0,
"like_icon": {
"action_url": "",
"end_url": "",
"id": 0,
"start_url": ""
},
"rid_str": ""
},
"id_str": null,
"modules": {
"module_author": {
"face": "",
"face_nft": false,
"following": false,
"jump_url": "",
"label": "",
"mid": 0,
"name": "",
"pub_action": "",
"pub_time": "",
"pub_ts": 0,
"type": "AUTHOR_TYPE_NORMAL"
},
"module_dynamic": {
"additional": null,
"desc": null,
"major": {
"none": {
"tips": "源动态已被作者删除"
},
"type": "MAJOR_TYPE_NONE"
},
"topic": null
}
},
"type": "DYNAMIC_TYPE_NONE",
"visible": true
},
"type": "DYNAMIC_TYPE_FORWARD",
"visible": true
}
],
"offset": "915793667264872453",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+3 -15
View File
@@ -407,18 +407,6 @@ async def test_dynamic_forward(bilibili: "Bilibili", bing_dy_list: list):
assert rp.url == "https://t.bilibili.com/915793667264872453"
async def test_dynamic_forword_deleted(bilibili: "Bilibili", bing_dy_list: list):
from nonebot_bison.post import Post
post: Post = await bilibili.parse(bing_dy_list[12])
assert post.content == "转发动态"
assert post.url == "https://t.bilibili.com/965806534205374473"
assert (repost := post.repost)
assert repost.url is None
assert not repost.title
assert repost.content == "源动态已被作者删除"
@pytest.mark.asyncio
@respx.mock
async def test_fetch_new_without_dynamic(bilibili, dummy_user_subinfo, without_dynamic):
@@ -426,7 +414,7 @@ async def test_fetch_new_without_dynamic(bilibili, dummy_user_subinfo, without_d
target = Target("161775300")
post_router = respx.get(
f"https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?host_mid={target}&timezone_offset=-480&offset=&features=itemOpusStyle"
f"https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?host_mid={target}&timezone_offset=-480&offset="
)
post_router.mock(return_value=Response(200, json=without_dynamic))
res = await bilibili.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
@@ -445,7 +433,7 @@ async def test_fetch_new(bilibili, dummy_user_subinfo):
target = Target("161775300")
post_router = respx.get(
f"https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?host_mid={target}&timezone_offset=-480&offset=&features=itemOpusStyle"
f"https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?host_mid={target}&timezone_offset=-480&offset="
)
post_list = type_validate_python(PostAPI, get_json("bilibili-new.json"))
assert post_list.data
@@ -489,7 +477,7 @@ async def test_fetch_new_live_rcmd(bilibili: "Bilibili", dummy_user_subinfo):
target = Target("13164144")
post_router = respx.get(
f"https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?host_mid={target}&timezone_offset=-480&offset=&features=itemOpusStyle"
f"https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?host_mid={target}&timezone_offset=-480&offset="
)
post_list = type_validate_python(PostAPI, get_json("bilibili-dynamic-live-rcmd.json"))
assert post_list.data
+1 -1
View File
@@ -166,7 +166,7 @@ async def test_batch_fetch_new_with_single(
targets_router = respx.get("https://server.ceobecanteen.top/api/v1/canteen/config/datasource/list")
comb_id_router = respx.post("https://server.ceobecanteen.top/api/v1/canteen/user/getDatasourceComb")
cookie_id_router = respx.get("https://cdn.ceobecanteen.top/datasource-comb/2")
cookie_id_router = respx.get("http://cdn.ceobecanteen.top/datasource-comb/2")
cookies_router = respx.get("https://server-cdn.ceobecanteen.top/api/v1/cdn/cookie/mainList/cookieList")
targets_router.mock(return_value=Response(200, json=ceobecanteen_targets, headers=mock_respone_headers))
+4 -4
View File
@@ -45,7 +45,7 @@ async def test_fetch_new(weibo, dummy_user_subinfo):
from nonebot_bison.types import Target, SubUnit
ak_list_router = respx.get("https://m.weibo.cn/api/container/getIndex?containerid=1076036279793937")
detail_router = respx.get("https://m.weibo.cn/statuses/extend?id=4649031014551911")
detail_router = respx.get("https://m.weibo.cn/statuses/show?id=4649031014551911")
ak_list_router.mock(return_value=Response(200, json=get_json("weibo_ak_list_0.json")))
detail_router.mock(return_value=Response(200, text=get_file("weibo_detail_4649031014551911")))
image_cdn_router.mock(Response(200, content=b""))
@@ -77,7 +77,7 @@ async def test_fetch_new(weibo, dummy_user_subinfo):
@pytest.mark.asyncio
@respx.mock
async def test_fetch_repost(weibo):
repost_detail_router = respx.get("https://m.weibo.cn/statuses/extend?id=4645748019299849")
repost_detail_router = respx.get("https://m.weibo.cn/statuses/show?id=4645748019299849")
repost_detail_router.mock(return_value=Response(200, text=get_file("weibo_detail_4645748019299849")))
image_cdn_router.mock(Response(200, content=b""))
raw_post = get_json("weibo_ak_list_1.json")["data"]["cards"][3]
@@ -121,7 +121,7 @@ async def test_fetch_repost(weibo):
@pytest.mark.asyncio
@respx.mock
async def test_video_cover(weibo):
router = respx.get("https://m.weibo.cn/statuses/extend?id=4645748019299849")
router = respx.get("https://m.weibo.cn/statuses/show?id=4645748019299849")
router.mock(return_value=Response(200, text=get_file("weibo_detail_4645748019299849")))
image_cdn_router.mock(Response(200, content=b""))
raw_post = get_json("weibo_ak_list_1.json")["data"]["cards"][0]
@@ -152,7 +152,7 @@ async def test_classification(weibo):
@pytest.mark.asyncio
@respx.mock
async def test_parse_long(weibo):
detail_router = respx.get("https://m.weibo.cn/statuses/extend?id=4645748019299849")
detail_router = respx.get("https://m.weibo.cn/statuses/show?id=4645748019299849")
detail_router.mock(return_value=Response(200, text=get_file("weibo_detail_4645748019299849")))
raw_post = get_json("weibo_ak_list_1.json")["data"]["cards"][0]
post = await weibo.parse(raw_post)
+1 -1
View File
@@ -78,7 +78,7 @@ async def test_subs_export(app: App, tmp_path: Path):
cookie_name="test cookie",
)
)
await config.add_cookie_target(TTarget("weibo_id"), "weibo", cookie_id)
await config.add_cookie_target("weibo_id", "weibo", cookie_id)
assert len(await config.list_subs_with_all_info()) == 3
+3 -3
View File
@@ -16,7 +16,7 @@ async def test_subs_export(app: App, init_scheduler):
await config.add_subscribe(
TargetQQGroup(group_id=1232),
target=TTarget(TTarget("weibo_id")),
target=TTarget("weibo_id"),
target_name="weibo_name",
platform_name="weibo",
cats=[],
@@ -24,7 +24,7 @@ async def test_subs_export(app: App, init_scheduler):
)
await config.add_subscribe(
TargetQQGroup(group_id=2342),
target=TTarget(TTarget("weibo_id")),
target=TTarget("weibo_id"),
target_name="weibo_name",
platform_name="weibo",
cats=[],
@@ -45,7 +45,7 @@ async def test_subs_export(app: App, init_scheduler):
cookie_name="test cookie",
)
)
await config.add_cookie_target(TTarget("weibo_id"), "weibo", cookie_id)
await config.add_cookie_target("weibo_id", "weibo", cookie_id)
data = await config.list_subs_with_all_info()
assert len(data) == 3