valentina-python-client 3.3.0__tar.gz → 3.5.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.
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/PKG-INFO +5 -4
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/pyproject.toml +16 -14
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/pyproject.toml.orig +17 -15
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/__init__.py +1 -1
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/character_traits.py +213 -1
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/characters.py +84 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/options.py +7 -2
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/constants.py +4 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/endpoints.py +6 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/exceptions.py +3 -1
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/models/__init__.py +22 -0
- valentina_python_client-3.5.0/src/vclient/models/character_trait.py +258 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/models/characters.py +10 -1
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/models/companies.py +3 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/models/shared.py +8 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/character_traits.py +234 -1
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/characters.py +95 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/options.py +7 -2
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/testing/__init__.py +8 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/testing/_factories.py +112 -6
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/testing/_router.py +14 -2
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/testing/_routes.py +26 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/validate_constants.py +2 -0
- valentina_python_client-3.3.0/src/vclient/models/character_trait.py +0 -117
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/LICENSE +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/README.md +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_codegen.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/__init__.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/client.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/registry.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/__init__.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/_audit_params.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/base.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/campaign_book_chapters.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/campaign_books.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/campaigns.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/character_autogen.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/character_blueprint.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/companies.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/developers.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/dicerolls.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/dictionary.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/global_admin.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/identity.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/system.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/user_lookup.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/services/users.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/testing/__init__.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/_sync/testing/_client.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/client.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/config.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/models/audit_logs.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/models/books.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/models/campaigns.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/models/chapters.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/models/character_autogen.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/models/character_blueprint.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/models/developers.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/models/diceroll.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/models/dictionary.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/models/full_sheet.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/models/global_admin.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/models/pagination.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/models/system.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/models/user_lookup.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/models/users.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/py.typed +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/registry.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/__init__.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/_audit_params.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/base.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/campaign_book_chapters.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/campaign_books.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/campaigns.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/character_autogen.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/character_blueprint.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/companies.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/developers.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/dicerolls.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/dictionary.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/global_admin.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/identity.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/system.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/user_lookup.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/services/users.py +0 -0
- {valentina_python_client-3.3.0 → valentina_python_client-3.5.0}/src/vclient/testing/_client.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: valentina-python-client
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.5.0
|
|
4
4
|
Summary: Async Python client library for the Valentina Noir API
|
|
5
5
|
Author: Nate Landau
|
|
6
6
|
Author-email: Nate Landau <github@natenate.org>
|
|
@@ -15,11 +15,12 @@ License: MIT License
|
|
|
15
15
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
-
|
|
19
|
-
Requires-Dist:
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
19
|
+
Requires-Dist: anyio>=4.15.1,<5.0.0
|
|
20
|
+
Requires-Dist: httpx2>=2.13.1
|
|
20
21
|
Requires-Dist: loguru>=0.7.3
|
|
21
22
|
Requires-Dist: polyfactory>=3.3.0,<4.0.0
|
|
22
|
-
Requires-Dist: pydantic[email]>=2.
|
|
23
|
+
Requires-Dist: pydantic[email]>=2.14.1,<3.0.0
|
|
23
24
|
Requires-Dist: polyfactory>=3.3.0 ; extra == 'testing'
|
|
24
25
|
Requires-Python: >=3.13
|
|
25
26
|
Project-URL: Homepage, https://docs.valentina-noir.com/python-api-client/
|
|
@@ -2,19 +2,20 @@
|
|
|
2
2
|
classifiers = [
|
|
3
3
|
"Programming Language :: Python :: 3.13",
|
|
4
4
|
"Programming Language :: Python :: 3.14",
|
|
5
|
+
"Programming Language :: Python :: 3.15",
|
|
5
6
|
]
|
|
6
7
|
dependencies = [
|
|
7
|
-
"anyio>=4.
|
|
8
|
-
"httpx2>=2.
|
|
8
|
+
"anyio>=4.15.1,<5.0.0",
|
|
9
|
+
"httpx2>=2.13.1",
|
|
9
10
|
"loguru>=0.7.3",
|
|
10
11
|
"polyfactory>=3.3.0, <4.0.0",
|
|
11
|
-
"pydantic[email]>=2.
|
|
12
|
+
"pydantic[email]>=2.14.1,<3.0.0",
|
|
12
13
|
]
|
|
13
14
|
description = "Async Python client library for the Valentina Noir API"
|
|
14
15
|
name = "valentina-python-client"
|
|
15
16
|
readme = "README.md"
|
|
16
17
|
requires-python = ">=3.13"
|
|
17
|
-
version = "3.
|
|
18
|
+
version = "3.5.0"
|
|
18
19
|
|
|
19
20
|
[[project.authors]]
|
|
20
21
|
name = "Nate Landau"
|
|
@@ -32,7 +33,7 @@ Repository = "https://github.com/natelandau/valentina-python-client"
|
|
|
32
33
|
|
|
33
34
|
[build-system]
|
|
34
35
|
build-backend = "uv_build"
|
|
35
|
-
requires = ["uv_build>=0.
|
|
36
|
+
requires = ["uv_build>=0.13.0,<0.14"]
|
|
36
37
|
|
|
37
38
|
[tool.uv.build-backend]
|
|
38
39
|
module-name = "vclient"
|
|
@@ -216,27 +217,28 @@ error-on-warning = true
|
|
|
216
217
|
|
|
217
218
|
[dependency-groups]
|
|
218
219
|
dev = [
|
|
219
|
-
"commitizen>=4.
|
|
220
|
-
"coverage>=7.
|
|
221
|
-
"duty>=1.
|
|
220
|
+
"commitizen>=4.19.2",
|
|
221
|
+
"coverage>=7.16.2",
|
|
222
|
+
"duty>=1.10.1",
|
|
223
|
+
"gate-cache>=0.6.0",
|
|
222
224
|
"httpx>=0.28.1",
|
|
223
|
-
"prek>=0.
|
|
225
|
+
"prek>=0.5.5",
|
|
224
226
|
"pytest-anyio>=0.0.0",
|
|
225
227
|
"pytest-clarity>=1.0.1",
|
|
226
228
|
"pytest-cov>=7.1.0",
|
|
227
229
|
"pytest-devtools>=1.3.0",
|
|
228
230
|
"pytest-httpx2>=1.0.0",
|
|
229
|
-
"pytest-mock>=3.
|
|
231
|
+
"pytest-mock>=3.16.0",
|
|
230
232
|
"pytest-repeat>=0.9.4",
|
|
231
233
|
"pytest-sugar>=1.1.1",
|
|
232
234
|
"pytest-xdist>=3.8.0",
|
|
233
235
|
"pytest>=9.1.1",
|
|
234
236
|
"respx>=0.23.1",
|
|
235
|
-
"ruff>=0.
|
|
237
|
+
"ruff>=0.17.0",
|
|
236
238
|
"shellcheck-py>=0.11.0.1",
|
|
237
|
-
"ty>=0.0.
|
|
238
|
-
"typos>=1.
|
|
239
|
+
"ty>=0.0.86",
|
|
240
|
+
"typos>=1.51.1",
|
|
239
241
|
"vulture>=2.16",
|
|
240
242
|
"yamllint>=1.38.0",
|
|
241
243
|
]
|
|
242
|
-
docs = ["zensical>=0.0.
|
|
244
|
+
docs = ["zensical>=0.0.70"]
|
|
@@ -3,20 +3,21 @@
|
|
|
3
3
|
classifiers = [
|
|
4
4
|
"Programming Language :: Python :: 3.13",
|
|
5
5
|
"Programming Language :: Python :: 3.14",
|
|
6
|
+
"Programming Language :: Python :: 3.15",
|
|
6
7
|
]
|
|
7
8
|
dependencies = [
|
|
8
|
-
"anyio>=4.
|
|
9
|
-
"httpx2>=2.
|
|
9
|
+
"anyio>=4.15.1,<5.0.0",
|
|
10
|
+
"httpx2>=2.13.1",
|
|
10
11
|
"loguru>=0.7.3",
|
|
11
12
|
"polyfactory>=3.3.0, <4.0.0",
|
|
12
|
-
"pydantic[email]>=2.
|
|
13
|
+
"pydantic[email]>=2.14.1,<3.0.0",
|
|
13
14
|
]
|
|
14
15
|
description = "Async Python client library for the Valentina Noir API"
|
|
15
16
|
license = { file = "LICENSE" }
|
|
16
17
|
name = "valentina-python-client"
|
|
17
18
|
readme = "README.md"
|
|
18
19
|
requires-python = ">=3.13"
|
|
19
|
-
version = "3.
|
|
20
|
+
version = "3.5.0"
|
|
20
21
|
|
|
21
22
|
[project.optional-dependencies]
|
|
22
23
|
testing = ["polyfactory>=3.3.0"]
|
|
@@ -27,37 +28,38 @@
|
|
|
27
28
|
|
|
28
29
|
[build-system]
|
|
29
30
|
build-backend = "uv_build"
|
|
30
|
-
requires = ["uv_build>=0.
|
|
31
|
+
requires = ["uv_build>=0.13.0,<0.14"]
|
|
31
32
|
|
|
32
33
|
[tool.uv.build-backend]
|
|
33
34
|
module-name = "vclient"
|
|
34
35
|
|
|
35
36
|
[dependency-groups]
|
|
36
37
|
dev = [
|
|
37
|
-
"commitizen>=4.
|
|
38
|
-
"coverage>=7.
|
|
39
|
-
"duty>=1.
|
|
38
|
+
"commitizen>=4.19.2",
|
|
39
|
+
"coverage>=7.16.2",
|
|
40
|
+
"duty>=1.10.1",
|
|
41
|
+
"gate-cache>=0.6.0",
|
|
40
42
|
"httpx>=0.28.1",
|
|
41
|
-
"prek>=0.
|
|
43
|
+
"prek>=0.5.5",
|
|
42
44
|
"pytest-anyio>=0.0.0",
|
|
43
45
|
"pytest-clarity>=1.0.1",
|
|
44
46
|
"pytest-cov>=7.1.0",
|
|
45
47
|
"pytest-devtools>=1.3.0",
|
|
46
48
|
"pytest-httpx2>=1.0.0",
|
|
47
|
-
"pytest-mock>=3.
|
|
49
|
+
"pytest-mock>=3.16.0",
|
|
48
50
|
"pytest-repeat>=0.9.4",
|
|
49
51
|
"pytest-sugar>=1.1.1",
|
|
50
52
|
"pytest-xdist>=3.8.0",
|
|
51
53
|
"pytest>=9.1.1",
|
|
52
54
|
"respx>=0.23.1",
|
|
53
|
-
"ruff>=0.
|
|
55
|
+
"ruff>=0.17.0",
|
|
54
56
|
"shellcheck-py>=0.11.0.1",
|
|
55
|
-
"ty>=0.0.
|
|
56
|
-
"typos>=1.
|
|
57
|
+
"ty>=0.0.86",
|
|
58
|
+
"typos>=1.51.1",
|
|
57
59
|
"vulture>=2.16",
|
|
58
60
|
"yamllint>=1.38.0",
|
|
59
|
-
|
|
60
|
-
docs = ["zensical>=0.0.
|
|
61
|
+
]
|
|
62
|
+
docs = ["zensical>=0.0.70"]
|
|
61
63
|
|
|
62
64
|
[tool.commitizen]
|
|
63
65
|
bump_message = "bump(release): v$current_version → v$new_version"
|
|
@@ -5,15 +5,21 @@ from collections.abc import Iterator
|
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
7
|
from vclient._sync.services.base import SyncBaseService
|
|
8
|
-
from vclient.constants import DEFAULT_PAGE_LIMIT, TraitModifyCurrency
|
|
8
|
+
from vclient.constants import DEFAULT_PAGE_LIMIT, TrackerAction, TraitModifyCurrency
|
|
9
9
|
from vclient.endpoints import Endpoints
|
|
10
10
|
from vclient.models import (
|
|
11
|
+
_TRACKER_ADAPTER,
|
|
11
12
|
BulkAssignTraitResponse,
|
|
12
13
|
CharacterTrait,
|
|
13
14
|
CharacterTraitAdd,
|
|
15
|
+
CharacterTraitSpecialtyCreate,
|
|
16
|
+
CharacterTraitSpecialtyUpdate,
|
|
14
17
|
CharacterTraitValueOptionsResponse,
|
|
15
18
|
PaginatedResponse,
|
|
19
|
+
Tracker,
|
|
20
|
+
TrackerStateSet,
|
|
16
21
|
TraitCreate,
|
|
22
|
+
_TrackerAction,
|
|
17
23
|
_TraitModify,
|
|
18
24
|
)
|
|
19
25
|
|
|
@@ -140,6 +146,8 @@ class SyncCharacterTraitsService(SyncBaseService):
|
|
|
140
146
|
def delete(self, character_trait_id: str, currency: TraitModifyCurrency | None = None) -> None:
|
|
141
147
|
"""Delete a character trait.
|
|
142
148
|
|
|
149
|
+
Any specialties on the trait are removed with it and the cost of each is refunded.
|
|
150
|
+
|
|
143
151
|
Args:
|
|
144
152
|
character_trait_id: The ID of the trait to delete.
|
|
145
153
|
currency: The currency to use to recoup the cost of the trait.
|
|
@@ -152,6 +160,8 @@ class SyncCharacterTraitsService(SyncBaseService):
|
|
|
152
160
|
AuthorizationError: If you don't have access to the character.
|
|
153
161
|
RequestValidationError: If the input parameters fail client-side validation.
|
|
154
162
|
ValidationError: If the request data is invalid.
|
|
163
|
+
ConflictError: If recouping ``XP`` or ``STARTING_POINTS`` and another request
|
|
164
|
+
changed the trait's value first. Refetch the trait and retry.
|
|
155
165
|
"""
|
|
156
166
|
params: dict[str, str | int] = {}
|
|
157
167
|
if currency is not None:
|
|
@@ -177,6 +187,8 @@ class SyncCharacterTraitsService(SyncBaseService):
|
|
|
177
187
|
AuthorizationError: If you don't have access to the character.
|
|
178
188
|
RequestValidationError: If the input parameters fail client-side validation.
|
|
179
189
|
ValidationError: If the request data is invalid.
|
|
190
|
+
ConflictError: If paying with ``XP`` or ``STARTING_POINTS`` and another request
|
|
191
|
+
changed the trait's value first. Refetch the trait and retry.
|
|
180
192
|
"""
|
|
181
193
|
body = self._validate_request(
|
|
182
194
|
CharacterTraitAdd, trait_id=trait_id, value=value, currency=currency
|
|
@@ -225,6 +237,13 @@ class SyncCharacterTraitsService(SyncBaseService):
|
|
|
225
237
|
show_when_zero (bool), is_rollable (bool, default True),
|
|
226
238
|
initial_cost (int | None), upgrade_cost (int | None),
|
|
227
239
|
count_based_cost_multiplier (int | None).
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
The newly created CharacterTrait object.
|
|
243
|
+
|
|
244
|
+
Raises:
|
|
245
|
+
ConflictError: If paying with ``XP`` or ``STARTING_POINTS`` and another request
|
|
246
|
+
changed the trait's value first. Refetch the trait and retry.
|
|
228
247
|
"""
|
|
229
248
|
body = request if request is not None else self._validate_request(TraitCreate, **kwargs)
|
|
230
249
|
response = self._post(
|
|
@@ -260,10 +279,20 @@ class SyncCharacterTraitsService(SyncBaseService):
|
|
|
260
279
|
) -> CharacterTrait:
|
|
261
280
|
"""Change the value of a character trait.
|
|
262
281
|
|
|
282
|
+
Lowering an ability to ``0`` also removes its specialties and refunds the cost of
|
|
283
|
+
each. The value change and the refunds are applied together or not at all.
|
|
284
|
+
|
|
263
285
|
Args:
|
|
264
286
|
character_trait_id: The ID of the trait to change the value of.
|
|
265
287
|
new_value: The new value of the trait.
|
|
266
288
|
currency: The currency to use for the modification.
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
The updated CharacterTrait object.
|
|
292
|
+
|
|
293
|
+
Raises:
|
|
294
|
+
ConflictError: If another request changed the trait's value after this request
|
|
295
|
+
read it. Nothing is spent or refunded. Refetch the trait and retry.
|
|
267
296
|
"""
|
|
268
297
|
body = self._validate_request(_TraitModify, target_value=new_value, currency=currency)
|
|
269
298
|
response = self._put(
|
|
@@ -273,3 +302,186 @@ class SyncCharacterTraitsService(SyncBaseService):
|
|
|
273
302
|
json=body.model_dump(exclude_none=True, exclude_unset=True, mode="json"),
|
|
274
303
|
)
|
|
275
304
|
return CharacterTrait.model_validate(response.json())
|
|
305
|
+
|
|
306
|
+
def add_specialty(
|
|
307
|
+
self, character_trait_id: str, name: str, currency: TraitModifyCurrency = "NO_COST"
|
|
308
|
+
) -> CharacterTrait:
|
|
309
|
+
"""Add a specialty to an ability trait.
|
|
310
|
+
|
|
311
|
+
Specialties are only available on abilities with a value of 1 or higher. When the
|
|
312
|
+
company's ``specialty_xp_cost_enabled`` setting is on, a specialty paid with ``XP``
|
|
313
|
+
or ``STARTING_POINTS`` costs 3 points, and ``NO_COST`` follows the company's
|
|
314
|
+
``permission_free_trait_changes`` setting. When the setting is off, specialties are free.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
character_trait_id: The ID of the ability trait.
|
|
318
|
+
name: The specialty name (3-50 characters), unique on the trait ignoring case.
|
|
319
|
+
currency: The currency to use to pay for the specialty.
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
The updated CharacterTrait object, including its specialties.
|
|
323
|
+
|
|
324
|
+
Raises:
|
|
325
|
+
NotFoundError: If the trait does not exist.
|
|
326
|
+
AuthorizationError: If you don't have access to the character.
|
|
327
|
+
RequestValidationError: If the input parameters fail client-side validation.
|
|
328
|
+
ValidationError: If the trait is not an ability, has a value of 0, already has a
|
|
329
|
+
specialty with this name, the character cannot afford the cost, or an
|
|
330
|
+
``NPC`` or ``STORYTELLER`` character pays with anything but ``NO_COST``.
|
|
331
|
+
"""
|
|
332
|
+
body = self._validate_request(CharacterTraitSpecialtyCreate, name=name, currency=currency)
|
|
333
|
+
response = self._post(
|
|
334
|
+
self._format_endpoint(
|
|
335
|
+
Endpoints.CHARACTER_TRAIT_SPECIALTIES, character_trait_id=character_trait_id
|
|
336
|
+
),
|
|
337
|
+
json=body.model_dump(exclude_none=True, mode="json"),
|
|
338
|
+
)
|
|
339
|
+
return CharacterTrait.model_validate(response.json())
|
|
340
|
+
|
|
341
|
+
def update_specialty(
|
|
342
|
+
self, character_trait_id: str, specialty_id: str, name: str
|
|
343
|
+
) -> CharacterTrait:
|
|
344
|
+
"""Rename a specialty on an ability trait.
|
|
345
|
+
|
|
346
|
+
Renaming never changes the cost paid or the currency used.
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
character_trait_id: The ID of the ability trait.
|
|
350
|
+
specialty_id: The ID of the specialty to rename.
|
|
351
|
+
name: The new specialty name (3-50 characters), unique on the trait ignoring case.
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
The updated CharacterTrait object, including its specialties.
|
|
355
|
+
|
|
356
|
+
Raises:
|
|
357
|
+
NotFoundError: If the trait or specialty does not exist, or the specialty
|
|
358
|
+
belongs to a different trait.
|
|
359
|
+
AuthorizationError: If you don't have access to the character.
|
|
360
|
+
RequestValidationError: If the input parameters fail client-side validation.
|
|
361
|
+
ValidationError: If the trait already has a specialty with this name.
|
|
362
|
+
"""
|
|
363
|
+
body = self._validate_request(CharacterTraitSpecialtyUpdate, name=name)
|
|
364
|
+
response = self._patch(
|
|
365
|
+
self._format_endpoint(
|
|
366
|
+
Endpoints.CHARACTER_TRAIT_SPECIALTY,
|
|
367
|
+
character_trait_id=character_trait_id,
|
|
368
|
+
specialty_id=specialty_id,
|
|
369
|
+
),
|
|
370
|
+
json=body.model_dump(exclude_none=True, mode="json"),
|
|
371
|
+
)
|
|
372
|
+
return CharacterTrait.model_validate(response.json())
|
|
373
|
+
|
|
374
|
+
def delete_specialty(self, character_trait_id: str, specialty_id: str) -> None:
|
|
375
|
+
"""Remove a specialty from an ability trait.
|
|
376
|
+
|
|
377
|
+
The cost paid is refunded in the currency used to buy it. An ``XP`` refund follows
|
|
378
|
+
the company's ``permission_recoup_xp`` setting; starting points are always refunded.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
character_trait_id: The ID of the ability trait.
|
|
382
|
+
specialty_id: The ID of the specialty to remove.
|
|
383
|
+
|
|
384
|
+
Returns:
|
|
385
|
+
None
|
|
386
|
+
|
|
387
|
+
Raises:
|
|
388
|
+
NotFoundError: If the trait or specialty does not exist, or the specialty
|
|
389
|
+
belongs to a different trait.
|
|
390
|
+
AuthorizationError: If you don't have access to the character.
|
|
391
|
+
"""
|
|
392
|
+
self._delete(
|
|
393
|
+
self._format_endpoint(
|
|
394
|
+
Endpoints.CHARACTER_TRAIT_SPECIALTY,
|
|
395
|
+
character_trait_id=character_trait_id,
|
|
396
|
+
specialty_id=specialty_id,
|
|
397
|
+
)
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
def get_tracker(self, character_trait_id: str) -> Tracker:
|
|
401
|
+
"""Get the in-play tracker state of a character trait.
|
|
402
|
+
|
|
403
|
+
The same object appears as ``CharacterTrait.tracker``. Use this to refresh one
|
|
404
|
+
tracker without fetching the whole trait.
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
character_trait_id: The ID of the character trait.
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
A CounterTracker or StainsTracker. Switch on ``type``.
|
|
411
|
+
|
|
412
|
+
Raises:
|
|
413
|
+
NotFoundError: If the trait has no tracker for the character's game version.
|
|
414
|
+
AuthorizationError: If you don't have access to the character.
|
|
415
|
+
"""
|
|
416
|
+
response = self._get(
|
|
417
|
+
self._format_endpoint(
|
|
418
|
+
Endpoints.CHARACTER_TRAIT_TRACKER, character_trait_id=character_trait_id
|
|
419
|
+
)
|
|
420
|
+
)
|
|
421
|
+
return _TRACKER_ADAPTER.validate_python(response.json())
|
|
422
|
+
|
|
423
|
+
def set_tracker(
|
|
424
|
+
self, character_trait_id: str, request: TrackerStateSet | None = None, **kwargs
|
|
425
|
+
) -> Tracker:
|
|
426
|
+
"""Replace the full state of a trait tracker, such as to correct a mistake.
|
|
427
|
+
|
|
428
|
+
Send only the field for the tracker's type: ``current`` for counters (required) or
|
|
429
|
+
``stains`` for stains. An empty request clears a stains tracker. Use
|
|
430
|
+
``apply_tracker_action`` for in-play changes instead.
|
|
431
|
+
|
|
432
|
+
Args:
|
|
433
|
+
character_trait_id: The ID of the character trait.
|
|
434
|
+
request: A TrackerStateSet model, OR pass fields as keyword arguments.
|
|
435
|
+
**kwargs: Fields for TrackerStateSet if request is not provided.
|
|
436
|
+
Accepts: current, stains (int >= 0).
|
|
437
|
+
|
|
438
|
+
Returns:
|
|
439
|
+
The updated tracker.
|
|
440
|
+
|
|
441
|
+
Raises:
|
|
442
|
+
NotFoundError: If the trait has no tracker for the character's game version.
|
|
443
|
+
AuthorizationError: If you are not the character's player or a storyteller.
|
|
444
|
+
RequestValidationError: If a value is negative or a field is unknown.
|
|
445
|
+
ValidationError: If a field does not fit the tracker type, a counter has no
|
|
446
|
+
``current``, or a value exceeds the tracker size.
|
|
447
|
+
"""
|
|
448
|
+
body = request if request is not None else self._validate_request(TrackerStateSet, **kwargs)
|
|
449
|
+
response = self._put(
|
|
450
|
+
self._format_endpoint(
|
|
451
|
+
Endpoints.CHARACTER_TRAIT_TRACKER, character_trait_id=character_trait_id
|
|
452
|
+
),
|
|
453
|
+
json=body.model_dump(exclude_none=True, mode="json"),
|
|
454
|
+
)
|
|
455
|
+
return _TRACKER_ADAPTER.validate_python(response.json())
|
|
456
|
+
|
|
457
|
+
def apply_tracker_action(
|
|
458
|
+
self, character_trait_id: str, action: TrackerAction, *, amount: int = 1
|
|
459
|
+
) -> Tracker:
|
|
460
|
+
"""Apply one in-play action to a trait tracker, such as spending Willpower.
|
|
461
|
+
|
|
462
|
+
The actions each tracker accepts are listed in
|
|
463
|
+
``characters.TrackerRules[game_version][tracker.type]`` from ``options.get_options()``.
|
|
464
|
+
|
|
465
|
+
Args:
|
|
466
|
+
character_trait_id: The ID of the character trait.
|
|
467
|
+
action: The action to apply.
|
|
468
|
+
amount: How many points to apply. Must be at least 1.
|
|
469
|
+
|
|
470
|
+
Returns:
|
|
471
|
+
The updated tracker.
|
|
472
|
+
|
|
473
|
+
Raises:
|
|
474
|
+
NotFoundError: If the trait has no tracker for the character's game version.
|
|
475
|
+
AuthorizationError: If you are not the character's player or a storyteller.
|
|
476
|
+
RequestValidationError: If ``amount`` is less than 1.
|
|
477
|
+
ValidationError: If the action does not fit the tracker or a spend exceeds the
|
|
478
|
+
current value.
|
|
479
|
+
"""
|
|
480
|
+
body = self._validate_request(_TrackerAction, action=action, amount=amount)
|
|
481
|
+
response = self._post(
|
|
482
|
+
self._format_endpoint(
|
|
483
|
+
Endpoints.CHARACTER_TRAIT_TRACKER_ACTIONS, character_trait_id=character_trait_id
|
|
484
|
+
),
|
|
485
|
+
json=body.model_dump(exclude_none=True, mode="json"),
|
|
486
|
+
)
|
|
487
|
+
return _TRACKER_ADAPTER.validate_python(response.json())
|
|
@@ -12,6 +12,7 @@ from vclient.constants import (
|
|
|
12
12
|
CharacterInclude,
|
|
13
13
|
CharacterStatus,
|
|
14
14
|
CharacterType,
|
|
15
|
+
TrackerAction,
|
|
15
16
|
)
|
|
16
17
|
from vclient.endpoints import Endpoints
|
|
17
18
|
from vclient.models import (
|
|
@@ -22,6 +23,8 @@ from vclient.models import (
|
|
|
22
23
|
CharacterFullSheet,
|
|
23
24
|
CharacterUpdate,
|
|
24
25
|
FullSheetTraitCategory,
|
|
26
|
+
Health,
|
|
27
|
+
HealthStateSet,
|
|
25
28
|
InventoryItem,
|
|
26
29
|
InventoryItemCreate,
|
|
27
30
|
InventoryItemUpdate,
|
|
@@ -30,6 +33,7 @@ from vclient.models import (
|
|
|
30
33
|
NoteUpdate,
|
|
31
34
|
PaginatedResponse,
|
|
32
35
|
RollStatistics,
|
|
36
|
+
_TrackerAction,
|
|
33
37
|
)
|
|
34
38
|
|
|
35
39
|
if TYPE_CHECKING:
|
|
@@ -873,3 +877,83 @@ class SyncCharactersService(SyncBaseService):
|
|
|
873
877
|
Endpoints.CHARACTER_INVENTORY_ITEM, character_id=character_id, item_id=item_id
|
|
874
878
|
)
|
|
875
879
|
)
|
|
880
|
+
|
|
881
|
+
def get_health(self, character_id: str) -> Health:
|
|
882
|
+
"""Get a character's health track.
|
|
883
|
+
|
|
884
|
+
The same object appears as ``Character.health``. Re-fetch it after changing V5
|
|
885
|
+
Stamina, which sets the track size.
|
|
886
|
+
|
|
887
|
+
Args:
|
|
888
|
+
character_id: The ID of the character.
|
|
889
|
+
|
|
890
|
+
Returns:
|
|
891
|
+
The character's Health.
|
|
892
|
+
|
|
893
|
+
Raises:
|
|
894
|
+
NotFoundError: If the character does not exist or has no health track.
|
|
895
|
+
AuthorizationError: If you don't have access to the character.
|
|
896
|
+
"""
|
|
897
|
+
response = self._get(
|
|
898
|
+
self._format_endpoint(Endpoints.CHARACTER_HEALTH, character_id=character_id)
|
|
899
|
+
)
|
|
900
|
+
return Health.model_validate(response.json())
|
|
901
|
+
|
|
902
|
+
def set_health(
|
|
903
|
+
self, character_id: str, request: HealthStateSet | None = None, **kwargs
|
|
904
|
+
) -> Health:
|
|
905
|
+
"""Replace a character's full health state, such as to correct a mistake.
|
|
906
|
+
|
|
907
|
+
Every field is sent, so omitted fields reset to 0. Use ``apply_health_action`` for
|
|
908
|
+
in-play changes.
|
|
909
|
+
|
|
910
|
+
Args:
|
|
911
|
+
character_id: The ID of the character.
|
|
912
|
+
request: A HealthStateSet model, OR pass fields as keyword arguments.
|
|
913
|
+
**kwargs: Fields for HealthStateSet if request is not provided.
|
|
914
|
+
Accepts: extra_levels, damage (int >= 0).
|
|
915
|
+
|
|
916
|
+
Returns:
|
|
917
|
+
The updated Health.
|
|
918
|
+
|
|
919
|
+
Raises:
|
|
920
|
+
NotFoundError: If the character does not exist or has no health track.
|
|
921
|
+
AuthorizationError: If you are not the character's player or a storyteller.
|
|
922
|
+
RequestValidationError: If a value is negative or a field is unknown.
|
|
923
|
+
ValidationError: If the damage exceeds the track size.
|
|
924
|
+
"""
|
|
925
|
+
body = request if request is not None else self._validate_request(HealthStateSet, **kwargs)
|
|
926
|
+
response = self._put(
|
|
927
|
+
self._format_endpoint(Endpoints.CHARACTER_HEALTH, character_id=character_id),
|
|
928
|
+
json=body.model_dump(mode="json"),
|
|
929
|
+
)
|
|
930
|
+
return Health.model_validate(response.json())
|
|
931
|
+
|
|
932
|
+
def apply_health_action(
|
|
933
|
+
self, character_id: str, action: TrackerAction, *, amount: int = 1
|
|
934
|
+
) -> Health:
|
|
935
|
+
"""Apply one in-play action to a character's health, such as taking damage.
|
|
936
|
+
|
|
937
|
+
Health accepts ``damage``, ``heal``, and ``reset``. Damage past a full track and
|
|
938
|
+
healing past 0 are dropped.
|
|
939
|
+
|
|
940
|
+
Args:
|
|
941
|
+
character_id: The ID of the character.
|
|
942
|
+
action: The action to apply.
|
|
943
|
+
amount: How many points to apply. Must be at least 1.
|
|
944
|
+
|
|
945
|
+
Returns:
|
|
946
|
+
The updated Health.
|
|
947
|
+
|
|
948
|
+
Raises:
|
|
949
|
+
NotFoundError: If the character does not exist or has no health track.
|
|
950
|
+
AuthorizationError: If you are not the character's player or a storyteller.
|
|
951
|
+
RequestValidationError: If ``amount`` is less than 1.
|
|
952
|
+
ValidationError: If the action does not apply to health.
|
|
953
|
+
"""
|
|
954
|
+
body = self._validate_request(_TrackerAction, action=action, amount=amount)
|
|
955
|
+
response = self._post(
|
|
956
|
+
self._format_endpoint(Endpoints.CHARACTER_HEALTH_ACTIONS, character_id=character_id),
|
|
957
|
+
json=body.model_dump(exclude_none=True, mode="json"),
|
|
958
|
+
)
|
|
959
|
+
return Health.model_validate(response.json())
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"""Options and enumerations service."""
|
|
3
3
|
|
|
4
4
|
import json
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
6
|
|
|
7
7
|
from vclient._sync.services.base import SyncBaseService
|
|
8
8
|
from vclient.endpoints import Endpoints
|
|
@@ -32,9 +32,14 @@ class SyncOptionsService(SyncBaseService):
|
|
|
32
32
|
"""Format an endpoint with the scoped company_id plus any extra params."""
|
|
33
33
|
return endpoint.format(company_id=self._company_id, **kwargs)
|
|
34
34
|
|
|
35
|
-
def get_options(self) -> dict[str, dict[str,
|
|
35
|
+
def get_options(self) -> dict[str, dict[str, Any]]:
|
|
36
36
|
"""Retrieve all options and enumerations for the api.
|
|
37
37
|
|
|
38
|
+
Most values are lists of enum values. ``characters.TrackerRules`` is a nested map
|
|
39
|
+
``{game_version: {tracker_type: {"actions": [...]}}}``. Health is keyed by its
|
|
40
|
+
``type``, ``DAMAGE``. Use it to choose which tracker actions to offer instead of
|
|
41
|
+
hardcoding them.
|
|
42
|
+
|
|
38
43
|
Returns:
|
|
39
44
|
A dictionary of options and enumerations for the api.
|
|
40
45
|
"""
|
|
@@ -50,8 +50,10 @@ AuditEntityType = Literal[
|
|
|
50
50
|
"CAMPAIGN",
|
|
51
51
|
"CHAPTER",
|
|
52
52
|
"CHARACTER",
|
|
53
|
+
"CHARACTER_HEALTH",
|
|
53
54
|
"CHARACTER_INVENTORY",
|
|
54
55
|
"CHARACTER_TRAIT",
|
|
56
|
+
"CHARACTER_TRAIT_SPECIALTY",
|
|
55
57
|
"CHARGEN_SESSION",
|
|
56
58
|
"COMPANY",
|
|
57
59
|
"DEVELOPER",
|
|
@@ -102,3 +104,5 @@ UserRole = Literal["ADMIN", "STORYTELLER", "PLAYER", "UNAPPROVED", "DEACTIVATED"
|
|
|
102
104
|
WerewolfRenown = Literal["HONOR", "GLORY", "WISDOM"]
|
|
103
105
|
BlueprintTraitOrderBy = Literal["NAME", "SHEET"]
|
|
104
106
|
TraitModifyCurrency = Literal["XP", "STARTING_POINTS", "NO_COST"]
|
|
107
|
+
TrackerType = Literal["COUNTER", "DAMAGE", "STAINS"]
|
|
108
|
+
TrackerAction = Literal["spend", "gain", "damage", "heal", "add_stain", "remove_stain", "reset"]
|
|
@@ -127,6 +127,12 @@ class Endpoints:
|
|
|
127
127
|
CHARACTER_TRAIT = f"{CHARACTER_TRAITS}/{{character_trait_id}}"
|
|
128
128
|
CHARACTER_TRAIT_VALUE_OPTIONS = f"{CHARACTER_TRAIT}/value-options"
|
|
129
129
|
CHARACTER_TRAIT_VALUE = f"{CHARACTER_TRAIT}/value"
|
|
130
|
+
CHARACTER_TRAIT_SPECIALTIES = f"{CHARACTER_TRAIT}/specialties"
|
|
131
|
+
CHARACTER_TRAIT_SPECIALTY = f"{CHARACTER_TRAIT_SPECIALTIES}/{{specialty_id}}"
|
|
132
|
+
CHARACTER_TRAIT_TRACKER = f"{CHARACTER_TRAIT}/tracker"
|
|
133
|
+
CHARACTER_TRAIT_TRACKER_ACTIONS = f"{CHARACTER_TRAIT_TRACKER}/actions"
|
|
134
|
+
CHARACTER_HEALTH = f"{CHARACTER}/health"
|
|
135
|
+
CHARACTER_HEALTH_ACTIONS = f"{CHARACTER_HEALTH}/actions"
|
|
130
136
|
|
|
131
137
|
# Character Blueprint endpoints
|
|
132
138
|
BLUEPRINT_BASE = f"{COMPANY}/characterblueprint"
|
|
@@ -131,7 +131,9 @@ class NotFoundError(APIError):
|
|
|
131
131
|
class ConflictError(APIError):
|
|
132
132
|
"""Raised when there is a conflict (409 Conflict).
|
|
133
133
|
|
|
134
|
-
Common
|
|
134
|
+
Common causes are reusing an idempotency key with a different request body, or
|
|
135
|
+
another request changing a character trait's value first. For the latter, refetch
|
|
136
|
+
the trait and retry.
|
|
135
137
|
"""
|
|
136
138
|
|
|
137
139
|
|