mirror of
https://gitflic.ru/project/maks1ms/ocab.git
synced 2025-11-28 10:21:55 +03:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8953b2a15 | ||
|
|
de9a954fe3 |
10
.gitignore
vendored
10
.gitignore
vendored
@@ -1,10 +0,0 @@
|
||||
.idea
|
||||
.vscode
|
||||
.env
|
||||
env
|
||||
.venv
|
||||
venv
|
||||
__pycache__
|
||||
OCAB.db
|
||||
src/paths.json
|
||||
src/core/config.yaml
|
||||
59
README.md
59
README.md
@@ -1,58 +1 @@
|
||||
# OpenChatAiBot V2
|
||||
|
||||
## Что такое OCAB?
|
||||
|
||||
OCAB - это бот для Telegram, который призван помочь во взаимодействии с чатом.
|
||||
Бот поддерживает интеграцию модулей для расширения функционала.
|
||||
Фактически бот является платформой для запуска созданных для него модулей.
|
||||
Модули могут взаимодействовать друг с другом или быть полностью независимыми.
|
||||
|
||||
## Что такое модуль?
|
||||
|
||||
Модуль - это директория, которая содержит в себе код модуля и его конфигурацию.
|
||||
|
||||
### Структура модуля
|
||||
|
||||
*Будет дополнено после закрытия [issue #17](https://gitflic.ru/project/armatik/ocab/issue/17).*
|
||||
|
||||
## Стандартные модули
|
||||
|
||||
В стандартный состав бота входят следующие модули:
|
||||
|
||||
* `admin` - модуль для модерирования чата. Позволяет удалять сообщения, банить пользователей и т.д.
|
||||
* `reputation` - модуль репутации пользователей. Позволяет оценивать ответы пользователей и накапливать репутацию.
|
||||
* `welcome` - модуль приветствия новых пользователей. Позволяет приветствовать новых пользователей в чате, а также
|
||||
проверять пользователя капчей для предотвращения спама.
|
||||
* `roles` - модуль ролей. Позволяет назначать пользователям роли и ограничивать доступ к командам бота по ролям.
|
||||
Является важной частью системы прав доступа и модуля `admin`.
|
||||
|
||||
## Дополнительные официальные модули
|
||||
|
||||
* `gpt` - модуль для генерации ответов на основе нейросети GPT-3.5. Позволяет боту отвечать на сообщения
|
||||
пользователей, используя нейросеть. Ключевой особенностью является построение линии контекста для нейросети,
|
||||
которая позволяет боту отвечать на вопросы, используя контекст предыдущих сообщений. Для этого используется
|
||||
модуль база данных хранящий историю сообщений.
|
||||
* `bugzilla` - модуль для интеграции с BugZilla. Позволяет получать уведомления о новых багах в BugZilla, отслеживать их
|
||||
статус, формировать стандартизированные сообщения для корректного описания багов. В будущем планируется интеграция с
|
||||
API BugZilla для возможности создания багов из чата.
|
||||
* `alt_packages` - модуль для интеграции с AltLinux Packages. Позволяет получать уведомления о новых пакетах в репозитории
|
||||
AltLinux, поиска пакетов по названию, получения истории изменений, команды для установки пакета из репозитория и
|
||||
прочей информации о пакете.
|
||||
* `notes` - модуль заметок. Позволяет сохранять заметки для пользователей и чатов. Заметки являются ссылками на
|
||||
сообщения в чате.
|
||||
|
||||
Список модулей будет пополняться. Идеи для модулей можно оставлять в [issues](https://gitflic.ru/project/armatik/ocab/issue/create).
|
||||
|
||||
## Установка бота
|
||||
|
||||
### Docker
|
||||
|
||||
### Вручную
|
||||
|
||||
## Технологический стек
|
||||
|
||||
* Python 3.11.6 или выше - основной язык программирования.
|
||||
* SQLite 3 - база данных для хранения информации о чате и пользователях.
|
||||
* [Poetry](https://gitflic.ru/project/armatik/ocab/blob?file=how-to%20install%20deps.md&branch=OCAB-V2) - менеджер зависимостей.
|
||||
* aiogram 3 - библиотека для работы с Telegram API.
|
||||
* peewee - ORM для работы с базой данных.
|
||||
# OpenChatAiBot
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
## Poetry
|
||||
|
||||
### Установка с официального сайта
|
||||
|
||||
```shell
|
||||
curl -sSL https://install.python-poetry.org | python3 -
|
||||
```
|
||||
|
||||
### Установка с PyPi
|
||||
|
||||
```shell
|
||||
python3 -m pip install poetry
|
||||
```
|
||||
|
||||
Доп информация:https://www.8host.com/blog/ustanovka-menedzhera-zavisimostej-poetry/
|
||||
|
||||
## Зависимости
|
||||
|
||||
### Добавление зависимости
|
||||
```shell
|
||||
poetry add NAME
|
||||
```
|
||||
`NAME` - название зависимости.
|
||||
|
||||
### Установка зависимостей
|
||||
```shell
|
||||
poetry install
|
||||
```
|
||||
|
||||
### Обновление зависимостей
|
||||
```shell
|
||||
poetry update
|
||||
```
|
||||
|
||||
## Виртуальное окружение
|
||||
|
||||
### Создание/активация
|
||||
```shell
|
||||
poetry shell
|
||||
```
|
||||
|
||||
### Настройка
|
||||
|
||||
Хранить окружение внутри проекта
|
||||
```shell
|
||||
poetry config virtualenvs.in-project true
|
||||
```
|
||||
15
init.py
15
init.py
@@ -1,15 +0,0 @@
|
||||
from pathlib import Path
|
||||
from json import dumps
|
||||
|
||||
pwd = Path().cwd()
|
||||
dir_core = pwd / "src" / "core"
|
||||
dir_modules_standard = pwd / "src" / "modules" / "standard"
|
||||
dir_modules_custom = pwd / "src" / "modules" / "custom"
|
||||
|
||||
json = {
|
||||
'core': str(dir_core),
|
||||
'modules standard': str(dir_modules_standard),
|
||||
'modules custom': str(dir_modules_custom),
|
||||
}
|
||||
with open("src/paths.json", "w", encoding="utf8") as f:
|
||||
f.write(dumps(json, indent=4))
|
||||
837
poetry.lock
generated
837
poetry.lock
generated
@@ -1,837 +0,0 @@
|
||||
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "aiofiles"
|
||||
version = "23.2.1"
|
||||
description = "File support for asyncio."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "aiofiles-23.2.1-py3-none-any.whl", hash = "sha256:19297512c647d4b27a2cf7c34caa7e405c0d60b5560618a29a9fe027b18b0107"},
|
||||
{file = "aiofiles-23.2.1.tar.gz", hash = "sha256:84ec2218d8419404abcb9f0c02df3f34c6e0a68ed41072acfb1cef5cbc29051a"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aiogram"
|
||||
version = "3.5.0"
|
||||
description = "Modern and fully asynchronous framework for Telegram Bot API"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "aiogram-3.5.0-py3-none-any.whl", hash = "sha256:70b5804671b87214768a2a63f19f1457684bd0c6cb6abd23e73bb16207fd7e58"},
|
||||
{file = "aiogram-3.5.0.tar.gz", hash = "sha256:1793deb24f36a6fc7b678c31d9a831cef7972765710a47a3e139645a99facba4"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
aiofiles = ">=23.2.1,<23.3.0"
|
||||
aiohttp = ">=3.9.0,<3.10.0"
|
||||
certifi = ">=2023.7.22"
|
||||
magic-filter = ">=1.0.12,<1.1"
|
||||
pydantic = ">=2.4.1,<2.8"
|
||||
typing-extensions = ">=4.7.0,<=5.0"
|
||||
|
||||
[package.extras]
|
||||
cli = ["aiogram-cli (>=1.0.3,<1.1.0)"]
|
||||
dev = ["black (>=23.10.0,<23.11.0)", "isort (>=5.12.0,<5.13.0)", "mypy (>=1.6.1,<1.7.0)", "packaging (>=23.1,<24.0)", "pre-commit (>=3.5.0,<3.6.0)", "ruff (>=0.1.1,<0.2.0)", "toml (>=0.10.2,<0.11.0)"]
|
||||
docs = ["furo (>=2023.9.10,<2023.10.0)", "markdown-include (>=0.8.1,<0.9.0)", "pygments (>=2.16.1,<2.17.0)", "pymdown-extensions (>=10.3,<11.0)", "sphinx (>=7.2.6,<7.3.0)", "sphinx-autobuild (>=2021.3.14,<2021.4.0)", "sphinx-copybutton (>=0.5.2,<0.6.0)", "sphinx-intl (>=2.1.0,<2.2.0)", "sphinx-substitution-extensions (>=2022.2.16,<2022.3.0)", "sphinxcontrib-towncrier (>=0.3.2a0,<0.4.0)", "towncrier (>=23.6.0,<23.7.0)"]
|
||||
fast = ["aiodns (>=3.0.0)", "uvloop (>=0.17.0)"]
|
||||
i18n = ["babel (>=2.13.0,<2.14.0)"]
|
||||
proxy = ["aiohttp-socks (>=0.8.3,<0.9.0)"]
|
||||
redis = ["redis[hiredis] (>=5.0.1,<5.1.0)"]
|
||||
test = ["aresponses (>=2.1.6,<2.2.0)", "pycryptodomex (>=3.19.0,<3.20.0)", "pytest (>=7.4.2,<7.5.0)", "pytest-aiohttp (>=1.0.5,<1.1.0)", "pytest-asyncio (>=0.21.1,<0.22.0)", "pytest-cov (>=4.1.0,<4.2.0)", "pytest-html (>=4.0.2,<4.1.0)", "pytest-lazy-fixture (>=0.6.3,<0.7.0)", "pytest-mock (>=3.12.0,<3.13.0)", "pytest-mypy (>=0.10.3,<0.11.0)", "pytz (>=2023.3,<2024.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "aiohttp"
|
||||
version = "3.9.5"
|
||||
description = "Async http client/server framework (asyncio)"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7"},
|
||||
{file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c"},
|
||||
{file = "aiohttp-3.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a"},
|
||||
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430"},
|
||||
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3"},
|
||||
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b"},
|
||||
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72"},
|
||||
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0"},
|
||||
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558"},
|
||||
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db"},
|
||||
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f"},
|
||||
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832"},
|
||||
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10"},
|
||||
{file = "aiohttp-3.9.5-cp310-cp310-win32.whl", hash = "sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb"},
|
||||
{file = "aiohttp-3.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb"},
|
||||
{file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342"},
|
||||
{file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d"},
|
||||
{file = "aiohttp-3.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424"},
|
||||
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee"},
|
||||
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2"},
|
||||
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233"},
|
||||
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595"},
|
||||
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6"},
|
||||
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d"},
|
||||
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323"},
|
||||
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9"},
|
||||
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771"},
|
||||
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75"},
|
||||
{file = "aiohttp-3.9.5-cp311-cp311-win32.whl", hash = "sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6"},
|
||||
{file = "aiohttp-3.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a"},
|
||||
{file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678"},
|
||||
{file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c"},
|
||||
{file = "aiohttp-3.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f"},
|
||||
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4"},
|
||||
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c"},
|
||||
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa"},
|
||||
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58"},
|
||||
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf"},
|
||||
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f"},
|
||||
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81"},
|
||||
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a"},
|
||||
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a"},
|
||||
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da"},
|
||||
{file = "aiohttp-3.9.5-cp312-cp312-win32.whl", hash = "sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59"},
|
||||
{file = "aiohttp-3.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888"},
|
||||
{file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8"},
|
||||
{file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8"},
|
||||
{file = "aiohttp-3.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78"},
|
||||
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b"},
|
||||
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de"},
|
||||
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac"},
|
||||
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11"},
|
||||
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd"},
|
||||
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1"},
|
||||
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e"},
|
||||
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156"},
|
||||
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031"},
|
||||
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe"},
|
||||
{file = "aiohttp-3.9.5-cp38-cp38-win32.whl", hash = "sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da"},
|
||||
{file = "aiohttp-3.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a"},
|
||||
{file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed"},
|
||||
{file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a"},
|
||||
{file = "aiohttp-3.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372"},
|
||||
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb"},
|
||||
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24"},
|
||||
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2"},
|
||||
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106"},
|
||||
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b"},
|
||||
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475"},
|
||||
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed"},
|
||||
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c"},
|
||||
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46"},
|
||||
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2"},
|
||||
{file = "aiohttp-3.9.5-cp39-cp39-win32.whl", hash = "sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09"},
|
||||
{file = "aiohttp-3.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1"},
|
||||
{file = "aiohttp-3.9.5.tar.gz", hash = "sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
aiosignal = ">=1.1.2"
|
||||
attrs = ">=17.3.0"
|
||||
frozenlist = ">=1.1.1"
|
||||
multidict = ">=4.5,<7.0"
|
||||
yarl = ">=1.0,<2.0"
|
||||
|
||||
[package.extras]
|
||||
speedups = ["Brotli", "aiodns", "brotlicffi"]
|
||||
|
||||
[[package]]
|
||||
name = "aiosignal"
|
||||
version = "1.3.1"
|
||||
description = "aiosignal: a list of registered asynchronous callbacks"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"},
|
||||
{file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
frozenlist = ">=1.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
version = "0.6.0"
|
||||
description = "Reusable constraint types to use with typing.Annotated"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"},
|
||||
{file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "23.2.0"
|
||||
description = "Classes Without Boilerplate"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"},
|
||||
{file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
cov = ["attrs[tests]", "coverage[toml] (>=5.3)"]
|
||||
dev = ["attrs[tests]", "pre-commit"]
|
||||
docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"]
|
||||
tests = ["attrs[tests-no-zope]", "zope-interface"]
|
||||
tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"]
|
||||
tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2024.2.2"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"},
|
||||
{file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "3.3.2"
|
||||
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
files = [
|
||||
{file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"},
|
||||
{file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frozenlist"
|
||||
version = "1.4.1"
|
||||
description = "A list-like structure which implements collections.abc.MutableSequence"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"},
|
||||
{file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"},
|
||||
{file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"},
|
||||
{file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"},
|
||||
{file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"},
|
||||
{file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"},
|
||||
{file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"},
|
||||
{file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"},
|
||||
{file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"},
|
||||
{file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"},
|
||||
{file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"},
|
||||
{file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"},
|
||||
{file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"},
|
||||
{file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"},
|
||||
{file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"},
|
||||
{file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"},
|
||||
{file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"},
|
||||
{file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"},
|
||||
{file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"},
|
||||
{file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"},
|
||||
{file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"},
|
||||
{file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"},
|
||||
{file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"},
|
||||
{file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"},
|
||||
{file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"},
|
||||
{file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"},
|
||||
{file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"},
|
||||
{file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"},
|
||||
{file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"},
|
||||
{file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"},
|
||||
{file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"},
|
||||
{file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"},
|
||||
{file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"},
|
||||
{file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"},
|
||||
{file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"},
|
||||
{file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"},
|
||||
{file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"},
|
||||
{file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"},
|
||||
{file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"},
|
||||
{file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"},
|
||||
{file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"},
|
||||
{file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"},
|
||||
{file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"},
|
||||
{file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"},
|
||||
{file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"},
|
||||
{file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"},
|
||||
{file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"},
|
||||
{file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"},
|
||||
{file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"},
|
||||
{file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"},
|
||||
{file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"},
|
||||
{file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"},
|
||||
{file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"},
|
||||
{file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"},
|
||||
{file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"},
|
||||
{file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"},
|
||||
{file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"},
|
||||
{file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"},
|
||||
{file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"},
|
||||
{file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"},
|
||||
{file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"},
|
||||
{file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"},
|
||||
{file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"},
|
||||
{file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"},
|
||||
{file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"},
|
||||
{file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"},
|
||||
{file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"},
|
||||
{file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"},
|
||||
{file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"},
|
||||
{file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"},
|
||||
{file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"},
|
||||
{file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"},
|
||||
{file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"},
|
||||
{file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"},
|
||||
{file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"},
|
||||
{file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"},
|
||||
{file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.7"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
|
||||
{file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "magic-filter"
|
||||
version = "1.0.12"
|
||||
description = ""
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "magic_filter-1.0.12-py3-none-any.whl", hash = "sha256:e5929e544f310c2b1f154318db8c5cdf544dd658efa998172acd2e4ba0f6c6a6"},
|
||||
{file = "magic_filter-1.0.12.tar.gz", hash = "sha256:4751d0b579a5045d1dc250625c4c508c18c3def5ea6afaf3957cb4530d03f7f9"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
dev = ["black (>=22.8.0,<22.9.0)", "flake8 (>=5.0.4,<5.1.0)", "isort (>=5.11.5,<5.12.0)", "mypy (>=1.4.1,<1.5.0)", "pre-commit (>=2.20.0,<2.21.0)", "pytest (>=7.1.3,<7.2.0)", "pytest-cov (>=3.0.0,<3.1.0)", "pytest-html (>=3.1.1,<3.2.0)", "types-setuptools (>=65.3.0,<65.4.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "multidict"
|
||||
version = "6.0.5"
|
||||
description = "multidict implementation"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"},
|
||||
{file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"},
|
||||
{file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"},
|
||||
{file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"},
|
||||
{file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"},
|
||||
{file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"},
|
||||
{file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"},
|
||||
{file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"},
|
||||
{file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peewee"
|
||||
version = "3.17.3"
|
||||
description = "a little orm"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "peewee-3.17.3.tar.gz", hash = "sha256:ef15f90b628e41a584be8306cdc3243c51f73ce88b06154d9572f6d0284a0169"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.7.1"
|
||||
description = "Data validation using Python type hints"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"},
|
||||
{file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
annotated-types = ">=0.4.0"
|
||||
pydantic-core = "2.18.2"
|
||||
typing-extensions = ">=4.6.1"
|
||||
|
||||
[package.extras]
|
||||
email = ["email-validator (>=2.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-core"
|
||||
version = "2.18.2"
|
||||
description = "Core functionality for Pydantic validation and serialization"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"},
|
||||
{file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"},
|
||||
{file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"},
|
||||
{file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"},
|
||||
{file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"},
|
||||
{file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"},
|
||||
{file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"},
|
||||
{file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"},
|
||||
{file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"},
|
||||
{file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"},
|
||||
{file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"},
|
||||
{file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"},
|
||||
{file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"},
|
||||
{file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.1"
|
||||
description = "YAML parser and emitter for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
|
||||
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.31.0"
|
||||
description = "Python HTTP for Humans."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
|
||||
{file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
certifi = ">=2017.4.17"
|
||||
charset-normalizer = ">=2,<4"
|
||||
idna = ">=2.5,<4"
|
||||
urllib3 = ">=1.21.1,<3"
|
||||
|
||||
[package.extras]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
|
||||
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.11.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.8+"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"},
|
||||
{file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.2.1"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
|
||||
{file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
|
||||
h2 = ["h2 (>=4,<5)"]
|
||||
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
zstd = ["zstandard (>=0.18.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "yarl"
|
||||
version = "1.9.4"
|
||||
description = "Yet another URL library"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"},
|
||||
{file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"},
|
||||
{file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"},
|
||||
{file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"},
|
||||
{file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"},
|
||||
{file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"},
|
||||
{file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"},
|
||||
{file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"},
|
||||
{file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"},
|
||||
{file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"},
|
||||
{file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"},
|
||||
{file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"},
|
||||
{file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"},
|
||||
{file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"},
|
||||
{file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"},
|
||||
{file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"},
|
||||
{file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"},
|
||||
{file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"},
|
||||
{file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"},
|
||||
{file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"},
|
||||
{file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"},
|
||||
{file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"},
|
||||
{file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"},
|
||||
{file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"},
|
||||
{file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"},
|
||||
{file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"},
|
||||
{file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"},
|
||||
{file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"},
|
||||
{file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"},
|
||||
{file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"},
|
||||
{file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"},
|
||||
{file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"},
|
||||
{file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"},
|
||||
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"},
|
||||
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"},
|
||||
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"},
|
||||
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"},
|
||||
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"},
|
||||
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"},
|
||||
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"},
|
||||
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"},
|
||||
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"},
|
||||
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"},
|
||||
{file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"},
|
||||
{file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"},
|
||||
{file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"},
|
||||
{file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"},
|
||||
{file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"},
|
||||
{file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"},
|
||||
{file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"},
|
||||
{file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"},
|
||||
{file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"},
|
||||
{file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"},
|
||||
{file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"},
|
||||
{file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"},
|
||||
{file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"},
|
||||
{file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"},
|
||||
{file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"},
|
||||
{file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"},
|
||||
{file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"},
|
||||
{file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"},
|
||||
{file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"},
|
||||
{file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"},
|
||||
{file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"},
|
||||
{file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"},
|
||||
{file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"},
|
||||
{file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"},
|
||||
{file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"},
|
||||
{file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"},
|
||||
{file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"},
|
||||
{file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"},
|
||||
{file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"},
|
||||
{file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"},
|
||||
{file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"},
|
||||
{file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"},
|
||||
{file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"},
|
||||
{file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"},
|
||||
{file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"},
|
||||
{file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"},
|
||||
{file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"},
|
||||
{file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"},
|
||||
{file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"},
|
||||
{file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"},
|
||||
{file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"},
|
||||
{file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"},
|
||||
{file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"},
|
||||
{file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"},
|
||||
{file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"},
|
||||
{file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"},
|
||||
{file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
idna = ">=2.0"
|
||||
multidict = ">=4.0"
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.11.6"
|
||||
content-hash = "001611942f2ccb553fc80099924dce739c3cd5febb1438e9bad3e865d1aa8f8b"
|
||||
@@ -1,25 +0,0 @@
|
||||
[tool.poetry]
|
||||
name = "ocab"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
license = "GPL-3.0-only"
|
||||
authors = ["Семён Фомченков <s.fomchenkov@yandex.ru>"]
|
||||
maintainers = [
|
||||
"Илья Женецкий <ilya_zhenetskij@vk.com>",
|
||||
"qualimock <qualimock@yandex.ru>",
|
||||
"Кирилл Уницаев fiersik.kouji@yandex.ru",
|
||||
]
|
||||
readme = "README.md"
|
||||
repository = "https://gitflic.ru/project/armatik/ocab"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.11.6"
|
||||
aiogram = "^3.2.0"
|
||||
peewee = "^3.17.0"
|
||||
pyyaml = "^6.0.1"
|
||||
requests = "^2.31.0"
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
aiogram==3.1.1
|
||||
openai==0.27.8
|
||||
PyYAML==6.0.1
|
||||
198
src/OpenAI/GPT35turbo/OA_processing.py
Normal file
198
src/OpenAI/GPT35turbo/OA_processing.py
Normal file
@@ -0,0 +1,198 @@
|
||||
import sqlite3
|
||||
import os
|
||||
import configparser
|
||||
from openai import OpenAIError
|
||||
import time
|
||||
|
||||
mother_path = os.path.dirname(os.path.dirname(os.getcwd()))
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read(os.path.join(mother_path, 'src/config.ini'))
|
||||
|
||||
database = sqlite3.connect(os.path.join(mother_path, 'DataBase/OCAB_DB.db'))
|
||||
cursor = database.cursor()
|
||||
reply_ignore = config['Telegram']['reply_ignore'].split('| ')
|
||||
reply_ignore = list(map(int, reply_ignore))
|
||||
#print(reply_ignore)
|
||||
|
||||
min_token_for_answer = int(config['Openai']['min_token_for_answer'])
|
||||
|
||||
# Импорт библиотек
|
||||
|
||||
import openai
|
||||
max_token_count = int(config['Openai']['max_token_count'])
|
||||
|
||||
# Создание файла лога если его нет
|
||||
if not os.path.exists(os.path.join(mother_path, 'src/OpenAI/GPT35turbo/log.txt')):
|
||||
with open(os.path.join(mother_path, 'src/OpenAI/GPT35turbo/log.txt'), 'w') as log_file:
|
||||
log_file.write('')
|
||||
|
||||
|
||||
def openai_response(message_formated_text):
|
||||
# Запуск OpenAI
|
||||
# Считаем размер полученного текста
|
||||
#print(message_formated_text)
|
||||
count_length = 0
|
||||
if len(message_formated_text) == 0:
|
||||
message_formated_text = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Напиши короткий ответ говорящий что контекст сообщения слишком длинный и попроси задать вопрос отдельно без ответа на другие сообщения по ключевому слову"
|
||||
}
|
||||
]
|
||||
for message in message_formated_text:
|
||||
#print(message["content"])
|
||||
count_length += int(len(message["content"]))
|
||||
#print(count_length)
|
||||
try:
|
||||
response = openai.ChatCompletion.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=message_formated_text,
|
||||
max_tokens=max_token_count - count_length
|
||||
)
|
||||
except OpenAIError as ex:
|
||||
if 'on requests per min. Limit: 3 / min. Please try again' in str(ex):
|
||||
response = ('Извини мой процессор перегрелся, дай мне минутку отдохнуть')
|
||||
elif 'Bad gateway.' in str(ex):
|
||||
response = (
|
||||
'Ой, где я? Кажется кто то перерзал мой интернет кабель, подожди немного пока я его починю')
|
||||
#запись ошибки в лог с указанием времени и даты
|
||||
with open(os.path.join(mother_path, 'src/OpenAI/GPT35turbo/log.txt'), 'a') as log_file:
|
||||
log_file.write('\n' + time.strftime("%d.%m.%Y %H:%M:%S") + ' ' + str(ex))
|
||||
return response
|
||||
|
||||
def sort_message_from_user(message_formated_text, message_id):
|
||||
# print(int(*(
|
||||
# cursor.execute("SELECT message_sender FROM message_list WHERE message_id = ?", (message_id,)).fetchone())))
|
||||
if int(*(
|
||||
cursor.execute("SELECT message_sender FROM message_list WHERE message_id = ?",
|
||||
(message_id,)).fetchone())) == 0:
|
||||
message_formated_text.append({
|
||||
"role": "assistant",
|
||||
"content": str(*(cursor.execute("SELECT message_text FROM message_list WHERE message_id = ?",
|
||||
(message_id,)).fetchone()))
|
||||
})
|
||||
else:
|
||||
message_formated_text.append({
|
||||
"role": "user",
|
||||
"content": str(*(cursor.execute("SELECT message_text FROM message_list WHERE message_id = ?",
|
||||
(message_id,)).fetchone()))
|
||||
})
|
||||
#Проверка что длина всех сообщений в кортеже не превышает max_token_count-min_token_for_answer
|
||||
return message_formated_text
|
||||
|
||||
def openai_collecting_message(message_id, message_formated_text):
|
||||
# собирает цепочку сообщений для OpenAI длинной до max_token_count
|
||||
# проверяем что сообщение отвечает на другое сообщение
|
||||
#print(int(*(cursor.execute("SELECT answer_id FROM message_list WHERE message_id = ?", (message_id,)).fetchone())))
|
||||
#print(reply_ignore)
|
||||
if int(*(cursor.execute("SELECT answer_id FROM message_list WHERE message_id = ?", (message_id,)).fetchone())) not in reply_ignore:
|
||||
# Продолжаем искать ответы на сообщения
|
||||
#print(int(*(cursor.execute("SELECT answer_id FROM message_list WHERE message_id = ?", (message_id,)).fetchone())))
|
||||
message_formated_text = openai_collecting_message(int(*(cursor.execute("SELECT answer_id FROM message_list WHERE message_id = ?", (message_id,)).fetchone())), message_formated_text)
|
||||
#Проверяем ID отправителя сообщения, если 0 то это сообщение от бота
|
||||
sort_message_from_user(message_formated_text, message_id)
|
||||
else:
|
||||
# Проверяем ID отправителя сообщения, если 0 то это сообщение от бота
|
||||
sort_message_from_user(message_formated_text, message_id)
|
||||
return message_formated_text
|
||||
|
||||
|
||||
def openai_message_processing(message_id):
|
||||
#проверяем на наличие сообщения в базе данных
|
||||
if cursor.execute("SELECT message_text FROM message_list WHERE message_id = ?", (message_id,)).fetchone() is None:
|
||||
return None
|
||||
else:
|
||||
# проверяем на то что сообщение влезает в max_token_count с учётом message_formated_text
|
||||
message_formated_text = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": config['Openai']['story_model']
|
||||
}
|
||||
]
|
||||
if ((len(str(cursor.execute("SELECT message_text FROM message_list WHERE message_id")))) < (max_token_count - len(message_formated_text[0]['content']))):
|
||||
message_formated_text = openai_collecting_message(message_id, message_formated_text)
|
||||
count_length = 0
|
||||
# Обработка невозможности ответить на сообщение
|
||||
try:
|
||||
for message in message_formated_text:
|
||||
count_length += len(message['content'])
|
||||
while count_length > max_token_count - min_token_for_answer:
|
||||
message_formated_text.pop(1)
|
||||
count_length = 0
|
||||
for message in message_formated_text:
|
||||
count_length += len(message['content'])
|
||||
except IndexError:
|
||||
message_formated_text = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "Выведи сообщение об ошибке."
|
||||
}
|
||||
]
|
||||
response = openai_response(message_formated_text)
|
||||
return response
|
||||
else:
|
||||
return f"Сообщение слишком длинное, максимальная длина сообщения \
|
||||
{max_token_count - len(message_formated_text[0]['content'])} символов, укоротите его на \
|
||||
{len(str(cursor.execute('SELECT message_text FROM message_list WHERE message_id'))) - max_token_count} символов"
|
||||
|
||||
def openai_collecting_history_context(start_id, end_id, message_formated_text, message_id = 0):
|
||||
# собираем список сообщений для OpenAI длинной до 14500 символов начиная с end_id и заканчивая start_id
|
||||
if message_id == 0:
|
||||
message_id = end_id
|
||||
message_formated_text = openai_collecting_history_context(start_id, end_id, message_formated_text, message_id)
|
||||
elif message_id > start_id:
|
||||
message_formated_text = openai_collecting_history_context(start_id, end_id, message_formated_text, (message_id - 1))
|
||||
try:
|
||||
message_formated_text.append({
|
||||
"role": "user",
|
||||
"content": str(*(cursor.execute("SELECT message_text FROM message_list WHERE message_id = ?",
|
||||
(message_id,)).fetchone()))
|
||||
})
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return message_formated_text
|
||||
|
||||
def openai_message_history_processing(start_id, end_id):
|
||||
message_formated_text = []
|
||||
message_formated_text = openai_collecting_history_context(start_id, end_id, message_formated_text)
|
||||
|
||||
max_token_count_history = 15000
|
||||
min_token_for_answer_history = 1500
|
||||
count_length = 0
|
||||
try:
|
||||
for message in message_formated_text:
|
||||
count_length += len(message['content'])
|
||||
while count_length > max_token_count_history - min_token_for_answer_history:
|
||||
message_formated_text.pop(1)
|
||||
count_length = 0
|
||||
for message in message_formated_text:
|
||||
count_length += len(message['content'])
|
||||
except IndexError:
|
||||
message_formated_text = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "Выведи сообщение об ошибке."
|
||||
}
|
||||
]
|
||||
message_formated_text.append({
|
||||
"role": "user",
|
||||
"content": "Сделай пересказ всей истории сообщений без потери смысла от 3-его лица."
|
||||
})
|
||||
try:
|
||||
response = openai.ChatCompletion.create(
|
||||
model="gpt-3.5-turbo-16k",
|
||||
messages=message_formated_text,
|
||||
max_tokens=max_token_count_history - count_length
|
||||
)
|
||||
except OpenAIError as ex:
|
||||
if 'on requests per min. Limit: 3 / min. Please try again' in str(ex):
|
||||
response = ('Извини мой процессор перегрелся, дай мне минутку отдохнуть')
|
||||
elif 'Bad gateway.' in str(ex):
|
||||
response = (
|
||||
'Ой, где я? Кажется кто то перерзал мой интернет кабель, подожди немного пока я его починю')
|
||||
# запись ошибки в лог с указанием времени и даты
|
||||
with open(os.path.join(mother_path, 'src/OpenAI/GPT35turbo/log.txt'), 'a') as log_file:
|
||||
log_file.write('\n' + time.strftime("%d.%m.%Y %H:%M:%S") + ' ' + str(ex))
|
||||
return response
|
||||
8
src/TelegramBot/MessageHandler.py
Normal file
8
src/TelegramBot/MessageHandler.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# Получение сообщений в чате, и запись их в базу данных
|
||||
from aiogram import types
|
||||
|
||||
from src.TelegramBot.main import dp
|
||||
|
||||
from main import cursor, config
|
||||
|
||||
#импортировать функцию для обработки сообщений из OpenAI
|
||||
566
src/TelegramBot/main.py
Normal file
566
src/TelegramBot/main.py
Normal file
@@ -0,0 +1,566 @@
|
||||
# Импорт библиотек
|
||||
import os
|
||||
from contextlib import suppress
|
||||
|
||||
import openai
|
||||
import configparser
|
||||
import sqlite3
|
||||
import asyncio
|
||||
import sys
|
||||
import datetime
|
||||
from time import mktime
|
||||
|
||||
from aiogram import Bot, Dispatcher, executor, types
|
||||
from aiogram.contrib.fsm_storage.memory import MemoryStorage
|
||||
from aiogram.utils.exceptions import MessageCantBeDeleted, MessageToDeleteNotFound
|
||||
|
||||
mother_path = os.path.dirname(os.path.dirname(os.getcwd()))
|
||||
sys.path.insert(1, mother_path)
|
||||
|
||||
|
||||
# Импорт переменных из файла .ini
|
||||
config = configparser.ConfigParser()
|
||||
config.read(os.path.join(mother_path, 'src/config.ini'))
|
||||
TOKEN = config['Telegram']['token']
|
||||
OPENAI_API_KEY = (config['Openai']['api_key'])
|
||||
bot_trigger_front = (config['Telegram']['bot_trigger_front']).split('|')
|
||||
bot_trigger_all = (config['Telegram']['bot_trigger_all']).split('|')
|
||||
# удаление лишних элементов массивов
|
||||
bot_trigger_front.remove('')
|
||||
bot_trigger_all.remove('')
|
||||
DB_message_limit = int(config['DataBase']['message_limit'])
|
||||
|
||||
# Инициализация бота
|
||||
|
||||
bot = Bot(token=TOKEN)
|
||||
dp = Dispatcher(bot, storage=MemoryStorage())
|
||||
|
||||
# Инициализация API OpenAI
|
||||
|
||||
openai.api_key = OPENAI_API_KEY
|
||||
|
||||
# Инициализация базы данных OCAB_DB в папке DataBase/OCAB_DB.db
|
||||
# Создаём базу данных sqlite3 по пути /home/armatik/PycharmProjects/OpenChatAiBot/DataBase/OCAB_DB.db
|
||||
database = sqlite3.connect(os.path.join(mother_path, 'DataBase/OCAB_DB.db'))
|
||||
cursor = database.cursor()
|
||||
# Создаём таблицу chat_list
|
||||
cursor.execute("""CREATE TABLE IF NOT EXISTS chat_list (
|
||||
chat_id INTEGER PRIMARY KEY,
|
||||
chat_role INTEGER NOT NULL,
|
||||
chat_stats INTEGER NOT NULL
|
||||
)""")
|
||||
# Создаём таблицу message_list
|
||||
cursor.execute("""CREATE TABLE IF NOT EXISTS message_list (
|
||||
message_id INTEGER PRIMARY KEY,
|
||||
message_text TEXT NOT NULL,
|
||||
message_sender INTEGER NOT NULL,
|
||||
answer_id INTEGER
|
||||
)""")
|
||||
# Создаём таблицу user_list
|
||||
cursor.execute("""CREATE TABLE IF NOT EXISTS user_list (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
user_name TEXT NOT NULL,
|
||||
user_role INTEGER,
|
||||
user_stats INTEGER
|
||||
user_rep INTEGER
|
||||
)""")
|
||||
#запись информации о чате в базу данных
|
||||
|
||||
async def empty_role(id):
|
||||
cursor.execute("UPDATE user_list SET user_role = ? WHERE user_id = ?", (0, id))
|
||||
database.commit()
|
||||
|
||||
async def check(id):
|
||||
#проверка что у человека есть роль
|
||||
user_role = cursor.execute("SELECT user_role FROM user_list WHERE user_id = ?", (id,)).fetchone()[0]
|
||||
if user_role not in [0, 1, 2]:
|
||||
await empty_role(id)
|
||||
|
||||
|
||||
async def get_role_name(rolenum):
|
||||
rolenum = int(rolenum)
|
||||
if rolenum == 0:
|
||||
role = config['Roles']['user']
|
||||
elif rolenum == 1:
|
||||
role = config['Roles']['moderator']
|
||||
elif rolenum == 2:
|
||||
role = config['Roles']['admin']
|
||||
return role
|
||||
|
||||
async def get_role(id):
|
||||
#получение роли пользователя
|
||||
user_role = cursor.execute("SELECT user_role FROM user_list WHERE user_id = ?", (id,)).fetchone()[0]
|
||||
return await get_role_name(user_role)
|
||||
|
||||
|
||||
async def check_admin(id, chat_id):
|
||||
#Проверка что человек есть в списке администраторов чата
|
||||
chat_admins = await bot.get_chat_administrators(chat_id)
|
||||
flag = False
|
||||
for admin in chat_admins:
|
||||
if admin.user.id == id:
|
||||
flag = True
|
||||
return flag
|
||||
|
||||
|
||||
async def check_moderator(id):
|
||||
#Проверка что человек имеет роль модератора
|
||||
if cursor.execute("SELECT user_role FROM user_list WHERE user_id = ?", (id,)).fetchone()[0] >= 1:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
async def check_user(id):
|
||||
#Проверка что человек имеет роль пользователя
|
||||
if cursor.execute("SELECT user_role FROM user_list WHERE user_id = ?", (id,)).fetchone()[0] == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
async def save_message(message):
|
||||
#Сохранение сообщения в базу данных
|
||||
cursor.execute("INSERT INTO message_list VALUES (?, ?, ?, ?)", (message.message_id, message.text, message.from_user.id, 0))
|
||||
if message.reply_to_message is not None:
|
||||
cursor.execute("UPDATE message_list SET answer_id = ? WHERE message_id = ?", (message.reply_to_message.message_id, message.message_id))
|
||||
#Если отправитель не зарегистрирован в базе данных, то добавляем его
|
||||
if cursor.execute("SELECT user_id FROM user_list WHERE user_id = ?", (message.from_user.id,)).fetchone() is None:
|
||||
cursor.execute("INSERT INTO user_list VALUES (?, ?, ?, ?)", (message.from_user.id, message.from_user.username, 0, 1))
|
||||
#Добавляем статистику в чат и в данные пользователя, если пользователь не бот.
|
||||
cursor.execute("UPDATE chat_list SET chat_stats = chat_stats + 1 WHERE chat_id = ?", (message.chat.id,))
|
||||
cursor.execute("UPDATE user_list SET user_stats = user_stats + 1 WHERE user_id = ?", (message.from_user.id,))
|
||||
database.commit()
|
||||
|
||||
|
||||
async def time_to_seconds(time):
|
||||
#Конвертация текстового указания времени по типу 3h, 5m, 10s в минуты
|
||||
if time[-1] == 'd':
|
||||
return int(time[:-1])*86400
|
||||
elif time[-1] == 'h':
|
||||
return int(time[:-1])*3600
|
||||
elif time[-1] == 'm':
|
||||
return int(time[:-1])*60
|
||||
elif time[-1] == 's':
|
||||
return int(time[:-1])
|
||||
|
||||
|
||||
async def short_time_to_time(time):
|
||||
#Конвертация времени в длинное название
|
||||
if time[-1] == 'd':
|
||||
return str(f"{time[0:-1]} дней")
|
||||
elif time[-1] == 'h':
|
||||
return str(f"{time[0:-1]} часов")
|
||||
elif time[-1] == 'm':
|
||||
return str(f"{time[0:-1]} минут")
|
||||
elif time[-1] == 's':
|
||||
return str(f"{time[0:-1]} секунд")
|
||||
|
||||
|
||||
@dp.message_handler(commands=['mute'])
|
||||
async def mute(message: types.Message):
|
||||
#Проверка что отправитель является администратором чата
|
||||
try:
|
||||
if await check_moderator(message.from_user.id):
|
||||
#Проверка отвечает ли сообщение на другое сообщение
|
||||
if message.reply_to_message is not None:
|
||||
time = message.text.split(' ')[1]
|
||||
#получаем id отправителя сообщение на которое отвечает message
|
||||
target_id = message.reply_to_message.from_user.id
|
||||
target_name = cursor.execute("SELECT user_name FROM user_list WHERE user_id = ?", (target_id,)).fetchone()[0]
|
||||
#Проверка, что человек пользователь
|
||||
if cursor.execute("SELECT user_role FROM user_list WHERE user_id = ?", (target_id,)).fetchone()[0] == 0:
|
||||
#ограничения прав пользователя по отправке сообщений на time секунд
|
||||
time_sec = await time_to_seconds(message.text.split(' ')[1])
|
||||
if time_sec <= 30 or time_sec >= 31536000:
|
||||
await message.reply("Время мута должно быть больше 30 секунд и меньше 365 дней")
|
||||
return
|
||||
date_string = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
date_time = datetime.datetime.strptime(date_string, "%Y-%m-%d %H:%M:%S")
|
||||
unix_time = int(mktime(date_time.timetuple()))
|
||||
await bot.restrict_chat_member(message.chat.id, target_id, until_date=unix_time+time_sec, permissions=types.ChatPermissions(can_send_messages=False))
|
||||
await message.reply(
|
||||
f"Пользователь {target_name} замьючен на {await short_time_to_time(time)}")
|
||||
else:
|
||||
await message.reply(f"Пользователь [{target_name}](tg://user?id={target_id}) является {await get_role(target_id)} и не может быть замьючен",
|
||||
parse_mode='Markdown')
|
||||
return
|
||||
else:
|
||||
target_tag = message.text.split(' ')[1]
|
||||
target_tag = target_tag[1:]
|
||||
target_id = int(cursor.execute("SELECT user_id FROM user_list WHERE user_name = ?", (target_tag,)).fetchone()[0])
|
||||
target_name = cursor.execute("SELECT user_name FROM user_list WHERE user_id = ?", (target_id,)).fetchone()[0]
|
||||
#ограничения прав пользователя по отправке сообщений на time секунд
|
||||
time_mute = message.text.split(' ')[2]
|
||||
|
||||
if cursor.execute("SELECT user_role FROM user_list WHERE user_id = ?", (target_id,)).fetchone()[0] == 0:
|
||||
#ограничения прав пользователя по отправке сообщений на time секунд
|
||||
time_sec = await time_to_seconds(message.text.split(' ')[2])
|
||||
if time_sec <= 30 or time_sec >= 31536000:
|
||||
await message.reply("Время мута должно быть больше 30 секунд и меньше 365 дней")
|
||||
return
|
||||
date_string = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
date_time = datetime.datetime.strptime(date_string, "%Y-%m-%d %H:%M:%S")
|
||||
unix_time = int(mktime(date_time.timetuple()))
|
||||
await bot.restrict_chat_member(message.chat.id, target_id, until_date=unix_time+time_sec, permissions=types.ChatPermissions(can_send_messages=False))
|
||||
await message.reply(
|
||||
f"Пользователь {target_name} замьючен на {await short_time_to_time(time_mute)}.")
|
||||
else:
|
||||
await message.reply(f"Пользователь [{target_name}](tg://user?id={target_id}) является {await get_role(target_id)} и не может быть замьючен",
|
||||
parse_mode="Markdown")
|
||||
return
|
||||
except:
|
||||
await message.reply("Ошибка данных. Возможно пользователь ещё ничего не написал.")
|
||||
|
||||
|
||||
@dp.message_handler(commands=['unmute'])
|
||||
async def unmute(message: types.Message):
|
||||
chat_id= message.chat.id
|
||||
if await check_moderator(message.from_user.id):
|
||||
if message.reply_to_message is not None:
|
||||
target_id = message.reply_to_message.from_user.id
|
||||
target_name = cursor.execute("SELECT user_name FROM user_list WHERE user_id = ?", (target_id,)).fetchone()[0]
|
||||
await bot.restrict_chat_member(message.chat.id, target_id, until_date=0, permissions=types.ChatPermissions(can_send_messages=True))
|
||||
await message.reply(
|
||||
f"Пользователь [{target_name}](tg://user?id={target_id}) размьючен",
|
||||
parse_mode="Markdown")
|
||||
else:
|
||||
target_tag = message.text.split(' ')[1]
|
||||
target_tag = target_tag[1:]
|
||||
target_id = int(cursor.execute("SELECT user_id FROM user_list WHERE user_name = ?", (target_tag,)).fetchone()[0])
|
||||
target_name = cursor.execute("SELECT user_name FROM user_list WHERE user_id = ?", (target_id,)).fetchone()[0]
|
||||
#получаем стандартные права чата
|
||||
|
||||
if cursor.execute("SELECT user_role FROM user_list WHERE user_id = ?", (target_id,)).fetchone()[0] == 0:
|
||||
#возвращаем пользователю стандартные права как у новых пользователей
|
||||
chat_permisson = await bot.get_chat(chat_id)
|
||||
await bot.restrict_chat_member(message.chat.id, target_id, until_date=0, permissions=chat_permisson.permissions)
|
||||
await message.reply(
|
||||
f"Пользователь [{target_name}](tg://user?id={target_id}) размьючен",
|
||||
parse_mode="Markdown")
|
||||
|
||||
|
||||
#обрабатываем вход пользователя в чат
|
||||
@dp.message_handler(content_types=['new_chat_members'])
|
||||
async def new_chat_members(message: types.Message):
|
||||
#проверяем что пользователь не бот
|
||||
if not message.new_chat_members[0].is_bot:
|
||||
#Заносим пользователя в базу данных
|
||||
cursor.execute("INSERT INTO user_list VALUES (?, ?, ?, ?)", (message.new_chat_members[0].id, message.new_chat_members[0].username, 0, 0))
|
||||
|
||||
|
||||
@dp.message_handler(commands=['chatinfo'])
|
||||
async def chat_info(message: types.Message):
|
||||
#Выводит информацию о чате. Название, количество пользователей, количество администраторов, количество модераторов, количество сообщений.
|
||||
if await check_moderator(message.from_user.id):
|
||||
chat_id = message.chat.id
|
||||
chat_title = message.chat.title
|
||||
chat_members = await bot.get_chat_members_count(chat_id)
|
||||
chat_admins = await bot.get_chat_administrators(chat_id)
|
||||
chat_moderators = cursor.execute("SELECT COUNT(user_id) FROM user_list WHERE user_role = 1").fetchone()[0]
|
||||
chat_messages = cursor.execute("SELECT chat_stats FROM chat_list WHERE chat_id = ?", (chat_id,)).fetchone()[0]
|
||||
await message.reply(
|
||||
f"<b>Название чата:</b> {chat_title}\n"
|
||||
f"<b>Количество пользователей:</b> {chat_members}\n"
|
||||
f"<b>Количество администраторов:</b> {len(chat_admins)}\n"
|
||||
f"<b>Количество модераторов:</b> {chat_moderators}\n"
|
||||
f"<b>Количество сообщений:</b> {chat_messages}",
|
||||
parse_mode="HTML")
|
||||
await top10(message)
|
||||
else:
|
||||
await message.reply(
|
||||
f"У вас недостаточно прав для выполнения этой команды.")
|
||||
|
||||
@dp.message_handler(commands=['start'])
|
||||
async def start(message: types.Message):
|
||||
#проверка что чат есть в базе данных
|
||||
if cursor.execute("SELECT chat_id FROM chat_list WHERE chat_id = ?", (message.chat.id,)).fetchone() is None:
|
||||
chat_id = message.chat.id
|
||||
chat_role = 0
|
||||
chat_stats = 1
|
||||
cursor.execute("INSERT INTO chat_list VALUES (?, ?, ?)", (chat_id, chat_role, chat_stats))
|
||||
database.commit()
|
||||
await message.reply(
|
||||
f"{config['Telegram']['start_answer']}",
|
||||
parse_mode="Markdown")
|
||||
else:
|
||||
await message.reply(
|
||||
f"Чат уже инициализирован.",
|
||||
parse_mode="Markdown")
|
||||
# Проверка наличия столбца имени пользователя в базе данных, если его нет, то добавить.
|
||||
try:
|
||||
cursor.execute("SELECT user_name FROM user_list")
|
||||
except sqlite3.OperationalError:
|
||||
cursor.execute("ALTER TABLE user_list ADD COLUMN user_name TEXT")
|
||||
database.commit()
|
||||
await message.reply("База данных пользователей реструктурирована.")
|
||||
# Проверка наличия столбца репутации пользователя в базе данных, если его нет, то добавить.
|
||||
try:
|
||||
cursor.execute("SELECT user_rep FROM user_list")
|
||||
except sqlite3.OperationalError:
|
||||
cursor.execute("ALTER TABLE user_list ADD COLUMN user_rep INTEGER")
|
||||
database.commit()
|
||||
await message.reply("База данных пользователей реструктурирована.")
|
||||
|
||||
|
||||
|
||||
@dp.message_handler(commands=['top10'])
|
||||
async def top10(message: types.Message):
|
||||
#топ 10 пользователей по количеству сообщений в user_stats в формате: Имя пользователя - количество сообщений
|
||||
top10 = cursor.execute("SELECT user_id, user_stats FROM user_list ORDER BY user_stats DESC LIMIT 10").fetchall()
|
||||
top10_message = ''
|
||||
for user in top10:
|
||||
username = (cursor.execute("SELECT user_name FROM user_list WHERE user_id = ?", (user[0],)).fetchone())[0]
|
||||
if username is None:
|
||||
username = "Аноним"
|
||||
top10_message += f"{username} - {user[1]}\n"
|
||||
#в начале сообщения берём текст из config.ini и вставляем в него топ 10 пользователей
|
||||
await message.reply(
|
||||
f"{config['Telegram']['top10_answer']}\n{top10_message}")
|
||||
|
||||
|
||||
@dp.message_handler(commands=['aboutme'])
|
||||
async def aboutme(message: types.Message):
|
||||
your_id = message.from_id
|
||||
await check(your_id)
|
||||
your_name = message.from_user.username
|
||||
user_stats = cursor.execute("SELECT user_stats FROM user_list WHERE user_id = ?", (message.from_user.id,)).fetchone()
|
||||
rolenum = cursor.execute("SELECT user_role FROM user_list WHERE user_id = ?", (message.from_user.id,)).fetchone()
|
||||
user_rep = cursor.execute("SELECT user_rep FROM user_list WHERE user_id = ?", (message.from_user.id,)).fetchone()
|
||||
if "None" in str(user_rep):
|
||||
user_rep = 0
|
||||
cursor.execute("UPDATE user_list SET user_rep = ? WHERE user_id = ?", (user_rep, your_id))
|
||||
role = await get_role_name(rolenum[0])
|
||||
#Имя пользователя на следующей строке статистика сообщений на следующей строке права пользователя
|
||||
#если пользователь есть в списке администраторов чата, то выдаём роль администратор
|
||||
chat_admins = await bot.get_chat_administrators(message.chat.id)
|
||||
default_for_admin = int(config['Roles']['default_for_admin'])
|
||||
no_answer = False
|
||||
if rolenum[0] < default_for_admin:
|
||||
for admin in chat_admins:
|
||||
if admin.user.id == your_id:
|
||||
if default_for_admin == 0:
|
||||
await message.reply(
|
||||
f"<b>Вы обнаружены в списке администраторов чата!</b> \n"
|
||||
f"<b>Вам будет выдана роль:</b> {config['Roles']['user']}\n"
|
||||
f"<b>Имя:</b> <a href=\"tg://user?id={str(your_id)}\">{your_name}</a>\n"
|
||||
f"<b>Кол-во сообщений:</b> {user_stats[0]}\n"
|
||||
f"<b>Репутация:</b> {user_rep[0]}\n"
|
||||
f"<b>Роль:</b> {role}",
|
||||
parse_mode="HTML")
|
||||
no_answer = True
|
||||
elif default_for_admin == 1:
|
||||
await message.reply(
|
||||
f"<b>Вы обнаружены в списке администраторов чата!</b> \n"
|
||||
f"<b>Вам будет выдана роль:</b> {config['Roles']['moderator']}\n"
|
||||
f"<b>Имя:</b> <a href=\"tg://user?id={str(your_id)}\">{your_name}</a>\n"
|
||||
f"<b>Кол-во сообщений:</b> {user_stats[0]}\n"
|
||||
f"<b>Репутация:</b> {user_rep[0]}\n"
|
||||
f"<b>Роль:</b> {role}",
|
||||
parse_mode="HTML")
|
||||
no_answer = True
|
||||
elif default_for_admin == 2:
|
||||
await message.reply(
|
||||
f"<b>Вы обнаружены в списке администраторов чата!</b> \n"
|
||||
f"<b>Вам будет выдана роль:</b> {config['Roles']['admin']}\n"
|
||||
f"<b>Имя:</b> <a href=\"tg://user?id={str(your_id)}\">{your_name}</a>\n"
|
||||
f"<b>Кол-во сообщений:</b> {user_stats[0]}\n"
|
||||
f"<b>Репутация:</b> {user_rep[0]}\n"
|
||||
f"<b>Роль:</b> {role}",
|
||||
parse_mode="HTML")
|
||||
no_answer = True
|
||||
cursor.execute("UPDATE user_list SET user_role = ? WHERE user_id = ?", (default_for_admin, your_id))
|
||||
database.commit()
|
||||
if no_answer == False:
|
||||
await message.reply(
|
||||
f"<b>Имя:</b> <a href=\"tg://user?id={str(your_id)}\">{your_name}</a>\n"
|
||||
f"<b>Кол-во сообщений:</b> {user_stats[0]}\n"
|
||||
f"<b>Репутация:</b> {user_rep[0]}\n"
|
||||
f"<b>Роль:</b> {role}",
|
||||
parse_mode="HTML")
|
||||
|
||||
@dp.message_handler(commands=['setrole'])
|
||||
async def setrole(message: types.Message):
|
||||
try:
|
||||
user_role = cursor.execute("SELECT user_role FROM user_list WHERE user_id = ?", (message.from_user.id,)).fetchone()
|
||||
user_name = message.from_user.username
|
||||
target_name = message.text.split()[1]
|
||||
target_name = target_name[1:]
|
||||
target_role = cursor.execute("SELECT user_role FROM user_list WHERE user_name = ?", (target_name,)).fetchone()[0]
|
||||
user_id = cursor.execute("SELECT user_id FROM user_list WHERE user_name = ?", (target_name,)).fetchone()
|
||||
user_id = user_id[0]
|
||||
except:
|
||||
await message.reply("Пользователь не найден!")
|
||||
return
|
||||
if await check_admin(user_id, message.chat.id) and target_role == 2:
|
||||
await message.reply("Вы не можете изменить роль этому пользователю!")
|
||||
else:
|
||||
if user_role[0] == 2:
|
||||
try:
|
||||
#Получаем id пользователя по нику в базе данных
|
||||
role = message.text.split()[2]
|
||||
if role == "0" or role == config['Roles']['user']:
|
||||
role = config['Roles']['user']
|
||||
rolenum = 0
|
||||
elif role == "1" or role == config['Roles']['moderator']:
|
||||
role = config['Roles']['moderator']
|
||||
rolenum = 1
|
||||
elif role == "2" or role == config['Roles']['admin']:
|
||||
role = config['Roles']['admin']
|
||||
rolenum = 2
|
||||
cursor.execute("UPDATE user_list SET user_role = ? WHERE user_id = ?", (rolenum, user_id))
|
||||
database.commit()
|
||||
await message.reply(
|
||||
f"Пользователю [{target_name}](tg://user?id={str(user_id)}) выдана роль: {role}",
|
||||
parse_mode="Markdown")
|
||||
except:
|
||||
await message.reply(
|
||||
f"Ошибка! Проверьте правильность написания команды.",
|
||||
parse_mode="Markdown")
|
||||
else:
|
||||
await message.reply(
|
||||
f"Ошибка! У вас нет прав на использование этой команды.",
|
||||
parse_mode="Markdown")
|
||||
|
||||
|
||||
@dp.message_handler(commands=['about'])
|
||||
async def about(message: types.Message):
|
||||
# проверка что в сообщении есть тег пользователя
|
||||
if len(message.text.split()) == 2:
|
||||
try:
|
||||
target_name = message.text.split()[1]
|
||||
target_name = target_name[1:]
|
||||
target_id = cursor.execute("SELECT user_id FROM user_list WHERE user_name = ?", (target_name,)).fetchone()
|
||||
target_id = target_id[0]
|
||||
user_rep = cursor.execute("SELECT user_rep FROM user_list WHERE user_id = ?", (target_id,)).fetchone()[0]
|
||||
if "None" in str(user_rep):
|
||||
user_rep = 0
|
||||
cursor.execute("UPDATE user_list SET user_rep = ? WHERE user_id = ?", (user_rep, target_id))
|
||||
await check(target_id)
|
||||
except:
|
||||
await message.reply(
|
||||
f"Ошибка! Проверьте правильность написания тега пользователя.",
|
||||
parse_mode="Markdown")
|
||||
user_stats = cursor.execute("SELECT user_stats FROM user_list WHERE user_id = ?", (target_id,)).fetchone()[0]
|
||||
user_role = cursor.execute("SELECT user_role FROM user_list WHERE user_id = ?", (target_id,)).fetchone()[0]
|
||||
user_role = await get_role_name(user_role)
|
||||
await message.reply(
|
||||
f"<b>Имя:</b> {target_name}\n"
|
||||
f"<b>Кол-во сообщений:</b> {user_stats}\n"
|
||||
f"<b>Репутация:</b> {user_rep}\n"
|
||||
f"<b>Роль:</b> {user_role}", parse_mode="HTML")
|
||||
else:
|
||||
await message.reply(
|
||||
f"Ошибка! Проверьте правильность написания команды.")
|
||||
|
||||
from src.OpenAI.GPT35turbo.OA_processing import openai_message_history_processing
|
||||
|
||||
@dp.message_handler(commands=['history'])
|
||||
async def history(message: types.Message):
|
||||
if message.reply_to_message is not None:
|
||||
# Получаем id сообщения на которое отвечает message
|
||||
start_message_id = message.reply_to_message.message_id
|
||||
# Получаем id сообщения message
|
||||
end_message_id = message.message_id
|
||||
elif message.text.split()[1] != '':
|
||||
end_message_id = message.message_id
|
||||
start_message_id = end_message_id - int(message.text.split()[1])
|
||||
else:
|
||||
await message.reply("Ошибка! Проверьте правильность написания команды.")
|
||||
return
|
||||
temp_message = await message.reply("Подождите немного, я обрабатываю историю сообщений... Функция ЭКСПЕРЕМЕНТАЛЬНАЯ "
|
||||
"и может работать некорректно.")
|
||||
# Получаем историю сообщений
|
||||
response = openai_message_history_processing(start_message_id, end_message_id)
|
||||
if response is None:
|
||||
await message.reply("Я не понял тебя, попробуй перефразировать")
|
||||
else:
|
||||
bot_message_id = await message.reply(response['choices'][0]['message']['content'], parse_mode="markdown")
|
||||
# заносим сообщение в базу данных в качестве message_id мы пишем id сообщения в bot_message_id
|
||||
cursor.execute("INSERT INTO message_list VALUES (?, ?, ?, ?)",
|
||||
(bot_message_id.message_id, response['choices'][0]['message']['content'], 0, message.message_id))
|
||||
# очищаем сообщение
|
||||
asyncio.create_task(delete_message(temp_message, 0))
|
||||
|
||||
from src.OpenAI.GPT35turbo.OA_processing import openai_message_processing
|
||||
|
||||
|
||||
async def delete_message(message: types.Message, sleep_time: int = 0):
|
||||
await asyncio.sleep(sleep_time)
|
||||
with suppress(MessageCantBeDeleted, MessageToDeleteNotFound):
|
||||
await message.delete()
|
||||
|
||||
@dp.message_handler()
|
||||
async def in_message(message: types.Message):
|
||||
chat_id = message.chat.id
|
||||
# Получение сообщений в чате, и запись их в базу данных
|
||||
if (message.chat.type == "private" or message.chat.type == "channel"):
|
||||
await message.reply(
|
||||
f"{config['Telegram']['private_answer']}",
|
||||
parse_mode="Markdown")
|
||||
elif (message.chat.type != "group" or message.chat.type != "supergroup") and \
|
||||
message.text != '' and message.text != ' ' and \
|
||||
(cursor.execute("SELECT chat_role FROM chat_list WHERE chat_id;") == 1): return None
|
||||
else:
|
||||
# Запись сообщения в базу данных
|
||||
await save_message(message)
|
||||
# Обработка сообщения OpenAI
|
||||
send_answer = False
|
||||
typing_mode = False
|
||||
# импортируем массив триггеров из файла .ini
|
||||
if message.reply_to_message and message.reply_to_message.from_user.id == (await bot.me).id:
|
||||
send_answer = True
|
||||
typing_mode = True
|
||||
for trigger in bot_trigger_all:
|
||||
if trigger.lower() in message.text.lower():
|
||||
send_answer = True
|
||||
typing_mode = False
|
||||
for trigger in bot_trigger_front:
|
||||
if message.text.lower().startswith(trigger.lower()):
|
||||
send_answer = True
|
||||
typing_mode = False
|
||||
|
||||
if send_answer == False:
|
||||
# Если сообщение отвечает на другое сообщение, то проверяем наличие слова спасибо
|
||||
if message.reply_to_message is not None:
|
||||
if message.text.startswith("Спасибо"):
|
||||
target_id = message.reply_to_message.from_user.id
|
||||
target_name = cursor.execute("SELECT user_name FROM user_list WHERE user_id = ?", (target_id,)).fetchone()[0]
|
||||
cursor.execute("UPDATE user_list SET user_rep = user_rep + 1 WHERE user_id = ?", (target_id,))
|
||||
database.commit()
|
||||
await message.reply(
|
||||
f"Спасибо, [{target_name}](tg://user?id={str(target_id)}) за ваш ответ!",
|
||||
parse_mode="Markdown")
|
||||
|
||||
if send_answer:
|
||||
if typing_mode is False:
|
||||
your_id = message.from_id
|
||||
your_name = message.from_user.username
|
||||
temp_msg = await message.reply(
|
||||
f"[{your_name}](tg://user?id={str(your_id)}), Подожди немного и я обязательно отвечу тебе!",
|
||||
parse_mode="Markdown")
|
||||
# Пишем что бот печатает
|
||||
await bot.send_chat_action(message.chat.id, "typing")
|
||||
# Получаем ответ от OpenAI
|
||||
response = openai_message_processing(message.message_id)
|
||||
if response is None:
|
||||
bot_message_id = await message.reply("Я не понял тебя, попробуй перефразировать")
|
||||
if typing_mode is False:
|
||||
asyncio.create_task(delete_message(temp_msg, 0))
|
||||
# заносим сообщение в базу данных в качестве message_id пишем id сообщения которое отправил бот
|
||||
cursor.execute("INSERT INTO message_list VALUES (?, ?, ?, ?)",
|
||||
(bot_message_id, "Я не понял тебя, попробуй перефразировать", 0, message.message_id))
|
||||
else:
|
||||
bot_message_id = await message.reply(response['choices'][0]['message']['content'], parse_mode="markdown")
|
||||
if typing_mode is False:
|
||||
asyncio.create_task(delete_message(temp_msg, 0))
|
||||
# заносим сообщение в базу данных в качестве message_id мы пишем id сообщения в bot_message_id
|
||||
cursor.execute("INSERT INTO message_list VALUES (?, ?, ?, ?)",
|
||||
(bot_message_id.message_id, response['choices'][0]['message']['content'], 0, message.message_id))
|
||||
# очищаем базу данных от старых сообщений
|
||||
cursor.execute("DELETE FROM message_list WHERE message_id < ?", (bot_message_id.message_id - DB_message_limit,))
|
||||
database.commit()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
executor.start_polling(dp, skip_updates=True)
|
||||
@@ -1,2 +0,0 @@
|
||||
import src.service
|
||||
import src.core
|
||||
43
src/config.ini
Normal file
43
src/config.ini
Normal file
@@ -0,0 +1,43 @@
|
||||
[Telegram]
|
||||
token=****
|
||||
admin_password="Test_pass"
|
||||
# Пока не используется
|
||||
|
||||
# Массивы заполнять через запятую. Пример: test|test2 |test3,
|
||||
bot_trigger_front=
|
||||
# Живой пример: Арма |Армат |Арма, |
|
||||
bot_trigger_all=
|
||||
# Живой пример: @arma_ai_bot |помогите |
|
||||
private_answer=
|
||||
# Живой пример: Я не понимаю тебя, но я могу поговорить с тобой в группе [название группы](https://t.me/) и ещё в некоторых других группах
|
||||
reply_ignore=0
|
||||
# По умолчанию: 0
|
||||
# Содержит в себе все id топиков чата для чатов с форумным типом, если не заполнить контекст бота СЛОМАЕТСЯ!
|
||||
# Пример заполнения для одного из чатов: 0| 643885| 476959| 1| 476977| 633077| 630664| 476966| 634567
|
||||
start_answer= Привет! Я OCAB, открытый чат бот с ИИ для вашего чата!
|
||||
top10_answer= Вот топ 10 самых разговорчивых пользователей чата:
|
||||
|
||||
[Roles]
|
||||
user= Пользователь
|
||||
moderator= Модератор
|
||||
admin= Администратор
|
||||
default_for_admin= 2
|
||||
|
||||
|
||||
[Openai]
|
||||
api_key=****
|
||||
chat_model=gpt-3.5-turbo
|
||||
story_model=
|
||||
# Тут должен быть текст истории бота, но я его не показываю)))
|
||||
max_token_count=4000
|
||||
# максимальное количество токенов в сообщении
|
||||
min_token_for_answer=800
|
||||
# минимальное количество токенов в сообщении ответа бота (чем больше, тем более длинный ответ ГАРАНТИРОВАН)
|
||||
|
||||
[DataBase]
|
||||
message_limit=3000
|
||||
# Максимальное количество сообщений в базе данных
|
||||
|
||||
[AI_Dungeon]
|
||||
use=openai
|
||||
# "openai" or "YaGPT" Пока не используется
|
||||
23
src/config.yaml
Normal file
23
src/config.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
TELEGRAM:
|
||||
TOKEN: 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890
|
||||
PRIVATE_ANSWER:
|
||||
|
||||
BOT:
|
||||
ANSWER:
|
||||
HELP_ANSWER:
|
||||
START_ANSWER:
|
||||
ABOUT_ANSWER:
|
||||
|
||||
ROLES:
|
||||
ADMIN:
|
||||
MODERATOR:
|
||||
USER:
|
||||
DEFAULT_FOR_ADMIN:
|
||||
|
||||
DATABASE:
|
||||
MESSAGE_LIMIT:
|
||||
|
||||
GPT_MODULE:
|
||||
TRIGGER_FRONT:
|
||||
TRIGGER_ALL:
|
||||
REPLY_IGNORE:
|
||||
@@ -1,13 +0,0 @@
|
||||
TELEGRAM:
|
||||
TOKEN:
|
||||
|
||||
YANDEXGPT:
|
||||
TOKEN:
|
||||
CATALOGID:
|
||||
PROMPT:
|
||||
|
||||
ROLES:
|
||||
ADMIN: 0
|
||||
MODERATOR: 1
|
||||
USER: 2
|
||||
BOT: 3
|
||||
@@ -1,21 +0,0 @@
|
||||
TELEGRAM:
|
||||
TOKEN: xxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
APPROVED_CHAT_ID: "-123456789 | -012345678"
|
||||
ADMINCHATID: -12345678
|
||||
DEFAULT_CHAT_TAG: "@alt_gnome_chat"
|
||||
CHECK_BOT: True
|
||||
|
||||
YANDEXGPT:
|
||||
TOKEN: xxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
TOKEN_FOR_REQUEST: 8000
|
||||
TOKEN_FOR_ANSWER: 2000
|
||||
CATALOGID: xxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
PROMPT: "Ты чат-бот ..."
|
||||
STARTWORD: "Бот| Бот, | бот | бот,"
|
||||
INWORD: "помогите | не работает"
|
||||
|
||||
ROLES:
|
||||
ADMIN: 2
|
||||
MODERATOR: 1
|
||||
USER: 0
|
||||
BOT: 3
|
||||
@@ -1,24 +0,0 @@
|
||||
import os
|
||||
import time
|
||||
|
||||
async def check_log_file():
|
||||
# Проверка наличия файла для логов в формате log-dd-mm-yyyy.log
|
||||
# Если файл существует, то pass
|
||||
# Если файл не существует, то создаём его. файл лежит в директории src.core.log
|
||||
current_data = time.strftime("%d-%m-%Y")
|
||||
log_file = os.path.join(os.path.dirname(__file__), "log/", f"log-{current_data}.log")
|
||||
if not os.path.exists(log_file):
|
||||
with open(log_file, 'w') as file:
|
||||
file.write("Log file created\n")
|
||||
file.close()
|
||||
else:
|
||||
pass
|
||||
|
||||
async def log(message):
|
||||
await check_log_file()
|
||||
current_data = time.strftime("%d-%m-%Y")
|
||||
log_file = os.path.join(os.path.dirname(__file__), "log/", f"log-{current_data}.log")
|
||||
# print(log_file)
|
||||
with open(log_file, 'a') as file:
|
||||
file.write(f"{time.strftime('%H:%M:%S')} {message}\n")
|
||||
file.close()
|
||||
@@ -1,26 +0,0 @@
|
||||
from routers import include_routers
|
||||
from src.modules.standard.config.config import get_telegram_token
|
||||
from src.modules.standard.database.db_api import connect_database, create_tables
|
||||
|
||||
from asyncio import run
|
||||
from aiogram import Bot, Dispatcher
|
||||
|
||||
|
||||
async def main(bot: Bot):
|
||||
try:
|
||||
database, path = connect_database()
|
||||
database.connect()
|
||||
create_tables(database)
|
||||
|
||||
dp = Dispatcher()
|
||||
await include_routers(dp)
|
||||
await dp.start_polling(bot)
|
||||
|
||||
finally:
|
||||
await bot.session.close()
|
||||
database.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
bot = Bot(token=get_telegram_token())
|
||||
run(main(bot))
|
||||
@@ -1,18 +0,0 @@
|
||||
from aiogram import Dispatcher, Router, F, Bot
|
||||
from aiogram.types import Message
|
||||
|
||||
from src.modules.standard.info.routers import router as info_router
|
||||
from src.modules.standard.admin.routers import router as admin_router
|
||||
from src.modules.standard.message_processing.message_api import router as process_message
|
||||
from src.modules.standard.welcome.routers import router as welcome_router
|
||||
|
||||
|
||||
async def include_routers(dp: Dispatcher):
|
||||
"""
|
||||
Подключение роутеров в бота
|
||||
dp.include_router()
|
||||
"""
|
||||
dp.include_router(info_router)
|
||||
dp.include_router(admin_router)
|
||||
dp.include_router(process_message)
|
||||
|
||||
21
src/modules/Config.py
Normal file
21
src/modules/Config.py
Normal file
@@ -0,0 +1,21 @@
|
||||
import os
|
||||
|
||||
from yaml import load, Loader
|
||||
|
||||
|
||||
class Config:
|
||||
def __init__(self):
|
||||
self.bot_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.getcwd())))
|
||||
self.config_dir = os.path.join(self.bot_dir, 'config.yaml')
|
||||
|
||||
def get_config(self):
|
||||
return load(open(self.config_dir), Loader=Loader)
|
||||
|
||||
def get_bot_token(self):
|
||||
return self.get_config()['BOT']['TOKEN']
|
||||
|
||||
def get_roles(self):
|
||||
return self.get_config()['ROLES']
|
||||
|
||||
def get_bot_answers(self):
|
||||
return self.get_config()['BOT']['ANSWER']
|
||||
131
src/modules/DataBase.py
Normal file
131
src/modules/DataBase.py
Normal file
@@ -0,0 +1,131 @@
|
||||
import os
|
||||
import sqlite3
|
||||
|
||||
|
||||
class DataBase:
|
||||
def __init__(self):
|
||||
self.bot_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.getcwd())))
|
||||
self.db = sqlite3.connect(os.path.join(self.bot_dir, 'DataBase/OCAB_DB.db'))
|
||||
self.cursor = self.db.cursor()
|
||||
|
||||
# Проверки наличия таблиц в базе данных
|
||||
def check_db(self):
|
||||
self.check_chatdb()
|
||||
self.check_userdb()
|
||||
self.check_messagedb()
|
||||
|
||||
def check_chatdb(self):
|
||||
self.cursor.execute("""SELECT name FROM sqlite_master WHERE type='table' AND name='chat_list'""")
|
||||
if self.cursor.fetchone() is None:
|
||||
self.create_chatdb()
|
||||
|
||||
def check_userdb(self):
|
||||
self.cursor.execute("""SELECT name FROM sqlite_master WHERE type='table' AND name='user_list'""")
|
||||
if self.cursor.fetchone() is None:
|
||||
self.create_userdb()
|
||||
|
||||
def check_messagedb(self):
|
||||
self.cursor.execute("""SELECT name FROM sqlite_master WHERE type='table' AND name='message_list'""")
|
||||
if self.cursor.fetchone() is None:
|
||||
self.create_messagedb()
|
||||
|
||||
# Создание таблиц в базе данных
|
||||
def create_chatdb(self):
|
||||
self.cursor.execute("""CREATE TABLE IF NOT EXISTS chat_list (
|
||||
chat_id INTEGER PRIMARY KEY,
|
||||
chat_role INTEGER NOT NULL,
|
||||
chat_stats INTEGER NOT NULL,
|
||||
chat_federation INTEGER,
|
||||
)""")
|
||||
self.db.commit()
|
||||
|
||||
def create_userdb(self):
|
||||
self.cursor.execute("""CREATE TABLE IF NOT EXISTS user_list (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
user_name TEXT NOT NULL,
|
||||
user_tag TEXT,
|
||||
user_role INTEGER,
|
||||
user_stats INTEGER
|
||||
user_rep INTEGER
|
||||
)""")
|
||||
self.db.commit()
|
||||
|
||||
def create_messagedb(self):
|
||||
self.cursor.execute("""CREATE TABLE IF NOT EXISTS message_list (
|
||||
message_id INTEGER PRIMARY KEY,
|
||||
message_text TEXT NOT NULL,
|
||||
message_id_sender INTEGER NOT NULL,
|
||||
answer_to_id INTEGER
|
||||
)""")
|
||||
self.db.commit()
|
||||
|
||||
async def add_chat(self, chat_id, chat_role, chat_stats=0, chat_federation=0):
|
||||
self.cursor.execute("""INSERT INTO chat_list VALUES (?, ?, ?, ?)""",
|
||||
(chat_id, chat_role, chat_stats, chat_federation))
|
||||
|
||||
async def add_user(self, user_id, user_name, user_tag=None, user_role=0, user_stats=0, user_rep=0):
|
||||
self.cursor.execute("""INSERT INTO user_list VALUES (?, ?, ?, ?, ?, ?)""",
|
||||
(user_id, user_name, user_tag, user_role, user_stats, user_rep))
|
||||
self.db.commit()
|
||||
|
||||
async def add_message(self, message_id, message_text, message_sender, answer_id):
|
||||
self.cursor.execute("""INSERT INTO message_list VALUES (?, ?, ?, ?)""",
|
||||
(message_id, message_text, message_sender, answer_id))
|
||||
self.db.commit()
|
||||
|
||||
async def get_chat(self, chat_id):
|
||||
self.cursor.execute("""SELECT * FROM chat_list WHERE chat_id=?""", (chat_id,))
|
||||
return self.cursor.fetchone()
|
||||
|
||||
async def get_user(self, user_id):
|
||||
self.cursor.execute("""SELECT * FROM user_list WHERE user_id=?""", (user_id,))
|
||||
return self.cursor.fetchone()
|
||||
|
||||
async def get_user_role(self, user_id):
|
||||
self.cursor.execute("""SELECT user_role FROM user_list WHERE user_id=?""", (user_id,))
|
||||
return self.cursor.fetchone()
|
||||
|
||||
async def get_message(self, message_id):
|
||||
self.cursor.execute("""SELECT * FROM message_list WHERE message_id=?""", (message_id,))
|
||||
return self.cursor.fetchone()
|
||||
|
||||
async def change_user_name(self, user_id, new_user_name):
|
||||
self.cursor.execute("""UPDATE user_list SET user_name=? WHERE user_id=?""", (new_user_name, user_id))
|
||||
self.db.commit()
|
||||
|
||||
async def change_user_role(self, user_id, new_user_role):
|
||||
self.cursor.execute("""UPDATE user_list SET user_role=? WHERE user_id=?""", (new_user_role, user_id))
|
||||
self.db.commit()
|
||||
|
||||
async def change_user_stats(self, user_id, new_user_stats):
|
||||
self.cursor.execute("""UPDATE user_list SET user_stats=? WHERE user_id=?""", (new_user_stats, user_id))
|
||||
self.db.commit()
|
||||
|
||||
async def change_user_rep(self, user_id, new_user_rep):
|
||||
self.cursor.execute("""UPDATE user_list SET user_rep=? WHERE user_id=?""", (new_user_rep, user_id))
|
||||
self.db.commit()
|
||||
|
||||
async def change_chat_role(self, chat_id, new_chat_role):
|
||||
self.cursor.execute("""UPDATE chat_list SET chat_role=? WHERE chat_id=?""", (new_chat_role, chat_id))
|
||||
self.db.commit()
|
||||
|
||||
async def change_chat_stats(self, chat_id, new_chat_stats):
|
||||
self.cursor.execute("""UPDATE chat_list SET chat_stats=? WHERE chat_id=?""", (new_chat_stats, chat_id))
|
||||
self.db.commit()
|
||||
|
||||
async def change_chat_federation(self, chat_id, new_chat_federation):
|
||||
self.cursor.execute("""UPDATE chat_list SET chat_federation=? WHERE chat_id=?""",
|
||||
(new_chat_federation, chat_id))
|
||||
self.db.commit()
|
||||
|
||||
async def update_user_stats(self):
|
||||
self.cursor.execute("""UPDATE user_list SET user_stats=user_stats+1""")
|
||||
self.db.commit()
|
||||
|
||||
async def update_user_rep(self):
|
||||
self.cursor.execute("""UPDATE user_list SET user_rep=user_rep+1""")
|
||||
self.db.commit()
|
||||
|
||||
async def update_chat_stats(self):
|
||||
self.cursor.execute("""UPDATE chat_list SET chat_stats=chat_stats+1""")
|
||||
self.db.commit()
|
||||
33
src/modules/Roles.py
Normal file
33
src/modules/Roles.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from Config import Config
|
||||
from DataBase import DataBase
|
||||
|
||||
|
||||
class Roles:
|
||||
def __init__(self):
|
||||
self.DB = DataBase()
|
||||
self.Config = Config()
|
||||
self.user_role_name = self.Config.get_roles()['USER']
|
||||
self.moderator_role_name = self.Config.get_roles()['MODERATOR']
|
||||
self.admin_role_name = self.Config.get_roles()['ADMIN']
|
||||
|
||||
async def check_admin_permission(self, user_id):
|
||||
if await self.DB.get_user_role(user_id) == 2:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
async def check_moderator_permission(self, user_id):
|
||||
if await self.DB.get_user_role(user_id) == 1:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
async def get_role_name(self, role_number):
|
||||
if role_number == 0:
|
||||
return self.user_role_name
|
||||
elif role_number == 1:
|
||||
return self.moderator_role_name
|
||||
elif role_number == 2:
|
||||
return self.admin_role_name
|
||||
else:
|
||||
return None
|
||||
1
src/modules/external/__init__.py
vendored
1
src/modules/external/__init__.py
vendored
@@ -1 +0,0 @@
|
||||
from . import yandexgpt
|
||||
19
src/modules/external/yandexgpt/handlers.py
vendored
19
src/modules/external/yandexgpt/handlers.py
vendored
@@ -1,19 +0,0 @@
|
||||
from aiogram import Bot
|
||||
from aiogram.types import Message
|
||||
from src.modules.external.yandexgpt.yandexgpt import *
|
||||
from src.modules.standard.config.config import get_yandexgpt_token, get_yandexgpt_catalog_id, get_yandexgpt_prompt
|
||||
from src.modules.standard.database.db_api import add_message
|
||||
from src.core.logger import log
|
||||
import asyncio
|
||||
|
||||
|
||||
async def answer_to_message(message: Message, bot: Bot):
|
||||
# print("answer_to_message")
|
||||
await log("answer_to_message")
|
||||
yagpt = YandexGPT(get_yandexgpt_token(), get_yandexgpt_catalog_id())
|
||||
text = message.text
|
||||
prompt = get_yandexgpt_prompt()
|
||||
# response = await yagpt.async_yandexgpt(system_prompt=prompt, input_messages=text)
|
||||
response = await yagpt.yandexgpt_request(chat_id = message.chat.id, message_id = message.message_id, type = "yandexgpt")
|
||||
reply = await message.reply(response, parse_mode="Markdown")
|
||||
add_message(reply, message_ai_model="yandexgpt")
|
||||
6
src/modules/external/yandexgpt/info.json
vendored
6
src/modules/external/yandexgpt/info.json
vendored
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "YandexGPT",
|
||||
"description": "Модуль для работы с Yandex GPT",
|
||||
"author": "OCAB Team",
|
||||
"version": "1.0"
|
||||
}
|
||||
7
src/modules/external/yandexgpt/routers.py
vendored
7
src/modules/external/yandexgpt/routers.py
vendored
@@ -1,7 +0,0 @@
|
||||
from aiogram import Router, F
|
||||
|
||||
from src.modules.external.yandexgpt.handlers import answer_to_message
|
||||
|
||||
router = Router()
|
||||
# Если сообщение содержит в начале текст "Гномик" или "гномик" или отвечает на сообщение бота, то вызывается функция answer_to_message
|
||||
router.message.register(answer_to_message, F.text.startswith("Гномик") | F.text.startswith("гномик"))
|
||||
249
src/modules/external/yandexgpt/yandexgpt.py
vendored
249
src/modules/external/yandexgpt/yandexgpt.py
vendored
@@ -1,249 +0,0 @@
|
||||
import requests
|
||||
import json
|
||||
import asyncio
|
||||
import aiohttp
|
||||
from src.core.logger import log
|
||||
|
||||
from ...standard.database import *
|
||||
from ...standard.config.config import *
|
||||
|
||||
|
||||
class YandexGPT:
|
||||
token = None
|
||||
catalog_id = None
|
||||
languages = {
|
||||
"ru": "русский язык",
|
||||
"en": "английский язык",
|
||||
"de": "немецкий язык",
|
||||
"uk": "украинский язык",
|
||||
"es": "испанский язык",
|
||||
"be": "белорусский язык",
|
||||
}
|
||||
|
||||
def __init__(self, token, catalog_id):
|
||||
self.token = token
|
||||
self.catalog_id = catalog_id
|
||||
|
||||
async def async_request(self, url, headers, prompt) -> dict:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(url, headers=headers, json=prompt) as response:
|
||||
return await response.json()
|
||||
|
||||
async def async_token_check(self, messages, gpt, max_tokens, stream, temperature, del_msg_id=1):
|
||||
url = "https://llm.api.cloud.yandex.net/foundationModels/v1/tokenizeCompletion"
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Api-Key {self.token}"
|
||||
}
|
||||
answer_token = get_yandexgpt_token_for_answer()
|
||||
while True:
|
||||
try:
|
||||
request = {
|
||||
"modelUri": gpt,
|
||||
"completionOptions": {
|
||||
"stream": stream,
|
||||
"temperature": temperature,
|
||||
"maxTokens": max_tokens
|
||||
},
|
||||
"messages": messages
|
||||
}
|
||||
response = await self.async_request(url=url, headers=headers, prompt=request)
|
||||
except Exception as e: # TODO: Переделать обработку ошибок
|
||||
# print(e)
|
||||
await log(f"Error: {e}")
|
||||
|
||||
continue
|
||||
if int(len(response["tokens"])) < (max_tokens - answer_token):
|
||||
break
|
||||
else:
|
||||
try:
|
||||
messages.pop(del_msg_id)
|
||||
except IndexError:
|
||||
Exception("IndexError: list index out of range")
|
||||
return messages
|
||||
|
||||
async def async_yandexgpt_lite(self, system_prompt, input_messages, stream=False, temperature=0.6, max_tokens=8000):
|
||||
url = "https://llm.api.cloud.yandex.net/foundationModels/v1/completion"
|
||||
gpt = f"gpt://{self.catalog_id}/yandexgpt-lite/latest"
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Api-Key {self.token}"
|
||||
}
|
||||
|
||||
messages = [{"role": "system", "text": system_prompt}]
|
||||
for message in input_messages:
|
||||
messages.append(message)
|
||||
messages = await self.async_token_check(messages, gpt, max_tokens)
|
||||
|
||||
prompt = {
|
||||
"modelUri": gpt,
|
||||
"completionOptions": {
|
||||
"stream": stream,
|
||||
"temperature": temperature,
|
||||
"maxTokens": max_tokens
|
||||
},
|
||||
"messages": messages
|
||||
}
|
||||
|
||||
response = requests.post(url, headers=headers, json=prompt).text
|
||||
return json.loads(response)["result"]["alternatives"][0]["message"]["text"]
|
||||
|
||||
async def async_yandexgpt(
|
||||
self,
|
||||
system_prompt,
|
||||
input_messages,
|
||||
stream=False,
|
||||
temperature=0.6,
|
||||
max_tokens=get_yandexgpt_token_for_request()
|
||||
):
|
||||
url = "https://llm.api.cloud.yandex.net/foundationModels/v1/completion"
|
||||
gpt = f"gpt://{self.catalog_id}/yandexgpt/latest"
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Api-Key {self.token}"
|
||||
}
|
||||
|
||||
messages = []
|
||||
messages.append({"role": "system", "text": system_prompt})
|
||||
for message in input_messages:
|
||||
messages.append(message)
|
||||
|
||||
messages = await self.async_token_check(messages, gpt, max_tokens, stream, temperature)
|
||||
|
||||
request = {
|
||||
"modelUri": gpt,
|
||||
"completionOptions": {
|
||||
"stream": stream,
|
||||
"temperature": temperature,
|
||||
"maxTokens": max_tokens
|
||||
},
|
||||
"messages": messages
|
||||
}
|
||||
response = await self.async_request(url=url, headers=headers, prompt=request)
|
||||
return response["result"]["alternatives"][0]["message"]["text"]
|
||||
|
||||
|
||||
async def async_yandexgpt_translate(self, input_language, output_language, text):
|
||||
input_language = self.languages[input_language]
|
||||
output_language = self.languages[output_language]
|
||||
|
||||
return await self.async_yandexgpt(
|
||||
f"Переведи на {output_language} сохранив оригинальный смысл текста. Верни только результат:",
|
||||
[{"role": "user", "text": text}],
|
||||
stream=False, temperature=0.6, max_tokens=8000
|
||||
)
|
||||
|
||||
async def async_yandexgpt_spelling_check(self, input_language, text):
|
||||
input_language = self.languages[input_language]
|
||||
|
||||
return await self.async_yandexgpt(
|
||||
f"Проверьте орфографию и пунктуацию текста на {input_language}. Верни исправленный текст "
|
||||
f"без смысловых искажений:",
|
||||
[{"role": "user", "text": text}],
|
||||
stream=False, temperature=0.6, max_tokens=8000
|
||||
)
|
||||
|
||||
async def async_yandexgpt_text_history(self, input_messages, stream=False, temperature=0.6, max_tokens=8000):
|
||||
url = "https://llm.api.cloud.yandex.net/foundationModels/v1/completion"
|
||||
gpt = f"gpt://{self.catalog_id}/summarization/latest"
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Api-Key {self.token}"
|
||||
}
|
||||
|
||||
messages = []
|
||||
for message in input_messages:
|
||||
messages.append(message)
|
||||
messages = await self.async_token_check(messages, gpt, max_tokens, del_msg_id=0)
|
||||
|
||||
prompt = {
|
||||
"modelUri": gpt,
|
||||
"completionOptions": {
|
||||
"stream": stream,
|
||||
"temperature": temperature,
|
||||
"maxTokens": max_tokens
|
||||
},
|
||||
"messages": messages
|
||||
}
|
||||
|
||||
response = requests.post(url, headers=headers, json=prompt).text
|
||||
return json.loads(response)["result"]["alternatives"][0]["message"]["text"]
|
||||
|
||||
async def async_yandex_cloud_text_to_speech(self, text, voice, emotion, speed, format, quality):
|
||||
tts = "tts.api.cloud.yandex.net/speech/v1/tts:synthesize"
|
||||
# TODO: Сделать функцию TTS
|
||||
return 0
|
||||
|
||||
async def async_yandex_cloud_vision(self, image, features, language):
|
||||
# TODO: Сделать функцию Vision
|
||||
return 0
|
||||
|
||||
async def collect_messages(self, message_id, chat_id):
|
||||
messages = []
|
||||
# Собираем цепочку сообщений в формате: [{"role": "user", "text": "<Имя_пользователя>: Привет!"},
|
||||
# {"role": "assistant", "text": "Привет!"}]
|
||||
while True:
|
||||
message = db_api.get_message_text(chat_id, message_id)
|
||||
if db_api.get_message_ai_model(chat_id, message_id) != None:
|
||||
messages.append({"role": "assistant", "text": message})
|
||||
else:
|
||||
sender_name = db_api.get_user_name(db_api.get_message_sender_id(chat_id, message_id))
|
||||
messages.append({"role": "user", "text": sender_name + ": " + message})
|
||||
message_id = db_api.get_answer_to_message_id(chat_id, message_id)
|
||||
if message_id is None:
|
||||
break
|
||||
return list(reversed(messages))
|
||||
|
||||
async def collecting_messages_for_history(self, start_message_id, end_message_id, chat_id):
|
||||
messages = []
|
||||
# Собираем цепочку сообщений в формате: [{"role": "user", "text": "<Имя_пользователя>: Привет!"},
|
||||
# {"role": "assistant", "text": "Привет!"}]
|
||||
while True:
|
||||
message = db_api.get_message_text(chat_id, start_message_id)
|
||||
if db_api.get_message_ai_model(chat_id, start_message_id) != None:
|
||||
messages.append({"role": "assistant", "text": message})
|
||||
else:
|
||||
sender_name = db_api.get_user_name(db_api.get_message_sender_id(chat_id, start_message_id))
|
||||
messages.append({"role": "user", "text": sender_name + ": " + message})
|
||||
start_message_id -= 1
|
||||
if start_message_id <= end_message_id:
|
||||
break
|
||||
return messages.reverse()
|
||||
|
||||
async def yandexgpt_request(self, message_id = None, type = "yandexgpt-lite", chat_id = None,
|
||||
message_id_end = None, input_language = None, output_language = None, text = None):
|
||||
if type == "yandexgpt-lite":
|
||||
messages = await self.collect_messages(message_id, chat_id)
|
||||
return await self.async_yandexgpt_lite(
|
||||
system_prompt=get_yandexgpt_prompt(),
|
||||
input_messages=messages,
|
||||
stream=False, temperature=0.6, max_tokens=8000
|
||||
)
|
||||
elif type == "yandexgpt":
|
||||
# print("yandexgpt_request")
|
||||
await log("yandexgpt_request")
|
||||
messages = await self.collect_messages(message_id, chat_id)
|
||||
return await self.async_yandexgpt(
|
||||
system_prompt=get_yandexgpt_prompt(),
|
||||
input_messages=messages,
|
||||
stream=False, temperature=0.6, max_tokens=get_yandexgpt_token_for_request()
|
||||
)
|
||||
elif type == "yandexgpt-translate":
|
||||
return await self.async_yandexgpt_translate(
|
||||
input_language,
|
||||
output_language,
|
||||
text=db_api.get_message_text(chat_id, message_id)
|
||||
)
|
||||
elif type == "yandexgpt-spelling-check":
|
||||
return await self.async_yandexgpt_spelling_check(
|
||||
input_language,
|
||||
text=db_api.get_message_text(chat_id, message_id)
|
||||
)
|
||||
elif type == "yandexgpt-text-history":
|
||||
messages = await self.collect_messages_for_history(message_id, message_id_end, chat_id)
|
||||
return await self.async_yandexgpt_text_history(
|
||||
messages=messages,
|
||||
stream=False, temperature=0.6, max_tokens=8000
|
||||
)
|
||||
else:
|
||||
return "Ошибка: Неизвестный тип запроса | Error: Unknown request type"
|
||||
@@ -1 +0,0 @@
|
||||
from . import config, database, exceptions, roles
|
||||
@@ -1 +0,0 @@
|
||||
from . import routers
|
||||
@@ -1,46 +0,0 @@
|
||||
from aiogram import Bot
|
||||
from aiogram.types import Message
|
||||
from src.modules.standard.config.config import get_default_chat_tag
|
||||
import time
|
||||
|
||||
|
||||
async def delete_message(message: Message, bot: Bot):
|
||||
reply_message_id = message.reply_to_message.message_id
|
||||
await bot.delete_message(message.chat.id, reply_message_id)
|
||||
|
||||
async def error_access(message: Message, bot: Bot):
|
||||
await message.reply("Вы не админ/модератор")
|
||||
|
||||
async def get_chat_id(message: Message, bot: Bot):
|
||||
await message.reply(f"ID данного чата: `{message.chat.id}`", parse_mode="MarkdownV2")
|
||||
|
||||
async def chat_not_in_approve_list(message: Message, bot: Bot):
|
||||
await message.reply(
|
||||
f"Бот недоступен в данном чате, пожалуйста,"
|
||||
f" обратитесь к администратору для добавления чата в список доступных или перейдите в чат "
|
||||
f"{get_default_chat_tag()}"
|
||||
)
|
||||
await get_chat_id(message, bot)
|
||||
|
||||
async def mute_user(chat_id: int, user_id: int, time: int, bot: Bot):
|
||||
# *, can_send_messages: bool | None = None, can_send_audios: bool | None = None, can_send_documents: bool | None = None, can_send_photos: bool | None = None, can_send_videos: bool | None = None, can_send_video_notes: bool | None = None, can_send_voice_notes: bool | None = None, can_send_polls: bool | None = None, can_send_other_messages: bool | None = None, can_add_web_page_previews: bool | None = None, can_change_info: bool | None = None, can_invite_users: bool | None = None, can_pin_messages: bool | None = None, can_manage_topics: bool | None = None, **extra_data: Any)
|
||||
|
||||
mutePermissions = {
|
||||
"can_send_messages": False,
|
||||
"can_send_audios": False,
|
||||
"can_send_documents": False,
|
||||
"can_send_photos": False,
|
||||
"can_send_videos": False,
|
||||
"can_send_video_notes": False,
|
||||
"can_send_voice_notes": False,
|
||||
"can_send_polls": False,
|
||||
"can_send_other_messages": False,
|
||||
"can_add_web_page_previews": False,
|
||||
"can_change_info": False,
|
||||
"can_invite_users": False,
|
||||
"can_pin_messages": False,
|
||||
"can_manage_topics": False
|
||||
}
|
||||
end_time = time + int(time.time())
|
||||
await bot.restrict_chat_member(chat_id, user_id, until_date=end_time, **mutePermissions)
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "Admin",
|
||||
"description": "Модуль для работы с админкой",
|
||||
"author": "OCAB Team",
|
||||
"version": "1.0"
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
from aiogram import Router, F
|
||||
|
||||
from src.modules.standard.admin.handlers import delete_message, error_access, get_chat_id, chat_not_in_approve_list
|
||||
from src.modules.standard.filters.filters import ChatModerOrAdminFilter, ChatNotInApproveFilter
|
||||
|
||||
router = Router()
|
||||
|
||||
# Если сообщение содержит какой либо текст и выполняется фильтр ChatNotInApproveFilter, то вызывается функция chat_not_in_approve_list
|
||||
router.message.register(chat_not_in_approve_list, ChatNotInApproveFilter(), F.text)
|
||||
|
||||
router.message.register(get_chat_id, ChatModerOrAdminFilter(), F.text == '/chatID')
|
||||
|
||||
router.message.register(delete_message, ChatModerOrAdminFilter(), F.text == '/rm')
|
||||
router.message.register(error_access, F.text == '/rm')
|
||||
router.message.register(error_access, F.text == '/chatID')
|
||||
@@ -1 +0,0 @@
|
||||
from . import config
|
||||
@@ -1,60 +0,0 @@
|
||||
import yaml
|
||||
from ....service import paths
|
||||
|
||||
|
||||
def get_config(is_test: bool = False) -> dict:
|
||||
if is_test:
|
||||
path = f"{paths.modules_standard}/config/tests"
|
||||
else:
|
||||
path = paths.core
|
||||
path = f"{path}/config.yaml"
|
||||
|
||||
with open(path, 'r') as file:
|
||||
return yaml.full_load(file)
|
||||
|
||||
config = get_config()
|
||||
|
||||
def get_telegram_token() -> str:
|
||||
return config["TELEGRAM"]["TOKEN"]
|
||||
|
||||
def get_telegram_check_bot() -> bool:
|
||||
return config["TELEGRAM"]["CHECK_BOT"]
|
||||
|
||||
def get_aproved_chat_id() -> list:
|
||||
# Возваращем сплитованный список id чатов в формате int
|
||||
return [int(chat_id) for chat_id in config["TELEGRAM"]["APPROVED_CHAT_ID"].split(" | ")]
|
||||
|
||||
def get_user_role_name(role_number) -> dict:
|
||||
# Возвращаем название роли пользвателя по номеру роли, если такой роли нет, возвращаем неизвестно
|
||||
return config["ROLES"].get(role_number, "Неизвестно")
|
||||
|
||||
def get_default_chat_tag() -> str:
|
||||
return config["TELEGRAM"]["DEFAULT_CHAT_TAG"]
|
||||
|
||||
def get_yandexgpt_token() -> str:
|
||||
return config["YANDEXGPT"]["TOKEN"]
|
||||
|
||||
def get_yandexgpt_catalog_id() -> str:
|
||||
return config["YANDEXGPT"]["CATALOGID"]
|
||||
|
||||
def get_yandexgpt_prompt() -> str:
|
||||
return config["YANDEXGPT"]["PROMPT"]
|
||||
|
||||
def get_yandexgpt_start_words() -> list:
|
||||
return config["YANDEXGPT"]["STARTWORD"].split(" | ")
|
||||
|
||||
def get_yandexgpt_in_words() -> list:
|
||||
return config["YANDEXGPT"]["INWORD"].split(" | ")
|
||||
|
||||
def get_yandexgpt_token_for_request() -> int:
|
||||
return config["YANDEXGPT"]["TOKEN_FOR_REQUEST"]
|
||||
|
||||
def get_yandexgpt_token_for_answer() -> int:
|
||||
return config["YANDEXGPT"]["TOKEN_FOR_ANSWER"]
|
||||
|
||||
def get_access_rights() -> dict:
|
||||
return get_config()["ACCESS_RIGHTS"]
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "Config YAML",
|
||||
"description": "Модуль для работы с конфигурационным файлом бота (YAML)",
|
||||
"author": "OCAB Team",
|
||||
"version": "1.0"
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
TELEGRAM:
|
||||
TOKEN: xxxxxxxxxxxxxxxxxxxx
|
||||
ROLES:
|
||||
ADMIN: 0
|
||||
MODERATOR: 1
|
||||
USER: 2
|
||||
BOT: 3
|
||||
@@ -1,37 +0,0 @@
|
||||
from src.modules.standard.config.config import get_config
|
||||
import unittest
|
||||
|
||||
yaml_load = get_config(is_test=True)
|
||||
|
||||
|
||||
class TestConfig(unittest.TestCase):
|
||||
def test_yaml_load_correctness(self):
|
||||
self.assertIsNotNone(yaml_load)
|
||||
self.assertIn("TELEGRAM", yaml_load)
|
||||
self.assertIn("TOKEN", yaml_load["TELEGRAM"])
|
||||
self.assertIn("ROLES", yaml_load)
|
||||
self.assertIn("ADMIN", yaml_load["ROLES"])
|
||||
self.assertIn("MODERATOR", yaml_load["ROLES"])
|
||||
self.assertIn("USER", yaml_load["ROLES"])
|
||||
self.assertIn("BOT", yaml_load["ROLES"])
|
||||
|
||||
def test_yaml_keys_existence(self):
|
||||
self.assertTrue(all(key in yaml_load for key in ["TELEGRAM", "ROLES"]))
|
||||
self.assertIn("TOKEN", yaml_load["TELEGRAM"])
|
||||
self.assertTrue(all(role in yaml_load["ROLES"] for role in ["ADMIN", "MODERATOR", "USER"]))
|
||||
|
||||
def test_yaml_yaml_load_types(self):
|
||||
self.assertIsInstance(yaml_load["TELEGRAM"]["TOKEN"], str)
|
||||
self.assertTrue(all(isinstance(yaml_load["ROLES"][role], int) for role in ["ADMIN", "MODERATOR", "USER"]))
|
||||
|
||||
def test_yaml_values(self):
|
||||
expected_token = 'xxxxxxxxxxxxxxxxxxxx'
|
||||
expected_role_values = {'ADMIN': 0, 'MODERATOR': 1, 'USER': 2, 'BOT': 3}
|
||||
|
||||
self.assertEqual(yaml_load["TELEGRAM"]["TOKEN"], expected_token)
|
||||
for role, value in expected_role_values.items():
|
||||
self.assertEqual(yaml_load["ROLES"][role], value)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,48 +0,0 @@
|
||||
## Модуль DataBase
|
||||
|
||||
Модуль DataBase предназначен для ведения и работы с базами данных OCAB.
|
||||
|
||||
Модуль содержит в себе следующие таблицы:
|
||||
|
||||
* `Chats` - таблица для хранения информации о чатах.
|
||||
* `Users` - таблица для хранения информации о пользователях.
|
||||
* `Messages` - таблица для хранения информации о сообщениях.
|
||||
* `ChatStats` - таблица для хранения статистики чатов по дням.
|
||||
* `UserStats` - таблица для хранения статистики пользователей по дням.
|
||||
|
||||
Cтруктура таблицы `Chats`:
|
||||
* `chat_id` - идентификатор чата.
|
||||
* `chat_name` - название чата.
|
||||
* `chat_type` - тип чата. (0 - Чат администраторов, 1 - Пользовательский чат, 3 - Чат разрешённых личных запросов к боту
|
||||
10 - Не инициализированный чат)
|
||||
* `chat_stats` - количество всех отправленных сообщений в чате.
|
||||
|
||||
Cтруктура таблицы `Users`:
|
||||
* `user_id` - идентификатор пользователя telegram.
|
||||
* `user_tag` - тег пользователя telegram.
|
||||
* `user_name` - имя пользователя telegram.
|
||||
* `user_role` - роль пользователя в чате. (0 - Администратор, 1 - Модератор, 2 - Пользователь)
|
||||
* `user_stats` - количество всех отправленных сообщений пользователем.
|
||||
* `user_rep` - репутация пользователя.
|
||||
|
||||
Cтруктура таблицы `Messages`:
|
||||
* `message_chat_id` - идентификатор чата в котором отправлено сообщение.
|
||||
* `message_id` - идентификатор сообщения.
|
||||
* `messag_sender_id` - идентификатор пользователя отправившего сообщение. Если сообщение отправил бот, то
|
||||
`messag_sender_id` = 0.
|
||||
* `answer_to_message_id` - идентификатор сообщения на которое дан ответ. Если ответа нет или ответ на служебное
|
||||
сообщение о создании топика в чатах с форумным типом, то `answer_to_message_id` = 0.
|
||||
* `message_ai_model` - идентификатор модели нейросети, которая использовалась для генерации ответа. Если ответ'
|
||||
сгенерирован не был, то `message_ai_model` = null.
|
||||
* `message_text` - текст сообщения.
|
||||
|
||||
Cтруктура таблицы `ChatStats`:
|
||||
* `chat_id` - идентификатор чата для которого собрана статистика.
|
||||
* `date` - дата на которую собрана статистика.
|
||||
* `messages_count` - количество сообщений отправленных в чат за день.
|
||||
|
||||
Cтруктура таблицы `UserStats`:
|
||||
* `chat_id` - идентификатор чата для которого собрана статистика.
|
||||
* `user_id` - идентификатор пользователя для которого собрана статистика.
|
||||
* `date` - дата на которую собрана статистика.
|
||||
* `messages_count` - количество сообщений отправленных пользователем в чат за день.
|
||||
@@ -1 +0,0 @@
|
||||
from . import db_api, models
|
||||
@@ -1,286 +0,0 @@
|
||||
from .models.chats import Chats
|
||||
from .models.messages import Messages
|
||||
from .models.users import Users
|
||||
from .models.user_stats import UserStats
|
||||
from .models.chat_stats import ChatStats
|
||||
from ....service import paths
|
||||
from ..exceptions.module_exceptions import MissingModuleName, NotExpectedModuleName
|
||||
import peewee as pw
|
||||
from aiogram.types import Message
|
||||
|
||||
|
||||
def connect_database(is_test: bool = False, module: str | None = None):
|
||||
if is_test:
|
||||
if not module:
|
||||
raise MissingModuleName()
|
||||
db_path = f"{paths.modules_standard}/{module}/tests/database"
|
||||
else:
|
||||
if module:
|
||||
raise NotExpectedModuleName()
|
||||
db_path = f"{paths.core}/database"
|
||||
|
||||
_database = pw.SqliteDatabase(f"{db_path}/OCAB.db")
|
||||
Chats._meta.database = _database
|
||||
Messages._meta.database = _database
|
||||
Users._meta.database = _database
|
||||
UserStats._meta.database = _database
|
||||
ChatStats._meta.database = _database
|
||||
|
||||
return _database, f"{db_path}/OCAB.db"
|
||||
|
||||
|
||||
def create_tables(db: pw.SqliteDatabase):
|
||||
"""Создание таблиц"""
|
||||
for table in Chats, Messages, Users, UserStats, ChatStats:
|
||||
if not table.table_exists():
|
||||
db.create_tables([table])
|
||||
|
||||
|
||||
def add_chat(chat_id, chat_name, chat_type=10, chat_stats=0):
|
||||
chat, created = Chats.get_or_create(id=chat_id, defaults={
|
||||
'chat_name': chat_name,
|
||||
'chat_type': chat_type,
|
||||
'chat_all_stat': chat_stats,
|
||||
})
|
||||
if not created:
|
||||
# Обновить существующий чат, если он уже существует
|
||||
chat.chat_name = chat_name
|
||||
chat.chat_type = chat_type
|
||||
chat.chat_stats = chat_stats
|
||||
chat.save()
|
||||
|
||||
|
||||
def add_user(user_id, user_first_name, user_last_name=None, user_tag=None, user_role=0, user_stats=0, user_rep=0):
|
||||
if user_last_name is None:
|
||||
user_name = user_first_name
|
||||
else:
|
||||
user_name = user_first_name + " " + user_last_name
|
||||
|
||||
user, created = Users.get_or_create(id=user_id, defaults={
|
||||
'user_tag': user_tag,
|
||||
'user_name': user_name,
|
||||
'user_role': user_role,
|
||||
'user_stats': user_stats,
|
||||
'user_rep': user_rep
|
||||
})
|
||||
if not created:
|
||||
# Обновить существующего пользователя, если он уже существует
|
||||
user.user_tag = user_tag
|
||||
user.user_name = user_name
|
||||
user.user_role = user_role
|
||||
user.user_stats = user_stats
|
||||
user.user_rep = user_rep
|
||||
user.save()
|
||||
|
||||
|
||||
def add_message(message: Message, message_ai_model=None):
|
||||
if message.reply_to_message:
|
||||
answer_to_message_id = message.reply_to_message.message_id
|
||||
else:
|
||||
answer_to_message_id = None
|
||||
Messages.create(
|
||||
message_chat_id=message.chat.id,
|
||||
message_id=message.message_id,
|
||||
message_sender_id=message.from_user.id,
|
||||
answer_to_message_id=answer_to_message_id,
|
||||
message_ai_model=message_ai_model,
|
||||
message_text=message.text
|
||||
)
|
||||
|
||||
|
||||
def add_chat_stats(chat_id, date, messages_count):
|
||||
ChatStats.create(
|
||||
chat_id=chat_id,
|
||||
date=date,
|
||||
messages_count=messages_count
|
||||
)
|
||||
|
||||
|
||||
def add_user_stats(chat_id, user_id, date, messages_count):
|
||||
UserStats.create(
|
||||
chat_id=chat_id,
|
||||
user_id=user_id,
|
||||
date=date,
|
||||
messages_count=messages_count
|
||||
)
|
||||
|
||||
# Работа с таблицей чатов
|
||||
|
||||
|
||||
def get_chat(chat_id):
|
||||
return Chats.get_or_none(Chats.id == chat_id)
|
||||
|
||||
|
||||
def change_chat_name(chat_id, new_chat_name):
|
||||
query = Chats.update(chat_name=new_chat_name).where(Chats.id == chat_id)
|
||||
query.execute()
|
||||
|
||||
|
||||
def change_chat_type(chat_id, new_chat_type):
|
||||
query = Chats.update(chat_type=new_chat_type).where(Chats.id == chat_id)
|
||||
query.execute()
|
||||
|
||||
|
||||
def get_chat_all_stat(chat_id):
|
||||
chat = Chats.get_or_none(Chats.id == chat_id)
|
||||
return chat.chat_all_stat if chat else None
|
||||
|
||||
# Работа с таблицей пользователей
|
||||
|
||||
|
||||
def get_user(user_id):
|
||||
return Users.get_or_none(Users.id == user_id)
|
||||
|
||||
|
||||
def get_user_tag(user_id):
|
||||
user = Users.get_or_none(Users.id == user_id)
|
||||
return user.user_tag if user else None
|
||||
|
||||
def get_user_id(user_tag):
|
||||
user = Users.get_or_none(Users.user_tag == user_tag)
|
||||
return user.id if user else None
|
||||
|
||||
|
||||
def get_user_name(user_id):
|
||||
user = Users.get_or_none(Users.id == user_id)
|
||||
return user.user_name if user else None
|
||||
|
||||
|
||||
def get_user_role(user_id):
|
||||
user = Users.get_or_none(Users.id == user_id)
|
||||
return user.user_role if user else None
|
||||
|
||||
|
||||
def get_user_all_stats(user_id):
|
||||
user = Users.get_or_none(Users.id == user_id)
|
||||
return user.user_stats if user else None
|
||||
|
||||
|
||||
def get_user_rep(user_id):
|
||||
user = Users.get_or_none(Users.id == user_id)
|
||||
return user.user_rep if user else None
|
||||
|
||||
|
||||
def change_user_name(user_id, user_first_name, user_last_name=None):
|
||||
if user_last_name is None:
|
||||
new_user_name = user_first_name
|
||||
else:
|
||||
new_user_name = user_first_name + " " + user_last_name
|
||||
query = Users.update(user_name=new_user_name).where(Users.id == user_id)
|
||||
query.execute()
|
||||
|
||||
|
||||
def change_user_tag(user_id, new_user_tag):
|
||||
query = Users.update(user_tag=new_user_tag).where(Users.id == user_id)
|
||||
query.execute()
|
||||
|
||||
|
||||
def change_user_role(user_id, new_user_role):
|
||||
query = Users.update(user_role=new_user_role).where(Users.id == user_id)
|
||||
query.execute()
|
||||
|
||||
# Работа с таблицей сообщений
|
||||
|
||||
|
||||
def get_message(message_chat_id, message_id):
|
||||
return Messages.get_or_none(Messages.message_chat_id == message_chat_id, Messages.message_id == message_id)
|
||||
|
||||
|
||||
def get_message_sender_id(message_chat_id, message_id):
|
||||
message = Messages.get_or_none(Messages.message_chat_id == message_chat_id, Messages.message_id == message_id)
|
||||
return message.message_sender_id if message else None
|
||||
|
||||
|
||||
def get_message_text(message_chat_id, message_id):
|
||||
message = Messages.get_or_none(Messages.message_chat_id == message_chat_id, Messages.message_id == message_id)
|
||||
return message.message_text if message else None
|
||||
|
||||
|
||||
def get_message_ai_model(message_chat_id, message_id):
|
||||
message = Messages.get_or_none(Messages.message_chat_id == message_chat_id, Messages.message_id == message_id)
|
||||
return message.message_ai_model if message else None
|
||||
|
||||
|
||||
def get_answer_to_message_id(message_chat_id, message_id):
|
||||
message = Messages.get_or_none(Messages.message_chat_id == message_chat_id, Messages.message_id == message_id)
|
||||
return message.answer_to_message_id if message else None
|
||||
|
||||
# Работа с таблицей статистики чатов
|
||||
|
||||
|
||||
def get_chat_stats(chat_id):
|
||||
chat_stats = {}
|
||||
for chat_stat in ChatStats.select().where(ChatStats.chat_id == chat_id):
|
||||
chat_stats[chat_stat.date] = chat_stat.messages_count
|
||||
return chat_stats
|
||||
|
||||
# Работа с таблицей статистики пользователей
|
||||
|
||||
|
||||
def get_user_stats(user_id):
|
||||
user_stats = {}
|
||||
for user_stat in UserStats.select().where(UserStats.user_id == user_id):
|
||||
user_stats[user_stat.date] = user_stat.messages_count
|
||||
return user_stats
|
||||
|
||||
# Функции обновления
|
||||
|
||||
|
||||
def update_chat_all_stat(chat_id):
|
||||
query = Chats.update(chat_all_stat=Chats.chat_all_stat + 1).where(Chats.id == chat_id)
|
||||
query.execute()
|
||||
|
||||
|
||||
def update_chat_stats(chat_id, date):
|
||||
chat_stats = ChatStats.get_or_none(ChatStats.chat_id == chat_id, ChatStats.date == date)
|
||||
if chat_stats:
|
||||
query = ChatStats.update(messages_count=ChatStats.messages_count + 1).where(ChatStats.chat_id == chat_id,
|
||||
ChatStats.date == date)
|
||||
query.execute()
|
||||
else:
|
||||
ChatStats.create(
|
||||
chat_id=chat_id,
|
||||
date=date,
|
||||
messages_count=1
|
||||
)
|
||||
|
||||
|
||||
def update_user_all_stat(user_id):
|
||||
user = Users.get_or_none(Users.id == user_id)
|
||||
if user:
|
||||
query = Users.update(user_stats=Users.user_stats + 1).where(Users.id == user_id)
|
||||
query.execute()
|
||||
else:
|
||||
Users.create(
|
||||
id=user_id,
|
||||
user_stats=1
|
||||
)
|
||||
|
||||
|
||||
def update_user_rep(user_id):
|
||||
user = Users.get_or_none(Users.id == user_id)
|
||||
if user:
|
||||
query = Users.update(user_rep=Users.user_rep + 1).where(Users.id == user_id)
|
||||
query.execute()
|
||||
else:
|
||||
Users.create(
|
||||
id=user_id,
|
||||
user_rep=1
|
||||
)
|
||||
|
||||
|
||||
def update_user_stats(chat_id, user_id, date):
|
||||
user_stats = UserStats.get_or_none(UserStats.chat_id == chat_id, UserStats.user_id == user_id,
|
||||
UserStats.date == date)
|
||||
if user_stats:
|
||||
query = UserStats.update(messages_count=UserStats.messages_count + 1).where(UserStats.chat_id == chat_id,
|
||||
UserStats.user_id == user_id,
|
||||
UserStats.date == date)
|
||||
query.execute()
|
||||
else:
|
||||
UserStats.create(
|
||||
chat_id=chat_id,
|
||||
user_id=user_id,
|
||||
date=date,
|
||||
messages_count=1
|
||||
)
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "Database",
|
||||
"description": "Модуль для работы с БД",
|
||||
"author": "OCAB Team",
|
||||
"version": "1.0"
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import peewee as pw
|
||||
|
||||
|
||||
class ChatStats(pw.Model):
|
||||
class Meta:
|
||||
...
|
||||
chat_id = pw.IntegerField(null=False)
|
||||
date = pw.DateField(null=False)
|
||||
messages_count = pw.IntegerField(null=False, default=0)
|
||||
@@ -1,9 +0,0 @@
|
||||
import peewee as pw
|
||||
|
||||
|
||||
class Chats(pw.Model):
|
||||
class Meta:
|
||||
...
|
||||
chat_name = pw.CharField(null=False)
|
||||
chat_type = pw.IntegerField(null=False, default=10)
|
||||
chat_all_stat = pw.IntegerField(null=False)
|
||||
@@ -1,13 +0,0 @@
|
||||
import peewee as pw
|
||||
|
||||
|
||||
class Messages(pw.Model):
|
||||
class Meta:
|
||||
...
|
||||
message_chat_id = pw.IntegerField(null=False)
|
||||
message_id = pw.IntegerField(null=False)
|
||||
message_sender_id = pw.IntegerField(null=False)
|
||||
answer_to_message_id = pw.IntegerField(null=True)
|
||||
message_ai_model = pw.TextField(null=True)
|
||||
message_text = pw.TextField(null=False)
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import peewee as pw
|
||||
|
||||
|
||||
class UserStats(pw.Model):
|
||||
class Meta:
|
||||
...
|
||||
chat_id = pw.IntegerField(null=False)
|
||||
user_id = pw.IntegerField(null=False)
|
||||
date = pw.DateField(null=False)
|
||||
messages_count = pw.IntegerField(null=False, default=0)
|
||||
@@ -1,11 +0,0 @@
|
||||
import peewee as pw
|
||||
|
||||
|
||||
class Users(pw.Model):
|
||||
class Meta:
|
||||
...
|
||||
user_tag = pw.CharField(null=True)
|
||||
user_name = pw.CharField(null=False) # до 255 символов
|
||||
user_role = pw.IntegerField(null=True, default=3)
|
||||
user_stats = pw.IntegerField(null=True, default=0)
|
||||
user_rep = pw.IntegerField(null=True, default=0)
|
||||
@@ -1 +0,0 @@
|
||||
Эта директория для тестовой БД
|
||||
@@ -1,93 +0,0 @@
|
||||
import unittest
|
||||
import os
|
||||
|
||||
from ..db_api import *
|
||||
from ...exceptions.module_exceptions import MissingModuleName, NotExpectedModuleName
|
||||
|
||||
|
||||
class TestDatabaseAPI(unittest.TestCase):
|
||||
database = None
|
||||
path = None
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.database, cls.path = connect_database(is_test=True, module="database")
|
||||
create_tables(cls.database)
|
||||
def test_fail_connect(cls):
|
||||
with cls.assertRaises(MissingModuleName):
|
||||
cls.database, cls.path = connect_database(is_test=True)
|
||||
|
||||
with cls.assertRaises(NotExpectedModuleName):
|
||||
cls.database, cls.path = connect_database(module="database")
|
||||
|
||||
def test_add_and_get_chat(self):
|
||||
add_chat(chat_id=21, chat_role=0, chat_stats=0, chat_federation=0)
|
||||
add_chat(chat_id=22, chat_role=1, chat_stats=100, chat_federation=1)
|
||||
|
||||
chat1 = get_chat(21)
|
||||
self.assertIsNotNone(chat1)
|
||||
self.assertEqual(chat1.id, 21)
|
||||
self.assertEqual(chat1.chat_role, 0)
|
||||
|
||||
chat2 = get_chat(22)
|
||||
self.assertIsNotNone(chat2)
|
||||
self.assertEqual(chat2.id, 22)
|
||||
self.assertEqual(chat2.chat_role, 1)
|
||||
|
||||
def test_add_and_get_message(self):
|
||||
add_message(message_id=1, message_text="Test Message 1", message_sender=1, answer_id=2)
|
||||
add_message(message_id=2, message_text="Test Message 2", message_sender=2, answer_id=1)
|
||||
|
||||
message1 = get_message(1)
|
||||
self.assertIsNotNone(message1)
|
||||
self.assertEqual(message1.id, 1)
|
||||
self.assertEqual(message1.message_text, "Test Message 1")
|
||||
|
||||
message2 = get_message(2)
|
||||
self.assertIsNotNone(message2)
|
||||
self.assertEqual(message2.id, 2)
|
||||
self.assertEqual(message2.message_text, "Test Message 2")
|
||||
|
||||
def test_add_and_get_user(self):
|
||||
add_user(user_id=100, user_name="TestUser1", user_tag="TestTag1", user_role=0, user_stats=10, user_rep=5)
|
||||
add_user(user_id=101, user_name="TestUser2", user_tag="TestTag2", user_role=1, user_stats=20, user_rep=10)
|
||||
|
||||
user1 = get_user(100)
|
||||
self.assertIsNotNone(user1)
|
||||
self.assertEqual(user1.id, 100)
|
||||
self.assertEqual(user1.user_name, "TestUser1")
|
||||
|
||||
user2 = get_user(101)
|
||||
self.assertIsNotNone(user2)
|
||||
self.assertEqual(user2.id, 101)
|
||||
self.assertEqual(user2.user_name, "TestUser2")
|
||||
|
||||
def test_get_user_role(self):
|
||||
add_user(user_id=102, user_name="TestUser3", user_tag="TestTag3", user_role=0, user_stats=30, user_rep=15)
|
||||
add_user(user_id=103, user_name="TestUser4", user_tag="TestTag4", user_role=1, user_stats=40, user_rep=20)
|
||||
|
||||
user_role1 = get_user_role(102)
|
||||
self.assertEqual(user_role1, 0)
|
||||
|
||||
user_role2 = get_user_role(103)
|
||||
self.assertEqual(user_role2, 1)
|
||||
|
||||
def test_change_user_name(self):
|
||||
add_user(user_id=104, user_name="OldName1", user_tag="TestTag5", user_role=0, user_stats=50, user_rep=25)
|
||||
change_user_name(104, "NewName1")
|
||||
updated_user1 = get_user(104)
|
||||
self.assertEqual(updated_user1.user_name, "NewName1")
|
||||
|
||||
add_user(user_id=105, user_name="OldName2", user_tag="TestTag6", user_role=1, user_stats=60, user_rep=30)
|
||||
change_user_name(105, "NewName2")
|
||||
updated_user2 = get_user(105)
|
||||
self.assertEqual(updated_user2.user_name, "NewName2")
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.database.close()
|
||||
os.system(f"rm {cls.path}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1 +0,0 @@
|
||||
from . import module_exceptions
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "Exceptions",
|
||||
"description": "Модуль с исключениями",
|
||||
"author": "OCAB Team",
|
||||
"version": "1.0"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
class MissingModuleName(BaseException):
|
||||
def __init__(self):
|
||||
self.message = "Пропущено название директории модуля"
|
||||
|
||||
super().__init__(self.message)
|
||||
|
||||
|
||||
class NotExpectedModuleName(BaseException):
|
||||
def __init__(self):
|
||||
self.message = "Не ожидалось название директории модуля"
|
||||
|
||||
super().__init__(self.message)
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
from aiogram.filters import BaseFilter
|
||||
from aiogram.types import Message
|
||||
from aiogram import Bot
|
||||
|
||||
from src.modules.standard.roles.roles import Roles
|
||||
from src.modules.standard.config.config import get_aproved_chat_id
|
||||
from src.core.logger import log
|
||||
|
||||
class ChatModerOrAdminFilter(BaseFilter):
|
||||
async def __call__(self, message: Message, bot: Bot) -> bool:
|
||||
user_id = message.from_user.id
|
||||
roles = Roles()
|
||||
admins = await bot.get_chat_administrators(message.chat.id)
|
||||
return await roles.check_admin_permission(user_id) or \
|
||||
await roles.check_moderator_permission(user_id) or any(user_id == admin.user.id for admin in admins)
|
||||
|
||||
class ChatNotInApproveFilter(BaseFilter):
|
||||
async def __call__(self, message: Message, bot: Bot) -> bool:
|
||||
# print("chat_check")
|
||||
await log("chat_check")
|
||||
chat_id = message.chat.id
|
||||
if chat_id in get_aproved_chat_id():
|
||||
# print(f"Chat in approve list: {chat_id}")
|
||||
await log(f"Chat in approve list: {chat_id}")
|
||||
return False
|
||||
else:
|
||||
# print(f"Chat not in approve list: {chat_id}")
|
||||
await log(f"Chat not in approve list: {chat_id}")
|
||||
return True
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "Filters",
|
||||
"description": "Модуль с фильтрами",
|
||||
"author": "OCAB Team",
|
||||
"version": "1.0"
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
from aiogram import Bot
|
||||
from aiogram.types import Message
|
||||
from src.modules.standard.config.config import get_user_role_name
|
||||
from src.modules.standard.roles.roles import Roles
|
||||
from src.modules.standard.database.db_api import *
|
||||
from src.core.logger import log
|
||||
|
||||
async def get_info_answer_by_id(message: Message, bot: Bot, user_id: int):
|
||||
if get_message_ai_model(message.chat.id, message.message_id) is not None:
|
||||
await message.reply("Это сообщение было сгенерировано ботом используя модель: " + get_message_ai_model(message.chat.id, message.message_id))
|
||||
elif user_id == bot.id:
|
||||
await message.reply("Это сообщение было отправлено ботом")
|
||||
elif get_user(user_id) is None:
|
||||
await message.reply("Пользователь не найден")
|
||||
# print(get_user(user_id))
|
||||
await log(f"Пользователь не найден: {user_id}, {get_user(user_id)}")
|
||||
else:
|
||||
roles = Roles()
|
||||
answer = (
|
||||
f"Пользователь: {get_user_name(user_id)}\n"
|
||||
f"Роль: {await roles.get_role_name(role_id=get_user_role(user_id))}\n"
|
||||
f"Тег: @{get_user_tag(user_id)}\n"
|
||||
f"Кол-во сообщений: {get_user_all_stats(user_id)}\n"
|
||||
f"Репутация: {get_user_rep(user_id)}"
|
||||
)
|
||||
await message.reply(answer)
|
||||
|
||||
|
||||
async def get_user_info(message: Message, bot: Bot):
|
||||
# Проверяем содержимое сообщения, если содержит вторым элементом тег пользователя, то выводим информацию о нем
|
||||
# Если сообщение отвечает на другое сообщение, то выводим информацию о пользователе, на чье сообщение был ответ
|
||||
# Если это бот то выводим информацию что это бот и какая модель yandexgpt используется
|
||||
try:
|
||||
if len(message.text.split()) > 1 and message.text.split()[1].startswith("@"):
|
||||
user_tag = message.text.split()[1][1:]
|
||||
user_id = get_user_id(user_tag)
|
||||
if user_id:
|
||||
await get_info_answer_by_id(message, bot, user_id)
|
||||
else:
|
||||
await message.reply(f"Пользователь с тегом @{user_tag} не найден")
|
||||
elif message.reply_to_message:
|
||||
user_id = message.reply_to_message.from_user.id
|
||||
await get_info_answer_by_id(message, bot, user_id)
|
||||
else:
|
||||
await get_info_answer_by_id(message, bot, message.from_user.id)
|
||||
except Exception as e:
|
||||
await message.reply("В вашем запросе что-то пошло не так,"
|
||||
" попробуйте запросить информацию о пользователе по его тегу или ответив на его сообщение")
|
||||
# print(e)
|
||||
await log(e)
|
||||
|
||||
async def get_chat_info(message: Message, bot: Bot):
|
||||
answer = (
|
||||
f"*Название чата:* {message.chat.title}\n"
|
||||
f"*ID чата:* `{message.chat.id}`\n \n"
|
||||
f"*Суммарное количество сообщений в чате:* {get_chat_all_stat(message.chat.id)}\n"
|
||||
f"*Количество пользователей в чате:* {await bot.get_chat_member_count(message.chat.id)}\n"
|
||||
f"*Количество администраторов в чате:* {len(await bot.get_chat_administrators(message.chat.id))}"
|
||||
)
|
||||
await message.reply(answer, parse_mode="MarkdownV2")
|
||||
@@ -1,8 +0,0 @@
|
||||
from aiogram import Router, F
|
||||
|
||||
from src.modules.standard.info.handlers import get_user_info, get_chat_info
|
||||
|
||||
router = Router()
|
||||
|
||||
router.message.register(get_user_info, F.text.startswith("/info") == True)
|
||||
router.message.register(get_chat_info, F.text.startswith("/chatinfo") == True)
|
||||
@@ -1,99 +0,0 @@
|
||||
from aiogram import Router, F, Bot, types
|
||||
|
||||
from src.modules.external.yandexgpt.handlers import answer_to_message
|
||||
from src.modules.standard.database.db_api import *
|
||||
from src.modules.standard.config.config import get_yandexgpt_start_words, get_yandexgpt_in_words, get_aproved_chat_id
|
||||
from src.core.logger import log
|
||||
|
||||
|
||||
async def chat_check(message: types.Message):
|
||||
# Проверка наличия id чата в базе данных чатов
|
||||
# Если чата нет в базе данных, то проверяем его в наличии в конфиге и если он там есть то добавляем его в БД
|
||||
# Если чат есть в базе данных, то pass
|
||||
if get_chat(message.chat.id) is None:
|
||||
if message.chat.id in get_aproved_chat_id():
|
||||
# print(f"Chat in approve list: {message.chat.id} {message.chat.title}")
|
||||
await log(f"Chat in approve list: {message.chat.id} {message.chat.title}")
|
||||
add_chat(message.chat.id, message.chat.title)
|
||||
# print(f"Chat added: {message.chat.id} {message.chat.title}")
|
||||
await log(f"Chat added: {message.chat.id} {message.chat.title}")
|
||||
else:
|
||||
# print(f"Chat not in approve list: {message.chat.id} {message.chat.title}")
|
||||
await log(f"Chat not in approve list: {message.chat.id} {message.chat.title}")
|
||||
pass
|
||||
else:
|
||||
# Проверяем обновление названия чата
|
||||
chat = get_chat(message.chat.id)
|
||||
if chat.chat_name != message.chat.title:
|
||||
chat.chat_name = message.chat.title
|
||||
chat.save()
|
||||
# print(f"Chat updated: {message.chat.id} {message.chat.title}")
|
||||
await log(f"Chat updated: {message.chat.id} {message.chat.title}")
|
||||
else:
|
||||
# print(f"Chat already exists: {message.chat.id} {message.chat.title}")
|
||||
await log(f"Chat already exists: {message.chat.id} {message.chat.title}")
|
||||
pass
|
||||
|
||||
async def user_check(message: types.Message):
|
||||
# Проверка наличия id пользователя в базе данных пользователей
|
||||
# Если пользователя нет в базе данных, то добавляем его
|
||||
# Если пользователь есть в базе данных, то pass
|
||||
current_user_name = ""
|
||||
if message.from_user.last_name is None:
|
||||
current_user_name = message.from_user.first_name
|
||||
else:
|
||||
current_user_name = message.from_user.first_name + " " + message.from_user.last_name
|
||||
|
||||
if get_user(message.from_user.id) is None:
|
||||
add_user(message.from_user.id,
|
||||
message.from_user.first_name, message.from_user.last_name,
|
||||
message.from_user.username)
|
||||
# print(f"User added: {message.from_user.id} {message.from_user.first_name} {message.from_user.last_name}")
|
||||
await log(f"User added: {message.from_user.id} {current_user_name}")
|
||||
else:
|
||||
# print(f"User already exists: {message.from_user.id} {message.from_user.first_name} {message.from_user.last_name} {message.from_user.username}")
|
||||
await log(f"User already exists: {message.from_user.id} {current_user_name} {message.from_user.username}")
|
||||
# Проверяем обновление имени пользователя
|
||||
if get_user_name(message.from_user.id) != current_user_name:
|
||||
change_user_name(message.from_user.id, current_user_name)
|
||||
# print(f"User updated: {message.from_user.id} {message.from_user.first_name} {message.from_user.last_name}")
|
||||
await log(f"User name updated: {message.from_user.id} {current_user_name}")
|
||||
# Проверяем обновление username пользователя
|
||||
if get_user_tag(message.from_user.id) != message.from_user.username:
|
||||
change_user_tag(message.from_user.id, message.from_user.username)
|
||||
# print(f"User updated: {message.from_user.id} {message.from_user.username}")
|
||||
await log(f"User tag updated: {message.from_user.id} {message.from_user.username}")
|
||||
pass
|
||||
|
||||
async def add_stats(message: types.Message):
|
||||
# Добавляем пользователю и чату статистику
|
||||
update_chat_all_stat(message.chat.id)
|
||||
update_user_all_stat(message.from_user.id)
|
||||
|
||||
|
||||
async def message_processing(message: types.Message, bot: Bot):
|
||||
await chat_check(message)
|
||||
await user_check(message)
|
||||
await add_stats(message)
|
||||
|
||||
add_message(message)
|
||||
# Если сообщение в начале содержит слово из списка или внутри сообщения содержится слово из списка или сообщение отвечает на сообщение бота
|
||||
|
||||
if ((message.text.split(" ")[0] in get_yandexgpt_start_words())
|
||||
or (any(word in message.text for word in get_yandexgpt_in_words()))):
|
||||
# print("message_processing")
|
||||
await log("message_processing")
|
||||
await answer_to_message(message, bot)
|
||||
|
||||
elif message.reply_to_message is not None:
|
||||
if message.reply_to_message.from_user.is_bot:
|
||||
# print("message_processing")
|
||||
await log("message_processing")
|
||||
await answer_to_message(message, bot)
|
||||
|
||||
|
||||
|
||||
|
||||
router = Router()
|
||||
# Если сообщение содержит текст то вызывается функция message_processing
|
||||
router.message.register(message_processing, F.text)
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "Moderation",
|
||||
"description": "Moderation commands for OCAB",
|
||||
"author": "OCAB Team",
|
||||
"version": "1.0"
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
import asyncio
|
||||
import aiohttp
|
||||
import aiogram
|
||||
import time
|
||||
from ...standard.config.config import *
|
||||
from ...standard.roles.roles import *
|
||||
|
||||
|
||||
|
||||
class Moderation:
|
||||
def __init__(self):
|
||||
access_rights = get_access_rights()
|
||||
bot_check_message = bool(self.access_rights["BOT_CHECK_MESSAGE"])
|
||||
bot_ban_user = bool(self.access_rights["BOT_BAN_USER"])
|
||||
bot_mute_user = bool(self.access_rights["BOT_MUTE_USER"])
|
||||
moderator_rights = self.access_rights["MODERATOR_RIGHTS"]
|
||||
ai_check_message = bool(self.access_rights["AI_CHECK_MESSAGE"])
|
||||
beta_ai_check_message = bool(self.access_rights["BETA_AI_CHECK_MESSAGE"])
|
||||
|
||||
async def time_to_seconds(time):
|
||||
# Конвертация текстового указания времени по типу 3h, 5m, 10s в минуты
|
||||
if time[-1] == 'd':
|
||||
return int(time[:-1]) * 86400
|
||||
elif time[-1] == 'h':
|
||||
return int(time[:-1]) * 3600
|
||||
elif time[-1] == 'm':
|
||||
return int(time[:-1]) * 60
|
||||
elif time[-1] == 's':
|
||||
return int(time[:-1])
|
||||
|
||||
async def short_time_to_time(self, time):
|
||||
# Конвертация времени в длинное название
|
||||
if time[-1] == 'd':
|
||||
return str(f"{time[0:-1]} дней")
|
||||
elif time[-1] == 'h':
|
||||
return str(f"{time[0:-1]} часов")
|
||||
elif time[-1] == 'm':
|
||||
return str(f"{time[0:-1]} минут")
|
||||
elif time[-1] == 's':
|
||||
return str(f"{time[0:-1]} секунд")
|
||||
|
||||
async def delete_message(self, chat_id, message_id, bot: aiogram.Bot):
|
||||
await bot.delete_message(chat_id, message_id)
|
||||
|
||||
async def ban_user(self, chat_id, user_id, bot: aiogram.Bot):
|
||||
await bot.ban_chat_member(chat_id, user_id)
|
||||
|
||||
async def mute_user(chat_id, user_id, time, bot: aiogram.Bot):
|
||||
mutePermissions = {
|
||||
"can_send_messages": False,
|
||||
"can_send_audios": False,
|
||||
"can_send_documents": False,
|
||||
"can_send_photos": False,
|
||||
"can_send_videos": False,
|
||||
"can_send_video_notes": False,
|
||||
"can_send_voice_notes": False,
|
||||
"can_send_polls": False,
|
||||
"can_send_other_messages": False,
|
||||
"can_add_web_page_previews": False,
|
||||
"can_change_info": False,
|
||||
"can_invite_users": False,
|
||||
"can_pin_messages": False,
|
||||
"can_manage_topics": False
|
||||
}
|
||||
end_time = time + int(time.time())
|
||||
await bot.restrict_chat_member(chat_id, user_id, until_date=end_time, **mutePermissions)
|
||||
|
||||
|
||||
async def unmute_user(chat_id, user_id, bot: aiogram.Bot):
|
||||
await bot.restrict_chat_member(chat_id, user_id, use_independent_chat_permissions=True)
|
||||
|
||||
async def ban_user(chat_id, user_id, bot: aiogram.Bot):
|
||||
await bot.ban_chat_member(chat_id, user_id)
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "Roles",
|
||||
"description": "Модуль для работы с ролями",
|
||||
"author": "OCAB Team",
|
||||
"version": "1.0"
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
from ..database.db_api import get_user_role
|
||||
from ..config.config import get_config
|
||||
|
||||
yaml_load = get_config()
|
||||
class Roles:
|
||||
user = "USER"
|
||||
moderator = "MODERATOR"
|
||||
admin = "ADMIN"
|
||||
bot = "BOT"
|
||||
__roles = yaml_load["ROLES"]
|
||||
|
||||
def __init__(self):
|
||||
self.user_role_id = self.__roles[self.user]
|
||||
self.moderator_role_id = self.__roles[self.moderator]
|
||||
self.admin_role_id = self.__roles[self.admin]
|
||||
self.bot_role_id = self.__roles[self.bot]
|
||||
|
||||
async def check_admin_permission(self, user_id):
|
||||
match get_user_role(user_id):
|
||||
case self.admin_role_id:
|
||||
return True
|
||||
case _:
|
||||
return False
|
||||
|
||||
async def check_moderator_permission(self, user_id):
|
||||
match get_user_role(user_id):
|
||||
case self.moderator_role_id:
|
||||
return True
|
||||
case _:
|
||||
return False
|
||||
|
||||
async def get_role_name(self, role_id):
|
||||
match role_id:
|
||||
case self.admin_role_id:
|
||||
return self.admin
|
||||
case self.moderator_role_id:
|
||||
return self.moderator
|
||||
case self.user_role_id:
|
||||
return self.user
|
||||
case self.bot_role_id:
|
||||
return self.bot
|
||||
case _:
|
||||
raise ValueError(f"Нет роли с id={role_id}")
|
||||
|
||||
async def get_user_permission(self, user_id):
|
||||
match get_user_role(user_id):
|
||||
case self.admin_role_id:
|
||||
return self.admin
|
||||
case self.moderator_role_id:
|
||||
return self.moderator
|
||||
case self.user_role_id:
|
||||
return self.user
|
||||
case self.bot_role_id:
|
||||
return self.bot
|
||||
case _:
|
||||
return None
|
||||
@@ -1,7 +0,0 @@
|
||||
TELEGRAM:
|
||||
TOKEN: xxxxxxxxxxxxxxxxxxxx
|
||||
ROLES:
|
||||
ADMIN: 0
|
||||
MODERATOR: 1
|
||||
USER: 2
|
||||
BOT: 3
|
||||
@@ -1,50 +0,0 @@
|
||||
import os
|
||||
import unittest
|
||||
from ...database.db_api import create_tables, connect_database, add_user
|
||||
from ..roles import Roles
|
||||
|
||||
|
||||
class TestRoles(unittest.IsolatedAsyncioTestCase):
|
||||
database = None
|
||||
path = None
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.roles = Roles()
|
||||
cls.database, cls.path = connect_database(is_test=True, module="roles")
|
||||
create_tables(cls.database)
|
||||
|
||||
add_user(user_id=1, user_name="TestUser1", user_tag="TestTag1", user_role=0, user_stats=30, user_rep=15)
|
||||
add_user(user_id=2, user_name="TestUser3", user_tag="TestTag3", user_role=1, user_stats=30, user_rep=15)
|
||||
add_user(user_id=3, user_name="TestUser4", user_tag="TestTag4", user_role=2, user_stats=30, user_rep=15)
|
||||
add_user(user_id=4, user_name="TestUser2", user_tag="TestTag2", user_role=3, user_stats=30, user_rep=15)
|
||||
async def test_check_admin_permission(cls):
|
||||
cls.assertTrue(await cls.roles.check_admin_permission(1))
|
||||
cls.assertFalse(await cls.roles.check_admin_permission(2))
|
||||
cls.assertFalse(await cls.roles.check_admin_permission(3))
|
||||
cls.assertFalse(await cls.roles.check_admin_permission(4))
|
||||
|
||||
async def test_check_moderator_permission(cls):
|
||||
cls.assertTrue(await cls.roles.check_moderator_permission(2))
|
||||
cls.assertFalse(await cls.roles.check_moderator_permission(0))
|
||||
cls.assertFalse(await cls.roles.check_moderator_permission(1))
|
||||
cls.assertFalse(await cls.roles.check_moderator_permission(3))
|
||||
|
||||
async def test_get_role_name(cls):
|
||||
cls.assertEqual(await cls.roles.get_role_name(cls.roles.admin_role_id), "ADMIN")
|
||||
cls.assertEqual(await cls.roles.get_role_name(cls.roles.moderator_role_id), "MODERATOR")
|
||||
cls.assertEqual(await cls.roles.get_role_name(cls.roles.user_role_id), "USER")
|
||||
cls.assertEqual(await cls.roles.get_role_name(cls.roles.bot_role_id), "BOT")
|
||||
with cls.assertRaises(ValueError):
|
||||
await cls.roles.get_role_name(999) # Несуществующий ID роли
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.database.close()
|
||||
os.system(f"rm {cls.path}")
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,74 +0,0 @@
|
||||
from aiogram import Bot
|
||||
from aiogram.types import Message
|
||||
from src.modules.standard.config.config import get_telegram_check_bot
|
||||
from src.modules.standard.roles.roles import Roles
|
||||
from src.modules.standard.database.db_api import *
|
||||
from src.modules.standard.moderation.moderation import mute_user, unmute_user, ban_user
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
from aiogram.types import inline_keyboard_button as types
|
||||
import random, asyncio
|
||||
from threading import Thread
|
||||
|
||||
async def create_math_task():
|
||||
first_number = random.randint(1, 100)
|
||||
second_number = random.randint(1, 100)
|
||||
answer = first_number + second_number
|
||||
fake_answers = []
|
||||
for i in range(3):
|
||||
diff = random.randint(1, 10)
|
||||
diff_sign = random.choice(["+", "-"])
|
||||
fake_answers.append(answer + diff if diff_sign == "+" else answer - diff)
|
||||
fake_answers.append(answer)
|
||||
random.shuffle(fake_answers)
|
||||
return [answer, first_number, second_number, fake_answers]
|
||||
|
||||
async def ban_user_timer(chat_id: int, user_id: int, time: int, bot: Bot):
|
||||
await asyncio.sleep(time)
|
||||
if get_user(user_id) is not None:
|
||||
pass
|
||||
else:
|
||||
await ban_user()
|
||||
|
||||
|
||||
|
||||
|
||||
async def check_new_user(message: Message, bot: Bot):
|
||||
print("check_new_user")
|
||||
if get_telegram_check_bot():
|
||||
# Проверяем наличие пользователя в базе данных
|
||||
|
||||
if get_user(message.from_user.id) is None:
|
||||
# Выдаём пользователю ограничение на отправку сообщений на 3 минуты
|
||||
ban_task = Thread(target=ban_user_timer, args=(message.chat.id, message.from_user.id, 180, bot))
|
||||
ban_task.start()
|
||||
# Создаём задачу с отложенным выполнением на 3 минуты
|
||||
|
||||
math_task = await create_math_task()
|
||||
text = f"{math_task[1]} + {math_task[2]}"
|
||||
builder = InlineKeyboardBuilder()
|
||||
for answer in math_task[3]:
|
||||
if answer == math_task[0]:
|
||||
builder.add(types.InlineKeyboardButton(
|
||||
text=answer,
|
||||
callback_data=f"check_math_task_true")
|
||||
)
|
||||
else:
|
||||
builder.add(types.InlineKeyboardButton(
|
||||
text=answer,
|
||||
callback_data=f"check_math_task_false")
|
||||
)
|
||||
await message.reply(
|
||||
f"Приветствую, {message.from_user.first_name}!\n"
|
||||
f"Для продолжения работы с ботом, пожалуйста, решите математический пример в течении 3х минут:\n"
|
||||
f"*{text}*",
|
||||
reply_markup=builder.as_markup()
|
||||
)
|
||||
|
||||
|
||||
async def math_task_true(message: Message, bot: Bot):
|
||||
await message.reply(f"Верно! Добро пожаловать в чат {message.from_user.first_name}")
|
||||
await unmute_user(message.chat.id, message.from_user.id, bot)
|
||||
add_user(message.from_user.id,
|
||||
message.from_user.first_name + ' ' + message.from_user.last_name,
|
||||
message.from_user.username)
|
||||
pass
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "Welcome",
|
||||
"description": "Мо",
|
||||
"author": "OCAB Team",
|
||||
"version": "1.0"
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
from aiogram import Router, F
|
||||
|
||||
from src.modules.standard.welcome.handlers import check_new_user
|
||||
|
||||
router = Router()
|
||||
|
||||
# Если в чат пришел новый пользователь
|
||||
router.message.register(check_new_user, F.new_chat_members.exists())
|
||||
# Ловин колбеки от кнопок с callback_data=f"check_math_task_true"
|
||||
router.callback_query.register(check_new_user, F.callback_data == "check_math_task_true")
|
||||
@@ -1,25 +0,0 @@
|
||||
import os.path
|
||||
from dataclasses import dataclass
|
||||
from json import loads
|
||||
|
||||
|
||||
@dataclass
|
||||
class Path:
|
||||
core: str
|
||||
modules_standard: str
|
||||
modules_custom: str
|
||||
|
||||
def _get_paths(path_to_json: str):
|
||||
with open(path_to_json, encoding="utf8") as f:
|
||||
paths = loads(f.read())
|
||||
return Path(
|
||||
core=paths["core"],
|
||||
modules_standard=paths["modules standard"],
|
||||
modules_custom=paths["modules custom"]
|
||||
)
|
||||
|
||||
|
||||
cwd = os.getcwd()
|
||||
cwd = cwd + "/src"
|
||||
|
||||
paths = _get_paths(f"{cwd}/paths.json")
|
||||
Reference in New Issue
Block a user