├── .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 | logo 5 | 6 | 7 | 8 | # nonebot_plugin_web_bottle 9 | ![Python](https://img.shields.io/badge/Python-3.11+-blue.svg) 10 | ![PyPI - Version](https://img.shields.io/pypi/v/nonebot-plugin-web-bottle) 11 | [![pdm-managed](https://img.shields.io/endpoint?url=https%3A%2F%2Fcdn.jsdelivr.net%2Fgh%2Fpdm-project%2F.github%2Fbadge.json)](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 | ![Image of Yaktocat](https://raw.githubusercontent.com/luosheng520qaq/nonebot-plugin-web-bottle/refs/heads/master/B66FEE6EE4B550CF930CF48FFB9EDC0D.png) 46 | ![Image of Yaktocat](https://github.com/luosheng520qaq/nonebot_plugin_web_bottle/blob/master/example/bottles.png) 47 | ![Image of Yaktocat](https://github.com/luosheng520qaq/nonebot_plugin_web_bottle/blob/master/example/comments.png) 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 | 122 | 123 | 124 | 125 | 129 | 130 |
119 | 120 |
瓶子本体 121 |
126 | 127 |
评论区 128 |
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 | 74 | -------------------------------------------------------------------------------- /nonebot_plugin_web_bottle/.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /nonebot_plugin_web_bottle/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 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 | 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 |
28 |

评论 ID: {{ comment.comment_id }}

29 |

瓶子 ID: {{ comment.bottle_id }}

30 |

评论内容: {{ comment.content }}

31 |

状态: {{ comment.state }}

32 |

用户 ID: {{ comment.uid }}

33 | 34 | 35 |
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 | 211 | 212 | -------------------------------------------------------------------------------- /nonebot_plugin_web_bottle/templates/login/1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 登录 7 | 8 | 9 | 10 | 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 | 88 |
89 | 90 | 91 | 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 | --------------------------------------------------------------------------------