voiceroute.py 0.1.0.dev0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- voiceroute_py-0.1.0.dev0/.editorconfig +13 -0
- voiceroute_py-0.1.0.dev0/.env.example +6 -0
- voiceroute_py-0.1.0.dev0/.github/workflows/ci.yml +39 -0
- voiceroute_py-0.1.0.dev0/.gitignore +16 -0
- voiceroute_py-0.1.0.dev0/CHANGELOG.md +26 -0
- voiceroute_py-0.1.0.dev0/CONTRIBUTING.md +45 -0
- voiceroute_py-0.1.0.dev0/LICENSE +22 -0
- voiceroute_py-0.1.0.dev0/PKG-INFO +170 -0
- voiceroute_py-0.1.0.dev0/README.md +130 -0
- voiceroute_py-0.1.0.dev0/SECURITY.md +11 -0
- voiceroute_py-0.1.0.dev0/docs/api-coverage.md +34 -0
- voiceroute_py-0.1.0.dev0/docs/architecture.md +80 -0
- voiceroute_py-0.1.0.dev0/docs/commands.md +158 -0
- voiceroute_py-0.1.0.dev0/docs/content-api.md +65 -0
- voiceroute_py-0.1.0.dev0/docs/events.md +64 -0
- voiceroute_py-0.1.0.dev0/docs/extensions.md +86 -0
- voiceroute_py-0.1.0.dev0/docs/network.md +38 -0
- voiceroute_py-0.1.0.dev0/docs/roadmap.md +42 -0
- voiceroute_py-0.1.0.dev0/docs/runtime.md +121 -0
- voiceroute_py-0.1.0.dev0/docs/voiceroute-bot-platform-api-spec.md +1916 -0
- voiceroute_py-0.1.0.dev0/examples/__init__.py +1 -0
- voiceroute_py-0.1.0.dev0/examples/community_assistant/README.md +74 -0
- voiceroute_py-0.1.0.dev0/examples/community_assistant/__init__.py +5 -0
- voiceroute_py-0.1.0.dev0/examples/community_assistant/bot.py +45 -0
- voiceroute_py-0.1.0.dev0/examples/community_assistant/config.py +57 -0
- voiceroute_py-0.1.0.dev0/examples/community_assistant/data/faq.json +17 -0
- voiceroute_py-0.1.0.dev0/examples/community_assistant/extensions/__init__.py +1 -0
- voiceroute_py-0.1.0.dev0/examples/community_assistant/extensions/diagnostics.py +103 -0
- voiceroute_py-0.1.0.dev0/examples/community_assistant/extensions/general.py +55 -0
- voiceroute_py-0.1.0.dev0/examples/community_assistant/extensions/knowledge.py +101 -0
- voiceroute_py-0.1.0.dev0/examples/community_assistant/extensions/messages.py +164 -0
- voiceroute_py-0.1.0.dev0/examples/identify_bot.py +24 -0
- voiceroute_py-0.1.0.dev0/examples/ping_bot.py +42 -0
- voiceroute_py-0.1.0.dev0/pyproject.toml +78 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/__init__.py +149 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/api/__init__.py +8 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/api/bots.py +40 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/api/communities.py +36 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/api/events.py +86 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/api/messages.py +215 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/bot.py +535 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/client.py +48 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/commands/__init__.py +41 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/commands/context.py +81 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/commands/core.py +263 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/commands/pipeline.py +257 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/commands/prefix.py +44 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/config.py +18 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/errors.py +191 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/events/__init__.py +38 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/events/cursors.py +245 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/events/models.py +24 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/events/sse.py +63 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/events/transport.py +172 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/extensions.py +79 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/models/__init__.py +8 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/models/bot.py +21 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/models/community.py +68 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/models/message.py +75 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/models/user.py +23 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/runtime.py +13 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/transport/__init__.py +6 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/transport/auth.py +18 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/transport/http.py +204 -0
- voiceroute_py-0.1.0.dev0/src/voiceroute/transport/rate_limit.py +45 -0
- voiceroute_py-0.1.0.dev0/tests/contract/README.md +5 -0
- voiceroute_py-0.1.0.dev0/tests/contract/test_communities_api.py +27 -0
- voiceroute_py-0.1.0.dev0/tests/contract/test_http_transport.py +135 -0
- voiceroute_py-0.1.0.dev0/tests/contract/test_messages_api.py +187 -0
- voiceroute_py-0.1.0.dev0/tests/examples/test_community_assistant.py +179 -0
- voiceroute_py-0.1.0.dev0/tests/integration/README.md +5 -0
- voiceroute_py-0.1.0.dev0/tests/unit/test_auth.py +15 -0
- voiceroute_py-0.1.0.dev0/tests/unit/test_bot.py +249 -0
- voiceroute_py-0.1.0.dev0/tests/unit/test_command_framework.py +193 -0
- voiceroute_py-0.1.0.dev0/tests/unit/test_command_pipeline.py +181 -0
- voiceroute_py-0.1.0.dev0/tests/unit/test_community_models.py +47 -0
- voiceroute_py-0.1.0.dev0/tests/unit/test_config.py +15 -0
- voiceroute_py-0.1.0.dev0/tests/unit/test_context.py +133 -0
- voiceroute_py-0.1.0.dev0/tests/unit/test_cursor_store.py +103 -0
- voiceroute_py-0.1.0.dev0/tests/unit/test_event_transport.py +69 -0
- voiceroute_py-0.1.0.dev0/tests/unit/test_events_api.py +60 -0
- voiceroute_py-0.1.0.dev0/tests/unit/test_extensions.py +188 -0
- voiceroute_py-0.1.0.dev0/tests/unit/test_message_models.py +21 -0
- voiceroute_py-0.1.0.dev0/tests/unit/test_models.py +18 -0
- voiceroute_py-0.1.0.dev0/tests/unit/test_rate_limit.py +32 -0
- voiceroute_py-0.1.0.dev0/tests/unit/test_runtime_lifecycle.py +170 -0
- voiceroute_py-0.1.0.dev0/tests/unit/test_sse.py +28 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
VOICEROUTE_URL=https://dev.voiceroute.cc
|
|
2
|
+
VOICEROUTE_BOT_TOKEN=vrb_replace_me
|
|
3
|
+
# Необязательные ограничения для бота, слушающего только один канал:
|
|
4
|
+
# VOICEROUTE_COMMUNITY_ID=00000000-0000-4000-8000-000000000000
|
|
5
|
+
# VOICEROUTE_CHANNEL_ID=00000000-0000-4000-8000-000000000000
|
|
6
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
quality:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.11"
|
|
19
|
+
cache: pip
|
|
20
|
+
- run: python -m pip install -e '.[dev]'
|
|
21
|
+
- run: ruff check .
|
|
22
|
+
- run: ruff format --check .
|
|
23
|
+
- run: mypy src
|
|
24
|
+
|
|
25
|
+
tests:
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
strategy:
|
|
28
|
+
fail-fast: false
|
|
29
|
+
matrix:
|
|
30
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v4
|
|
33
|
+
- uses: actions/setup-python@v5
|
|
34
|
+
with:
|
|
35
|
+
python-version: ${{ matrix.python-version }}
|
|
36
|
+
cache: pip
|
|
37
|
+
- run: python -m pip install -e '.[dev]'
|
|
38
|
+
- run: pytest --cov=voiceroute --cov-report=term-missing
|
|
39
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# История изменений
|
|
2
|
+
|
|
3
|
+
Проект следует [Semantic Versioning](https://semver.org/) и рекомендациям
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/).
|
|
5
|
+
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- Начальная структура Python-пакета.
|
|
11
|
+
- Асинхронный HTTP transport и bot-аутентификация.
|
|
12
|
+
- Базовые модели и API профиля текущего бота.
|
|
13
|
+
- Восстанавливаемые SSE-потоки с checkpoint после успешной обработки.
|
|
14
|
+
- Атомарное файловое и process-local хранилища cursor.
|
|
15
|
+
- Общий sliding-window rate limiter и безопасные retry с учётом `Retry-After`.
|
|
16
|
+
- Постраничное восстановление backlog перед переходом к live SSE.
|
|
17
|
+
- Публичный `EventTransport` и `LegacySSETransport`, отделяющие runtime от сетевого
|
|
18
|
+
протокола событий.
|
|
19
|
+
- Независимые `Command`, `CommandRegistry`, `CommandInvocation` и
|
|
20
|
+
`PrefixCommandAdapter` с aliases, группами, типизированными аргументами и command
|
|
21
|
+
lifecycle events.
|
|
22
|
+
- Command pipeline с async middleware, checks, cooldowns и concurrency limits.
|
|
23
|
+
- Расширения с декларативными командами/listeners, атомарной регистрацией и module
|
|
24
|
+
load/unload/reload lifecycle.
|
|
25
|
+
- Явный runtime lifecycle, graceful shutdown и независимый supervision channel consumers.
|
|
26
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Участие в разработке
|
|
2
|
+
|
|
3
|
+
## Ветки
|
|
4
|
+
|
|
5
|
+
Используйте общепринятые префиксы:
|
|
6
|
+
|
|
7
|
+
- `feature/` — новая функциональность;
|
|
8
|
+
- `fix/` — исправление ошибки;
|
|
9
|
+
- `docs/` — документация;
|
|
10
|
+
- `refactor/` — внутреннее изменение без нового поведения;
|
|
11
|
+
- `test/` — тесты;
|
|
12
|
+
- `chore/` — инфраструктура и обслуживание;
|
|
13
|
+
- `release/` — подготовка выпуска;
|
|
14
|
+
- `hotfix/` — срочное исправление стабильной версии.
|
|
15
|
+
|
|
16
|
+
Ветки с префиксом `codex/` запрещены.
|
|
17
|
+
|
|
18
|
+
## Коммиты
|
|
19
|
+
|
|
20
|
+
Сообщения пишутся на английском языке в формате Conventional Commits со scope:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
feat(events): add resumable channel stream
|
|
24
|
+
fix(http): preserve idempotency key across retries
|
|
25
|
+
docs(readme): document development setup
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Слияния
|
|
29
|
+
|
|
30
|
+
- изменения попадают в `main` через pull request;
|
|
31
|
+
- fast-forward merge запрещён;
|
|
32
|
+
- используйте merge commit (`git merge --no-ff`);
|
|
33
|
+
- перед слиянием должны пройти lint, format, type check и tests;
|
|
34
|
+
- публичное поведение сопровождается тестами и документацией;
|
|
35
|
+
- несовместимые изменения до `1.0.0` явно отмечаются в changelog.
|
|
36
|
+
|
|
37
|
+
## Локальная проверка
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
ruff check .
|
|
41
|
+
ruff format --check .
|
|
42
|
+
mypy src
|
|
43
|
+
pytest
|
|
44
|
+
```
|
|
45
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dog-bobik
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: voiceroute.py
|
|
3
|
+
Version: 0.1.0.dev0
|
|
4
|
+
Summary: Async Python SDK and bot framework for the VoiceRoute HTTP and SSE APIs.
|
|
5
|
+
Project-URL: Homepage, https://github.com/dog-bobik/voiceroute.py
|
|
6
|
+
Project-URL: Documentation, https://github.com/dog-bobik/voiceroute.py#readme
|
|
7
|
+
Project-URL: Issues, https://github.com/dog-bobik/voiceroute.py/issues
|
|
8
|
+
Project-URL: Repository, https://github.com/dog-bobik/voiceroute.py.git
|
|
9
|
+
Author: dog-bobik
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: asyncio,bot,sdk,sse,voiceroute
|
|
13
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
14
|
+
Classifier: Framework :: AsyncIO
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Requires-Dist: httpx<1,>=0.27
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: mypy>=1.13; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest-cov>=6; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=8.3; extra == 'dev'
|
|
29
|
+
Requires-Dist: python-dotenv<2,>=1.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: respx>=0.22; extra == 'dev'
|
|
31
|
+
Requires-Dist: ruff>=0.8; extra == 'dev'
|
|
32
|
+
Provides-Extra: docs
|
|
33
|
+
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
34
|
+
Requires-Dist: mkdocstrings[python]>=0.27; extra == 'docs'
|
|
35
|
+
Provides-Extra: examples
|
|
36
|
+
Requires-Dist: python-dotenv<2,>=1.0; extra == 'examples'
|
|
37
|
+
Provides-Extra: redis
|
|
38
|
+
Requires-Dist: redis<8,>=5; extra == 'redis'
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
|
|
41
|
+
# voiceroute.py
|
|
42
|
+
|
|
43
|
+
Асинхронный Python SDK и фреймворк для разработки ботов в
|
|
44
|
+
[VoiceRoute](https://voiceroute.cc).
|
|
45
|
+
|
|
46
|
+
> [!WARNING]
|
|
47
|
+
> Проект находится на ранней стадии разработки. Публичный API может меняться до
|
|
48
|
+
> выпуска версии `1.0.0`.
|
|
49
|
+
|
|
50
|
+
## Цели проекта
|
|
51
|
+
|
|
52
|
+
- типизированный асинхронный клиент HTTP API;
|
|
53
|
+
- надёжная обработка SSE с восстановлением курсора;
|
|
54
|
+
- идемпотентная отправка сообщений;
|
|
55
|
+
- удобная событийная модель и команды в стиле современных bot-фреймворков;
|
|
56
|
+
- расширяемая архитектура без смешивания bot token и пользовательской сессии;
|
|
57
|
+
- безопасная работа с лимитами запросов, повторными попытками и секретами.
|
|
58
|
+
|
|
59
|
+
## Установка для разработки
|
|
60
|
+
|
|
61
|
+
Требуется Python 3.11 или новее.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
python -m venv .venv
|
|
65
|
+
source .venv/bin/activate
|
|
66
|
+
python -m pip install -e '.[dev]'
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Примеры автоматически читают локальный `.env` из корня репозитория. В нём
|
|
70
|
+
достаточно указать `VOICEROUTE_BOT_TOKEN`; production используется по умолчанию.
|
|
71
|
+
|
|
72
|
+
Полноценный демонстрационный пример: [Community Assistant Bot](examples/community_assistant/README.md).
|
|
73
|
+
|
|
74
|
+
## Минимальный бот
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
import os
|
|
78
|
+
|
|
79
|
+
from voiceroute import Bot, Context, FileCursorStore
|
|
80
|
+
|
|
81
|
+
bot = Bot(
|
|
82
|
+
token=os.environ["VOICEROUTE_BOT_TOKEN"],
|
|
83
|
+
cursor_store=FileCursorStore(".voiceroute/cursors.json"),
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@bot.command()
|
|
88
|
+
async def ping(context: Context) -> None:
|
|
89
|
+
await context.reply("Pong!")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
bot.run()
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Ответ использует стабильный UUID, производный от исходного сообщения. Повторная
|
|
96
|
+
доставка SSE-события поэтому не создаст дубликат ответа.
|
|
97
|
+
|
|
98
|
+
По умолчанию команды принимают оба привычных префикса: `/ping` и `!ping`.
|
|
99
|
+
Собственный префикс задаётся через `Bot(..., command_prefix="?")`.
|
|
100
|
+
|
|
101
|
+
Команды поддерживают aliases, группы и типизированные позиционные аргументы. Метаданные и
|
|
102
|
+
вызов не зависят от prefix transport, поэтому будущие server-native interactions можно
|
|
103
|
+
будет подключить отдельным адаптером. Подробнее: [командный фреймворк](docs/commands.md).
|
|
104
|
+
|
|
105
|
+
Команды и listeners больших ботов можно объединять в расширения с атомарной загрузкой,
|
|
106
|
+
выгрузкой и reload. Подробнее: [расширения](docs/extensions.md).
|
|
107
|
+
|
|
108
|
+
Runtime поддерживает явные состояния, graceful shutdown и изоляцию channel consumers с
|
|
109
|
+
ограниченным backoff. Подробнее: [жизненный цикл runtime](docs/runtime.md).
|
|
110
|
+
|
|
111
|
+
Клиент умеет редактировать и удалять сообщения, управлять закреплением и реакцией,
|
|
112
|
+
читать pins и выполнять поиск. Подробнее: [контентный API](docs/content-api.md).
|
|
113
|
+
|
|
114
|
+
По умолчанию бот обнаруживает все доступные ему сообщества и текстовые каналы.
|
|
115
|
+
Для ограничения одним каналом можно явно передать `community_id` и `channel_id`.
|
|
116
|
+
При первом подключении он запрашивает историю после `seq=0`, чтобы не пропустить
|
|
117
|
+
команды, отправленные незадолго до запуска. Идемпотентные ответы защищают от
|
|
118
|
+
дубликатов при повторном получении тех же сообщений.
|
|
119
|
+
|
|
120
|
+
`FileCursorStore` атомарно сохраняет обработанную позицию с правами `0600`.
|
|
121
|
+
Cursor обновляется только после успешного завершения обработчика, поэтому после
|
|
122
|
+
сбоя событие будет доставлено повторно. Для нескольких процессов доступны SQLite и
|
|
123
|
+
Redis. Подробнее: [события и восстановление](docs/events.md).
|
|
124
|
+
|
|
125
|
+
Все HTTP-операции одного клиента используют общий лимитер 50 запросов в секунду.
|
|
126
|
+
Безопасные запросы и идемпотентная отправка сообщений повторяются после временных
|
|
127
|
+
сетевых ошибок; `429` соблюдает `Retry-After`. Подробнее: [сеть и повторы](docs/network.md).
|
|
128
|
+
|
|
129
|
+
## Состояние реализации
|
|
130
|
+
|
|
131
|
+
| Возможность | Состояние |
|
|
132
|
+
| --- | --- |
|
|
133
|
+
| Bot-аутентификация | Основа готова |
|
|
134
|
+
| Профиль текущего бота | Основа готова |
|
|
135
|
+
| Сообщества и каналы | Базовое чтение готово |
|
|
136
|
+
| Сообщения | История, отправка и изменение готовы |
|
|
137
|
+
| Реакции, закрепления и поиск | Основа готова |
|
|
138
|
+
| SSE и восстановление курсора | Основа готова |
|
|
139
|
+
| Prefix-команды | Основа готова |
|
|
140
|
+
| Middleware | Основа готова |
|
|
141
|
+
| Треды и вложения | Запланировано |
|
|
142
|
+
| Роли и модерация | Запланировано |
|
|
143
|
+
| Голос для сторонних ботов | Не поддерживается API [VoiceRoute](https://voiceroute.cc) |
|
|
144
|
+
|
|
145
|
+
Подробная последовательность работ находится в [дорожной карте](docs/roadmap.md),
|
|
146
|
+
текущее покрытие — в [матрице API](docs/api-coverage.md), а принятые границы проекта —
|
|
147
|
+
в [описании архитектуры](docs/architecture.md).
|
|
148
|
+
|
|
149
|
+
## Разработка
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
ruff check .
|
|
153
|
+
ruff format --check .
|
|
154
|
+
mypy src
|
|
155
|
+
pytest --cov=voiceroute --cov-report=term-missing
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Правила веток, коммитов и слияний описаны в [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
159
|
+
|
|
160
|
+
## Безопасность
|
|
161
|
+
|
|
162
|
+
Не добавляйте bot token в исходный код, URL, query-параметры или логи. Используйте
|
|
163
|
+
переменные окружения и передавайте токен только через официальный заголовок
|
|
164
|
+
авторизации. Инструкции по сообщению об уязвимостях находятся в
|
|
165
|
+
[SECURITY.md](SECURITY.md).
|
|
166
|
+
|
|
167
|
+
## Лицензия
|
|
168
|
+
|
|
169
|
+
[MIT](LICENSE)
|
|
170
|
+
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# voiceroute.py
|
|
2
|
+
|
|
3
|
+
Асинхронный Python SDK и фреймворк для разработки ботов в
|
|
4
|
+
[VoiceRoute](https://voiceroute.cc).
|
|
5
|
+
|
|
6
|
+
> [!WARNING]
|
|
7
|
+
> Проект находится на ранней стадии разработки. Публичный API может меняться до
|
|
8
|
+
> выпуска версии `1.0.0`.
|
|
9
|
+
|
|
10
|
+
## Цели проекта
|
|
11
|
+
|
|
12
|
+
- типизированный асинхронный клиент HTTP API;
|
|
13
|
+
- надёжная обработка SSE с восстановлением курсора;
|
|
14
|
+
- идемпотентная отправка сообщений;
|
|
15
|
+
- удобная событийная модель и команды в стиле современных bot-фреймворков;
|
|
16
|
+
- расширяемая архитектура без смешивания bot token и пользовательской сессии;
|
|
17
|
+
- безопасная работа с лимитами запросов, повторными попытками и секретами.
|
|
18
|
+
|
|
19
|
+
## Установка для разработки
|
|
20
|
+
|
|
21
|
+
Требуется Python 3.11 или новее.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
python -m venv .venv
|
|
25
|
+
source .venv/bin/activate
|
|
26
|
+
python -m pip install -e '.[dev]'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Примеры автоматически читают локальный `.env` из корня репозитория. В нём
|
|
30
|
+
достаточно указать `VOICEROUTE_BOT_TOKEN`; production используется по умолчанию.
|
|
31
|
+
|
|
32
|
+
Полноценный демонстрационный пример: [Community Assistant Bot](examples/community_assistant/README.md).
|
|
33
|
+
|
|
34
|
+
## Минимальный бот
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
import os
|
|
38
|
+
|
|
39
|
+
from voiceroute import Bot, Context, FileCursorStore
|
|
40
|
+
|
|
41
|
+
bot = Bot(
|
|
42
|
+
token=os.environ["VOICEROUTE_BOT_TOKEN"],
|
|
43
|
+
cursor_store=FileCursorStore(".voiceroute/cursors.json"),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@bot.command()
|
|
48
|
+
async def ping(context: Context) -> None:
|
|
49
|
+
await context.reply("Pong!")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
bot.run()
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Ответ использует стабильный UUID, производный от исходного сообщения. Повторная
|
|
56
|
+
доставка SSE-события поэтому не создаст дубликат ответа.
|
|
57
|
+
|
|
58
|
+
По умолчанию команды принимают оба привычных префикса: `/ping` и `!ping`.
|
|
59
|
+
Собственный префикс задаётся через `Bot(..., command_prefix="?")`.
|
|
60
|
+
|
|
61
|
+
Команды поддерживают aliases, группы и типизированные позиционные аргументы. Метаданные и
|
|
62
|
+
вызов не зависят от prefix transport, поэтому будущие server-native interactions можно
|
|
63
|
+
будет подключить отдельным адаптером. Подробнее: [командный фреймворк](docs/commands.md).
|
|
64
|
+
|
|
65
|
+
Команды и listeners больших ботов можно объединять в расширения с атомарной загрузкой,
|
|
66
|
+
выгрузкой и reload. Подробнее: [расширения](docs/extensions.md).
|
|
67
|
+
|
|
68
|
+
Runtime поддерживает явные состояния, graceful shutdown и изоляцию channel consumers с
|
|
69
|
+
ограниченным backoff. Подробнее: [жизненный цикл runtime](docs/runtime.md).
|
|
70
|
+
|
|
71
|
+
Клиент умеет редактировать и удалять сообщения, управлять закреплением и реакцией,
|
|
72
|
+
читать pins и выполнять поиск. Подробнее: [контентный API](docs/content-api.md).
|
|
73
|
+
|
|
74
|
+
По умолчанию бот обнаруживает все доступные ему сообщества и текстовые каналы.
|
|
75
|
+
Для ограничения одним каналом можно явно передать `community_id` и `channel_id`.
|
|
76
|
+
При первом подключении он запрашивает историю после `seq=0`, чтобы не пропустить
|
|
77
|
+
команды, отправленные незадолго до запуска. Идемпотентные ответы защищают от
|
|
78
|
+
дубликатов при повторном получении тех же сообщений.
|
|
79
|
+
|
|
80
|
+
`FileCursorStore` атомарно сохраняет обработанную позицию с правами `0600`.
|
|
81
|
+
Cursor обновляется только после успешного завершения обработчика, поэтому после
|
|
82
|
+
сбоя событие будет доставлено повторно. Для нескольких процессов доступны SQLite и
|
|
83
|
+
Redis. Подробнее: [события и восстановление](docs/events.md).
|
|
84
|
+
|
|
85
|
+
Все HTTP-операции одного клиента используют общий лимитер 50 запросов в секунду.
|
|
86
|
+
Безопасные запросы и идемпотентная отправка сообщений повторяются после временных
|
|
87
|
+
сетевых ошибок; `429` соблюдает `Retry-After`. Подробнее: [сеть и повторы](docs/network.md).
|
|
88
|
+
|
|
89
|
+
## Состояние реализации
|
|
90
|
+
|
|
91
|
+
| Возможность | Состояние |
|
|
92
|
+
| --- | --- |
|
|
93
|
+
| Bot-аутентификация | Основа готова |
|
|
94
|
+
| Профиль текущего бота | Основа готова |
|
|
95
|
+
| Сообщества и каналы | Базовое чтение готово |
|
|
96
|
+
| Сообщения | История, отправка и изменение готовы |
|
|
97
|
+
| Реакции, закрепления и поиск | Основа готова |
|
|
98
|
+
| SSE и восстановление курсора | Основа готова |
|
|
99
|
+
| Prefix-команды | Основа готова |
|
|
100
|
+
| Middleware | Основа готова |
|
|
101
|
+
| Треды и вложения | Запланировано |
|
|
102
|
+
| Роли и модерация | Запланировано |
|
|
103
|
+
| Голос для сторонних ботов | Не поддерживается API [VoiceRoute](https://voiceroute.cc) |
|
|
104
|
+
|
|
105
|
+
Подробная последовательность работ находится в [дорожной карте](docs/roadmap.md),
|
|
106
|
+
текущее покрытие — в [матрице API](docs/api-coverage.md), а принятые границы проекта —
|
|
107
|
+
в [описании архитектуры](docs/architecture.md).
|
|
108
|
+
|
|
109
|
+
## Разработка
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
ruff check .
|
|
113
|
+
ruff format --check .
|
|
114
|
+
mypy src
|
|
115
|
+
pytest --cov=voiceroute --cov-report=term-missing
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Правила веток, коммитов и слияний описаны в [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
119
|
+
|
|
120
|
+
## Безопасность
|
|
121
|
+
|
|
122
|
+
Не добавляйте bot token в исходный код, URL, query-параметры или логи. Используйте
|
|
123
|
+
переменные окружения и передавайте токен только через официальный заголовок
|
|
124
|
+
авторизации. Инструкции по сообщению об уязвимостях находятся в
|
|
125
|
+
[SECURITY.md](SECURITY.md).
|
|
126
|
+
|
|
127
|
+
## Лицензия
|
|
128
|
+
|
|
129
|
+
[MIT](LICENSE)
|
|
130
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Политика безопасности
|
|
2
|
+
|
|
3
|
+
Не публикуйте сведения об уязвимости в открытом issue до согласования исправления.
|
|
4
|
+
Свяжитесь с владельцем репозитория через приватный канал GitHub.
|
|
5
|
+
|
|
6
|
+
Никогда не прикладывайте к отчёту действующие bot token, cookie, пароль,
|
|
7
|
+
recovery code или полный секретный URL вебхука. Перед отправкой логов удалите из
|
|
8
|
+
них все секреты.
|
|
9
|
+
|
|
10
|
+
Поддерживаемые версии будут перечислены здесь после первого стабильного релиза.
|
|
11
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Покрытие API VoiceRoute
|
|
2
|
+
|
|
3
|
+
Покрытие кода тестами и покрытие API — разные метрики. Процент из `pytest-cov`
|
|
4
|
+
показывает, сколько веток Python-кода выполнили тесты. Эта матрица показывает, какие
|
|
5
|
+
bot-compatible ресурсы [VoiceRoute](https://voiceroute.cc) представлены публичным API
|
|
6
|
+
библиотеки.
|
|
7
|
+
|
|
8
|
+
В знаменатель не входят пользовательские сессии, пароль, MFA, друзья, личные и
|
|
9
|
+
групповые чаты, управление bot applications/installations и создание сообщества: по
|
|
10
|
+
предоставленному контракту bot token получает для них `403`. Голос также не считается
|
|
11
|
+
реализуемым ресурсом, пока сервер не выдаёт сторонним ботам WebRTC token.
|
|
12
|
+
|
|
13
|
+
| Домен | Реализовано | Контрактные тесты | Статус |
|
|
14
|
+
| --- | --- | --- | --- |
|
|
15
|
+
| Текущий бот | профиль и совместимый `/auth/me` | частично | базовый |
|
|
16
|
+
| Сообщества | список, одно сообщество | да | базовый |
|
|
17
|
+
| Каналы | список доступных каналов | да | базовый |
|
|
18
|
+
| События | channel SSE, catch-up, checkpoint | да | готово |
|
|
19
|
+
| Сообщения | история, отправка, edit, delete | да | готово |
|
|
20
|
+
| Контент сообщений | pins, reactions, search | да | готово |
|
|
21
|
+
| Вложения | — | — | следующий домен |
|
|
22
|
+
| Треды и форумы | — | — | следующий домен |
|
|
23
|
+
| Участники, роли, permissions | — | — | запланировано |
|
|
24
|
+
| Баны, timeout, AutoMod, audit | — | — | запланировано |
|
|
25
|
+
| Categories и управление channels | — | — | запланировано |
|
|
26
|
+
| Expressions, sounds, soundboard | — | — | запланировано |
|
|
27
|
+
| Webhooks | — | — | запланировано |
|
|
28
|
+
| Stage и music | — | — | требует отдельного контракта |
|
|
29
|
+
|
|
30
|
+
Матрица ведётся по доменам, а не одним вводящим в заблуждение процентом. Некоторые
|
|
31
|
+
маршруты в реестре являются alias одного обработчика, а отдельные маршруты формально
|
|
32
|
+
находятся под `/api/communities/:id/...`, но требуют внешнего сервиса или недоступного
|
|
33
|
+
ботам транспорта. Числовой процент появится после машиночитаемого OpenAPI со
|
|
34
|
+
стабильными operation ID и признаками bot compatibility.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Архитектура
|
|
2
|
+
|
|
3
|
+
Библиотека строится слоями и не связывает пользовательский API с деталями сети.
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
Bot framework
|
|
7
|
+
-> Resource APIs
|
|
8
|
+
-> HTTP and SSE transports
|
|
9
|
+
-> VoiceRoute API
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Transport
|
|
13
|
+
|
|
14
|
+
Transport отвечает за авторизацию, URL, JSON, таймауты, rate limits, retries и
|
|
15
|
+
преобразование HTTP-ошибок. Он не содержит бизнес-объектов и команд.
|
|
16
|
+
|
|
17
|
+
Один `VoiceRouteClient` разделяет sliding-window limiter между resource API и
|
|
18
|
+
открытием SSE. Retry разрешён по умолчанию только безопасным HTTP-методам.
|
|
19
|
+
Изменяющая операция обязана явно подтвердить идемпотентность; отправка сообщения
|
|
20
|
+
делает это благодаря стабильному `clientMessageId`.
|
|
21
|
+
|
|
22
|
+
## Resource API
|
|
23
|
+
|
|
24
|
+
Модули `api` группируют endpoint-ы по предметной области. Сырые ответы
|
|
25
|
+
преобразуются в типизированные DTO из `models`.
|
|
26
|
+
|
|
27
|
+
## Events
|
|
28
|
+
|
|
29
|
+
Runtime получает события через протокол `EventTransport` и не зависит от сетевого
|
|
30
|
+
формата. Текущий `LegacySSETransport` объединяет постраничное восстановление истории и
|
|
31
|
+
канальный SSE в поток `EventDelivery`. Каждая доставка содержит нормализованное событие,
|
|
32
|
+
источник и checkpoint, который runtime сохраняет только после успешной обработки.
|
|
33
|
+
|
|
34
|
+
Пользовательский transport можно передать через `Bot(..., event_transport=...)`. Это
|
|
35
|
+
позволяет тестировать runtime без сети и позже добавить Gateway-адаптер без изменения
|
|
36
|
+
команд и обработчиков бота.
|
|
37
|
+
|
|
38
|
+
SSE-потоки используют отдельные курсоры. События сообщений применяются как
|
|
39
|
+
upsert, пока серверный контракт не позволяет достоверно различить создание и
|
|
40
|
+
изменение без локального состояния.
|
|
41
|
+
|
|
42
|
+
Runtime сохраняет составной checkpoint: `message_seq` закрывает начальную выдачу,
|
|
43
|
+
у которой ещё нет SSE ID, а `change_cursor` восстанавливает поток изменений.
|
|
44
|
+
Checkpoint фиксируется только после успешной обработки события. Хранилище
|
|
45
|
+
задаётся через протокол `CursorStore`; встроены process-local и атомарный файловый
|
|
46
|
+
адаптеры. Файловый адаптер предназначен для одного процесса.
|
|
47
|
+
|
|
48
|
+
## Commands
|
|
49
|
+
|
|
50
|
+
Команды являются клиентской абстракцией поверх обычных сообщений. Библиотека не
|
|
51
|
+
имитирует отсутствующие на сервере interactions, intents или gateway.
|
|
52
|
+
|
|
53
|
+
`CommandRegistry` хранит метаданные, aliases и многоуровневые пути, а `Command` отвечает
|
|
54
|
+
за типизированное преобразование аргументов и вызов callback. `PrefixCommandAdapter`
|
|
55
|
+
только преобразует текст сообщения в независимый `CommandInvocation`. Будущий adapter
|
|
56
|
+
interactions сможет использовать тот же registry и pipeline.
|
|
57
|
+
|
|
58
|
+
`CommandPipeline` последовательно выполняет checks, cooldown, concurrency gate и
|
|
59
|
+
middleware. Состояние встроенных ограничителей локально процессу; распределённые
|
|
60
|
+
адаптеры хранения будут добавлены отдельно и не должны менять публичные декораторы.
|
|
61
|
+
|
|
62
|
+
## Extensions
|
|
63
|
+
|
|
64
|
+
`Extension` группирует bound commands и event listeners. Подключение атомарно: конфликт
|
|
65
|
+
регистрации откатывает уже добавленные handlers и вызывает teardown. Модульная загрузка
|
|
66
|
+
требует `async setup(bot) -> Extension`; unload удаляет exact handlers экземпляра, а reload
|
|
67
|
+
восстанавливает предыдущий экземпляр при ошибке новой версии.
|
|
68
|
+
|
|
69
|
+
## Границы безопасности
|
|
70
|
+
|
|
71
|
+
Runtime-клиент принимает только bot token. Управление приложениями через cookie,
|
|
72
|
+
пароль и MFA не входит в него и при необходимости будет реализовано отдельно.
|
|
73
|
+
|
|
74
|
+
## Runtime lifecycle
|
|
75
|
+
|
|
76
|
+
`Bot` имеет явные состояния от `CREATED` до `CLOSED`. Channel consumers изолированы
|
|
77
|
+
supervisor-ами: необработанная ошибка одного канала запускает bounded backoff и чтение с
|
|
78
|
+
последнего подтверждённого checkpoint, не отменяя остальные каналы. `stop()` сериализует
|
|
79
|
+
конкурирующие остановки, отменяет consumers и освобождает extensions и HTTP transport.
|
|
80
|
+
|