├── .gitignore
├── B66FEE6EE4B550CF930CF48FFB9EDC0D.png
├── LICENSE.txt
├── README.md
├── example
├── bottles.png
├── comments.png
├── md01.png
├── md02.png
└── md03.jpg
├── img
└── NoneBotPlugin.png
├── nonebot_plugin_web_bottle
├── .idea
│ ├── .gitignore
│ ├── .name
│ ├── inspectionProfiles
│ │ ├── Project_Default.xml
│ │ └── profiles_settings.xml
│ ├── misc.xml
│ ├── modules.xml
│ └── nonebot_plugin_web_bottle.iml
├── __init__.py
├── config.py
├── data_deal.py
├── templates
│ ├── comments.html
│ ├── index.html
│ ├── login
│ │ ├── 1.html
│ │ ├── login.html
│ │ └── static
│ │ │ ├── css
│ │ │ └── backend.css
│ │ │ ├── font
│ │ │ └── qiantu.ttf
│ │ │ ├── image
│ │ │ └── background.png
│ │ │ ├── images
│ │ │ ├── background.jpg
│ │ │ ├── background.webp
│ │ │ ├── background2.png
│ │ │ ├── background2.webp
│ │ │ ├── linglan.png
│ │ │ ├── linglan.webp
│ │ │ ├── luoxiqi.png
│ │ │ ├── luoxiqi.webp
│ │ │ ├── xilufei.png
│ │ │ └── xilufei.webp
│ │ │ ├── js1
│ │ │ └── backend-bundle.min.js
│ │ │ ├── picture
│ │ │ ├── linglan.png
│ │ │ ├── luoxiqi.png
│ │ │ └── xilufei.png
│ │ │ └── styles.css
│ └── static
│ │ ├── comments_styles.css
│ │ ├── images
│ │ ├── Thumbs.db
│ │ ├── background.jpg
│ │ ├── background.webp
│ │ ├── comments_background.jpg
│ │ └── comments_background.webp
│ │ └── styles.css
├── to_msg.py
└── web_bottle.py
├── pdm.lock
└── pyproject.toml
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # poetry
98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99 | # This is especially recommended for binary packages to ensure reproducibility, and is more
100 | # commonly ignored for libraries.
101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102 | #poetry.lock
103 |
104 | # pdm
105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106 | #pdm.lock
107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108 | # in version control.
109 | # https://pdm-project.org/#use-with-ide
110 | .pdm.toml
111 | .pdm-python
112 | .pdm-build/
113 |
114 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
115 | __pypackages__/
116 |
117 | # Celery stuff
118 | celerybeat-schedule
119 | celerybeat.pid
120 |
121 | # SageMath parsed files
122 | *.sage.py
123 |
124 | # Environments
125 | .env
126 | .venv
127 | env/
128 | venv/
129 | ENV/
130 | env.bak/
131 | venv.bak/
132 |
133 | # Spyder project settings
134 | .spyderproject
135 | .spyproject
136 |
137 | # Rope project settings
138 | .ropeproject
139 |
140 | # mkdocs documentation
141 | /site
142 |
143 | # mypy
144 | .mypy_cache/
145 | .dmypy.json
146 | dmypy.json
147 |
148 | # Pyre type checker
149 | .pyre/
150 |
151 | # pytype static type analyzer
152 | .pytype/
153 |
154 | # Cython debug symbols
155 | cython_debug/
156 |
157 | # PyCharm
158 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
159 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
160 | # and can be added to the global gitignore or merged into this file. For a more nuclear
161 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
162 | .idea/
163 |
--------------------------------------------------------------------------------
/B66FEE6EE4B550CF930CF48FFB9EDC0D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/B66FEE6EE4B550CF930CF48FFB9EDC0D.png
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) [year] [fullname]
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | # nonebot_plugin_web_bottle
9 | 
10 | 
11 | [](https://pdm-project.org)
12 |
13 | ✨一个基于nonebot2与onebotv11 使用fastapi驱动的漂流瓶插件,有一个简单的web用于审核用户提交的漂流瓶✨
14 |
15 |
16 |
17 |
18 |
19 | ## 在2025年1月1日更新了图片存储位置,请务必更新最新版!
20 |
21 | # 如何安装?
22 | (建议使用pip下载后手动在github页面下载源代码并将插件放入插件目录中以使用最新的更改)
23 | **Pypi**
24 | ```bash
25 | pip install nonebot-plugin-web-bottle
26 | ```
27 |
28 | **Nonebot**
29 | ```bash
30 | nb plugin install nonebot_plugin_web_bottle
31 | ```
32 |
33 | # 目前实现了什么?
34 | - [x] 在QQ内
35 | - [x] 丢瓶子
36 | - [x] 捡瓶子
37 | - [x] 评论漂流瓶 [编号] [评论内容]
38 | - [x] 点赞漂流瓶 [编号]
39 | - [x] 在网页端
40 | - [x] 审核漂流瓶
41 | - [x] 审核评论
42 | - [x] 登录验证
43 |
44 | # 效果图:
45 | 
46 | 
47 | 
48 | # 关于插件的其他注意事项
49 | ## 存储位置
50 | 本插件使用商店的 plugin-localstore(https://github.com/nonebot/plugin-localstore)
51 | 默认存储地址请前往其文档查看。
52 | 可以自己配置到机器人主目录,方便后续随时查看
53 | ```
54 | localstore_cache_dir=
55 | localstore_config_dir=
56 | localstore_data_dir=
57 | ```
58 | 在这个插件里,你通常只需要配置修改 localstore_data_dir= 即可
59 |
60 | ## 建议nonebot配置
61 | 如果想要在其他机器上访问到审核web,请修改nonebot运行的IP,在配置文件中修改如下:
62 |
63 | ```
64 | HOST=0.0.0.0
65 | ```
66 |
67 | ## web页面地址:
68 | ```
69 | http://location:nonebot端口/login 登录页面
70 | http://location:nonebot端口/check 漂流瓶审核
71 | http://location:nonebot端口/comments 评论审核
72 |
73 | 或者将location替换为nonebot所在机器的IPv4地址
74 | ```
75 | ## 背景图片
76 | 位于:
77 | ```
78 | 插件目录\templates\static\images
79 | ```
80 | 可自行修改,修改时请修改相应的webp图片
81 | ## 关于漂流瓶配置选项:
82 | 为防止过多读取时内存占用过高,一个瓶子内最多允许有两张图片,如果需要更多,请在nonebot配置项写入
83 |
84 | 以下配置为插件默认值,如果您认为不需要修改,可以不添加
85 | ```
86 | # 网页相关
87 | 默认登录密钥
88 | bottle_account = 'admin'
89 | bottle_password = 'password'
90 | expire_time=12 # 登录态过期时间(单位:小时)
91 | gzip_level=9 # gzip压缩等级(一般情况无需修改)
92 |
93 |
94 | # 丢瓶子规则配置
95 | cooling_time = 6 # 默认指令冷却,单位秒
96 | max_bottle_pic=2 # 丢瓶子允许最多图片数量
97 | max_bottle_liens=9 # 丢瓶子允许最多文字行数
98 | max_bottle_word=1200 # 丢瓶子允许最多字符数量
99 | embedded_help=True # 开启后,丢瓶子时未添加任何内容,则返回指令帮助
100 |
101 | # 瓶子评论区规则配置
102 | default_nickname="未知昵称" # 定义获取昵称失败时对评论区用户默认称呼
103 | bottle_msg_split=True # 分离瓶子和评论区为两条独立消息
104 | max_bottle_comments=3 # 捡瓶子最多展示评论条数
105 |
106 | # 适配官方Bot或提升响应速度
107 | bottle_msg_uname=True # 为False时关闭发送者昵称获取展示 适用于官方Bot或想要提高响应速度时
108 | bottle_msg_gname=True # 为False时关闭群聊昵称获取展示 同上
109 | qq_markdown=False # QQMD适配,请自行申请并修改to_msg.py中的模板
110 |
111 | ```
112 |
113 | 官方bot仅试过使用 [Gensokyo](https://github.com/Hoshinonyaruko/Gensokyo) 正常运行,野生机器人推荐使用NapCat,LLOneBot ,Lagrange 等
114 |
115 | 以下是适合本项目的markdown模板和实际效果展示,你需要在QQ开放平台>bot>开发>高阶能力下进行申请,过审后将平台分配的模板ID填写在本项目的tomsg.py模块中(此外,涉及模板图片和头像获取转换,您还需要参考Gensokyo接口文档,在本模块内填写所需IP和端口):
116 |
117 |
118 |
119 |
120 | 瓶子本体
121 | |
122 |  |
123 |
124 |
125 |
126 |
127 | 评论区
128 | |
129 |
130 |
131 |
132 |
133 |
134 |
135 | # 未来计划
136 | - [x] 提交至nonebot商店
137 | - [x] 修改漂流瓶投掷者输出方式为 QQ昵称 与 群昵称(已经编写 具体适配情况取决于你的协议端)
138 | - [x] 针对使用QQ开放平台BOT的场景进行调整(支持Gensokyo项目的适配)
139 | - [x] 增加登录验证
140 | - [ ] 新增提醒
141 | - [ ] 美化页面 (等几百年后我学会css再说吧)
142 | - [ ] 优化性能
143 |
--------------------------------------------------------------------------------
/example/bottles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/example/bottles.png
--------------------------------------------------------------------------------
/example/comments.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/example/comments.png
--------------------------------------------------------------------------------
/example/md01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/example/md01.png
--------------------------------------------------------------------------------
/example/md02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/example/md02.png
--------------------------------------------------------------------------------
/example/md03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/example/md03.jpg
--------------------------------------------------------------------------------
/img/NoneBotPlugin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/img/NoneBotPlugin.png
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # 默认忽略的文件
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/.idea/.name:
--------------------------------------------------------------------------------
1 | __init__.py
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
24 |
25 |
26 |
27 |
28 |
29 |
40 |
41 |
42 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/.idea/nonebot_plugin_web_bottle.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/__init__.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | import re
3 | import json
4 | from nonebot.adapters.onebot.v11 import Message, GroupMessageEvent, Bot
5 | from nonebot.adapters.onebot.v11.helpers import Cooldown
6 | from nonebot.plugin import PluginMetadata
7 | from nonebot.params import CommandArg
8 | from nonebot import on_command
9 | import nonebot
10 | import base64
11 | from .web_bottle import Bottle, id_add, serialize_message
12 | from .to_msg import botte_routing
13 | from .config import Config
14 | from . import data_deal
15 |
16 | driver = nonebot.get_driver()
17 | global_config = driver.config
18 | config = Config.parse_obj(global_config)
19 | bottle_msg_split = config.bottle_msg_split
20 | max_bottle_liens = config.max_bottle_liens
21 | max_bottle_word = config.max_bottle_word
22 | max_bottle_pic = config.max_bottle_pic
23 | embedded_help = config.embedded_help
24 | coll_time = config.cooling_time
25 | Config = config
26 |
27 | __plugin_meta__ = PluginMetadata(
28 | name="漂流瓶",
29 | description="一个基于nonebot2与onebotv11 使用fastapi驱动的漂流瓶插件,有一个简单的web用于审核用户提交的漂流瓶",
30 | usage="""
31 | 扔瓶子 [图片/文本]
32 | 捡瓶子
33 | 评论漂流瓶 [编号] [文本]
34 | 点赞漂流瓶 [编号]
35 | """,
36 | type="application",
37 | # 发布必填,当前有效类型有:`library`(为其他插件编写提供功能),`application`(向机器人用户提供功能)。
38 | homepage="https://github.com/luosheng520qaq/nonebot-plugin-web-bottle",
39 | # 发布必填。
40 | config=Config,
41 | supported_adapters={"~onebot.v11"},
42 | # 支持的适配器集合,其中 `~` 在此处代表前缀 `nonebot.adapters.`,其余适配器亦按此格式填写。
43 | # 若插件可以保证兼容所有适配器(即仅使用基本适配器功能)可不填写,否则应该列出插件支持的适配器。
44 | )
45 |
46 | bottle_help_text = __plugin_meta__.usage
47 |
48 | throw = on_command("丢瓶子", aliases={"扔瓶子"}, priority=1, block=True)
49 | get_bottle = on_command("捡瓶子", aliases={"捡漂流瓶"}, priority=1, block=True)
50 | up_bottle = on_command("点赞漂流瓶", priority=1, block=True)
51 | comment = on_command("评论漂流瓶", priority=1, block=True)
52 | read_bottle = on_command("查看漂流瓶", priority=1, block=True)
53 | bottle_help = on_command("漂流瓶", aliases={"漂流瓶菜单"}, priority=1, block=True)
54 |
55 |
56 | @bottle_help.handle()
57 | async def _():
58 | await bottle_help.finish(
59 | "\n漂流瓶使用帮助" + bottle_help_text
60 | )
61 |
62 |
63 | @read_bottle.handle()
64 | async def _(bot: Bot, foo: Message = CommandArg()):
65 | try:
66 | a = int(foo.extract_plain_text())
67 | except ValueError:
68 | await read_bottle.finish("请输入正确的漂流瓶id")
69 | bottle = Bottle(data_deal.conn_bottle)
70 | bottle_data = await bottle.get_approved_bottle_by_id(a)
71 | if bottle_data is None:
72 | cursor = data_deal.conn_bottle.cursor()
73 | query = """
74 | SELECT state
75 | FROM pending
76 | WHERE id = ?
77 | """
78 | # 执行查询
79 | cursor.execute(query, (a,))
80 | # 获取查询结果
81 | result = cursor.fetchone()
82 | # 关闭游标
83 | cursor.close()
84 | # 返回查询结果,如果没有找到则返回None
85 | c = result[0]
86 | if c == 100: # noqa: PLR2004
87 | await read_bottle.finish("漂流瓶已拒绝无法查看!")
88 | elif c == 0:
89 | await read_bottle.finish("漂流瓶未审核")
90 | else:
91 | await read_bottle.finish("发生未知错误!")
92 |
93 | # 处理消息
94 | messages = await botte_routing(bot, bottle_data, bottle)
95 |
96 | # 发送消息
97 | for message in messages:
98 | await read_bottle.send(message)
99 | await read_bottle.finish()
100 |
101 |
102 | @comment.handle()
103 | async def _(event: GroupMessageEvent, foo: Message = CommandArg()):
104 | try:
105 | a = str(foo).split(maxsplit=1)
106 | bottle_id = int(a[0])
107 | text = str(foo)[len(a[0]):].strip()
108 | except ValueError:
109 | await comment.finish("请输入正确的漂流瓶id和评论内容")
110 | except IndexError:
111 | await comment.finish("请输入评论内容")
112 | bottle = Bottle(data_deal.conn_bottle)
113 | a = await bottle.add_comment_if_approved(bottle_id, text, str(event.user_id))
114 | if not a:
115 | await comment.send("评论失败,漂流瓶不存在")
116 | else:
117 | await comment.send("评论成功!")
118 | botid = event.self_id
119 | if str(botid) != '102050518':
120 | await comment.finish()
121 | else:
122 | md = {"keyboard": {"id": "102050518_1725470003"}}
123 | json1 = json.dumps(md)
124 | bytes = json1.encode('utf-8')
125 | data = base64.b64encode(bytes).decode('utf-8')
126 | await comment.finish(Message(f"[CQ:markdown,data=base64://{data}]"))
127 |
128 | @up_bottle.handle()
129 | async def _(event: GroupMessageEvent, args: Message = CommandArg()):
130 | try:
131 | bid = int(args.extract_plain_text())
132 | except ValueError:
133 | await up_bottle.finish("请输入正确的漂流瓶id")
134 | bottle = Bottle(data_deal.conn_bottle)
135 | a, num = await bottle.up_bottle(bid, str(event.user_id))
136 | if not a:
137 | await up_bottle.send("点赞失败,漂流瓶不存在或你已经点赞过了")
138 | else:
139 | await up_bottle.send(f"点赞成功,现在有{num}个赞!")
140 | botid = event.self_id
141 | if str(botid) != '102050518':
142 | await up_bottle.finish()
143 | else:
144 | md = {"keyboard": {"id": "102050518_1725470003"}}
145 | json1 = json.dumps(md)
146 | bytes = json1.encode('utf-8')
147 | data = base64.b64encode(bytes).decode('utf-8')
148 | await up_bottle.finish(Message(f"[CQ:markdown,data=base64://{data}]"))
149 |
150 | @get_bottle.handle(parameterless=[Cooldown(cooldown=coll_time)])
151 | async def _(bot: Bot,event: GroupMessageEvent):
152 | bottle = Bottle(data_deal.conn_bottle)
153 | bottle_data = await bottle.random_get_approves_bottle()
154 | if not bottle_data:
155 | await get_bottle.finish("捞瓶子失败,没有漂流瓶~")
156 |
157 | # 处理消息
158 | messages = await botte_routing(bot, bottle_data, bottle)
159 |
160 | # 发送消息
161 | for message in messages:
162 | await get_bottle.send(message)
163 | botid = event.self_id
164 | if str(botid) != '102050518':
165 | await get_bottle.finish()
166 | else:
167 | md = {"keyboard": {"id": "102050518_1725470003"}}
168 | json1 = json.dumps(md)
169 | bytes = json1.encode('utf-8')
170 | data = base64.b64encode(bytes).decode('utf-8')
171 | await get_bottle.finish(Message(f"[CQ:markdown,data=base64://{data}]"))
172 | await get_bottle.finish()
173 |
174 |
175 | @throw.handle(parameterless=[Cooldown(cooldown=coll_time)])
176 | async def _(event: GroupMessageEvent, args: Message = CommandArg()):
177 | if not args:
178 | if embedded_help:
179 | await throw.finish(f"您还没有写好瓶子的内容哦~\n漂流瓶食用方法:{bottle_help_text}")
180 | await throw.finish("您还没有写好瓶子的内容哦~")
181 | else:
182 | content = args.extract_plain_text().strip()
183 | if len(content) > max_bottle_word:
184 | await throw.finish(f"丢瓶子失败啦,请不要超过{max_bottle_word}字符哦~")
185 | # 匹配 \n, \r\n 和 \r
186 | newline_pattern = r"[\r\n]+"
187 | msg = event.get_message()
188 | if len(re.findall(newline_pattern, content)) > max_bottle_liens:
189 | await throw.finish(f"丢瓶子失败啦,请不要超过{max_bottle_liens}行内容哦~")
190 | if sum(1 for seg in msg if seg.type == "image") > max_bottle_pic:
191 | await throw.finish(f"丢瓶子失败啦,请不要超过{max_bottle_pic}张图片哦~")
192 | bid = await id_add()
193 | conn = data_deal.conn_bottle
194 | await serialize_message(event.get_message(), bid)
195 | bottle = Bottle(conn)
196 | time_info = datetime.now().strftime("%Y-%m-%d %H:%M:%S") # TODO)): 统一规定时区 # noqa: DTZ005
197 | if await bottle.add_pending_bottle(bid, content, str(event.user_id), str(event.group_id), time_info):
198 | await throw.finish(f"丢瓶子成功!瓶子ID是:{bid},将在神秘存在审核通过后出现在大海中~")
199 | else:
200 | await throw.finish(f"丢瓶子失败啦,出现未知异常")
201 |
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/config.py:
--------------------------------------------------------------------------------
1 | from pydantic import BaseModel
2 |
3 |
4 | class Config(BaseModel):
5 | # 丢瓶子规则配置
6 | max_bottle_pic: int = 2 # 丢瓶子允许最多图片数量
7 | max_bottle_liens: int = 9 # 丢瓶子允许最多文字行数
8 | max_bottle_word: int = 1200 # 丢瓶子允许最多字符数量
9 | embedded_help: bool = True # 开启后,丢瓶子时未添加任何内容,则返回指令帮助
10 | cooling_time: int = 6 # 默认指令冷却 单位秒
11 | # 瓶子评论规区则配置
12 | default_nickname: str = "未知昵称" # 定义获取昵称失败时对评论区用户默认称呼
13 | bottle_msg_split: bool = True # 分离瓶子和评论区为两条独立消息
14 | max_bottle_comments: int = 3 # 捡瓶子最多展示评论条数
15 |
16 | # 适配官方Bot或提升响应速度
17 | bottle_msg_uname: bool = True # 为False时关闭发送者昵称获取展示 适用于官方Bot或想要提高响应速度时
18 | bottle_msg_gname: bool = True # 为False时关闭群聊昵称获取展示 同上
19 | qq_markdown: bool = False # QQMD适配,请自行申请并修改to_msg.py中的模板
20 | bottle_account: str = 'admin'
21 | bottle_password: str = 'password'
22 | expire_time: int = 12 # 登录态过期时间(单位:小时)
23 |
24 | gzip_level: int = 9
25 |
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/data_deal.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 |
3 | from nonebot import get_driver, logger, require
4 |
5 | require("nonebot_plugin_localstore")
6 | from PIL import Image,UnidentifiedImageError
7 | import nonebot_plugin_localstore as store # noqa: E402
8 | from io import BytesIO
9 | drive = get_driver()
10 | conn_bottle: sqlite3.Connection
11 | plugin_data = store.get_data_dir("nonebot_plugin_web_bottle")
12 |
13 | image_path = plugin_data / 'img'
14 | image_path.mkdir(parents=True, exist_ok=True)
15 |
16 | @drive.on_startup
17 | def _():
18 | global conn_bottle # noqa: PLW0603 # !WTF
19 |
20 | # 获取插件的数据目录
21 | plugin_data = store.get_data_dir("nonebot_plugin_web_bottle")
22 | logger.info(f"漂流瓶插件数据存储目录将会在:{plugin_data}")
23 |
24 | # 确保目录存在
25 | plugin_data.mkdir(exist_ok=True)
26 |
27 | # 数据库文件路径
28 | db_path = plugin_data / "bottle.db"
29 | logger.info("正在检查数据库是否存在!")
30 |
31 | # 检查数据库文件是否存在
32 | if not db_path.exists():
33 | logger.warning("数据库不存在,将跳过创建 images 表!")
34 |
35 | # 创建并连接到数据库(但不创建 images 表)
36 | conn = sqlite3.connect(db_path)
37 | cursor = conn.cursor()
38 | logger.info(f"尝试在路径 {db_path} 中建立表")
39 |
40 | # 执行多个 SQL 语句创建表
41 | cursor.execute("""
42 | CREATE TABLE approved (
43 | id INTEGER PRIMARY KEY,
44 | content TEXT,
45 | userid TEXT,
46 | groupid TEXT,
47 | timeinfo TEXT,
48 | up INTEGER
49 | )
50 | """)
51 |
52 | cursor.execute("""
53 | CREATE TABLE comments (
54 | comment_id INTEGER PRIMARY KEY,
55 | id INTEGER,
56 | content TEXT,
57 | state TEXT,
58 | uid TEXT
59 | )
60 | """)
61 |
62 | cursor.execute("""
63 | CREATE TABLE pending (
64 | id INTEGER PRIMARY KEY,
65 | content TEXT,
66 | userid TEXT,
67 | groupid TEXT,
68 | timeinfo TEXT,
69 | state TEXT
70 | )
71 | """)
72 |
73 | cursor.execute("""
74 | CREATE TABLE user_up (
75 | uid INTEGER PRIMARY KEY UNIQUE,
76 | ids TEXT
77 | )
78 | """)
79 |
80 | # 提交更改并关闭连接
81 | conn.commit()
82 | conn.close()
83 | logger.success("数据库和表成功创建!")
84 | else:
85 | logger.info("数据库已存在,跳过创建步骤!")
86 |
87 | # 连接到数据库检查 images 表结构
88 | conn = sqlite3.connect(db_path)
89 | cursor = conn.cursor()
90 |
91 | # 检查 images 表是否存在
92 | cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='images'")
93 | images_table_exists = cursor.fetchone()
94 |
95 | if images_table_exists:
96 | logger.info("检测到 images 表,正在迁移数据,请稍候...")
97 | cursor.execute("SELECT id, data FROM images")
98 | rows = cursor.fetchall()
99 |
100 | # 在迁移数据的循环中添加校验
101 | for image_id, image_data in rows:
102 | if not image_data:
103 | logger.warning(f"ID 为 {image_id} 的数据为空,跳过迁移!")
104 | continue
105 |
106 | # 确保对应的 ID 文件夹存在
107 | target_dir = image_path / str(image_id)
108 | target_dir.mkdir(parents=True, exist_ok=True)
109 |
110 | try:
111 | # 打开二进制数据为图像
112 | image = Image.open(BytesIO(image_data))
113 | # 将图片转换为 webp 格式
114 | image = image.convert("RGB") # 确保兼容性
115 |
116 | # 找到子文件夹中最大索引值,生成下一个文件名
117 | existing_files = list(target_dir.glob("*.webp"))
118 | max_index = -1 # 初始为 -1,如果没有文件则从 0 开始
119 | for file in existing_files:
120 | try:
121 | # 提取文件名中的数字索引(假设文件名格式为 image_.webp)
122 | index = int(file.stem.split('_')[-1]) # 提取 "image_" 中的
123 | max_index = max(max_index, index)
124 | except ValueError:
125 | continue # 忽略无法解析为数字的文件名
126 |
127 | next_index = max_index + 1 # 下一个文件名的索引
128 | target_path = target_dir / f"image_{next_index}.webp"
129 |
130 | # 保存图片
131 | image.save(target_path, format="WEBP", quality=80) # 调整质量以平衡大小
132 | logger.info(f"成功迁移 ID 为 {image_id} 的图片至 {target_path}")
133 | except UnidentifiedImageError:
134 | logger.error(f"ID 为 {image_id} 的数据不是有效图片,跳过迁移!")
135 | except Exception as e:
136 | logger.error(f"迁移 ID 为 {image_id} 的图片时发生未知错误:{e}")
137 |
138 | logger.info("迁移完成,正在删除 images 表...")
139 | cursor.execute("DROP TABLE images")
140 | conn.commit()
141 | logger.success("images 表已删除,数据迁移成功!")
142 |
143 | conn.close()
144 |
145 | logger.success("加载成功!")
146 |
147 | # 创建全局数据库连接
148 | db_path = store.get_data_dir("nonebot_plugin_web_bottle") / "bottle.db"
149 | conn_bottle = sqlite3.connect(db_path, check_same_thread=False)
150 |
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/comments.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 评论审核
7 |
8 |
23 |
24 |
25 | 评论审核
26 | {% if comment %}
27 |
36 | {% else %}
37 | 没有待审核的评论。
38 | {% endif %}
39 |
40 |
41 |
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 瓶子审核
7 |
8 |
9 |
30 |
196 |
197 |
198 |
199 | 剩余待审核的瓶子数量: {{ pending_count }}
200 |
201 |
ID:
202 |
内容:
203 |
用户ID:
204 |
群组ID:
205 |
时间信息:
206 |
审核状态:
207 |
208 |
209 |
210 |
211 |
212 |
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/login/1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 登录
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/login/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 漂流瓶审核登录 | 每次登录都是与你の邂逅。
8 |
9 |
10 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
53 |
54 |
55 |
漂流瓶审核登录
56 |
Ciallo~(∠・ω< )⌒★
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
98 |
99 |
100 |
101 |
104 |
105 |
106 |
107 |
108 |
162 |
163 |
164 |
165 |
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/login/static/css/backend.css:
--------------------------------------------------------------------------------
1 | /* 全局样式 */
2 | body {
3 | font-family: 'qiantu', Arial, sans-serif;
4 | background-color: #ffe4e1; /* 浅粉色背景 */
5 | margin: 0;
6 | padding: 0;
7 | overflow-x: hidden;
8 | color: #333; /* 文本颜色 */
9 | }
10 |
11 | /* 卡片样式 */
12 | .card {
13 | background: linear-gradient(145deg, #ffccf9, #ffc3a0); /* 渐变背景 */
14 | box-shadow: 0 8px 15px rgba(0, 0, 0, 0.1);
15 | border-radius: 16px;
16 | border: 2px solid #ffd1dc; /* 可爱的粉色边框 */
17 | }
18 |
19 | /* 按钮样式 */
20 | .btn {
21 | background-color: #ff87ab; /* 按钮背景色 */
22 | color: #fff;
23 | font-size: 1.2rem;
24 | padding: 0.8rem 1.5rem;
25 | border: none;
26 | border-radius: 25px;
27 | transition: transform 0.2s, background-color 0.3s;
28 | }
29 |
30 | .btn:hover {
31 | background-color: #ff5e78; /* 按钮悬停颜色 */
32 | transform: scale(1.05); /* 放大效果 */
33 | }
34 |
35 | /* 输入框样式 */
36 | .floating-input {
37 | border: 2px solid #ffd1dc;
38 | border-radius: 12px;
39 | background-color: #fff;
40 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
41 | font-size: 1rem;
42 | padding: 0.8rem 1rem;
43 | transition: border-color 0.3s;
44 | }
45 |
46 | .floating-input:focus {
47 | border-color: #ff87ab; /* 焦点时的颜色 */
48 | outline: none;
49 | }
50 |
51 | /* 标签样式 */
52 | label {
53 | color: #ff6f91; /* 浅粉色文字 */
54 | font-size: 1rem;
55 | }
56 |
57 | /* 标题样式 */
58 | h2 {
59 | color: #ff5e78; /* 深粉色标题 */
60 | font-size: 2rem;
61 | font-weight: bold;
62 | }
63 |
64 | /* 左侧内容样式 */
65 | .content-left {
66 | background: linear-gradient(145deg, #ffdde1, #ffc4d6);
67 | padding: 2rem;
68 | border-radius: 16px 0 0 16px;
69 | }
70 |
71 | /* 图片样式 */
72 | .image-right {
73 | border-radius: 16px;
74 | box-shadow: 0 8px 15px rgba(0, 0, 0, 0.1);
75 | transition: transform 0.3s;
76 | }
77 |
78 | .image-right:hover {
79 | transform: scale(1.1); /* 图片放大效果 */
80 | }
81 |
82 | /* 错误弹窗样式 */
83 | .modal-content {
84 | background: linear-gradient(145deg, #ffe4e1, #ffd1dc); /* 渐变背景 */
85 | border-radius: 16px;
86 | color: #ff5e78; /* 文本颜色 */
87 | }
88 |
89 | .modal-header {
90 | background-color: #ff87ab; /* 标题背景 */
91 | border-top-left-radius: 16px;
92 | border-top-right-radius: 16px;
93 | color: #fff;
94 | }
95 |
96 | .modal-footer {
97 | border-top: none;
98 | background-color: #ffdde1; /* 底部背景 */
99 | }
100 |
101 | /* 垂直居中对齐 */
102 | .height-self-center {
103 | min-height: 100vh;
104 | display: flex;
105 | align-items: center;
106 | justify-content: center;
107 | }
108 |
109 | /* 按钮悬停时的动效 */
110 | .marlene-btn-login:hover {
111 | background-color: #ff5e78;
112 | color: #fff;
113 | transform: translateY(-3px);
114 | }
115 |
116 | /* 自适应处理 */
117 | @media (max-width: 768px) {
118 | .content-left,
119 | .content-right {
120 | border-radius: 16px !important;
121 | }
122 |
123 | .image-right {
124 | width: 100%; /* 在小屏幕时图片占满 */
125 | margin-top: 1rem;
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/login/static/font/qiantu.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/login/static/font/qiantu.ttf
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/login/static/image/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/login/static/image/background.png
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/login/static/images/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/login/static/images/background.jpg
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/login/static/images/background.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/login/static/images/background.webp
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/login/static/images/background2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/login/static/images/background2.png
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/login/static/images/background2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/login/static/images/background2.webp
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/login/static/images/linglan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/login/static/images/linglan.png
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/login/static/images/linglan.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/login/static/images/linglan.webp
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/login/static/images/luoxiqi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/login/static/images/luoxiqi.png
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/login/static/images/luoxiqi.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/login/static/images/luoxiqi.webp
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/login/static/images/xilufei.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/login/static/images/xilufei.png
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/login/static/images/xilufei.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/login/static/images/xilufei.webp
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/login/static/picture/linglan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/login/static/picture/linglan.png
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/login/static/picture/luoxiqi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/login/static/picture/luoxiqi.png
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/login/static/picture/xilufei.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/login/static/picture/xilufei.png
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/login/static/styles.css:
--------------------------------------------------------------------------------
1 | /* 全局样式 */
2 | body {
3 | font-family: 'qiantu', Arial, sans-serif;
4 | background-color: #ffe4e1; /* 浅粉色背景 */
5 | margin: 0;
6 | padding: 0;
7 | overflow-x: hidden;
8 | color: #333; /* 文本颜色 */
9 | }
10 |
11 | /* 卡片样式 */
12 | .card {
13 | background: linear-gradient(145deg, #ffccf9, #ffc3a0); /* 渐变背景 */
14 | box-shadow: 0 8px 15px rgba(0, 0, 0, 0.1);
15 | border-radius: 16px;
16 | border: 2px solid #ffd1dc; /* 可爱的粉色边框 */
17 | }
18 |
19 | /* 按钮样式 */
20 | .btn {
21 | background-color: #ff87ab; /* 按钮背景色 */
22 | color: #fff;
23 | font-size: 1.2rem;
24 | padding: 0.8rem 1.5rem;
25 | border: none;
26 | border-radius: 25px;
27 | transition: transform 0.2s, background-color 0.3s;
28 | }
29 |
30 | .btn:hover {
31 | background-color: #ff5e78; /* 按钮悬停颜色 */
32 | transform: scale(1.05); /* 放大效果 */
33 | }
34 |
35 | /* 输入框样式 */
36 | .floating-input {
37 | border: 2px solid #ffd1dc;
38 | border-radius: 12px;
39 | background-color: #fff;
40 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
41 | font-size: 1rem;
42 | padding: 0.8rem 1rem;
43 | transition: border-color 0.3s;
44 | }
45 |
46 | .floating-input:focus {
47 | border-color: #ff87ab; /* 焦点时的颜色 */
48 | outline: none;
49 | }
50 |
51 | /* 标签样式 */
52 | label {
53 | color: #ff6f91; /* 浅粉色文字 */
54 | font-size: 1rem;
55 | }
56 |
57 | /* 标题样式 */
58 | h2 {
59 | color: #ff5e78; /* 深粉色标题 */
60 | font-size: 2rem;
61 | font-weight: bold;
62 | }
63 |
64 | /* 左侧内容样式 */
65 | .content-left {
66 | background: linear-gradient(145deg, #ffdde1, #ffc4d6);
67 | padding: 2rem;
68 | border-radius: 16px 0 0 16px;
69 | }
70 |
71 | /* 图片样式 */
72 | .image-right {
73 | border-radius: 16px;
74 | box-shadow: 0 8px 15px rgba(0, 0, 0, 0.1);
75 | transition: transform 0.3s;
76 | }
77 |
78 | .image-right:hover {
79 | transform: scale(1.1); /* 图片放大效果 */
80 | }
81 |
82 | /* 错误弹窗样式 */
83 | .modal-content {
84 | background: linear-gradient(145deg, #ffe4e1, #ffd1dc); /* 渐变背景 */
85 | border-radius: 16px;
86 | color: #ff5e78; /* 文本颜色 */
87 | }
88 |
89 | .modal-header {
90 | background-color: #ff87ab; /* 标题背景 */
91 | border-top-left-radius: 16px;
92 | border-top-right-radius: 16px;
93 | color: #fff;
94 | }
95 |
96 | .modal-footer {
97 | border-top: none;
98 | background-color: #ffdde1; /* 底部背景 */
99 | }
100 |
101 | /* 垂直居中对齐 */
102 | .height-self-center {
103 | min-height: 100vh;
104 | display: flex;
105 | align-items: center;
106 | justify-content: center;
107 | }
108 |
109 | /* 按钮悬停时的动效 */
110 | .marlene-btn-login:hover {
111 | background-color: #ff5e78;
112 | color: #fff;
113 | transform: translateY(-3px);
114 | }
115 |
116 | /* 自适应处理 */
117 | @media (max-width: 768px) {
118 | .content-left,
119 | .content-right {
120 | border-radius: 16px !important;
121 | }
122 |
123 | .image-right {
124 | width: 100%; /* 在小屏幕时图片占满 */
125 | margin-top: 1rem;
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/static/comments_styles.css:
--------------------------------------------------------------------------------
1 | /* comments_styles.css */
2 | body {
3 | font-family: 'Comic Sans MS', cursive, sans-serif; /* 更换为可爱的字体 */
4 | background: url('/static/images/background.jpg') no-repeat center center fixed;
5 | background-size: cover;
6 | background-attachment: fixed;
7 | margin: 0;
8 | padding: 0;
9 | color: #ff6666; /* 更改文字颜色为粉红色 */
10 | height: 100;
11 | overflow: hidden;
12 | }
13 |
14 | h1 {
15 | text-align: center;
16 | color: #ff6666; /* 更改为粉红色 */
17 | margin-top: 20px;
18 | font-size: 2em; /* 增大字体大小 */
19 | }
20 |
21 | #comment-container {
22 | max-width: 600px;
23 | margin: 40px auto;
24 | background: rgba(255, 255, 255, 0.9);
25 | padding: 20px;
26 | border-radius: 20px; /* 增加圆角 */
27 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* 添加阴影 */
28 | }
29 |
30 | #comment-container p {
31 | margin: 10px 0;
32 | font-size: 1.1em;
33 | line-height: 1.4;
34 | }
35 |
36 | #comment-container strong {
37 | color: #ff6666; /* 更改为粉红色 */
38 | font-size: 1.2em;
39 | }
40 |
41 | button {
42 | padding: 10px 20px;
43 | margin: 10px 5px;
44 | border: none;
45 | border-radius: 20px; /* 增加圆角 */
46 | cursor: pointer;
47 | font-size: 1em;
48 | background-position: center;
49 | background-size: contain;
50 | }
51 |
52 | button:hover {
53 | opacity: 0.8;
54 | }
55 |
56 | button:active {
57 | transform: scale(0.98);
58 | }
59 |
60 | button:nth-of-type(1) {
61 | background-color: #ff99cc; /* 更改为淡粉色 */
62 | color: white;
63 | }
64 |
65 | button:nth-of-type(2) {
66 | background-color: #ff6666; /* 更改为粉红色 */
67 | color: white;
68 | }
69 |
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/static/images/Thumbs.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/static/images/Thumbs.db
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/static/images/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/static/images/background.jpg
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/static/images/background.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/static/images/background.webp
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/static/images/comments_background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/static/images/comments_background.jpg
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/static/images/comments_background.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/fcefb4fde227d570a0a416e45a4e16e232c2595c/nonebot_plugin_web_bottle/templates/static/images/comments_background.webp
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/templates/static/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: 'Comic Sans MS', cursive, sans-serif; /* 更换为可爱的字体 */
3 | background: image-set(url('/static/images/background.webp') type('image/webp'), url('/static/images/background.jpg') type('image/jpeg')) no-repeat center center fixed;
4 | background-size: cover;
5 | background-attachment: fixed; /* 确保背景图片在滚动时保持固定 */
6 | margin: 0;
7 | padding: 0;
8 | color: #ff6666; /* 更改文字颜色为粉红色 */
9 | height: 100vh; /* 使背景图片占满整个屏幕 */
10 | overflow: hidden; /* 防止滚动条出现 */
11 | }
12 |
13 | #message {
14 | text-align: center;
15 | font-size: 1.5em;
16 | margin-top: 20px;
17 | color: #ff6666; /* 更改为粉红色 */
18 | font-weight: bold; /* 加粗文字 */
19 | }
20 |
21 | #bottle {
22 | max-width: 600px;
23 | margin: 40px auto;
24 | background: rgba(255, 255, 255, 0.8);
25 | padding: 20px;
26 | border-radius: 20px; /* 增加圆角 */
27 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* 添加阴影 */
28 | }
29 |
30 | #bottle p {
31 | margin: 10px 0;
32 | font-size: 1.1em;
33 | line-height: 1.4;
34 | }
35 |
36 | #images {
37 | display: flex;
38 | flex-wrap: wrap;
39 | gap: 10px;
40 | margin: 10px 0;
41 | position: relative; /* 新增部分 */
42 | }
43 |
44 | #images img {
45 | max-width: 150px; /* 调整宽度 */
46 | max-height: 150px; /* 调整高度 */
47 | border: 4px solid #ffcccc; /* 改变边框颜色和宽度 */
48 | border-radius: 20px; /* 增加圆角 */
49 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 添加阴影 */
50 | }
51 |
52 | .zoomed {
53 | position: fixed !important;
54 | top: 50% !important;
55 | left: 50% !important;
56 | transform: translate(-50%, -50%) scale(10) !important; /* 放大5倍 */
57 | max-width: 100% !important;
58 | max-height: 100% !important;
59 | z-index: 1000 !important;
60 | border: 6px solid #ff6666 !important;
61 | border-radius: 30px !important;
62 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2) !important;
63 | }
64 |
65 |
66 |
67 | button {
68 | padding: 10px 20px;
69 | margin: 10px 5px;
70 | border: none;
71 | border-radius: 20px; /* 增加圆角 */
72 | cursor: pointer;
73 | font-size: 1em;
74 | }
75 |
76 | button:hover {
77 | opacity: 0.8;
78 | }
79 |
80 | button:active {
81 | transform: scale(0.98);
82 | }
83 |
84 | button:nth-of-type(1) {
85 | background-color: #ff99cc; /* 更改为淡粉色 */
86 | color: white;
87 | }
88 |
89 | button:nth-of-type(2) {
90 | background-color: #ff6666; /* 更改为粉红色 */
91 | color: white;
92 | }
93 |
94 | #bottle span {
95 | font-weight: bold;
96 | color: #ff6666;
97 | }
98 |
99 | #bottle button {
100 | padding: 12px 24px;
101 | font-size: 1.1em;
102 | }
103 |
104 | #bottle button + button {
105 | margin-left: 20px;
106 | }
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/to_msg.py:
--------------------------------------------------------------------------------
1 | from nonebot.adapters.onebot.v11 import Bot, Message, MessageSegment
2 | from .web_bottle import Bottle
3 | from .config import Config
4 | import nonebot
5 |
6 | from PIL import Image
7 |
8 | import base64
9 | import httpx
10 | import json
11 | import io
12 |
13 | driver = nonebot.get_driver()
14 | global_config = driver.config
15 | config = Config.parse_obj(global_config)
16 | max_bottle_comments = config.max_bottle_comments
17 | bottle_msg_uname = config.bottle_msg_uname
18 | bottle_msg_gname = config.bottle_msg_gname
19 | bottle_msg_split = config.bottle_msg_split
20 | default_nickname = config.default_nickname
21 | qq_markdown = config.qq_markdown
22 |
23 | # require("Tea_你好茶茶")
24 | # require("Tea_API")
25 | # from src.core.Tea_你好茶茶 import 玩家昵称接口
26 | # from src.core.Tea_API import 停用MD, ServerAPI
27 | ServerAPI = "127.0.0.1:8080" # 请参考Gensokyo文档进行接口配置
28 | MDID01 = "000000000_0000000000"
29 | MDID02 = "000000001_0000000001"
30 |
31 |
32 | # 以下为普通消息处理
33 | async def get_botte_all(bot: Bot, bottle_data: dict, bottle: Bottle) -> list:
34 | """
35 | 获取瓶子内容、评论,并以列表形式返回
36 | """
37 | message = []
38 |
39 | # 瓶子内容获取
40 | msg_one = f"漂流瓶ID:{bottle_data['id']}\n内容:{bottle_data['content']}\n"
41 | if bottle_msg_uname:
42 | msg_one += f"发送者:{await get_user_name(bot, bottle_data['userid'])}\n"
43 | if bottle_msg_gname:
44 | msg_one += f"发送群:{await get_group_name(bot, bottle_data['groupid'])}\n"
45 | msg_one += f"发送时间:{bottle_data['timeinfo']}"
46 | msg_one = Message(msg_one)
47 |
48 | # 将图片添加到瓶子内容中
49 | img_bytes_list = await bottle.get_bottle_images(bottle_data["id"])
50 | for img_bytes in img_bytes_list:
51 | img_base64 = base64.b64encode(img_bytes).decode("utf-8")
52 | img_segment = MessageSegment.image(f"base64://{img_base64}")
53 | msg_one += img_segment
54 |
55 | message.append(msg_one)
56 |
57 | # 格式化瓶子评论
58 | comments = await bottle.get_comments(int(bottle_data["id"]))
59 | if comments:
60 | formatted_comments = await format_comments(bot, comments)
61 | if bottle_msg_split:
62 | message.append(formatted_comments)
63 | else:
64 | message[0] += Message(formatted_comments)
65 |
66 | return message
67 |
68 |
69 | async def get_user_name(bot: Bot, user_id: int) -> str:
70 | """
71 | 获取用户昵称
72 | """
73 | try:
74 | user_info = await bot.call_api(api="get_stranger_info", user_id=user_id)
75 | return user_info["nickname"]
76 | except:
77 | return str(user_id)
78 |
79 |
80 | async def get_group_name(bot: Bot, group_id: int) -> str:
81 | """
82 | 获取群名称
83 | """
84 | try:
85 | group_info = await bot.call_api(api="get_group_info", group_id=group_id)
86 | return group_info["group_name"]
87 | except:
88 | return str(group_id)
89 |
90 |
91 | async def format_comments(bot: Bot, comments: str) -> str:
92 | """
93 | 格式化评论
94 | """
95 | comment_lines = comments.split("\n")
96 | formatted_comments = "评论区:\n"
97 | comment_count = 0
98 |
99 | for line in comment_lines:
100 | if comment_count >= max_bottle_comments:
101 | break
102 | user_id, comment = line.split(": ", 1)
103 | user_name = await get_user_name(bot, int(user_id)) if bottle_msg_uname else default_nickname
104 | formatted_comments += f"{user_name}: {comment}\n"
105 | comment_count += 1
106 |
107 | return formatted_comments
108 |
109 |
110 | # 以下为QQMD适配
111 | async def get_botte_tomd(bot: Bot, bottle_data: dict, bottle: Bottle) -> list:
112 | """
113 | 获取瓶子内容、评论,并以列表形式返回
114 | ---
115 | 最终内容为符合 Gensokyo 协议端的 markdown segment
116 | 它属于对 onebot v11 的扩展,用以发送 通过QQ开放平台申请的 Markdown模板
117 | ---
118 | 示例:
119 | {
120 | "type": "markdown",
121 | "data": {
122 | "data": "文本内容"
123 | }
124 | }
125 | """
126 | message = []
127 |
128 | # 瓶子本体文本获取
129 | msg_one = f"Time:{bottle_data['timeinfo']}\rBottle_id:{bottle_data['id']}\r\r{default_nickname}:" # {玩家昵称接口(bottle_data['userid'])}:"
130 | msg_one += bottle_data['content'].replace("\n", "\r") if bottle_data['content'] else "什么都没写"
131 |
132 | # 插入图片部分
133 | img_bytes_list = await bottle.get_bottle_images(bottle_data["id"])
134 | if not img_bytes_list:
135 | message.append(await create_markdown_segment(msg_one))
136 | else:
137 | for index, img_bytes in enumerate(img_bytes_list):
138 | img_b64 = base64.b64encode(img_bytes).decode()
139 | try:
140 | w, h = get_image_size(img_bytes)
141 | img_url = await post_image_to_server(img_b64, f"http://{ServerAPI}/uploadpic")
142 | if index == 0:
143 | message.append(await create_markdown_segment(msg_one, [w, h, img_url]))
144 | else:
145 | message.append(await create_markdown_segment("-", [w, h, img_url]))
146 | except Exception:
147 | pass
148 |
149 | # 格式化瓶子评论
150 | comments = await bottle.get_comments(int(bottle_data["id"]))
151 | # if comments:
152 | formatted_comments = await format_comments_md(bot, comments, bottle_data["id"])
153 | message.append(formatted_comments)
154 |
155 | return message
156 |
157 |
158 | async def post_image_to_server(base64_image: str, target_url: str) -> str:
159 | """
160 | 将图片上传到服务器
161 | """
162 | data = {'base64Image': base64_image}
163 | async with httpx.AsyncClient() as client:
164 | response = await client.post(target_url, data=data)
165 | if response.status_code != 200:
166 | raise Exception("Error response from server: {}".format(response.status_code))
167 | response_data = json.loads(response.text)
168 | if "url" in response_data:
169 | return response_data["url"]
170 | else:
171 | raise Exception("URL not found in response")
172 |
173 |
174 | def get_image_size(img_bytes: bytes) -> tuple:
175 | """
176 | 获取图片尺寸
177 | """
178 | with Image.open(io.BytesIO(img_bytes)) as image:
179 | width, height = image.size
180 | return str(width), str(height)
181 |
182 |
183 | async def create_markdown_segment(msg_one: str, img: list = None) -> str:
184 | """
185 | 创建 Markdown 段落
186 | """
187 | if img:
188 | data = {
189 | "markdown": {
190 | "custom_template_id": MDID01,
191 | "params": [
192 | {"key": "w", "values": [img[0]]},
193 | {"key": "h", "values": [img[1]]},
194 | {"key": "url", "values": [img[2]]},
195 | {"key": "msg", "values": [msg_one]}
196 | ]
197 | }
198 | }
199 | else:
200 | data = {
201 | "markdown": {
202 | "custom_template_id": MDID02,
203 | "params": [{"key": "msg", "values": [msg_one]}]
204 | }
205 | }
206 | json_str = json.dumps(data)
207 | encoded_data = base64.b64encode(json_str.encode('utf-8')).decode('utf-8')
208 | return f'[CQ:markdown,data=base64://{encoded_data}]'
209 |
210 |
211 | async def format_comments_md(bot: Bot, comments: str, bottle_id: int) -> str:
212 | """
213 | 格式化 Markdown 评论
214 | """
215 | comment_lines = comments.split("\n")
216 | formatted_comments = f"漂流瓶 {bottle_id} 的评论区\r"
217 | users_avatar = []
218 | comment_count = 0
219 |
220 | for line in comment_lines:
221 | if comment_count >= max_bottle_comments:
222 | break
223 | id_and_comment = line.split(": ")
224 | if len(id_and_comment) == 2:
225 | user_id, comment = id_and_comment[0], ": ".join(id_and_comment[1:])
226 | else:
227 | continue
228 | user_name = 玩家昵称接口(user_id)
229 | avatar_url = await fetch_avatar_url(user_id)
230 | users_avatar.append(avatar_url)
231 | formatted_comments += f"{user_name}: {comment}\r"
232 | comment_count += 1
233 |
234 | all_user = len(comment_lines)
235 | default_avatar = "https://tse1-mm.cn.bing.net/th/id/OIP-C.FQeBsP0v7u7cZQO1Z5do9gAAAA?w=34&h=34&c=7&r=0&o=5&dpr=2" \
236 | ".5&pid=1.7"
237 | data = {
238 | "markdown": {
239 | "custom_template_id": "102069827_1723800235",
240 | "params": [
241 | {"key": "pic1", "values": [users_avatar[0] if len(users_avatar) > 0 else default_avatar]},
242 | {"key": "pic2", "values": [users_avatar[1] if len(users_avatar) > 1 else default_avatar]},
243 | {"key": "pic3", "values": [users_avatar[2] if len(users_avatar) > 2 else default_avatar]},
244 | {"key": "tip", "values": [f"... 共{all_user}条评论~" if all_user else " 还没有内容哦~"]},
245 | {"key": "msg", "values": [formatted_comments]},
246 | {"key": "cmd1", "values": ['show="ⓘ" text="漂流瓶帮助"']},
247 | {"key": "cmd2", "values": ['show="丢瓶子" text="扔瓶子"']},
248 | {"key": "cmd3", "values": ['show="捡瓶子" text="捡瓶子"']},
249 | {"key": "cmd4", "values": [f'show="点赞" text="点赞漂流瓶 {bottle_id}"']},
250 | {"key": "cmd5", "values": [f'show="发评论" text="评论漂流瓶 {bottle_id}"']}
251 | ]
252 | }
253 | }
254 | json_str = json.dumps(data)
255 | encoded_data = base64.b64encode(json_str.encode('utf-8')).decode('utf-8')
256 | return f'[CQ:markdown,data=base64://{encoded_data}]'
257 |
258 |
259 | async def fetch_avatar_url(user_id: str) -> str:
260 | """
261 | 获取用户头像 URL
262 | """
263 | async with httpx.AsyncClient() as client:
264 | response = await client.get(f"http://{ServerAPI}/getid?id={user_id}&type=2")
265 | response.raise_for_status()
266 | data = response.json()
267 | return f"https://q.qlogo.cn/qqapp/102069827/{data.get('id', '')}/640"
268 |
269 |
270 | async def botte_routing(bot: Bot, bottle_data: dict, bottle: Bottle):
271 | """
272 | 根据配置选择发送 Markdown 或普通消息
273 | """
274 | if qq_markdown: # and not 停用MD(bottle_data["userid"]):
275 | return await get_botte_tomd(bot, bottle_data, bottle)
276 | return await get_botte_all(bot, bottle_data, bottle)
277 |
--------------------------------------------------------------------------------
/nonebot_plugin_web_bottle/web_bottle.py:
--------------------------------------------------------------------------------
1 | from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
2 | from cryptography.hazmat.backends import default_backend
3 | from cryptography.hazmat.primitives import padding
4 | import aiofiles
5 | import httpx
6 | import ssl
7 | from nonebot.adapters.onebot.v11 import Message, MessageSegment
8 | from nonebot import get_app, get_driver, require
9 | from nonebot.log import logger
10 | from .config import Config
11 | from . import data_deal
12 |
13 | from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse
14 | from fastapi import Depends, FastAPI, HTTPException, Request, Form
15 | from starlette.middleware.sessions import SessionMiddleware
16 | from starlette.middleware.gzip import GZipMiddleware
17 | from starlette.templating import _TemplateResponse
18 | from fastapi.templating import Jinja2Templates
19 | from fastapi.staticfiles import StaticFiles
20 | from starlette.status import HTTP_302_FOUND
21 | from fastapi.security import HTTPBasic
22 | from pydantic import BaseModel
23 |
24 | from datetime import datetime, timedelta
25 | from hmac import compare_digest
26 | from typing import Any, Literal
27 | from sqlite3 import Connection
28 | from http import HTTPStatus
29 | from pathlib import Path
30 | from io import BytesIO
31 | from PIL import Image,ImageSequence
32 | import secrets
33 | import asyncio
34 | import hashlib
35 | import base64
36 | import random
37 | import os
38 |
39 | require("nonebot_plugin_localstore")
40 |
41 | import nonebot_plugin_localstore as store # noqa: E402
42 |
43 | app = get_app()
44 |
45 | if not isinstance(app, FastAPI):
46 | msg = "本插件需要 FastAPI 驱动器才能正常运行"
47 | raise RuntimeError(msg) # noqa: TRY004
48 |
49 | driver = get_driver()
50 | config = driver.config
51 | config: Config = Config.parse_obj(config)
52 |
53 | gzip_level = config.gzip_level
54 | print(gzip_level)
55 | # 添加会话中间件
56 | app.add_middleware(SessionMiddleware, secret_key=secrets.token_hex(32))
57 | app.add_middleware(GZipMiddleware, minimum_size=100, compresslevel=int(gzip_level))
58 |
59 | # 定义账号和密码
60 | account = config.bottle_account
61 | password = config.bottle_password
62 | password_sha256 = hashlib.sha256(password.encode("utf-8")).hexdigest()
63 | account_sha256 = hashlib.sha256(account.encode("utf-8")).hexdigest()
64 |
65 | security = HTTPBasic()
66 |
67 | # 获取当前文件所在目录
68 | plugin_dir = Path(__file__).parent
69 |
70 | # 设置静态文件目录路径
71 | static_dir = plugin_dir / "templates" / "static"
72 | app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
73 |
74 | # 设置模板文件目录路径
75 | templates_dir = plugin_dir / "templates"
76 | templates = Jinja2Templates(directory=str(templates_dir))
77 |
78 |
79 | class BottleInfo(BaseModel):
80 | ID: int
81 | Content: str
82 | UserID: int
83 | GroupID: int
84 | TimeInfo: str
85 | State: int
86 | Images: list[str]
87 |
88 |
89 | class AESCryptoData(BaseModel):
90 | Data: str
91 | a: str
92 |
93 |
94 | # 登录依赖项
95 | def login_required(request: Request):
96 | if 'user' not in request.session and (
97 | datetime.now() >= datetime.fromtimestamp(request.session.get('expire_time', datetime.now().timestamp()))):
98 | raise HTTPException(
99 | status_code=HTTP_302_FOUND,
100 | detail="未登录或登录已过期,请访问/login进行登录",
101 | headers={"Location": "/login"}
102 | )
103 |
104 |
105 | login_static_dir = plugin_dir / "templates" / "login" / "static"
106 | app.mount("/login/static", StaticFiles(directory=str(login_static_dir)), name="login-static")
107 |
108 |
109 | @app.get("/login", response_class=HTMLResponse)
110 | async def login_page(request: Request):
111 | return templates.TemplateResponse("login/login.html", {"request": request})
112 |
113 |
114 | @app.post("/login")
115 | async def login(username: str = Form(...), password: str = Form(...), request: Request = None):
116 | if compare_digest(username, account_sha256) and compare_digest(password, password_sha256):
117 | request.session['user'] = username
118 | current_time = datetime.now()
119 | request.session['expire_time'] = (current_time + timedelta(hours=config.expire_time)).timestamp()
120 | return RedirectResponse(url="/check", status_code=HTTP_302_FOUND)
121 |
122 | # 登录失败时返回 JSON 响应,带有错误信息
123 | return JSONResponse(
124 | status_code=401,
125 | content={"detail": "用户名或密码错误"}
126 | )
127 |
128 |
129 | @app.get("/check", response_class=HTMLResponse)
130 | async def read_item(request: Request, user: str = Depends(login_required)) -> _TemplateResponse:
131 | bottle = Bottle(conn=data_deal.conn_bottle)
132 | pending_count = await bottle.get_pending_count()
133 | return templates.TemplateResponse("index.html", {"request": request, "pending_count": pending_count})
134 |
135 |
136 | @app.get("/bottles/random", response_model=AESCryptoData)
137 | async def get_random_bottle(request: Request, user: str = Depends(login_required)) -> AESCryptoData:
138 | bottle = Bottle(conn=data_deal.conn_bottle)
139 | b = await bottle.random_get_bottle()
140 | if not b:
141 | raise HTTPException(status_code=404, detail="漂流瓶不存在")
142 |
143 | images = await bottle.get_bottle_images(b["id"])
144 | images_base64 = [base64.b64encode(img).decode("utf-8") for img in images]
145 | data = BottleInfo(
146 | ID=b["id"],
147 | Content=b["content"],
148 | UserID=b["userid"],
149 | GroupID=b["groupid"],
150 | TimeInfo=b["timeinfo"],
151 | State=b["state"],
152 | Images=images_base64,
153 | ).json().encode("utf-8")
154 | iv = os.urandom(16)
155 | key = hashlib.sha256(base64.b64encode(iv)).digest()
156 | print(hashlib.sha256(base64.b64encode(iv)).hexdigest())
157 | padder = padding.PKCS7(algorithms.AES.block_size).padder()
158 | padded_data = padder.update(data) + padder.finalize()
159 |
160 | cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
161 | encryptor = cipher.encryptor()
162 |
163 | encrypted_data = encryptor.update(padded_data) + encryptor.finalize()
164 | return AESCryptoData(
165 | Data=base64.b64encode(encrypted_data),
166 | a=base64.b64encode(iv)
167 | )
168 |
169 |
170 | @app.post("/bottles/approve/{id}")
171 | async def approve_bottle(id: int, user: str = Depends(login_required)) -> dict[str, str]:
172 | b = Bottle(conn=data_deal.conn_bottle)
173 | await b.add_approved_bottle(id)
174 | return {"status": "approved"}
175 |
176 |
177 | @app.post("/bottles/refuse/{id}")
178 | async def refuse_bottle(id: int, user: str = Depends(login_required)) -> dict[str, str]:
179 | b = Bottle(conn=data_deal.conn_bottle)
180 | await b.refuse_bottle(id)
181 | return {"status": "refused"}
182 |
183 |
184 | @app.get("/comments", response_class=HTMLResponse)
185 | async def review_comments(request: Request, user: str = Depends(login_required)) -> _TemplateResponse:
186 | conn = data_deal.conn_bottle
187 | bottle = Bottle(conn)
188 | comment = await bottle.get_random_comment_with_state_zero()
189 | if not comment:
190 | return templates.TemplateResponse("comments.html", {"request": request, "comment": None})
191 | return templates.TemplateResponse("comments.html", {"request": request, "comment": comment})
192 |
193 |
194 | @app.get("/comments/random")
195 | async def get_random_comment(user: str = Depends(login_required)) -> dict[str, Any]:
196 | conn = data_deal.conn_bottle
197 | bottle = Bottle(conn)
198 | comment = await bottle.get_random_comment_with_state_zero()
199 | if not comment:
200 | raise HTTPException(status_code=404, detail="No comments found")
201 | return comment
202 |
203 |
204 | @app.post("/comments/approve/{comment_id}")
205 | async def approve_comment(comment_id: int, user: str = Depends(login_required)) -> dict[str, str]:
206 | conn = data_deal.conn_bottle
207 | bottle = Bottle(conn)
208 | success = await bottle.pass_comment_state(comment_id)
209 | if not success:
210 | raise HTTPException(status_code=404, detail="Comment not found")
211 | return {"status": "approved"}
212 |
213 |
214 | @app.post("/comments/refuse/{comment_id}")
215 | async def refuse_comment(comment_id: int, user: str = Depends(login_required)) -> dict[str, str]:
216 | bottle = Bottle(data_deal.conn_bottle)
217 | success = await bottle.refuse_comment_state(comment_id)
218 | if not success:
219 | raise HTTPException(status_code=404, detail="Comment not found")
220 | return {"status": "refused"}
221 |
222 |
223 | @driver.on_startup
224 | def _():
225 | logger.info("成功加载 web")
226 |
227 |
228 | class NotSupportMessageError(Exception):
229 | def __init__(self, *args: object):
230 | super().__init__(*args)
231 |
232 | def __str__(self) -> str:
233 | return super().__str__()
234 |
235 |
236 |
237 | async def store_image_file(image_id: int, image_data: bytes) -> None:
238 | """
239 | 存储图像数据到文件系统,使用有损压缩减小文件大小。
240 | 如果是动图,则保存为动态 WebP,否则保存静态 WebP。
241 | :param image_id: 图像对应的 ID
242 | :param image_data: 图像的二进制数据
243 | """
244 | image = Image.open(BytesIO(image_data))
245 |
246 | # 创建以 image_id 为名的子文件夹
247 | folder = image_path / str(image_id)
248 | folder.mkdir(parents=True, exist_ok=True)
249 |
250 | # 找到子文件夹中最大索引值,生成下一个文件名
251 | existing_files = list(folder.glob("*.webp"))
252 | max_index = -1
253 | for file in existing_files:
254 | try:
255 | index = int(file.stem)
256 | max_index = max(max_index, index)
257 | except ValueError:
258 | continue
259 | next_index = max_index + 1
260 |
261 | output_file = folder / f"{next_index}.webp"
262 |
263 | # 判断是否为动图
264 | is_animated = getattr(image, "is_animated", False) or getattr(image, "n_frames", 1) > 1
265 | if is_animated:
266 | # 收集所有帧
267 | frames = [frame.copy() for frame in ImageSequence.Iterator(image)]
268 | # 保存为动态 WebP
269 | frames[0].save(
270 | output_file,
271 | format='WEBP',
272 | save_all=True,
273 | append_images=frames[1:],
274 | duration=image.info.get('duration', 100),
275 | loop=0,
276 | quality=80
277 | )
278 | else:
279 | # 保存静态 WebP
280 | image.save(output_file, format='WEBP', quality=80)
281 |
282 | logger.info(f"图像 ID {image_id} 成功保存为 {output_file}")
283 |
284 | async def cache_file(msg: Message, image_id: int) -> None:
285 | """
286 | 缓存消息中的图片数据到文件系统,最多只缓存两张图片。
287 | """
288 | ssl_context = ssl.create_default_context()
289 | ssl_context.options |= ssl.OP_NO_SSLv3
290 | ssl_context.set_ciphers('HIGH:!DH:!aNULL')
291 |
292 | semaphore = asyncio.Semaphore(2)
293 | max_number = config.max_bottle_pic
294 | async with httpx.AsyncClient(
295 | verify=ssl_context,
296 | timeout=30,
297 | limits=httpx.Limits(max_connections=5)
298 | ) as client:
299 | tasks = [
300 | cache_image_url(seg, client, image_id, semaphore)
301 | for i, seg in enumerate(msg)
302 | if seg.type == "image" and i < max_number
303 | ]
304 | await asyncio.gather(*tasks)
305 |
306 | async def cache_image_url(
307 | seg: MessageSegment,
308 | client: httpx.AsyncClient,
309 | image_id: int,
310 | semaphore: asyncio.Semaphore,
311 | ) -> None:
312 | """
313 | 缓存单个图片 URL 到文件系统。
314 | """
315 | async with semaphore:
316 | url = seg.data.get("url")
317 | if not url:
318 | return
319 |
320 | seg.type = "cached_image"
321 | seg.data.clear()
322 | try:
323 | r = await client.get(
324 | url,
325 | follow_redirects=True,
326 | headers={"User-Agent": "Mozilla/5.0"}
327 | )
328 | data = r.content
329 | except (httpx.TimeoutException, httpx.ConnectError) as e:
330 | logger.warning(f"下载失败: {str(e)}")
331 | return
332 |
333 | if r.status_code != HTTPStatus.OK or not data:
334 | return
335 |
336 | await store_image_file(image_id, data)
337 | seg.data = {"file": f"image_{image_id}"}
338 |
339 | class Bottle:
340 | def __init__(self, conn: Connection):
341 | self.conn = conn
342 |
343 | async def get_random_comment_with_state_zero(self) -> None | dict[str, Any]:
344 | """
345 | 随机获取一个 state 为 0 的评论并返回它的详情
346 | """
347 | conn = self.conn
348 | cursor = conn.cursor()
349 |
350 | # 获取所有 state 为 0 的评论
351 | cursor.execute("SELECT * FROM comments WHERE state = 0")
352 | comments = cursor.fetchall()
353 |
354 | if not comments:
355 | return None
356 |
357 | # 随机选择一个评论
358 | random_comment = random.choice(comments) # noqa: S311
359 |
360 | # 返回随机评论的详情
361 | return {
362 | "comment_id": random_comment[0],
363 | "bottle_id": random_comment[1],
364 | "content": random_comment[2],
365 | "state": random_comment[3],
366 | "uid": random_comment[4],
367 | }
368 |
369 | async def get_comments(self, id: int) -> str:
370 | """
371 | 获取 comments 表中所有 id 对应的记录,且 state 为 200,
372 | 并将结果以 'uid: content' 的格式返回,每条记录之间用换行符分隔。
373 | """
374 | # 创建一个异步 Cursor 对象
375 | cursor = self.conn.cursor()
376 | # SQL 查询语句
377 | query = "SELECT uid, content FROM comments WHERE id = ? AND state = 200"
378 |
379 | # 执行查询
380 | cursor.execute(query, (id,))
381 |
382 | # 获取所有查询结果
383 | rows = cursor.fetchall()
384 |
385 | # 如果找到了记录,则按指定格式组合结果
386 | if rows:
387 | return "\n".join([f"{row[0]}: {row[1]}" for row in rows])
388 | return ""
389 |
390 | async def pass_comment_state(self, comment_id: int) -> bool:
391 | """
392 | 更新评论状态 200
393 | """
394 | conn = self.conn
395 | cursor = conn.cursor()
396 |
397 | # 检查 comments 表中是否存在给定的 comment_id
398 | cursor.execute("SELECT COUNT(*) FROM comments WHERE comment_id = ?", (comment_id,))
399 | count = cursor.fetchone()[0]
400 | if count == 0:
401 | return False
402 |
403 | # 更新 comments 表中对应记录的 state
404 | cursor.execute("UPDATE comments SET state = ? WHERE comment_id = ?", (200, comment_id))
405 |
406 | # 提交事务
407 | conn.commit()
408 | return True
409 |
410 | async def refuse_comment_state(self, comment_id: int) -> bool:
411 | """
412 | 更新评论状态 100
413 | """
414 | conn = self.conn
415 | cursor = conn.cursor()
416 |
417 | # 检查 comments 表中是否存在给定的 comment_id
418 | cursor.execute("SELECT COUNT(*) FROM comments WHERE comment_id = ?", (comment_id,))
419 | count = cursor.fetchone()[0]
420 | if count == 0:
421 | return False
422 |
423 | # 更新 comments 表中对应记录的 state
424 | cursor.execute("UPDATE comments SET state = ? WHERE comment_id = ?", (100, comment_id))
425 |
426 | # 提交事务
427 | conn.commit()
428 | return True
429 |
430 | async def find_all_pass_comments(self, bottle_id: int) -> list[dict[str, Any]]:
431 | """
432 | 查找所有状态为 200 的评论(即已通过审核的评论)
433 | """
434 | conn = self.conn
435 | cursor = conn.cursor()
436 |
437 | # 查找所有状态为 200 的评论
438 | cursor.execute("SELECT * FROM comments WHERE id = ? AND state = 200", (bottle_id,))
439 | comments = cursor.fetchall()
440 |
441 | # 如果没有找到任何评论,返回空列表
442 | if not comments:
443 | return []
444 |
445 | # 返回评论的详情列表
446 | return [
447 | {
448 | "comment_id": comment[0],
449 | "bottle_id": comment[1],
450 | "content": comment[2],
451 | "state": comment[3],
452 | "uid": comment[4],
453 | }
454 | for comment in comments
455 | ]
456 |
457 | async def add_comment_if_approved(self, bottle_id: int, text: str, uid: str) -> bool:
458 | """
459 | 评论瓶子
460 | """
461 | conn = self.conn
462 | cursor = conn.cursor()
463 |
464 | # 检查 approved 表中是否存在 bottle_id
465 | cursor.execute("SELECT COUNT(*) FROM approved WHERE id = ?", (bottle_id,))
466 | count = cursor.fetchone()[0]
467 | if count == 0:
468 | return False
469 |
470 | # 获取当前最大的 comment_id
471 | cursor.execute("SELECT MAX(comment_id) FROM comments")
472 | max_comment_id = cursor.fetchone()[0]
473 | if max_comment_id is None:
474 | max_comment_id = 0
475 | new_comment_id = max_comment_id + 1
476 |
477 | # 向 comments 表中添加评论
478 | cursor.execute(
479 | "INSERT INTO comments (comment_id, id, content, state, uid) VALUES (?, ?, ?, ?, ?)",
480 | (new_comment_id, bottle_id, text, 0, uid),
481 | )
482 |
483 | # 提交事务
484 | conn.commit()
485 | return True
486 |
487 | async def get_approved_bottle_by_id(self, bottle_id: int) -> dict[str, Any] | None:
488 | """
489 | 根据给定的ID获取一个已过审的瓶子
490 | """
491 |
492 | # 查询特定行
493 | select_sql = """
494 | SELECT id, content, userid, groupid, timeinfo
495 | FROM approved
496 | WHERE id = ?
497 | """
498 | result = self.conn.execute(select_sql, (bottle_id,))
499 | row = result.fetchone()
500 |
501 | logger.debug(f"Row fetched: {row}") # 调试信息
502 |
503 | if row:
504 | id, content, userid, groupid, timeinfo = row
505 | return {
506 | "id": id,
507 | "content": content,
508 | "userid": userid,
509 | "groupid": groupid,
510 | "timeinfo": timeinfo,
511 | }
512 | return None
513 |
514 | async def random_get_approves_bottle(self) -> None | dict[str, Any]:
515 | """
516 | 随机获取一个已过审的瓶子
517 | """
518 |
519 | # 获取表中的行数
520 | row_count_sql = "SELECT COUNT(*) FROM approved"
521 | row_count = self.conn.execute(row_count_sql)
522 | row_count = (row_count.fetchone())[0]
523 | logger.debug(f"Total rows in approved table: {row_count}") # 调试信息
524 |
525 | if row_count == 0:
526 | return None
527 |
528 | # 生成随机索引
529 | random_index = random.randint(0, row_count - 1) # noqa: S311
530 | logger.debug(f"Random index generated: {random_index}") # 调试信息
531 |
532 | # 查询特定行
533 | select_sql = """
534 | SELECT id, content, userid, groupid, timeinfo
535 | FROM approved
536 | LIMIT 1 OFFSET ?
537 | """
538 | result = self.conn.execute(select_sql, (random_index,))
539 | row = result.fetchone()
540 |
541 | logger.debug(f"Row fetched: {row}") # 调试信息
542 |
543 | if row:
544 | id, content, userid, groupid, timeinfo = row
545 | return {
546 | "id": id,
547 | "content": content,
548 | "userid": userid,
549 | "groupid": groupid,
550 | "timeinfo": timeinfo,
551 | }
552 | return None
553 |
554 | async def fetch_bottles(self, id: int) -> Any: # noqa: ANN401
555 | """
556 | 异步查询某个漂流瓶。
557 | """
558 | # 定义从 pending 表中获取数据的 SQL 语句
559 | select_query = """
560 | SELECT id, content, userid, groupid, timeinfo
561 | FROM pending
562 | WHERE id = ?;
563 | """
564 | # 执行查询
565 | cursor = self.conn.cursor()
566 | cursor.execute(select_query, (int(id),))
567 | return cursor.fetchone()
568 |
569 | async def add_pending_bottle(
570 | self, id: int, message: str, userid: str, groupid: str, timeinfo: str
571 | ) -> Literal[True]:
572 | """
573 | 异步增加待审核的漂流瓶。
574 | """
575 |
576 | data = {
577 | "id": int(id),
578 | "user_id": str(userid),
579 | "group_id": str(groupid),
580 | "content": message,
581 | "time": str(timeinfo),
582 | "state": 0,
583 | }
584 | sql = """
585 | INSERT INTO pending (id, content, userid, groupid, timeinfo, state)
586 | VALUES (?, ?, ?, ?, ?, ?)
587 | """
588 | self.conn.execute(
589 | sql,
590 | (
591 | data["id"],
592 | data["content"],
593 | data["user_id"],
594 | data["group_id"],
595 | data["time"],
596 | data["state"],
597 | ),
598 | )
599 | self.conn.commit()
600 | return True
601 |
602 | async def refuse_bottle(self, id: int) -> Literal[True]:
603 | """
604 | 异步拒绝待审核的漂流瓶,并删除相关图片文件夹及其所有文件。
605 | :param id: 待拒绝的漂流瓶 ID
606 | :return: 总是返回 True
607 | """
608 | # 连接到数据库
609 | conn = self.conn
610 | cursor = conn.cursor()
611 |
612 | # 定义更新 pending 表状态的 SQL 语句
613 | update_pending_query = """
614 | UPDATE pending
615 | SET state = ?
616 | WHERE id = ?;
617 | """
618 |
619 | # 更新 pending 表的状态
620 | new_state = 100
621 | cursor.execute(update_pending_query, (new_state, id))
622 |
623 | # 提交更改到数据库
624 | conn.commit()
625 |
626 | # 拼接目标路径
627 | target_folder = image_path / str(id)
628 |
629 | # 检查文件夹是否存在
630 | if target_folder.exists() and target_folder.is_dir():
631 | try:
632 | # 删除文件夹及其所有内容
633 | for file in target_folder.glob("*"):
634 | file.unlink() # 删除文件
635 | target_folder.rmdir() # 删除空文件夹
636 | logger.info(f"成功删除 ID 为 {id} 的图片文件夹及其内容!")
637 | except Exception as e:
638 | logger.error(f"删除文件夹 {target_folder} 时发生错误:{e}")
639 | else:
640 | logger.warning(f"ID 为 {id} 的图片文件夹不存在或不是文件夹!")
641 |
642 | return True
643 |
644 | async def add_approved_bottle(self, id: int) -> None:
645 | """
646 | 异步增加已通过审核的漂流瓶。
647 | """
648 |
649 | # 连接到数据库
650 | conn = self.conn
651 | cursor = conn.cursor()
652 |
653 | # 定义从 pending 表中获取数据的 SQL 语句
654 | select_query = """
655 | SELECT id, content, userid, groupid, timeinfo
656 | FROM pending
657 | WHERE id = ?;
658 | """
659 |
660 | # 定义将数据插入 approved 表的 SQL 语句
661 | insert_query = """
662 | INSERT INTO approved (id, content, userid, groupid, timeinfo,up)
663 | VALUES (?, ?, ?, ?, ?, 0);
664 | """
665 |
666 | # 定义更新 pending 表状态的 SQL 语句
667 | update_query = """
668 | UPDATE pending
669 | SET state = ?
670 | WHERE id = ?;
671 | """
672 |
673 | try:
674 | # 执行查询
675 | cursor.execute(select_query, (id,))
676 | result = cursor.fetchone()
677 |
678 | if result:
679 | # 如果找到了记录,则插入到 approved 表
680 | cursor.execute(insert_query, result)
681 |
682 | # 更新 pending 表的状态
683 | new_state = 200
684 | cursor.execute(update_query, (new_state, id))
685 |
686 | # 提交事务
687 | conn.commit()
688 | logger.debug("Data inserted and state updated successfully.")
689 | else:
690 | logger.debug("No data found for the given ID.")
691 |
692 | except Exception as e: # noqa: BLE001
693 | logger.error("An error occurred: ")
694 | logger.exception(e)
695 | conn.rollback()
696 |
697 | async def up_bottle(self, bottle_id: int, uid: str) -> tuple[Literal[False], None] | tuple[Literal[True], Any]:
698 | """
699 | 点赞某个瓶子
700 | """
701 | conn = self.conn
702 | cursor = conn.cursor()
703 |
704 | # 初始化 ids_list 为空列表
705 | ids_list = []
706 |
707 | # 检查 user_up 表中 uid 对应的列表是否存在 bottle_id
708 | cursor.execute("SELECT ids FROM user_up WHERE uid = ?", (uid,))
709 | row = cursor.fetchone()
710 | if row:
711 | ids_list = row[0].split(",") if row[0] else []
712 |
713 | if str(bottle_id) in ids_list:
714 | return False, None
715 |
716 | # 确保 bottle_id 存在于 approved 表中
717 | cursor.execute("SELECT COUNT(*) FROM approved WHERE id = ?", (bottle_id,))
718 | if cursor.fetchone()[0] == 0:
719 | return False, None # Bottle ID does not exist
720 |
721 | # 确保 up 列有默认值
722 | cursor.execute("SELECT up FROM approved WHERE id = ?", (bottle_id,))
723 | current_up_value = cursor.fetchone()[0]
724 |
725 | if current_up_value is None:
726 | current_up_value = 0
727 |
728 | # 修改 approved 表中 bottle_id 对应的 up 值 +1
729 | cursor.execute("UPDATE approved SET up = ? WHERE id = ?", (current_up_value + 1, bottle_id))
730 |
731 | # 提交事务
732 | conn.commit()
733 |
734 | # 获取更新后的 up 值
735 | cursor.execute("SELECT up FROM approved WHERE id = ?", (bottle_id,))
736 | new_up_value = cursor.fetchone()[0]
737 |
738 | if new_up_value is None:
739 | return False, None # Failed to get updated value
740 |
741 | # 更新 user_up 表中 uid 对应的列表,添加 bottle_id
742 | ids_list.append(str(bottle_id))
743 | updated_ids = ",".join(ids_list)
744 | cursor.execute(
745 | "INSERT OR REPLACE INTO user_up (uid, ids) VALUES (?, ?)",
746 | (uid, updated_ids),
747 | )
748 |
749 | # 提交事务
750 | conn.commit()
751 |
752 | return True, new_up_value
753 |
754 | async def random_get_bottle(self) -> None | dict[str, Any]:
755 | """
756 | 随机获取一个待审的瓶子(state 等于 0)
757 | """
758 |
759 | # 获取表中 state 等于 0 的行数
760 | row_count_sql = "SELECT COUNT(*) FROM pending WHERE state = 0"
761 | row_count = self.conn.execute(row_count_sql)
762 | row_count = (row_count.fetchone())[0]
763 | logger.debug(f"Total rows in pending table with state 0: {row_count}") # 调试信息
764 | if row_count == 0:
765 | return None
766 |
767 | # 生成随机索引
768 | random_index = random.randint(0, row_count - 1) # noqa: S311
769 | logger.debug(f"Random index generated: {random_index}") # 调试信息
770 |
771 | # 查询特定行
772 | select_sql = """
773 | SELECT id, content, userid, groupid, timeinfo, state
774 | FROM pending
775 | WHERE state = 0
776 | LIMIT 1 OFFSET ?
777 | """
778 | result = self.conn.execute(select_sql, (random_index,))
779 | row = result.fetchone()
780 |
781 | logger.debug(f"Row fetched: {row}") # 调试信息
782 |
783 | if row:
784 | id, content, userid, groupid, timeinfo, state = row
785 | return {
786 | "id": id,
787 | "content": content,
788 | "userid": userid,
789 | "groupid": groupid,
790 | "timeinfo": timeinfo,
791 | "state": state,
792 | }
793 | return None
794 |
795 | async def get_pending_count(self) -> int:
796 | """
797 | 获取剩余待审核的瓶子数量
798 | """
799 |
800 | # 获取表中 state 等于 0 的行数
801 | count_sql = "SELECT COUNT(*) FROM pending WHERE state = 0"
802 | count_result = self.conn.execute(count_sql)
803 | count = (count_result.fetchone())[0]
804 | logger.debug(f"Total pending bottles with state 0: {count}") # 调试信息
805 |
806 | return count
807 |
808 | async def get_bottle_images(self, id: int) -> list[bytes]:
809 | """
810 | 获取指定 ID 的图片数据。
811 | :param id: 图片的 ID
812 | :return: 图片的二进制数据列表
813 | """
814 | # 拼接目标路径
815 | image_folder = image_path / str(id)
816 |
817 | # 检查文件夹是否存在
818 | if not image_folder.exists() or not image_folder.is_dir():
819 | logger.warning(f"ID 为 {id} 的图片文件夹不存在!")
820 | return []
821 |
822 | # 获取文件夹中所有 .webp 文件
823 | image_files = sorted(image_folder.glob("*.webp"))
824 |
825 | # 读取文件内容
826 | images = []
827 | for image_file in image_files:
828 | try:
829 | with open(image_file, "rb") as f:
830 | images.append(f.read())
831 | except Exception as e:
832 | logger.error(f"读取文件 {image_file} 时发生错误:{e}")
833 |
834 | return images
835 |
836 |
837 | plugin_data = store.get_data_dir("nonebot_plugin_web_bottle")
838 | file_path = plugin_data / "bottle_id.txt"
839 | image_path = plugin_data / 'img'
840 | image_path.mkdir(parents=True, exist_ok=True)
841 |
842 | async def id_add() -> int:
843 | # Ensure the directory exists
844 | plugin_data.mkdir(exist_ok=True)
845 |
846 | # Check if the file exists, if not, create it with an initial value of 0
847 | if not file_path.exists():
848 | async with aiofiles.open(file_path, "w+", encoding="utf_8") as f:
849 | await f.write("0")
850 | await f.close()
851 |
852 | # Read the current ID, increment, and write back the new value
853 | async with aiofiles.open(file_path, "r+", encoding="utf_8") as f:
854 | k = int(await f.read()) + 1
855 | await f.close()
856 |
857 | async with aiofiles.open(file_path, "w+", encoding="utf_8") as b:
858 | await b.write(str(k))
859 | await b.close()
860 |
861 | return k
862 |
863 |
864 | async def extract_and_join_text_from_message(message_list: list) -> str:
865 | """
866 | 从消息列表中提取所有类型为 'text' 的消息段中的文本内容,并将它们合并成一个字符串。
867 |
868 | 参数:
869 | message_list (list): 包含 MessageSegment 的列表。
870 |
871 | 返回:
872 | str: 合并后的文本字符串。
873 | """
874 | return "".join(segment["data"]["text"] for segment in message_list if segment["type"] == "text")
875 |
876 |
877 | async def serialize_message(message: Message, id: int) -> list[dict[str, Any]]:
878 | for seg in message:
879 | if seg.type not in ("text", "image"):
880 | msg = "漂流瓶只支持文字和图片~"
881 | raise NotSupportMessageError(msg)
882 |
883 | await cache_file(msg=message, image_id=id)
884 | return [seg.__dict__ for seg in message]
885 |
--------------------------------------------------------------------------------
/pdm.lock:
--------------------------------------------------------------------------------
1 | # This file is @generated by PDM.
2 | # It is not intended for manual editing.
3 |
4 | [metadata]
5 | groups = ["default", "dev"]
6 | strategy = ["inherit_metadata"]
7 | lock_version = "4.5.0"
8 | content_hash = "sha256:a7c69f449dfb1cc2ddb554f373bf336a9071af66b1e02f507582dd77aaa9eda2"
9 |
10 | [[metadata.targets]]
11 | requires_python = ">=3.10"
12 |
13 | [[package]]
14 | name = "aiofiles"
15 | version = "24.1.0"
16 | requires_python = ">=3.8"
17 | summary = "File support for asyncio."
18 | groups = ["default"]
19 | files = [
20 | {file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"},
21 | {file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"},
22 | ]
23 |
24 | [[package]]
25 | name = "annotated-types"
26 | version = "0.7.0"
27 | requires_python = ">=3.8"
28 | summary = "Reusable constraint types to use with typing.Annotated"
29 | groups = ["default"]
30 | dependencies = [
31 | "typing-extensions>=4.0.0; python_version < \"3.9\"",
32 | ]
33 | files = [
34 | {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
35 | {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
36 | ]
37 |
38 | [[package]]
39 | name = "anyio"
40 | version = "4.4.0"
41 | requires_python = ">=3.8"
42 | summary = "High level compatibility layer for multiple asynchronous event loop implementations"
43 | groups = ["default"]
44 | dependencies = [
45 | "exceptiongroup>=1.0.2; python_version < \"3.11\"",
46 | "idna>=2.8",
47 | "sniffio>=1.1",
48 | "typing-extensions>=4.1; python_version < \"3.11\"",
49 | ]
50 | files = [
51 | {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"},
52 | {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"},
53 | ]
54 |
55 | [[package]]
56 | name = "certifi"
57 | version = "2024.7.4"
58 | requires_python = ">=3.6"
59 | summary = "Python package for providing Mozilla's CA Bundle."
60 | groups = ["default"]
61 | files = [
62 | {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"},
63 | {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"},
64 | ]
65 |
66 | [[package]]
67 | name = "click"
68 | version = "8.1.7"
69 | requires_python = ">=3.7"
70 | summary = "Composable command line interface toolkit"
71 | groups = ["default"]
72 | dependencies = [
73 | "colorama; platform_system == \"Windows\"",
74 | "importlib-metadata; python_version < \"3.8\"",
75 | ]
76 | files = [
77 | {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
78 | {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
79 | ]
80 |
81 | [[package]]
82 | name = "colorama"
83 | version = "0.4.6"
84 | requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
85 | summary = "Cross-platform colored terminal text."
86 | groups = ["default"]
87 | marker = "sys_platform == \"win32\" or platform_system == \"Windows\""
88 | files = [
89 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
90 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
91 | ]
92 |
93 | [[package]]
94 | name = "exceptiongroup"
95 | version = "1.2.2"
96 | requires_python = ">=3.7"
97 | summary = "Backport of PEP 654 (exception groups)"
98 | groups = ["default"]
99 | marker = "python_version < \"3.11\""
100 | files = [
101 | {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
102 | {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
103 | ]
104 |
105 | [[package]]
106 | name = "fastapi"
107 | version = "0.112.0"
108 | requires_python = ">=3.8"
109 | summary = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
110 | groups = ["default"]
111 | dependencies = [
112 | "pydantic!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0,>=1.7.4",
113 | "starlette<0.38.0,>=0.37.2",
114 | "typing-extensions>=4.8.0",
115 | ]
116 | files = [
117 | {file = "fastapi-0.112.0-py3-none-any.whl", hash = "sha256:3487ded9778006a45834b8c816ec4a48d522e2631ca9e75ec5a774f1b052f821"},
118 | {file = "fastapi-0.112.0.tar.gz", hash = "sha256:d262bc56b7d101d1f4e8fc0ad2ac75bb9935fec504d2b7117686cec50710cf05"},
119 | ]
120 |
121 | [[package]]
122 | name = "h11"
123 | version = "0.14.0"
124 | requires_python = ">=3.7"
125 | summary = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
126 | groups = ["default"]
127 | dependencies = [
128 | "typing-extensions; python_version < \"3.8\"",
129 | ]
130 | files = [
131 | {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
132 | {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
133 | ]
134 |
135 | [[package]]
136 | name = "httpcore"
137 | version = "1.0.5"
138 | requires_python = ">=3.8"
139 | summary = "A minimal low-level HTTP client."
140 | groups = ["default"]
141 | dependencies = [
142 | "certifi",
143 | "h11<0.15,>=0.13",
144 | ]
145 | files = [
146 | {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"},
147 | {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"},
148 | ]
149 |
150 | [[package]]
151 | name = "httptools"
152 | version = "0.6.1"
153 | requires_python = ">=3.8.0"
154 | summary = "A collection of framework independent HTTP protocol utils."
155 | groups = ["default"]
156 | files = [
157 | {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"},
158 | {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"},
159 | {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"},
160 | {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"},
161 | {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"},
162 | {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"},
163 | {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"},
164 | {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"},
165 | {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"},
166 | {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"},
167 | {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"},
168 | {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"},
169 | {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"},
170 | {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"},
171 | {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"},
172 | {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"},
173 | {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"},
174 | {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"},
175 | {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"},
176 | {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"},
177 | {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"},
178 | {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"},
179 | ]
180 |
181 | [[package]]
182 | name = "httpx"
183 | version = "0.27.0"
184 | requires_python = ">=3.8"
185 | summary = "The next generation HTTP client."
186 | groups = ["default"]
187 | dependencies = [
188 | "anyio",
189 | "certifi",
190 | "httpcore==1.*",
191 | "idna",
192 | "sniffio",
193 | ]
194 | files = [
195 | {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"},
196 | {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"},
197 | ]
198 |
199 | [[package]]
200 | name = "idna"
201 | version = "3.7"
202 | requires_python = ">=3.5"
203 | summary = "Internationalized Domain Names in Applications (IDNA)"
204 | groups = ["default"]
205 | files = [
206 | {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
207 | {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
208 | ]
209 |
210 | [[package]]
211 | name = "jinja2"
212 | version = "3.1.4"
213 | requires_python = ">=3.7"
214 | summary = "A very fast and expressive template engine."
215 | groups = ["default"]
216 | dependencies = [
217 | "MarkupSafe>=2.0",
218 | ]
219 | files = [
220 | {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"},
221 | {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"},
222 | ]
223 |
224 | [[package]]
225 | name = "loguru"
226 | version = "0.7.2"
227 | requires_python = ">=3.5"
228 | summary = "Python logging made (stupidly) simple"
229 | groups = ["default"]
230 | dependencies = [
231 | "aiocontextvars>=0.2.0; python_version < \"3.7\"",
232 | "colorama>=0.3.4; sys_platform == \"win32\"",
233 | "win32-setctime>=1.0.0; sys_platform == \"win32\"",
234 | ]
235 | files = [
236 | {file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"},
237 | {file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"},
238 | ]
239 |
240 | [[package]]
241 | name = "markupsafe"
242 | version = "2.1.5"
243 | requires_python = ">=3.7"
244 | summary = "Safely add untrusted strings to HTML/XML markup."
245 | groups = ["default"]
246 | files = [
247 | {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"},
248 | {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"},
249 | {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"},
250 | {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"},
251 | {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"},
252 | {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"},
253 | {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"},
254 | {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"},
255 | {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"},
256 | {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"},
257 | {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"},
258 | {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"},
259 | {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"},
260 | {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"},
261 | {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"},
262 | {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"},
263 | {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"},
264 | {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"},
265 | {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"},
266 | {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"},
267 | {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"},
268 | {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"},
269 | {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"},
270 | {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"},
271 | {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"},
272 | {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"},
273 | {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"},
274 | {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"},
275 | {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"},
276 | {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"},
277 | {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
278 | ]
279 |
280 | [[package]]
281 | name = "msgpack"
282 | version = "1.0.8"
283 | requires_python = ">=3.8"
284 | summary = "MessagePack serializer"
285 | groups = ["default"]
286 | files = [
287 | {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868"},
288 | {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c"},
289 | {file = "msgpack-1.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659"},
290 | {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2"},
291 | {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982"},
292 | {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa"},
293 | {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128"},
294 | {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d"},
295 | {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653"},
296 | {file = "msgpack-1.0.8-cp310-cp310-win32.whl", hash = "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693"},
297 | {file = "msgpack-1.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a"},
298 | {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836"},
299 | {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad"},
300 | {file = "msgpack-1.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b"},
301 | {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba"},
302 | {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85"},
303 | {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950"},
304 | {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a"},
305 | {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b"},
306 | {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce"},
307 | {file = "msgpack-1.0.8-cp311-cp311-win32.whl", hash = "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305"},
308 | {file = "msgpack-1.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e"},
309 | {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee"},
310 | {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b"},
311 | {file = "msgpack-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8"},
312 | {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3"},
313 | {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc"},
314 | {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58"},
315 | {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f"},
316 | {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04"},
317 | {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543"},
318 | {file = "msgpack-1.0.8-cp312-cp312-win32.whl", hash = "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c"},
319 | {file = "msgpack-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd"},
320 | {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"},
321 | ]
322 |
323 | [[package]]
324 | name = "multidict"
325 | version = "6.0.5"
326 | requires_python = ">=3.7"
327 | summary = "multidict implementation"
328 | groups = ["default"]
329 | files = [
330 | {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"},
331 | {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"},
332 | {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"},
333 | {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"},
334 | {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"},
335 | {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"},
336 | {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"},
337 | {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"},
338 | {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"},
339 | {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"},
340 | {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"},
341 | {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"},
342 | {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"},
343 | {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"},
344 | {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"},
345 | {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"},
346 | {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"},
347 | {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"},
348 | {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"},
349 | {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"},
350 | {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"},
351 | {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"},
352 | {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"},
353 | {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"},
354 | {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"},
355 | {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"},
356 | {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"},
357 | {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"},
358 | {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"},
359 | {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"},
360 | {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"},
361 | {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"},
362 | {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"},
363 | {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"},
364 | {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"},
365 | {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"},
366 | {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"},
367 | {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"},
368 | {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"},
369 | {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"},
370 | {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"},
371 | {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"},
372 | {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"},
373 | {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"},
374 | {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"},
375 | {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"},
376 | {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"},
377 | ]
378 |
379 | [[package]]
380 | name = "nonebot-adapter-onebot"
381 | version = "2.4.4"
382 | requires_python = "<4.0,>=3.9"
383 | summary = "OneBot(CQHTTP) adapter for nonebot2"
384 | groups = ["default"]
385 | dependencies = [
386 | "msgpack<2.0.0,>=1.0.3",
387 | "nonebot2<3.0.0,>=2.2.0",
388 | "pydantic!=2.5.0,!=2.5.1,<3.0.0,>=1.10.0",
389 | "typing-extensions<5.0.0,>=4.0.0",
390 | ]
391 | files = [
392 | {file = "nonebot_adapter_onebot-2.4.4-py3-none-any.whl", hash = "sha256:4dceeec7332bb560652c764405e9dd350268303f69b7c0e92b7cfebe876e8d39"},
393 | {file = "nonebot_adapter_onebot-2.4.4.tar.gz", hash = "sha256:c8a3645f74a3e43c85f092fb670508c662c36831f019a15e4d74eaac686089f0"},
394 | ]
395 |
396 | [[package]]
397 | name = "nonebot-plugin-localstore"
398 | version = "0.7.1"
399 | requires_python = "<4.0,>=3.9"
400 | summary = "Local Storage Support for NoneBot2"
401 | groups = ["default"]
402 | dependencies = [
403 | "nonebot2<3.0.0,>=2.2.0",
404 | "pydantic!=2.5.0,!=2.5.1,<3.0.0,>=1.10.0",
405 | "typing-extensions<5.0.0,>=4.0.0",
406 | ]
407 | files = [
408 | {file = "nonebot_plugin_localstore-0.7.1-py3-none-any.whl", hash = "sha256:7908af162d1d0e8c736ae8863723325d057289f6b080ae44ff9ff39294f9ed16"},
409 | {file = "nonebot_plugin_localstore-0.7.1.tar.gz", hash = "sha256:9c2a7b39b50240896d9306adb79d8047308d0e77b88e3272b19be4908bdaabc9"},
410 | ]
411 |
412 | [[package]]
413 | name = "nonebot2"
414 | version = "2.3.2"
415 | requires_python = "<4.0,>=3.9"
416 | summary = "An asynchronous python bot framework."
417 | groups = ["default"]
418 | dependencies = [
419 | "loguru<1.0.0,>=0.6.0",
420 | "pydantic!=2.5.0,!=2.5.1,<3.0.0,>=1.10.0",
421 | "pygtrie<3.0.0,>=2.4.1",
422 | "python-dotenv<2.0.0,>=0.21.0",
423 | "tomli<3.0.0,>=2.0.1; python_version < \"3.11\"",
424 | "typing-extensions<5.0.0,>=4.4.0",
425 | "yarl<2.0.0,>=1.7.2",
426 | ]
427 | files = [
428 | {file = "nonebot2-2.3.2-py3-none-any.whl", hash = "sha256:c51aa3c1f23d8062ce6d13c8423dcb9a8bf0c44f21687916095f825da79a9a55"},
429 | {file = "nonebot2-2.3.2.tar.gz", hash = "sha256:af52e27e03e7fe147f2b642151eec81f264d058efe53b974eb08b5d90177cd14"},
430 | ]
431 |
432 | [[package]]
433 | name = "nonebot2"
434 | version = "2.3.2"
435 | extras = ["fastapi"]
436 | requires_python = "<4.0,>=3.9"
437 | summary = "An asynchronous python bot framework."
438 | groups = ["default"]
439 | dependencies = [
440 | "fastapi<1.0.0,>=0.93.0",
441 | "nonebot2==2.3.2",
442 | "uvicorn[standard]<1.0.0,>=0.20.0",
443 | ]
444 | files = [
445 | {file = "nonebot2-2.3.2-py3-none-any.whl", hash = "sha256:c51aa3c1f23d8062ce6d13c8423dcb9a8bf0c44f21687916095f825da79a9a55"},
446 | {file = "nonebot2-2.3.2.tar.gz", hash = "sha256:af52e27e03e7fe147f2b642151eec81f264d058efe53b974eb08b5d90177cd14"},
447 | ]
448 |
449 | [[package]]
450 | name = "pydantic"
451 | version = "2.8.2"
452 | requires_python = ">=3.8"
453 | summary = "Data validation using Python type hints"
454 | groups = ["default"]
455 | dependencies = [
456 | "annotated-types>=0.4.0",
457 | "pydantic-core==2.20.1",
458 | "typing-extensions>=4.12.2; python_version >= \"3.13\"",
459 | "typing-extensions>=4.6.1; python_version < \"3.13\"",
460 | ]
461 | files = [
462 | {file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"},
463 | {file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"},
464 | ]
465 |
466 | [[package]]
467 | name = "pydantic-core"
468 | version = "2.20.1"
469 | requires_python = ">=3.8"
470 | summary = "Core functionality for Pydantic validation and serialization"
471 | groups = ["default"]
472 | dependencies = [
473 | "typing-extensions!=4.7.0,>=4.6.0",
474 | ]
475 | files = [
476 | {file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"},
477 | {file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"},
478 | {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a"},
479 | {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3"},
480 | {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1"},
481 | {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953"},
482 | {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98"},
483 | {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a"},
484 | {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a"},
485 | {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840"},
486 | {file = "pydantic_core-2.20.1-cp310-none-win32.whl", hash = "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250"},
487 | {file = "pydantic_core-2.20.1-cp310-none-win_amd64.whl", hash = "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c"},
488 | {file = "pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312"},
489 | {file = "pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88"},
490 | {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc"},
491 | {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43"},
492 | {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6"},
493 | {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121"},
494 | {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1"},
495 | {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b"},
496 | {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27"},
497 | {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b"},
498 | {file = "pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a"},
499 | {file = "pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2"},
500 | {file = "pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231"},
501 | {file = "pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9"},
502 | {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f"},
503 | {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52"},
504 | {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237"},
505 | {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe"},
506 | {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e"},
507 | {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24"},
508 | {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1"},
509 | {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd"},
510 | {file = "pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688"},
511 | {file = "pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d"},
512 | {file = "pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686"},
513 | {file = "pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a"},
514 | {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b"},
515 | {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19"},
516 | {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac"},
517 | {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703"},
518 | {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c"},
519 | {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83"},
520 | {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203"},
521 | {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0"},
522 | {file = "pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e"},
523 | {file = "pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20"},
524 | {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906"},
525 | {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94"},
526 | {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f"},
527 | {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482"},
528 | {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6"},
529 | {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc"},
530 | {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99"},
531 | {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6"},
532 | {file = "pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4"},
533 | ]
534 |
535 | [[package]]
536 | name = "pygtrie"
537 | version = "2.5.0"
538 | summary = "A pure Python trie data structure implementation."
539 | groups = ["default"]
540 | files = [
541 | {file = "pygtrie-2.5.0-py3-none-any.whl", hash = "sha256:8795cda8105493d5ae159a5bef313ff13156c5d4d72feddefacaad59f8c8ce16"},
542 | {file = "pygtrie-2.5.0.tar.gz", hash = "sha256:203514ad826eb403dab1d2e2ddd034e0d1534bbe4dbe0213bb0593f66beba4e2"},
543 | ]
544 |
545 | [[package]]
546 | name = "python-dotenv"
547 | version = "1.0.1"
548 | requires_python = ">=3.8"
549 | summary = "Read key-value pairs from a .env file and set them as environment variables"
550 | groups = ["default"]
551 | files = [
552 | {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"},
553 | {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"},
554 | ]
555 |
556 | [[package]]
557 | name = "pyyaml"
558 | version = "6.0.2"
559 | requires_python = ">=3.8"
560 | summary = "YAML parser and emitter for Python"
561 | groups = ["default"]
562 | files = [
563 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
564 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
565 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"},
566 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"},
567 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"},
568 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"},
569 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"},
570 | {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"},
571 | {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"},
572 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"},
573 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"},
574 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"},
575 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"},
576 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"},
577 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"},
578 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"},
579 | {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"},
580 | {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"},
581 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"},
582 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"},
583 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"},
584 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"},
585 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"},
586 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"},
587 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"},
588 | {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"},
589 | {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"},
590 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"},
591 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"},
592 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"},
593 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"},
594 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"},
595 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"},
596 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"},
597 | {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"},
598 | {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"},
599 | {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
600 | ]
601 |
602 | [[package]]
603 | name = "ruff"
604 | version = "0.5.6"
605 | requires_python = ">=3.7"
606 | summary = "An extremely fast Python linter and code formatter, written in Rust."
607 | groups = ["dev"]
608 | files = [
609 | {file = "ruff-0.5.6-py3-none-linux_armv6l.whl", hash = "sha256:a0ef5930799a05522985b9cec8290b185952f3fcd86c1772c3bdbd732667fdcd"},
610 | {file = "ruff-0.5.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b652dc14f6ef5d1552821e006f747802cc32d98d5509349e168f6bf0ee9f8f42"},
611 | {file = "ruff-0.5.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:80521b88d26a45e871f31e4b88938fd87db7011bb961d8afd2664982dfc3641a"},
612 | {file = "ruff-0.5.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9bc8f328a9f1309ae80e4d392836e7dbc77303b38ed4a7112699e63d3b066ab"},
613 | {file = "ruff-0.5.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d394940f61f7720ad371ddedf14722ee1d6250fd8d020f5ea5a86e7be217daf"},
614 | {file = "ruff-0.5.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111a99cdb02f69ddb2571e2756e017a1496c2c3a2aeefe7b988ddab38b416d36"},
615 | {file = "ruff-0.5.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e395daba77a79f6dc0d07311f94cc0560375ca20c06f354c7c99af3bf4560c5d"},
616 | {file = "ruff-0.5.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c476acb43c3c51e3c614a2e878ee1589655fa02dab19fe2db0423a06d6a5b1b6"},
617 | {file = "ruff-0.5.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e2ff8003f5252fd68425fd53d27c1f08b201d7ed714bb31a55c9ac1d4c13e2eb"},
618 | {file = "ruff-0.5.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c94e084ba3eaa80c2172918c2ca2eb2230c3f15925f4ed8b6297260c6ef179ad"},
619 | {file = "ruff-0.5.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1f77c1c3aa0669fb230b06fb24ffa3e879391a3ba3f15e3d633a752da5a3e670"},
620 | {file = "ruff-0.5.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f908148c93c02873210a52cad75a6eda856b2cbb72250370ce3afef6fb99b1ed"},
621 | {file = "ruff-0.5.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:563a7ae61ad284187d3071d9041c08019975693ff655438d8d4be26e492760bd"},
622 | {file = "ruff-0.5.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:94fe60869bfbf0521e04fd62b74cbca21cbc5beb67cbb75ab33fe8c174f54414"},
623 | {file = "ruff-0.5.6-py3-none-win32.whl", hash = "sha256:e6a584c1de6f8591c2570e171cc7ce482bb983d49c70ddf014393cd39e9dfaed"},
624 | {file = "ruff-0.5.6-py3-none-win_amd64.whl", hash = "sha256:d7fe7dccb1a89dc66785d7aa0ac283b2269712d8ed19c63af908fdccca5ccc1a"},
625 | {file = "ruff-0.5.6-py3-none-win_arm64.whl", hash = "sha256:57c6c0dd997b31b536bff49b9eee5ed3194d60605a4427f735eeb1f9c1b8d264"},
626 | {file = "ruff-0.5.6.tar.gz", hash = "sha256:07c9e3c2a8e1fe377dd460371c3462671a728c981c3205a5217291422209f642"},
627 | ]
628 |
629 | [[package]]
630 | name = "sniffio"
631 | version = "1.3.1"
632 | requires_python = ">=3.7"
633 | summary = "Sniff out which async library your code is running under"
634 | groups = ["default"]
635 | files = [
636 | {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
637 | {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
638 | ]
639 |
640 | [[package]]
641 | name = "starlette"
642 | version = "0.37.2"
643 | requires_python = ">=3.8"
644 | summary = "The little ASGI library that shines."
645 | groups = ["default"]
646 | dependencies = [
647 | "anyio<5,>=3.4.0",
648 | "typing-extensions>=3.10.0; python_version < \"3.10\"",
649 | ]
650 | files = [
651 | {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"},
652 | {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"},
653 | ]
654 |
655 | [[package]]
656 | name = "tomli"
657 | version = "2.0.1"
658 | requires_python = ">=3.7"
659 | summary = "A lil' TOML parser"
660 | groups = ["default"]
661 | marker = "python_version < \"3.11\""
662 | files = [
663 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
664 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
665 | ]
666 |
667 | [[package]]
668 | name = "typing-extensions"
669 | version = "4.12.2"
670 | requires_python = ">=3.8"
671 | summary = "Backported and Experimental Type Hints for Python 3.8+"
672 | groups = ["default"]
673 | files = [
674 | {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
675 | {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
676 | ]
677 |
678 | [[package]]
679 | name = "uvicorn"
680 | version = "0.30.5"
681 | requires_python = ">=3.8"
682 | summary = "The lightning-fast ASGI server."
683 | groups = ["default"]
684 | dependencies = [
685 | "click>=7.0",
686 | "h11>=0.8",
687 | "typing-extensions>=4.0; python_version < \"3.11\"",
688 | ]
689 | files = [
690 | {file = "uvicorn-0.30.5-py3-none-any.whl", hash = "sha256:b2d86de274726e9878188fa07576c9ceeff90a839e2b6e25c917fe05f5a6c835"},
691 | {file = "uvicorn-0.30.5.tar.gz", hash = "sha256:ac6fdbd4425c5fd17a9fe39daf4d4d075da6fdc80f653e5894cdc2fd98752bee"},
692 | ]
693 |
694 | [[package]]
695 | name = "uvicorn"
696 | version = "0.30.5"
697 | extras = ["standard"]
698 | requires_python = ">=3.8"
699 | summary = "The lightning-fast ASGI server."
700 | groups = ["default"]
701 | dependencies = [
702 | "colorama>=0.4; sys_platform == \"win32\"",
703 | "httptools>=0.5.0",
704 | "python-dotenv>=0.13",
705 | "pyyaml>=5.1",
706 | "uvicorn==0.30.5",
707 | "uvloop!=0.15.0,!=0.15.1,>=0.14.0; (sys_platform != \"cygwin\" and sys_platform != \"win32\") and platform_python_implementation != \"PyPy\"",
708 | "watchfiles>=0.13",
709 | "websockets>=10.4",
710 | ]
711 | files = [
712 | {file = "uvicorn-0.30.5-py3-none-any.whl", hash = "sha256:b2d86de274726e9878188fa07576c9ceeff90a839e2b6e25c917fe05f5a6c835"},
713 | {file = "uvicorn-0.30.5.tar.gz", hash = "sha256:ac6fdbd4425c5fd17a9fe39daf4d4d075da6fdc80f653e5894cdc2fd98752bee"},
714 | ]
715 |
716 | [[package]]
717 | name = "uvloop"
718 | version = "0.19.0"
719 | requires_python = ">=3.8.0"
720 | summary = "Fast implementation of asyncio event loop on top of libuv"
721 | groups = ["default"]
722 | marker = "(sys_platform != \"cygwin\" and sys_platform != \"win32\") and platform_python_implementation != \"PyPy\""
723 | files = [
724 | {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e"},
725 | {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428"},
726 | {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8"},
727 | {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849"},
728 | {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957"},
729 | {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd"},
730 | {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef"},
731 | {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2"},
732 | {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1"},
733 | {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24"},
734 | {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533"},
735 | {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12"},
736 | {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650"},
737 | {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec"},
738 | {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc"},
739 | {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6"},
740 | {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593"},
741 | {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3"},
742 | {file = "uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd"},
743 | ]
744 |
745 | [[package]]
746 | name = "watchfiles"
747 | version = "0.23.0"
748 | requires_python = ">=3.8"
749 | summary = "Simple, modern and high performance file watching and code reload in python."
750 | groups = ["default"]
751 | dependencies = [
752 | "anyio>=3.0.0",
753 | ]
754 | files = [
755 | {file = "watchfiles-0.23.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:bee8ce357a05c20db04f46c22be2d1a2c6a8ed365b325d08af94358e0688eeb4"},
756 | {file = "watchfiles-0.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ccd3011cc7ee2f789af9ebe04745436371d36afe610028921cab9f24bb2987b"},
757 | {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb02d41c33be667e6135e6686f1bb76104c88a312a18faa0ef0262b5bf7f1a0f"},
758 | {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cf12ac34c444362f3261fb3ff548f0037ddd4c5bb85f66c4be30d2936beb3c5"},
759 | {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0b2c25040a3c0ce0e66c7779cc045fdfbbb8d59e5aabfe033000b42fe44b53e"},
760 | {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecf2be4b9eece4f3da8ba5f244b9e51932ebc441c0867bd6af46a3d97eb068d6"},
761 | {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40cb8fa00028908211eb9f8d47744dca21a4be6766672e1ff3280bee320436f1"},
762 | {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f48c917ffd36ff9a5212614c2d0d585fa8b064ca7e66206fb5c095015bc8207"},
763 | {file = "watchfiles-0.23.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9d183e3888ada88185ab17064079c0db8c17e32023f5c278d7bf8014713b1b5b"},
764 | {file = "watchfiles-0.23.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9837edf328b2805346f91209b7e660f65fb0e9ca18b7459d075d58db082bf981"},
765 | {file = "watchfiles-0.23.0-cp310-none-win32.whl", hash = "sha256:296e0b29ab0276ca59d82d2da22cbbdb39a23eed94cca69aed274595fb3dfe42"},
766 | {file = "watchfiles-0.23.0-cp310-none-win_amd64.whl", hash = "sha256:4ea756e425ab2dfc8ef2a0cb87af8aa7ef7dfc6fc46c6f89bcf382121d4fff75"},
767 | {file = "watchfiles-0.23.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:e397b64f7aaf26915bf2ad0f1190f75c855d11eb111cc00f12f97430153c2eab"},
768 | {file = "watchfiles-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b4ac73b02ca1824ec0a7351588241fd3953748d3774694aa7ddb5e8e46aef3e3"},
769 | {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130a896d53b48a1cecccfa903f37a1d87dbb74295305f865a3e816452f6e49e4"},
770 | {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c5e7803a65eb2d563c73230e9d693c6539e3c975ccfe62526cadde69f3fda0cf"},
771 | {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1aa4cc85202956d1a65c88d18c7b687b8319dbe6b1aec8969784ef7a10e7d1a"},
772 | {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87f889f6e58849ddb7c5d2cb19e2e074917ed1c6e3ceca50405775166492cca8"},
773 | {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37fd826dac84c6441615aa3f04077adcc5cac7194a021c9f0d69af20fb9fa788"},
774 | {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee7db6e36e7a2c15923072e41ea24d9a0cf39658cb0637ecc9307b09d28827e1"},
775 | {file = "watchfiles-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2368c5371c17fdcb5a2ea71c5c9d49f9b128821bfee69503cc38eae00feb3220"},
776 | {file = "watchfiles-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:857af85d445b9ba9178db95658c219dbd77b71b8264e66836a6eba4fbf49c320"},
777 | {file = "watchfiles-0.23.0-cp311-none-win32.whl", hash = "sha256:1d636c8aeb28cdd04a4aa89030c4b48f8b2954d8483e5f989774fa441c0ed57b"},
778 | {file = "watchfiles-0.23.0-cp311-none-win_amd64.whl", hash = "sha256:46f1d8069a95885ca529645cdbb05aea5837d799965676e1b2b1f95a4206313e"},
779 | {file = "watchfiles-0.23.0-cp311-none-win_arm64.whl", hash = "sha256:e495ed2a7943503766c5d1ff05ae9212dc2ce1c0e30a80d4f0d84889298fa304"},
780 | {file = "watchfiles-0.23.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1db691bad0243aed27c8354b12d60e8e266b75216ae99d33e927ff5238d270b5"},
781 | {file = "watchfiles-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:62d2b18cb1edaba311fbbfe83fb5e53a858ba37cacb01e69bc20553bb70911b8"},
782 | {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e087e8fdf1270d000913c12e6eca44edd02aad3559b3e6b8ef00f0ce76e0636f"},
783 | {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dd41d5c72417b87c00b1b635738f3c283e737d75c5fa5c3e1c60cd03eac3af77"},
784 | {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e5f3ca0ff47940ce0a389457b35d6df601c317c1e1a9615981c474452f98de1"},
785 | {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6991e3a78f642368b8b1b669327eb6751439f9f7eaaa625fae67dd6070ecfa0b"},
786 | {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f7252f52a09f8fa5435dc82b6af79483118ce6bd51eb74e6269f05ee22a7b9f"},
787 | {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e01bcb8d767c58865207a6c2f2792ad763a0fe1119fb0a430f444f5b02a5ea0"},
788 | {file = "watchfiles-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8e56fbcdd27fce061854ddec99e015dd779cae186eb36b14471fc9ae713b118c"},
789 | {file = "watchfiles-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bd3e2d64500a6cad28bcd710ee6269fbeb2e5320525acd0cfab5f269ade68581"},
790 | {file = "watchfiles-0.23.0-cp312-none-win32.whl", hash = "sha256:eb99c954291b2fad0eff98b490aa641e128fbc4a03b11c8a0086de8b7077fb75"},
791 | {file = "watchfiles-0.23.0-cp312-none-win_amd64.whl", hash = "sha256:dccc858372a56080332ea89b78cfb18efb945da858fabeb67f5a44fa0bcb4ebb"},
792 | {file = "watchfiles-0.23.0-cp312-none-win_arm64.whl", hash = "sha256:6c21a5467f35c61eafb4e394303720893066897fca937bade5b4f5877d350ff8"},
793 | {file = "watchfiles-0.23.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ba31c32f6b4dceeb2be04f717811565159617e28d61a60bb616b6442027fd4b9"},
794 | {file = "watchfiles-0.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:85042ab91814fca99cec4678fc063fb46df4cbb57b4835a1cc2cb7a51e10250e"},
795 | {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24655e8c1c9c114005c3868a3d432c8aa595a786b8493500071e6a52f3d09217"},
796 | {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b1a950ab299a4a78fd6369a97b8763732bfb154fdb433356ec55a5bce9515c1"},
797 | {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8d3c5cd327dd6ce0edfc94374fb5883d254fe78a5e9d9dfc237a1897dc73cd1"},
798 | {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ff785af8bacdf0be863ec0c428e3288b817e82f3d0c1d652cd9c6d509020dd0"},
799 | {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02b7ba9d4557149410747353e7325010d48edcfe9d609a85cb450f17fd50dc3d"},
800 | {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a1b05c0afb2cd2f48c1ed2ae5487b116e34b93b13074ed3c22ad5c743109f0"},
801 | {file = "watchfiles-0.23.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:109a61763e7318d9f821b878589e71229f97366fa6a5c7720687d367f3ab9eef"},
802 | {file = "watchfiles-0.23.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:9f8e6bb5ac007d4a4027b25f09827ed78cbbd5b9700fd6c54429278dacce05d1"},
803 | {file = "watchfiles-0.23.0-cp313-none-win32.whl", hash = "sha256:f46c6f0aec8d02a52d97a583782d9af38c19a29900747eb048af358a9c1d8e5b"},
804 | {file = "watchfiles-0.23.0-cp313-none-win_amd64.whl", hash = "sha256:f449afbb971df5c6faeb0a27bca0427d7b600dd8f4a068492faec18023f0dcff"},
805 | {file = "watchfiles-0.23.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6a9265cf87a5b70147bfb2fec14770ed5b11a5bb83353f0eee1c25a81af5abfe"},
806 | {file = "watchfiles-0.23.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9f02a259fcbbb5fcfe7a0805b1097ead5ba7a043e318eef1db59f93067f0b49b"},
807 | {file = "watchfiles-0.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ebaebb53b34690da0936c256c1cdb0914f24fb0e03da76d185806df9328abed"},
808 | {file = "watchfiles-0.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd257f98cff9c6cb39eee1a83c7c3183970d8a8d23e8cf4f47d9a21329285cee"},
809 | {file = "watchfiles-0.23.0.tar.gz", hash = "sha256:9338ade39ff24f8086bb005d16c29f8e9f19e55b18dcb04dfa26fcbc09da497b"},
810 | ]
811 |
812 | [[package]]
813 | name = "websockets"
814 | version = "12.0"
815 | requires_python = ">=3.8"
816 | summary = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
817 | groups = ["default"]
818 | files = [
819 | {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"},
820 | {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"},
821 | {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"},
822 | {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"},
823 | {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"},
824 | {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"},
825 | {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"},
826 | {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"},
827 | {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"},
828 | {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"},
829 | {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"},
830 | {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"},
831 | {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"},
832 | {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"},
833 | {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"},
834 | {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"},
835 | {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"},
836 | {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"},
837 | {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"},
838 | {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"},
839 | {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"},
840 | {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"},
841 | {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"},
842 | {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"},
843 | {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"},
844 | {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"},
845 | {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"},
846 | {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"},
847 | {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"},
848 | {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"},
849 | {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"},
850 | {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"},
851 | {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"},
852 | {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"},
853 | {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"},
854 | {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"},
855 | {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"},
856 | {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"},
857 | {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"},
858 | {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"},
859 | ]
860 |
861 | [[package]]
862 | name = "win32-setctime"
863 | version = "1.1.0"
864 | requires_python = ">=3.5"
865 | summary = "A small Python utility to set file creation time on Windows"
866 | groups = ["default"]
867 | marker = "sys_platform == \"win32\""
868 | files = [
869 | {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"},
870 | {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
871 | ]
872 |
873 | [[package]]
874 | name = "yarl"
875 | version = "1.9.4"
876 | requires_python = ">=3.7"
877 | summary = "Yet another URL library"
878 | groups = ["default"]
879 | dependencies = [
880 | "idna>=2.0",
881 | "multidict>=4.0",
882 | "typing-extensions>=3.7.4; python_version < \"3.8\"",
883 | ]
884 | files = [
885 | {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"},
886 | {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"},
887 | {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"},
888 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"},
889 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"},
890 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"},
891 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"},
892 | {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"},
893 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"},
894 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"},
895 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"},
896 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"},
897 | {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"},
898 | {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"},
899 | {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"},
900 | {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"},
901 | {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"},
902 | {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"},
903 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"},
904 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"},
905 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"},
906 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"},
907 | {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"},
908 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"},
909 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"},
910 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"},
911 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"},
912 | {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"},
913 | {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"},
914 | {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"},
915 | {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"},
916 | {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"},
917 | {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"},
918 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"},
919 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"},
920 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"},
921 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"},
922 | {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"},
923 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"},
924 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"},
925 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"},
926 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"},
927 | {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"},
928 | {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"},
929 | {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"},
930 | {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"},
931 | {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"},
932 | ]
933 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "nonebot_plugin_web_bottle"
3 | version = "1.6.0"
4 | description = "有web的漂流瓶"
5 | authors = [{ name = "Xican", email = "723926109@qq.com" }]
6 | requires-python = ">=3.10"
7 | readme = "README.md"
8 | license = { text = "MIT" }
9 |
10 | dependencies = [
11 | "aiofiles>=23.2.1",
12 | "httpx>=0.23.0",
13 | "nonebot-plugin-localstore>=0.7.1",
14 | "nonebot-adapter-onebot>=2.4.4",
15 | "nonebot2[fastapi]>=2.3.2",
16 | "jinja2>=3.1.4",
17 | "pillow>=11.0.0",
18 | "cryptography",
19 | "itsdangerous>=2.2.0",
20 | "python-multipart>=0.0.20",
21 | "fastapi>=0.110.0",
22 | "pydantic>=1.10.11"
23 | ]
24 |
25 | [project.urls]
26 | homepage = "https://github.com/luosheng520qaq/nonebot-plugin-web-bottle/"
27 | repository = "https://github.com/luosheng520qaq/nonebot-plugin-web-bottle/"
28 |
29 | [tool.pdm.dev-dependencies]
30 | dev = [
31 | "nonebot2[fastapi,uvicorn]>=2.3.2",
32 | "nonebot-adapter-onebot>=2.4.4",
33 | "ruff>=0.5.6"
34 | ]
35 |
36 | [build-system]
37 | requires = ["pdm-backend"]
38 | build-backend = "pdm.backend"
39 |
40 | [tool.pdm]
41 | distribution = true
42 |
43 | [tool.ruff]
44 | line-length = 120
45 | target-version = "py310"
46 |
47 | [tool.ruff.format]
48 | quote-style = "double"
49 |
50 | [tool.ruff.lint]
51 | select = [
52 | "F", "E", "W", "C90", "I", "N", "UP", "YTT", "ANN", "ASYNC", "S", "BLE", "FBT", "B", "A",
53 | "COM", "C4", "DTZ", "T10", "EM", "FA", "ISC", "ICN", "PIE", "T20", "Q", "RSE", "RET", "SLF",
54 | "SIM", "TID", "TCH", "ARG", "PTH", "ERA", "PD", "PGH", "PL", "TRY", "FLY", "PERF", "FURB", "RUF"
55 | ]
56 | ignore = [
57 | "E501", "ANN101", "ANN102", "ANN202", "B008", "TRY003", "COM812",
58 | "TID252", "ISC001", "RUF001", "RUF002", "RUF003"
59 | ]
60 |
61 | [tool.ruff.lint.flake8-builtins]
62 | builtins-ignorelist = ["id"]
63 |
64 | [tool.ruff.lint.flake8-annotations]
65 | mypy-init-return = true
66 |
--------------------------------------------------------------------------------
评论 ID: {{ comment.comment_id }}
29 |瓶子 ID: {{ comment.bottle_id }}
30 |评论内容: {{ comment.content }}
31 |状态: {{ comment.state }}
32 |用户 ID: {{ comment.uid }}
33 | 34 | 35 |