ya-dialogs-api 1.0.0__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.
- ya_dialogs_api-1.0.0/.gitignore +55 -0
- ya_dialogs_api-1.0.0/CHANGELOG.md +65 -0
- ya_dialogs_api-1.0.0/LICENSE +21 -0
- ya_dialogs_api-1.0.0/NOTICE +29 -0
- ya_dialogs_api-1.0.0/PKG-INFO +170 -0
- ya_dialogs_api-1.0.0/README.md +107 -0
- ya_dialogs_api-1.0.0/SECURITY.md +50 -0
- ya_dialogs_api-1.0.0/VERSION +1 -0
- ya_dialogs_api-1.0.0/pyproject.toml +211 -0
- ya_dialogs_api-1.0.0/src/ya_dialogs_api/__init__.py +85 -0
- ya_dialogs_api-1.0.0/src/ya_dialogs_api/api_client.py +1119 -0
- ya_dialogs_api-1.0.0/src/ya_dialogs_api/assets/__init__.py +1 -0
- ya_dialogs_api-1.0.0/src/ya_dialogs_api/assets/default_logo.png +0 -0
- ya_dialogs_api-1.0.0/src/ya_dialogs_api/py.typed +0 -0
- ya_dialogs_api-1.0.0/src/ya_dialogs_api/state.py +122 -0
- ya_dialogs_api-1.0.0/tests/__init__.py +0 -0
- ya_dialogs_api-1.0.0/tests/test_api_client.py +874 -0
- ya_dialogs_api-1.0.0/tests/test_state.py +129 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Byte-compiled / optimized
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
|
|
7
|
+
# Distribution / packaging
|
|
8
|
+
build/
|
|
9
|
+
dist/
|
|
10
|
+
*.egg-info/
|
|
11
|
+
*.egg
|
|
12
|
+
wheels/
|
|
13
|
+
share/python-wheels/
|
|
14
|
+
.eggs/
|
|
15
|
+
MANIFEST
|
|
16
|
+
|
|
17
|
+
# Virtual environments
|
|
18
|
+
.venv/
|
|
19
|
+
venv/
|
|
20
|
+
env/
|
|
21
|
+
ENV/
|
|
22
|
+
|
|
23
|
+
# Tooling caches
|
|
24
|
+
.mypy_cache/
|
|
25
|
+
.ruff_cache/
|
|
26
|
+
.pytest_cache/
|
|
27
|
+
.tox/
|
|
28
|
+
.nox/
|
|
29
|
+
.cache/
|
|
30
|
+
htmlcov/
|
|
31
|
+
.coverage
|
|
32
|
+
.coverage.*
|
|
33
|
+
coverage.xml
|
|
34
|
+
*.cover
|
|
35
|
+
.hypothesis/
|
|
36
|
+
|
|
37
|
+
# Secrets scan
|
|
38
|
+
.secrets.baseline
|
|
39
|
+
|
|
40
|
+
# Editors / OS
|
|
41
|
+
.vscode/
|
|
42
|
+
.idea/
|
|
43
|
+
*.swp
|
|
44
|
+
*.swo
|
|
45
|
+
.DS_Store
|
|
46
|
+
Thumbs.db
|
|
47
|
+
|
|
48
|
+
# Generated
|
|
49
|
+
_version.py
|
|
50
|
+
sbom.json
|
|
51
|
+
*.sigstore
|
|
52
|
+
*.intoto.jsonl
|
|
53
|
+
|
|
54
|
+
# Local test artifacts
|
|
55
|
+
manual_qr_output.txt
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file. The format is based on
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres to
|
|
5
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [1.0.0] — 2026-05-06
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
Initial public release. Async Yandex Dialogs Developer API client extracted
|
|
14
|
+
from `ma-provider-yandex-smarthome/provider/auto_skill.py` and made
|
|
15
|
+
framework-agnostic.
|
|
16
|
+
|
|
17
|
+
**Public API:**
|
|
18
|
+
|
|
19
|
+
- `auto_create_skill(...)` — full skill-creation pipeline (CSRF/cookie session
|
|
20
|
+
→ POST `/apps` → upload logo → PATCH draft → POST OAuth app → attach OAuth
|
|
21
|
+
→ request deploy). Handles both `skill_type="smart_home"` and
|
|
22
|
+
`skill_type="dialog"` channels. Resumable via `SkillCreationArtifacts`.
|
|
23
|
+
- `auto_rename_dialog_skill(...)` — patches a dialog skill draft name and
|
|
24
|
+
re-deploys.
|
|
25
|
+
- `DialogsSkillCreator` — low-level dev-console client (one method per
|
|
26
|
+
pipeline step).
|
|
27
|
+
- `SkillCreationArtifacts` / `SkillCreationState` — incremental state
|
|
28
|
+
machine; callers persist between calls so transient failures resume from
|
|
29
|
+
the last completed step.
|
|
30
|
+
- Payload builders: `build_smart_home_draft_payload`,
|
|
31
|
+
`build_dialog_draft_payload`, `build_oauth_app_payload`.
|
|
32
|
+
- `load_default_logo_bytes()` — bundled fallback skill logo.
|
|
33
|
+
- `SecretStr` re-exported from `ya-passport-auth` for token redaction.
|
|
34
|
+
- Typed errors: `DialogsApiError`, `DialogsCsrfError`,
|
|
35
|
+
`DialogsDuplicateSkillError`.
|
|
36
|
+
|
|
37
|
+
**Architecture:**
|
|
38
|
+
|
|
39
|
+
The library is **framework-agnostic**. Authentication is the caller's
|
|
40
|
+
responsibility — pass a no-arg async-context-manager factory (`AuthenticatorCM`)
|
|
41
|
+
that yields an authenticated `aiohttp.ClientSession`. Typically the caller
|
|
42
|
+
wraps `ya_passport_auth.PassportClient.login_device_code` and any UX surface
|
|
43
|
+
they want around the Device Flow user-code prompt (CLI, web page, Telegram,
|
|
44
|
+
Music Assistant config-flow, etc.).
|
|
45
|
+
|
|
46
|
+
All URLs are pre-computed by the caller (`backend_uri`, `oauth_authorize_url`,
|
|
47
|
+
`oauth_token_url`, `oauth_client_id`, `oauth_client_secret`) — the lib has
|
|
48
|
+
no opinion on connection-type or hosting.
|
|
49
|
+
|
|
50
|
+
Failures are values, not exceptions: pipeline errors land as
|
|
51
|
+
`artifacts.state=FAILED` with `last_error` set. Truly unexpected exceptions
|
|
52
|
+
still propagate.
|
|
53
|
+
|
|
54
|
+
**Testing:**
|
|
55
|
+
|
|
56
|
+
- 47 tests across `DialogsSkillCreator` methods, payload builders,
|
|
57
|
+
state-machine serialisation, orchestrator happy paths, resume from each
|
|
58
|
+
state, and failure recovery.
|
|
59
|
+
- `mypy --strict` clean.
|
|
60
|
+
- 86% coverage (state.py 100%, api_client.py 83%).
|
|
61
|
+
|
|
62
|
+
**Runtime dependencies:**
|
|
63
|
+
|
|
64
|
+
- `aiohttp >= 3.10, < 4`
|
|
65
|
+
- `ya-passport-auth >= 1.3.0`
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mikhail Nevskiy
|
|
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.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
ya-dialogs-api
|
|
2
|
+
Copyright (c) 2026 Mikhail Nevskiy
|
|
3
|
+
|
|
4
|
+
This library implements a client for the Yandex Dialogs Developer API
|
|
5
|
+
(`dialogs.yandex.ru/developer-api/v2/`). The API is undocumented and
|
|
6
|
+
private — the request shapes, endpoint paths, CSRF extraction pattern,
|
|
7
|
+
and pipeline ordering were captured from Chrome DevTools HAR files
|
|
8
|
+
during interactive sessions with the Yandex developer console.
|
|
9
|
+
|
|
10
|
+
This product includes software originally developed as part of the
|
|
11
|
+
ma-provider-yandex-smarthome Music Assistant plugin:
|
|
12
|
+
|
|
13
|
+
ma-provider-yandex-smarthome
|
|
14
|
+
Copyright (c) 2025-2026 Mikhail Nevskiy
|
|
15
|
+
https://github.com/trudenboy/ma-provider-yandex-smarthome
|
|
16
|
+
Licensed under the MIT License
|
|
17
|
+
|
|
18
|
+
The skill auto-creation pipeline (Device Flow OAuth → CSRF/cookie session
|
|
19
|
+
→ POST /apps → POST /skills/{id}/draft → publication polling) was
|
|
20
|
+
extracted from `provider/auto_skill.py` of that project and made
|
|
21
|
+
framework-agnostic for general use.
|
|
22
|
+
|
|
23
|
+
The default skill logo asset (`src/ya_dialogs_api/assets/default_logo.png`)
|
|
24
|
+
is also drawn from that upstream project.
|
|
25
|
+
|
|
26
|
+
This library uses ya-passport-auth (https://github.com/trudenboy/ya-passport-auth)
|
|
27
|
+
for the Yandex Passport authentication side of the flow. ya-passport-auth
|
|
28
|
+
in turn re-implements work originally derived from AlexxIT/YandexStation;
|
|
29
|
+
see ya-passport-auth's NOTICE for that upstream attribution.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ya-dialogs-api
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Yandex Dialogs Developer API client — programmatic skill creation, draft management, OAuth Device Flow
|
|
5
|
+
Project-URL: Homepage, https://github.com/trudenboy/ya-dialogs-api
|
|
6
|
+
Project-URL: Repository, https://github.com/trudenboy/ya-dialogs-api
|
|
7
|
+
Project-URL: Issues, https://github.com/trudenboy/ya-dialogs-api/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/trudenboy/ya-dialogs-api/blob/main/CHANGELOG.md
|
|
9
|
+
Author: Mikhail Nevskiy
|
|
10
|
+
License: MIT License
|
|
11
|
+
|
|
12
|
+
Copyright (c) 2026 Mikhail Nevskiy
|
|
13
|
+
|
|
14
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
15
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
16
|
+
in the Software without restriction, including without limitation the rights
|
|
17
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
18
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
19
|
+
furnished to do so, subject to the following conditions:
|
|
20
|
+
|
|
21
|
+
The above copyright notice and this permission notice shall be included in all
|
|
22
|
+
copies or substantial portions of the Software.
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
25
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
26
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
27
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
28
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
29
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
30
|
+
SOFTWARE.
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
License-File: NOTICE
|
|
33
|
+
Keywords: aiohttp,alice,api-client,async,dialogs,oauth,skill,yandex
|
|
34
|
+
Classifier: Development Status :: 4 - Beta
|
|
35
|
+
Classifier: Framework :: AsyncIO
|
|
36
|
+
Classifier: Intended Audience :: Developers
|
|
37
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
38
|
+
Classifier: Operating System :: OS Independent
|
|
39
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
42
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
43
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
44
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
45
|
+
Classifier: Typing :: Typed
|
|
46
|
+
Requires-Python: >=3.12
|
|
47
|
+
Requires-Dist: aiohttp<4,>=3.10
|
|
48
|
+
Requires-Dist: ya-passport-auth>=1.3.0
|
|
49
|
+
Provides-Extra: dev
|
|
50
|
+
Requires-Dist: aioresponses>=0.7.6; extra == 'dev'
|
|
51
|
+
Requires-Dist: bandit[toml]>=1.7; extra == 'dev'
|
|
52
|
+
Requires-Dist: cyclonedx-bom>=4; extra == 'dev'
|
|
53
|
+
Requires-Dist: hypothesis>=6; extra == 'dev'
|
|
54
|
+
Requires-Dist: liccheck>=0.9; extra == 'dev'
|
|
55
|
+
Requires-Dist: mypy>=1.11; extra == 'dev'
|
|
56
|
+
Requires-Dist: pip-audit>=2.7; extra == 'dev'
|
|
57
|
+
Requires-Dist: pre-commit>=3.8; extra == 'dev'
|
|
58
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
59
|
+
Requires-Dist: pytest-cov>=5; extra == 'dev'
|
|
60
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
61
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
62
|
+
Description-Content-Type: text/markdown
|
|
63
|
+
|
|
64
|
+
# ya-dialogs-api
|
|
65
|
+
|
|
66
|
+
> Async Yandex Dialogs Developer API client — programmatic skill creation, draft management, OAuth Device Flow.
|
|
67
|
+
|
|
68
|
+
[](https://github.com/trudenboy/ya-dialogs-api/actions/workflows/ci.yml)
|
|
69
|
+
[](https://pypi.org/project/ya-dialogs-api/)
|
|
70
|
+
[](https://www.python.org/downloads/)
|
|
71
|
+
[](LICENSE)
|
|
72
|
+
|
|
73
|
+
## What this is
|
|
74
|
+
|
|
75
|
+
A framework-agnostic Python library that drives the **Yandex Dialogs Developer
|
|
76
|
+
API** — the meta-API at `dialogs.yandex.ru/developer-api/v2/` used to
|
|
77
|
+
programmatically create and manage Alice skills (Smart Home and custom dialog
|
|
78
|
+
skills). Where every other Yandex Alice library on PyPI handles the *runtime*
|
|
79
|
+
side (incoming webhook requests from end users), this one handles the
|
|
80
|
+
*provisioning* side: sign in via Yandex Passport Device Flow, create a skill,
|
|
81
|
+
upload a logo, set the webhook backend URL, publish a draft, poll for
|
|
82
|
+
publication state.
|
|
83
|
+
|
|
84
|
+
It exists because Yandex doesn't publish a developer-API SDK and the only
|
|
85
|
+
known Python implementation lived inside the Music Assistant
|
|
86
|
+
`ma-provider-yandex-smarthome` plugin. This library is that code, extracted
|
|
87
|
+
and made generic.
|
|
88
|
+
|
|
89
|
+
## Features
|
|
90
|
+
|
|
91
|
+
- **Full skill auto-creation pipeline** — Device Flow OAuth → CSRF/cookie
|
|
92
|
+
session → POST `/apps` → POST `/skills/{id}/draft` → publication polling →
|
|
93
|
+
callback URL extraction.
|
|
94
|
+
- **Two channel types** — `skill_type="smart_home"` for Yandex Smart Home
|
|
95
|
+
skills, `skill_type="dialog"` for Alice custom dialog (`aliceSkill`) skills.
|
|
96
|
+
- **Incremental state machine** — `SkillCreationArtifacts` snapshots progress
|
|
97
|
+
after every step. On transient failure, retry resumes from the last
|
|
98
|
+
completed step instead of restarting.
|
|
99
|
+
- **Framework-agnostic** — caller provides a `WebserverAdapter` Protocol
|
|
100
|
+
implementation for hosting the short-lived Device Code activation page.
|
|
101
|
+
Works with aiohttp, FastAPI, Starlette, or any ASGI/aiohttp-compatible
|
|
102
|
+
webserver.
|
|
103
|
+
- **Strictly typed** — `mypy --strict` clean, PEP 561 `py.typed` marker.
|
|
104
|
+
- **Security-aware** — `SecretStr` redacts tokens in repr/str/format/tracebacks
|
|
105
|
+
(re-exported from `ya-passport-auth`).
|
|
106
|
+
|
|
107
|
+
## Installation
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
pip install ya-dialogs-api
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Quick start
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
import asyncio
|
|
117
|
+
from ya_dialogs_api import (
|
|
118
|
+
SkillCreationArtifacts,
|
|
119
|
+
SkillCreationState,
|
|
120
|
+
WebserverAdapter,
|
|
121
|
+
auto_create_skill,
|
|
122
|
+
load_default_logo_bytes,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# 1. Implement WebserverAdapter against your HTTP framework.
|
|
126
|
+
# See docs for an aiohttp reference implementation.
|
|
127
|
+
my_webserver: WebserverAdapter = ...
|
|
128
|
+
|
|
129
|
+
# 2. Drive the pipeline. Persist artifacts between calls — the next
|
|
130
|
+
# invocation will resume from the last completed step.
|
|
131
|
+
async def main() -> None:
|
|
132
|
+
artifacts = SkillCreationArtifacts(state=SkillCreationState.PENDING)
|
|
133
|
+
result = await auto_create_skill(
|
|
134
|
+
webserver=my_webserver,
|
|
135
|
+
connection_type="direct",
|
|
136
|
+
skill_name="My Smart Home",
|
|
137
|
+
artifacts=artifacts,
|
|
138
|
+
cloud_instance_id="...",
|
|
139
|
+
direct_client_secret="...",
|
|
140
|
+
logo_bytes=load_default_logo_bytes(),
|
|
141
|
+
session_id="cfg-flow-1",
|
|
142
|
+
skill_type="smart_home",
|
|
143
|
+
)
|
|
144
|
+
if result.state == SkillCreationState.DONE:
|
|
145
|
+
print(f"Skill created: skill_id={result.skill_id}")
|
|
146
|
+
else:
|
|
147
|
+
print(f"Failed at step {result.state}: {result.last_error}")
|
|
148
|
+
|
|
149
|
+
asyncio.run(main())
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Status
|
|
153
|
+
|
|
154
|
+
**Beta.** The API is stable enough to depend on, but the underlying Yandex dev-
|
|
155
|
+
console endpoints are unofficial (reverse-engineered from DevTools traces) and
|
|
156
|
+
may break without notice. The library is actively maintained against the
|
|
157
|
+
current production endpoint behavior.
|
|
158
|
+
|
|
159
|
+
## See also
|
|
160
|
+
|
|
161
|
+
- [`ya-passport-auth`](https://github.com/trudenboy/ya-passport-auth) — Yandex
|
|
162
|
+
Passport authentication library. Required runtime dependency for the OAuth
|
|
163
|
+
Device Flow used by this library.
|
|
164
|
+
- [`ma-provider-yandex-smarthome`](https://github.com/trudenboy/ma-provider-yandex-smarthome)
|
|
165
|
+
and [`ma-provider-yandex-alice`](https://github.com/trudenboy/ma-provider-yandex-alice)
|
|
166
|
+
— Music Assistant providers that consume this library.
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# ya-dialogs-api
|
|
2
|
+
|
|
3
|
+
> Async Yandex Dialogs Developer API client — programmatic skill creation, draft management, OAuth Device Flow.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/trudenboy/ya-dialogs-api/actions/workflows/ci.yml)
|
|
6
|
+
[](https://pypi.org/project/ya-dialogs-api/)
|
|
7
|
+
[](https://www.python.org/downloads/)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
## What this is
|
|
11
|
+
|
|
12
|
+
A framework-agnostic Python library that drives the **Yandex Dialogs Developer
|
|
13
|
+
API** — the meta-API at `dialogs.yandex.ru/developer-api/v2/` used to
|
|
14
|
+
programmatically create and manage Alice skills (Smart Home and custom dialog
|
|
15
|
+
skills). Where every other Yandex Alice library on PyPI handles the *runtime*
|
|
16
|
+
side (incoming webhook requests from end users), this one handles the
|
|
17
|
+
*provisioning* side: sign in via Yandex Passport Device Flow, create a skill,
|
|
18
|
+
upload a logo, set the webhook backend URL, publish a draft, poll for
|
|
19
|
+
publication state.
|
|
20
|
+
|
|
21
|
+
It exists because Yandex doesn't publish a developer-API SDK and the only
|
|
22
|
+
known Python implementation lived inside the Music Assistant
|
|
23
|
+
`ma-provider-yandex-smarthome` plugin. This library is that code, extracted
|
|
24
|
+
and made generic.
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- **Full skill auto-creation pipeline** — Device Flow OAuth → CSRF/cookie
|
|
29
|
+
session → POST `/apps` → POST `/skills/{id}/draft` → publication polling →
|
|
30
|
+
callback URL extraction.
|
|
31
|
+
- **Two channel types** — `skill_type="smart_home"` for Yandex Smart Home
|
|
32
|
+
skills, `skill_type="dialog"` for Alice custom dialog (`aliceSkill`) skills.
|
|
33
|
+
- **Incremental state machine** — `SkillCreationArtifacts` snapshots progress
|
|
34
|
+
after every step. On transient failure, retry resumes from the last
|
|
35
|
+
completed step instead of restarting.
|
|
36
|
+
- **Framework-agnostic** — caller provides a `WebserverAdapter` Protocol
|
|
37
|
+
implementation for hosting the short-lived Device Code activation page.
|
|
38
|
+
Works with aiohttp, FastAPI, Starlette, or any ASGI/aiohttp-compatible
|
|
39
|
+
webserver.
|
|
40
|
+
- **Strictly typed** — `mypy --strict` clean, PEP 561 `py.typed` marker.
|
|
41
|
+
- **Security-aware** — `SecretStr` redacts tokens in repr/str/format/tracebacks
|
|
42
|
+
(re-exported from `ya-passport-auth`).
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install ya-dialogs-api
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Quick start
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
import asyncio
|
|
54
|
+
from ya_dialogs_api import (
|
|
55
|
+
SkillCreationArtifacts,
|
|
56
|
+
SkillCreationState,
|
|
57
|
+
WebserverAdapter,
|
|
58
|
+
auto_create_skill,
|
|
59
|
+
load_default_logo_bytes,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# 1. Implement WebserverAdapter against your HTTP framework.
|
|
63
|
+
# See docs for an aiohttp reference implementation.
|
|
64
|
+
my_webserver: WebserverAdapter = ...
|
|
65
|
+
|
|
66
|
+
# 2. Drive the pipeline. Persist artifacts between calls — the next
|
|
67
|
+
# invocation will resume from the last completed step.
|
|
68
|
+
async def main() -> None:
|
|
69
|
+
artifacts = SkillCreationArtifacts(state=SkillCreationState.PENDING)
|
|
70
|
+
result = await auto_create_skill(
|
|
71
|
+
webserver=my_webserver,
|
|
72
|
+
connection_type="direct",
|
|
73
|
+
skill_name="My Smart Home",
|
|
74
|
+
artifacts=artifacts,
|
|
75
|
+
cloud_instance_id="...",
|
|
76
|
+
direct_client_secret="...",
|
|
77
|
+
logo_bytes=load_default_logo_bytes(),
|
|
78
|
+
session_id="cfg-flow-1",
|
|
79
|
+
skill_type="smart_home",
|
|
80
|
+
)
|
|
81
|
+
if result.state == SkillCreationState.DONE:
|
|
82
|
+
print(f"Skill created: skill_id={result.skill_id}")
|
|
83
|
+
else:
|
|
84
|
+
print(f"Failed at step {result.state}: {result.last_error}")
|
|
85
|
+
|
|
86
|
+
asyncio.run(main())
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Status
|
|
90
|
+
|
|
91
|
+
**Beta.** The API is stable enough to depend on, but the underlying Yandex dev-
|
|
92
|
+
console endpoints are unofficial (reverse-engineered from DevTools traces) and
|
|
93
|
+
may break without notice. The library is actively maintained against the
|
|
94
|
+
current production endpoint behavior.
|
|
95
|
+
|
|
96
|
+
## See also
|
|
97
|
+
|
|
98
|
+
- [`ya-passport-auth`](https://github.com/trudenboy/ya-passport-auth) — Yandex
|
|
99
|
+
Passport authentication library. Required runtime dependency for the OAuth
|
|
100
|
+
Device Flow used by this library.
|
|
101
|
+
- [`ma-provider-yandex-smarthome`](https://github.com/trudenboy/ma-provider-yandex-smarthome)
|
|
102
|
+
and [`ma-provider-yandex-alice`](https://github.com/trudenboy/ma-provider-yandex-alice)
|
|
103
|
+
— Music Assistant providers that consume this library.
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting a vulnerability
|
|
4
|
+
|
|
5
|
+
If you discover a security issue, **please do not open a public issue.**
|
|
6
|
+
|
|
7
|
+
Instead, email the maintainer directly or use GitHub's private vulnerability
|
|
8
|
+
reporting feature at:
|
|
9
|
+
https://github.com/trudenboy/ya-dialogs-api/security/advisories/new
|
|
10
|
+
|
|
11
|
+
You will receive an acknowledgement within 72 hours and a resolution timeline
|
|
12
|
+
within 7 days.
|
|
13
|
+
|
|
14
|
+
## Supported versions
|
|
15
|
+
|
|
16
|
+
| Version | Supported |
|
|
17
|
+
|---------|-----------|
|
|
18
|
+
| 1.0.x | Yes |
|
|
19
|
+
|
|
20
|
+
## Threat model summary
|
|
21
|
+
|
|
22
|
+
This library is a relatively thin HTTP client over the Yandex Dialogs developer
|
|
23
|
+
API. The hot security perimeter (Yandex Passport credentials, OAuth tokens,
|
|
24
|
+
session cookies) is owned by `ya-passport-auth`, which has its own threat
|
|
25
|
+
model. The threats specific to this library are:
|
|
26
|
+
|
|
27
|
+
| # | Threat | Mitigation |
|
|
28
|
+
|---|--------|------------|
|
|
29
|
+
| D1 | Skill credentials leak via logs (skill_id is sensitive enough that an attacker who learns it can call dialogs.yandex.net/api/v1/skills/{id}/callback/state if they also have skill_token) | Library-side: never logs full credentials; only logs `step` + status code + first 200 chars of error body, which never contain skill_token |
|
|
30
|
+
| D2 | DoS via unbounded HTML response from dev-console | 2 MiB hard cap on developer page HTML (`_MAX_HTML_RESPONSE_BYTES`) |
|
|
31
|
+
| D3 | ReDoS on CSRF regex | Single explicit character class, non-greedy. The regex matches inside a 2 MiB cap |
|
|
32
|
+
| D4 | Spoofed dev-console responses (e.g. on a malicious proxy) | TLS verification is the caller's responsibility on the supplied `aiohttp.ClientSession` (this library does not own the session) |
|
|
33
|
+
| D5 | CSRF token reuse across attempts | `fetch_csrf` is called once per attempt; orchestrator does not cache the token across `auto_create_skill` calls |
|
|
34
|
+
| D6 | Pickled `SkillCreationArtifacts` carrying secrets | Artifacts dataclass is frozen but does not currently carry secrets — only IDs (skill_id, logo_id, oauth_app_id). The actual `skill_token` is owned by the caller and never enters the artifacts object |
|
|
35
|
+
| D7 | Supply-chain compromise via this library | Hash-pinned `uv.lock`, `pip-audit` in CI, Dependabot enabled, OpenSSF Scorecard published |
|
|
36
|
+
| D8 | Real Yandex tokens in test fixtures | All test data uses synthetic tokens (`csrf-token`, `test-secret`, `skill-id-123`); no production credentials in source tree |
|
|
37
|
+
|
|
38
|
+
For the security of the Passport authentication flow, OAuth Device Flow,
|
|
39
|
+
SecretStr handling, host allow-list, and TLS verification, see the threat model
|
|
40
|
+
of [`ya-passport-auth`](https://github.com/trudenboy/ya-passport-auth) — this
|
|
41
|
+
library uses it as a runtime dependency.
|
|
42
|
+
|
|
43
|
+
## Out of scope
|
|
44
|
+
|
|
45
|
+
- Host compromise / memory scraping
|
|
46
|
+
- Side-channel timing attacks
|
|
47
|
+
- Encrypted at-rest storage of artifacts (caller responsibility)
|
|
48
|
+
- The undocumented Yandex API itself — if Yandex changes endpoint shapes or
|
|
49
|
+
auth requirements, this library's release cadence is the mitigation
|
|
50
|
+
- CAPTCHA / anti-bot bypass
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.0.0
|