warframe-market.py 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.
Files changed (35) hide show
  1. warframe_market_py-1.0.0/.github/workflows/publish.yml +41 -0
  2. warframe_market_py-1.0.0/.gitignore +5 -0
  3. warframe_market_py-1.0.0/LICENSE +21 -0
  4. warframe_market_py-1.0.0/PKG-INFO +10 -0
  5. warframe_market_py-1.0.0/README.md +32 -0
  6. warframe_market_py-1.0.0/pyproject.toml +27 -0
  7. warframe_market_py-1.0.0/requirements.txt +2 -0
  8. warframe_market_py-1.0.0/warframe_market/__init__.py +0 -0
  9. warframe_market_py-1.0.0/warframe_market/api/__init__.py +40 -0
  10. warframe_market_py-1.0.0/warframe_market/api/item.py +42 -0
  11. warframe_market_py-1.0.0/warframe_market/api/lich.py +36 -0
  12. warframe_market_py-1.0.0/warframe_market/api/misc.py +62 -0
  13. warframe_market_py-1.0.0/warframe_market/api/order.py +96 -0
  14. warframe_market_py-1.0.0/warframe_market/api/riven.py +29 -0
  15. warframe_market_py-1.0.0/warframe_market/api/sister.py +36 -0
  16. warframe_market_py-1.0.0/warframe_market/api/user.py +16 -0
  17. warframe_market_py-1.0.0/warframe_market/client.py +42 -0
  18. warframe_market_py-1.0.0/warframe_market/common/__init__.py +17 -0
  19. warframe_market_py-1.0.0/warframe_market/common/base.py +57 -0
  20. warframe_market_py-1.0.0/warframe_market/common/enums.py +30 -0
  21. warframe_market_py-1.0.0/warframe_market/common/options.py +3 -0
  22. warframe_market_py-1.0.0/warframe_market/models/__init__.py +0 -0
  23. warframe_market_py-1.0.0/warframe_market/models/achievement.py +34 -0
  24. warframe_market_py-1.0.0/warframe_market/models/activity.py +27 -0
  25. warframe_market_py-1.0.0/warframe_market/models/item.py +48 -0
  26. warframe_market_py-1.0.0/warframe_market/models/lich.py +63 -0
  27. warframe_market_py-1.0.0/warframe_market/models/location.py +23 -0
  28. warframe_market_py-1.0.0/warframe_market/models/mission.py +19 -0
  29. warframe_market_py-1.0.0/warframe_market/models/npc.py +19 -0
  30. warframe_market_py-1.0.0/warframe_market/models/order.py +56 -0
  31. warframe_market_py-1.0.0/warframe_market/models/riven.py +56 -0
  32. warframe_market_py-1.0.0/warframe_market/models/sister.py +63 -0
  33. warframe_market_py-1.0.0/warframe_market/models/user.py +61 -0
  34. warframe_market_py-1.0.0/warframe_market/models/user_private.py +112 -0
  35. warframe_market_py-1.0.0/warframe_market/utils.py +0 -0
@@ -0,0 +1,41 @@
1
+ name: publish
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+ strategy:
11
+ matrix:
12
+ python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - name: Set up Python
16
+ uses: actions/setup-python@v5
17
+ with:
18
+ python-version: ${{ matrix.python-version }}
19
+ - name: Install Hatch
20
+ run: python -m pip install --upgrade hatch
21
+ - name: Build package
22
+ run: hatch build
23
+
24
+ publish:
25
+ needs: test
26
+ runs-on: ubuntu-latest
27
+ steps:
28
+ - uses: actions/checkout@v4
29
+ - name: Set up Python
30
+ uses: actions/setup-python@v5
31
+ with:
32
+ python-version: '3.9'
33
+ - name: Install Hatch
34
+ run: python -m pip install --upgrade hatch
35
+ - name: Build package
36
+ run: hatch build
37
+ - name: Publish to PyPI
38
+ env:
39
+ HATCH_INDEX_USER: "__token__"
40
+ HATCH_INDEX_AUTH: ${{ secrets.PYPI_TOKEN }}
41
+ run: hatch publish --yes
@@ -0,0 +1,5 @@
1
+ venv
2
+ env
3
+ **.ipynb
4
+ __pycache__
5
+ dist
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Aldi Filopati
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,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: warframe-market.py
3
+ Version: 1.0.0
4
+ Summary: An asynchronous Python wrapper for Warframe.Market API
5
+ Project-URL: Homepage, https://github.com/aldi-f/warframe-market.py
6
+ Author: aldi-f
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.9
9
+ Requires-Dist: aiohttp==3.12.6
10
+ Requires-Dist: msgspec==0.19.0
@@ -0,0 +1,32 @@
1
+ # warframe-market.py
2
+ > **Warning**: This project is still in development and is not yet ready for production use.
3
+
4
+ Warframe Market API for Python
5
+
6
+ # Installation
7
+ ```bash
8
+ pip install warframe-market.py
9
+ ```
10
+
11
+ # Usage
12
+ ```python
13
+ import asyncio
14
+ from warframe_market.client import WarframeMarketClient
15
+ from warframe_market.api.item import Items, Item
16
+
17
+ async def main():
18
+ async with WarframeMarketClient() as client:
19
+ # Get all items in English
20
+ items = await client.get(Items)
21
+ for item in items.data:
22
+ print(item.i18n["en"].name)
23
+
24
+ # Get a single item
25
+ item = await client.get(Item,"nova_prime_set")
26
+ print(item)
27
+
28
+ if __name__ == "__main__":
29
+ loop = asyncio.get_event_loop()
30
+ loop.run_until_complete(main())
31
+ loop.close()
32
+ ```
@@ -0,0 +1,27 @@
1
+ [project]
2
+ name = "warframe-market.py"
3
+ description = "An asynchronous Python wrapper for Warframe.Market API"
4
+ authors = [{ name = "aldi-f" }]
5
+ dependencies = ["aiohttp==3.12.6", "msgspec==0.19.0"]
6
+ dynamic = ["version"]
7
+ requires-python = ">=3.9"
8
+
9
+ [project.urls]
10
+ Homepage = "https://github.com/aldi-f/warframe-market.py"
11
+
12
+ [build-system]
13
+ requires = ["hatchling", "hatch-vcs"]
14
+ build-backend = "hatchling.build"
15
+
16
+ [tool.hatch.metadata]
17
+ allow-direct-url = true
18
+
19
+ [tool.hatch.build.targets.wheel]
20
+ packages = ["warframe_market"]
21
+
22
+ [tool.hatch.version]
23
+ source = "vcs"
24
+
25
+ [tool.pyright]
26
+ venvPath = "."
27
+ venv = "venv"
@@ -0,0 +1,2 @@
1
+ aiohttp==3.12.6
2
+ msgspec==0.19.0
File without changes
@@ -0,0 +1,40 @@
1
+ from .item import (
2
+ Items,
3
+ Item,
4
+ ItemSet,
5
+ )
6
+ from .lich import (
7
+ LichWeapons,
8
+ LichWeapon,
9
+ LichEphemeras,
10
+ LichQuirks,
11
+ )
12
+
13
+ from .misc import (
14
+ Locations,
15
+ Missions,
16
+ NPCs,
17
+ Versions,
18
+ )
19
+ from .order import (
20
+ OrdersItem,
21
+ OrdersItemTop,
22
+ OrdersUser,
23
+ OrderId,
24
+ )
25
+
26
+ from .riven import (
27
+ Rivens,
28
+ Riven,
29
+ RivenAttributes,
30
+ )
31
+
32
+ from .sister import (
33
+ SisterWeapons,
34
+ SisterWeapon,
35
+ SisterEphemeras,
36
+ SisterQuirks,
37
+ )
38
+ from .user import (
39
+ User,
40
+ )
@@ -0,0 +1,42 @@
1
+ import msgspec
2
+ from typing import List
3
+ from ..common.base import BaseRequest
4
+ from ..models.item import ItemShortModel, ItemModel
5
+
6
+
7
+ class Items(BaseRequest):
8
+ """Get list of all tradable items"""
9
+
10
+ __endpoint__ = "/items"
11
+
12
+ data: List[ItemShortModel]
13
+
14
+
15
+ class Item(BaseRequest):
16
+ """Get full info about one, particular item. Requires slug"""
17
+
18
+ __endpoint__ = "/item/{slug}"
19
+ __slug__ = True
20
+
21
+ data: ItemModel
22
+
23
+
24
+ class _ItemSetData(msgspec.Struct):
25
+ """Internal model for ItemSet data
26
+
27
+ Attributes:
28
+ id (str): Unique identifier for the item set
29
+ items (List[ItemModel]): List of items in the set
30
+ """
31
+
32
+ id: str
33
+ items: List[ItemModel]
34
+
35
+
36
+ class ItemSet(BaseRequest):
37
+ """Retrieve Information on Item Sets. Requires slug"""
38
+
39
+ __endpoint__ = "/item/{slug}/set"
40
+ __slug__ = True
41
+
42
+ data: _ItemSetData
@@ -0,0 +1,36 @@
1
+ from typing import List
2
+ from ..common.base import BaseRequest
3
+ from ..models.lich import LichWeaponModel, LichEphemeraModel, LichQuirkModel
4
+
5
+
6
+ class LichWeapons(BaseRequest):
7
+ """Get list of all tradable lich weapons"""
8
+
9
+ __endpoint__ = "/lich/weapons"
10
+
11
+ data: List[LichWeaponModel]
12
+
13
+
14
+ class LichWeapon(BaseRequest):
15
+ """Get full info about one, particular lich weapon. Requires slug"""
16
+
17
+ __endpoint__ = "/lich/weapon/{slug}"
18
+ __slug__ = True
19
+
20
+ data: LichWeaponModel
21
+
22
+
23
+ class LichEphemeras(BaseRequest):
24
+ """Get list of all tradable lich ephemeras"""
25
+
26
+ __endpoint__ = "/lich/ephemeras"
27
+
28
+ data: List[LichEphemeraModel]
29
+
30
+
31
+ class LichQuirks(BaseRequest):
32
+ """Get list of all tradable lich quirks"""
33
+
34
+ __endpoint__ = "/lich/quirks"
35
+
36
+ data: List[LichQuirkModel]
@@ -0,0 +1,62 @@
1
+ import msgspec
2
+ from datetime import datetime
3
+ from typing import List
4
+ from ..common.base import BaseRequest
5
+ from ..models.location import LocationModel
6
+ from ..models.npc import NpcModel
7
+ from ..models.mission import MissionModel
8
+
9
+
10
+ class _AppsData(msgspec.Struct):
11
+ ios: str
12
+ android: str
13
+ minIos: str
14
+ minAndroid: str
15
+
16
+
17
+ class _CollectionsData(msgspec.Struct):
18
+ items: str
19
+ rivens: str
20
+ liches: str
21
+ sisters: str
22
+ missions: str
23
+ npcs: str
24
+ locations: str
25
+
26
+
27
+ class _VersionData(msgspec.Struct):
28
+ apps: _AppsData
29
+ collections: _CollectionsData
30
+ updatedAt: datetime
31
+
32
+
33
+ class Versions(BaseRequest):
34
+ """Get current API version"""
35
+
36
+ __endpoint__ = "/versions"
37
+
38
+ data: _VersionData
39
+
40
+
41
+ class Locations(BaseRequest):
42
+ """Get list of all tradable lich weapons"""
43
+
44
+ __endpoint__ = "/locations"
45
+
46
+ data: List[LocationModel]
47
+
48
+
49
+ class NPCs(BaseRequest):
50
+ """Get list of all NPCs"""
51
+
52
+ __endpoint__ = "/npcs"
53
+
54
+ data: List[NpcModel]
55
+
56
+
57
+ class Missions(BaseRequest):
58
+ """Get list of all missions"""
59
+
60
+ __endpoint__ = "/missions"
61
+
62
+ data: List[MissionModel]
@@ -0,0 +1,96 @@
1
+ import msgspec
2
+ from typing import List
3
+ from ..common.base import BaseRequest
4
+ from ..models.order import OrderModel, OrderWithUserModel
5
+
6
+
7
+ class OrdersRecent(BaseRequest):
8
+ """Get the most recent orders.
9
+ 500 max, for the last 4 hours, sorted by createdAt
10
+ """
11
+
12
+ __endpoint__ = "/orders/recent"
13
+
14
+ data: List[OrderWithUserModel]
15
+
16
+
17
+ class OrdersItem(BaseRequest):
18
+ """Get a list of all orders for an item from users
19
+ who was online within the last 7 days.
20
+
21
+ Requires slug of the item.
22
+ """
23
+
24
+ __endpoint__ = "/orders/item/{slug}"
25
+ __slug__ = True
26
+
27
+ data: List[OrderWithUserModel]
28
+
29
+
30
+ class _OrdersItemData(msgspec.Struct):
31
+ """Internal model for OrdersItemTop data
32
+
33
+ Attributes:
34
+ buy (List[OrderWithUserModel]): List of top buy orders
35
+ sell (List[OrderWithUserModel]): List of top sell orders
36
+ """
37
+
38
+ buy: List[OrderWithUserModel]
39
+ sell: List[OrderWithUserModel]
40
+
41
+
42
+ class OrdersItemTop(BaseRequest):
43
+ """This endpoint is designed to fetch the top 5 buy
44
+ and top 5 sell orders for a specific item, exclusively from online users.
45
+ Orders are sorted by price.
46
+
47
+ Available query parameters:
48
+ - rank: Filter by rank (e.g., 0, 1, 2, etc.)
49
+ - rankLt: Filter by rank less than specified value
50
+ - charges: Filter by charges (e.g., 0, 1, 2, etc.)
51
+ - chargesLt: Filter by charges less than specified value
52
+ - amberStars: Filter by amber stars (e.g., 0, 1, 2, etc.)
53
+ - amberStarsLt: Filter by amber stars less than specified value
54
+ - cyanStars: Filter by cyan stars (e.g., 0, 1, 2, etc.)
55
+ - cyanStarsLt: Filter by cyan stars less than specified value
56
+ - subtype: Filter by Item subtype (e.g. "blueprint", "crafted)
57
+
58
+ Requires slug of the item.
59
+ """
60
+
61
+ __endpoint__ = "/orders/item/{slug}/top"
62
+ __params__ = [
63
+ "rank",
64
+ "rankLt",
65
+ "charges",
66
+ "chargesLt",
67
+ "amberStars",
68
+ "amberStarsLt",
69
+ "cyanStars",
70
+ "cyanStarsLt",
71
+ "subtype",
72
+ ]
73
+ __slug__ = True
74
+
75
+ data: _OrdersItemData
76
+
77
+
78
+ class OrdersUser(BaseRequest):
79
+ """Getting public orders from specified user.
80
+
81
+ Requires user ID (same as slug)
82
+ """
83
+
84
+ __endpoint__ = "/orders/user/{slug}"
85
+ __slug__ = True
86
+
87
+ data: List[OrderModel]
88
+
89
+
90
+ class OrderId(BaseRequest):
91
+ """Get full info about one, particular order. Requires order ID (same as slug)"""
92
+
93
+ __endpoint__ = "/order/{slug}"
94
+ __slug__ = True
95
+
96
+ data: OrderWithUserModel
@@ -0,0 +1,29 @@
1
+ import msgspec
2
+ from typing import List
3
+ from ..common.base import BaseRequest
4
+ from ..models.riven import RivenModel, RivenAttributeModel
5
+
6
+
7
+ class Rivens(BaseRequest):
8
+ """Get list of all tradable riven items"""
9
+
10
+ __endpoint__ = "/riven/weapons"
11
+
12
+ data: List[RivenModel]
13
+
14
+
15
+ class Riven(BaseRequest):
16
+ """Get full info about one, particular riven item. Requires slug"""
17
+
18
+ __endpoint__ = "/riven/weapon/{slug}"
19
+ __slug__ = True
20
+
21
+ data: RivenModel
22
+
23
+
24
+ class RivenAttributes(BaseRequest):
25
+ """Get list of all attributes for riven weapons"""
26
+
27
+ __endpoint__ = "/riven/attributes"
28
+
29
+ data: List[RivenAttributeModel]
@@ -0,0 +1,36 @@
1
+ from typing import List
2
+ from ..common.base import BaseRequest
3
+ from ..models.sister import SisterWeaponModel, SisterEphemeraModel, SisterQuirkModel
4
+
5
+
6
+ class SisterWeapons(BaseRequest):
7
+ """Get list of all tradable sister weapons"""
8
+
9
+ __endpoint__ = "/sister/weapons"
10
+
11
+ data: List[SisterWeaponModel]
12
+
13
+
14
+ class SisterWeapon(BaseRequest):
15
+ """Get full info about one, particular sister weapon. Requires slug"""
16
+
17
+ __endpoint__ = "/sister/weapon/{slug}"
18
+ __slug__ = True
19
+
20
+ data: SisterWeaponModel
21
+
22
+
23
+ class SisterEphemeras(BaseRequest):
24
+ """Get list of all tradable sister ephemeras"""
25
+
26
+ __endpoint__ = "/sister/ephemeras"
27
+
28
+ data: List[SisterEphemeraModel]
29
+
30
+
31
+ class SisterQuirks(BaseRequest):
32
+ """Get list of all tradable sister quirks"""
33
+
34
+ __endpoint__ = "/sister/quirks"
35
+
36
+ data: List[SisterQuirkModel]
@@ -0,0 +1,16 @@
1
+ import msgspec
2
+ from typing import List
3
+ from ..common.base import BaseRequest
4
+ from ..models.user import UserModel
5
+
6
+
7
+ class User(BaseRequest):
8
+ """Getting information about particular user
9
+
10
+ Requires user ID (same as slug)
11
+ """
12
+
13
+ __endpoint__ = "/userId/{slug}"
14
+ __slug__ = True
15
+
16
+ data: UserModel
@@ -0,0 +1,42 @@
1
+ import aiohttp
2
+ from typing import TypeVar, Type
3
+ from .common import BASE_URL
4
+ from .common.base import BaseRequest
5
+
6
+ T = TypeVar("T", bound=BaseRequest)
7
+
8
+
9
+ class WarframeMarketClient:
10
+ """Client for interacting with Warframe Market API."""
11
+
12
+ def __init__(self, base_url: str = BASE_URL):
13
+ self.base_url = base_url
14
+ self.response = None
15
+
16
+ async def get(self, request_class: Type[T], slug="", **kwargs) -> T:
17
+ """Perform a GET request using the specified request class.
18
+
19
+ Args:
20
+ request_class: The request class that defines the endpoint and response type
21
+ slug: Optional slug to append to the endpoint URL
22
+ **kwargs: Additional query parameters to include in the request
23
+
24
+ Returns:
25
+ The parsed API response
26
+ """
27
+ endpoint = request_class._get_endpoint(slug=slug, **kwargs)
28
+ url = f"{self.base_url}{endpoint}"
29
+ async with aiohttp.ClientSession() as session:
30
+ async with session.get(url) as response:
31
+ if response.status != 200:
32
+ raise Exception(f"Error {response.status}: {await response.text()}")
33
+ data = await response.text()
34
+ return request_class._decode(data)
35
+
36
+ async def __aenter__(self):
37
+ """Enter async context manager."""
38
+ return self
39
+
40
+ async def __aexit__(self, exc_type, exc_value, traceback):
41
+ """Exit async context manager."""
42
+ pass
@@ -0,0 +1,17 @@
1
+
2
+ BASE_URL = "https://api.warframe.market/v2"
3
+ BASE_STATIC_ASSETS_URL = "https://warframe.market/static/assets"
4
+
5
+ from .enums import (
6
+ Language,
7
+ Platform,
8
+ )
9
+
10
+ from .base import (
11
+ Base,
12
+ BaseRequest,
13
+ )
14
+
15
+ from .options import (
16
+ LanguageCode,
17
+ )
@@ -0,0 +1,57 @@
1
+ import msgspec
2
+ from datetime import datetime
3
+ from typing import TypeVar, ClassVar, Type, Optional, Any, List
4
+
5
+
6
+ def _dec_hook(type: Type, obj: Any) -> Any:
7
+
8
+ # Decode UTC dates
9
+ if isinstance(type, datetime) and isinstance(obj, str):
10
+ return datetime.fromisoformat(obj.strip("Z"))
11
+
12
+ # Will add more when needed
13
+
14
+ return obj
15
+
16
+
17
+ class Base(msgspec.Struct, kw_only=True):
18
+ """Base model"""
19
+
20
+ api_version: str = msgspec.field(name="apiVersion")
21
+ error: Optional[Any] = None
22
+
23
+
24
+ T = TypeVar("T", bound=Base)
25
+
26
+
27
+ class BaseRequest(Base):
28
+ """Base model for all Warframe Market API requests.
29
+
30
+ Attributes:
31
+ __endpoint__: The API endpoint for the request.
32
+ __params__: List of query parameters that can be used in the request.
33
+ __slug__: Whether the request requires a slug in the endpoint.
34
+ """
35
+
36
+ __endpoint__: ClassVar[str]
37
+ __params__: ClassVar[List[str]] = []
38
+ __slug__: ClassVar[bool] = False
39
+
40
+ @classmethod
41
+ def _decode(cls: Type[T], response: str) -> T:
42
+ """Decode the response string into a BaseResponse object."""
43
+ return msgspec.json.decode(response, type=cls, dec_hook=_dec_hook)
44
+
45
+ @classmethod
46
+ def _get_endpoint(cls, slug: Optional[str] = None, **kwargs) -> str:
47
+ """Build the endpoint URL with optional slug and query parameters."""
48
+ endpoint = cls.__endpoint__
49
+ if cls.__slug__ and slug:
50
+ endpoint = endpoint.format(slug=slug)
51
+ if cls.__params__:
52
+ params = "&".join(
53
+ f"{k}={v}" for k, v in kwargs.items() if k in cls.__params__
54
+ )
55
+ if params:
56
+ endpoint += f"?{params}"
57
+ return endpoint
@@ -0,0 +1,30 @@
1
+ from enum import Enum
2
+
3
+
4
+ class Language(Enum):
5
+ KOREAN = "ko"
6
+ RUSSIAN = "ru"
7
+ GERMAN = "de"
8
+ FRENCH = "fr"
9
+ PORTUGUESE = "pt"
10
+ CHINESE_SIMPLIFIED = "zh-hans"
11
+ CHINESE_TRADITIONAL = "zh-hant"
12
+ SPANISH = "es"
13
+ ITALIAN = "it"
14
+ POLISH = "pl"
15
+ UKRAINIAN = "uk"
16
+ ENGLISH = "en"
17
+
18
+ def __str__(self):
19
+ return self.value
20
+
21
+
22
+ class Platform(Enum):
23
+ PC = "pc"
24
+ PS4 = "ps4"
25
+ XBOX = "xbox"
26
+ SWITCH = "switch"
27
+ MOBILE = "mobile"
28
+
29
+ def __str__(self):
30
+ return self.value
@@ -0,0 +1,3 @@
1
+ from typing import Literal
2
+
3
+ LanguageCode = Literal["en", "es", "fr", "de", "it", "ko", "pl", "pt", "ru", "zh-hans", "zh-hant", "uk"]
@@ -0,0 +1,34 @@
1
+ import msgspec
2
+ from ..common.options import LanguageCode
3
+
4
+
5
+ class AchievementI18NModel(msgspec.Struct):
6
+ """
7
+ Localized information for an achievement.
8
+
9
+ Attributes:
10
+ name: Localized name of the achievement
11
+ description: Localized description of the achievement
12
+ """
13
+
14
+ name: str
15
+ description: str
16
+
17
+
18
+ class AchievementModel(msgspec.Struct):
19
+ """
20
+ Model for site achievements.
21
+
22
+ Attributes:
23
+ id: Unique identifier for the achievement
24
+ icon: URL to the achievement's icon
25
+ thumb: URL to the achievement's thumbnail image
26
+ type: Type or category of the achievement
27
+ i18n: Localized text in various languages
28
+ """
29
+
30
+ id: str
31
+ icon: str
32
+ thumb: str
33
+ type: str
34
+ i18n: dict[LanguageCode, AchievementI18NModel]
@@ -0,0 +1,27 @@
1
+ from typing import Optional
2
+ import msgspec
3
+ from enum import Enum
4
+
5
+
6
+ class ActivityType(str, Enum):
7
+ """Types of activities a user can be engaged in."""
8
+
9
+ ON_MISSION = "on_mission"
10
+ DOJO = "dojo"
11
+ UNKNOWN = "unknown"
12
+ EMPTY = ""
13
+
14
+
15
+ class ActivityModel(msgspec.Struct):
16
+ """
17
+ Model for user activity information.
18
+
19
+ Attributes:
20
+ type: Name of the activity (e.g., 'on mission', 'dojo')
21
+ details: Optional specifics about the activity
22
+ started_at: Timestamp of when the activity started
23
+ """
24
+
25
+ type: ActivityType
26
+ details: Optional[str] = None
27
+ started_at: Optional[str] = msgspec.field(default=None, name="startedAt")
@@ -0,0 +1,48 @@
1
+ import msgspec
2
+ from ..common.options import LanguageCode
3
+
4
+
5
+ class ItemI18NModel(msgspec.Struct):
6
+ """Localization data for an item."""
7
+
8
+ name: str
9
+ icon: str
10
+ thumb: str
11
+ description: str | None = None
12
+ wiki_link: str | None = msgspec.field(default=None, name="wikiLink")
13
+ sub_icon: str | None = msgspec.field(default=None, name="subIcon")
14
+
15
+
16
+ class ItemShortModel(msgspec.Struct):
17
+ """Short form item data model."""
18
+
19
+ id: str
20
+ slug: str
21
+ game_ref: str = msgspec.field(name="gameRef")
22
+ tags: list[str] = msgspec.field(default_factory=list)
23
+ i18n: dict[LanguageCode, ItemI18NModel] = msgspec.field(default_factory=dict)
24
+ max_rank: int | None = msgspec.field(default=None, name="maxRank")
25
+ max_charges: int | None = msgspec.field(default=None, name="maxCharges")
26
+ vaulted: bool | None = None
27
+ ducats: int | None = None
28
+ amber_stars: int | None = msgspec.field(default=None, name="amberStars")
29
+ cyan_stars: int | None = msgspec.field(default=None, name="cyanStars")
30
+ base_endo: int | None = msgspec.field(default=None, name="baseEndo")
31
+ endo_multiplier: float | None = msgspec.field(default=None, name="endoMultiplier")
32
+ subtypes: list[str] = msgspec.field(default_factory=list)
33
+
34
+
35
+ class ItemModel(ItemShortModel):
36
+ """Full item data model that extends ItemShortModel."""
37
+
38
+ tradable: bool | None = None
39
+ set_root: bool | None = msgspec.field(default=None, name="setRoot")
40
+ set_parts: list[str] | None = msgspec.field(default=None, name="setParts")
41
+ quantity_in_set: int | None = msgspec.field(default=None, name="quantityInSet")
42
+ rarity: str | None = None
43
+ bulk_tradable: bool | None = msgspec.field(default=None, name="bulkTradable")
44
+ max_amber_stars: int | None = msgspec.field(default=None, name="maxAmberStars")
45
+ max_cyan_stars: int | None = msgspec.field(default=None, name="maxCyanStars")
46
+ req_mastery_rank: int | None = msgspec.field(default=None, name="reqMasteryRank")
47
+ trading_tax: int | None = msgspec.field(default=None, name="tradingTax")
48
+ vosfor: int | None = None
@@ -0,0 +1,63 @@
1
+ import msgspec
2
+ from ..common.options import LanguageCode
3
+
4
+
5
+ # Lich Weapon
6
+ class LichWeaponI18NModel(msgspec.Struct):
7
+ """Localization data for a Lich weapon."""
8
+
9
+ name: str = msgspec.field(name="itemName") # Changed to match Go struct's json tag
10
+ icon: str
11
+ thumb: str
12
+ wiki_link: str | None = msgspec.field(default=None, name="wikiLink")
13
+
14
+
15
+ class LichWeaponModel(msgspec.Struct):
16
+ """Model for Kuva/Sister Lich weapons."""
17
+
18
+ id: str
19
+ slug: str
20
+ game_ref: str = msgspec.field(name="gameRef")
21
+ req_mastery_rank: int = msgspec.field(name="reqMasteryRank")
22
+ i18n: dict[LanguageCode, LichWeaponI18NModel] = msgspec.field(default_factory=dict)
23
+
24
+
25
+ # Lich Ephemera
26
+ class LichEphemeraI18NModel(msgspec.Struct):
27
+ """Localization data for a Lich ephemera."""
28
+
29
+ name: str = msgspec.field(name="itemName")
30
+ icon: str
31
+ thumb: str
32
+
33
+
34
+ class LichEphemeraModel(msgspec.Struct):
35
+ """Model for Kuva/Sister Lich ephemeras."""
36
+
37
+ id: str
38
+ slug: str
39
+ game_ref: str = msgspec.field(name="gameRef")
40
+ animation: str
41
+ element: str
42
+ i18n: dict[LanguageCode, LichEphemeraI18NModel] = msgspec.field(
43
+ default_factory=dict
44
+ )
45
+
46
+
47
+ # Lich Quirk
48
+ class LichQuirkI18NModel(msgspec.Struct):
49
+ """Localization data for a Lich quirk."""
50
+
51
+ name: str = msgspec.field(name="itemName")
52
+ description: str | None = None
53
+ icon: str | None = None
54
+ thumb: str | None = None
55
+
56
+
57
+ class LichQuirkModel(msgspec.Struct):
58
+ """Model for Kuva/Sister Lich quirks."""
59
+
60
+ id: str
61
+ slug: str
62
+ group: str | None = None
63
+ i18n: dict[LanguageCode, LichQuirkI18NModel] = msgspec.field(default_factory=dict)
@@ -0,0 +1,23 @@
1
+ import msgspec
2
+ from ..common.options import LanguageCode
3
+
4
+
5
+ class LocationI18NModel(msgspec.Struct):
6
+ """Localization data for a location."""
7
+
8
+ node_name: str = msgspec.field(name="nodeName")
9
+ icon: str
10
+ thumb: str
11
+ system_name: str | None = msgspec.field(default=None, name="systemName")
12
+
13
+
14
+ class LocationModel(msgspec.Struct):
15
+ """Model for locations."""
16
+
17
+ id: str
18
+ slug: str
19
+ game_ref: str = msgspec.field(name="gameRef")
20
+ faction: str | None = None
21
+ min_level: int | None = msgspec.field(default=None, name="minLevel")
22
+ max_level: int | None = msgspec.field(default=None, name="maxLevel")
23
+ i18n: dict[LanguageCode, LocationI18NModel] = msgspec.field(default_factory=dict)
@@ -0,0 +1,19 @@
1
+ import msgspec
2
+ from ..common.options import LanguageCode
3
+
4
+
5
+ class MissionI18NModel(msgspec.Struct):
6
+ """Localization data for a mission."""
7
+
8
+ name: str
9
+ icon: str | None = None
10
+ thumb: str | None = None
11
+
12
+
13
+ class MissionModel(msgspec.Struct):
14
+ """Model for missions."""
15
+
16
+ id: str
17
+ slug: str
18
+ game_ref: str = msgspec.field(name="gameRef")
19
+ i18n: dict[LanguageCode, MissionI18NModel] = msgspec.field(default_factory=dict)
@@ -0,0 +1,19 @@
1
+ import msgspec
2
+ from ..common.options import LanguageCode
3
+
4
+
5
+ class NpcI18NModel(msgspec.Struct):
6
+ """Localization data for an NPC."""
7
+
8
+ name: str
9
+ icon: str
10
+ thumb: str
11
+
12
+
13
+ class NpcModel(msgspec.Struct):
14
+ """Model for NPCs."""
15
+
16
+ id: str
17
+ slug: str
18
+ game_ref: str = msgspec.field(name="gameRef")
19
+ i18n: dict[LanguageCode, NpcI18NModel] = msgspec.field(default_factory=dict)
@@ -0,0 +1,56 @@
1
+ import msgspec
2
+ from typing import Optional
3
+
4
+ from .user import UserShortModel
5
+
6
+
7
+ class OrderModel(msgspec.Struct):
8
+ """
9
+ Model for trade orders.
10
+
11
+ Attributes:
12
+ id: Unique identifier of the order
13
+ type: Type of order ('buy' or 'sell')
14
+ platinum: Total platinum currency involved
15
+ quantity: Number of items in the order
16
+ per_trade: Optional items quantity per transaction
17
+ rank: Optional rank/level of the item
18
+ charges: Optional number of charges left (for requiem mods)
19
+ subtype: Optional specific subtype/category of the item
20
+ amber_stars: Optional count of amber stars (for sculpture orders)
21
+ cyan_stars: Optional count of cyan stars (for sculpture orders)
22
+ visible: Whether the order is publicly visible
23
+ created_at: Creation timestamp of the order
24
+ updated_at: Last modification timestamp
25
+ item_id: Unique identifier of the involved item
26
+ group: User-defined group for the order
27
+ """
28
+
29
+ id: str
30
+ type: str
31
+ platinum: int
32
+ quantity: int
33
+ visible: bool
34
+ created_at: str = msgspec.field(name="createdAt")
35
+ updated_at: str = msgspec.field(name="updatedAt")
36
+ item_id: str = msgspec.field(name="itemId")
37
+ group: str
38
+ per_trade: Optional[int] = msgspec.field(default=None, name="perTrade")
39
+ rank: Optional[int] = None
40
+ charges: Optional[int] = None
41
+ subtype: Optional[str] = None
42
+ amber_stars: Optional[int] = msgspec.field(default=None, name="amberStars")
43
+ cyan_stars: Optional[int] = msgspec.field(default=None, name="cyanStars")
44
+
45
+
46
+ class OrderWithUserModel(OrderModel, kw_only=True):
47
+ """
48
+ Model for trade orders with associated user information.
49
+
50
+ Extends the base Order model to include the user who created the order.
51
+
52
+ Additional Attributes:
53
+ user: Basic profile information of the order's creator
54
+ """
55
+
56
+ user: UserShortModel
@@ -0,0 +1,56 @@
1
+ import msgspec
2
+ from typing import Literal
3
+
4
+ from ..common.options import LanguageCode
5
+
6
+
7
+ RivenType = Literal["rifle", "shotgun", "pistol", "melee", "kitgun", "zaw"]
8
+
9
+
10
+ class RivenI18N(msgspec.Struct):
11
+ """Localization data for a Riven mod."""
12
+
13
+ name: str = msgspec.field(name="itemName")
14
+ icon: str
15
+ thumb: str
16
+ wiki_link: str | None = msgspec.field(default=None, name="wikiLink")
17
+
18
+
19
+ class RivenAttributeI18N(msgspec.Struct):
20
+ """Localization data for a Riven attribute."""
21
+
22
+ name: str = msgspec.field(name="effect")
23
+ icon: str
24
+ thumb: str
25
+
26
+
27
+ class RivenModel(msgspec.Struct):
28
+ """Model for Riven mods."""
29
+
30
+ id: str
31
+ slug: str
32
+ game_ref: str = msgspec.field(name="gameRef")
33
+ disposition: float
34
+ req_mastery_rank: int = msgspec.field(name="reqMasteryRank")
35
+ group: str | None = None
36
+ riven_type: str | None = msgspec.field(default=None, name="rivenType")
37
+ i18n: dict[LanguageCode, RivenI18N] = msgspec.field(default_factory=dict)
38
+
39
+
40
+ class RivenAttributeModel(msgspec.Struct):
41
+ """Model for Riven mod attributes."""
42
+
43
+ id: str
44
+ slug: str
45
+ game_ref: str = msgspec.field(name="gameRef")
46
+ prefix: str
47
+ suffix: str
48
+ group: str | None = None
49
+ i18n: dict[LanguageCode, RivenAttributeI18N] = msgspec.field(default_factory=dict)
50
+ exclusive_to: list[str] | None = msgspec.field(default=None, name="exclusiveTo")
51
+ positive_is_negative: bool | None = msgspec.field(
52
+ default=None, name="positiveIsNegative"
53
+ )
54
+ unit: str | None = None
55
+ positive_only: bool | None = msgspec.field(default=None, name="positiveOnly")
56
+ negative_only: bool | None = msgspec.field(default=None, name="negativeOnly")
@@ -0,0 +1,63 @@
1
+ import msgspec
2
+
3
+ from ..common.options import LanguageCode
4
+
5
+
6
+ class SisterWeaponI18NModel(msgspec.Struct):
7
+ """Localization data for a Sister weapon."""
8
+
9
+ name: str = msgspec.field(name="itemName")
10
+ icon: str
11
+ thumb: str
12
+ wiki_link: str | None = msgspec.field(default=None, name="wikiLink")
13
+
14
+
15
+ class SisterWeaponModel(msgspec.Struct):
16
+ """Model for Sister weapons."""
17
+
18
+ id: str
19
+ slug: str
20
+ game_ref: str = msgspec.field(name="gameRef")
21
+ req_mastery_rank: int = msgspec.field(name="reqMasteryRank")
22
+ i18n: dict[LanguageCode, SisterWeaponI18NModel] = msgspec.field(
23
+ default_factory=dict
24
+ )
25
+
26
+
27
+ class SisterEphemeraI18NModel(msgspec.Struct):
28
+ """Localization data for a Sister ephemera."""
29
+
30
+ name: str = msgspec.field(name="itemName")
31
+ icon: str
32
+ thumb: str
33
+
34
+
35
+ class SisterEphemeraModel(msgspec.Struct):
36
+ """Model for Sister ephemeras."""
37
+
38
+ id: str
39
+ slug: str
40
+ game_ref: str = msgspec.field(name="gameRef")
41
+ animation: str
42
+ element: str
43
+ i18n: dict[LanguageCode, SisterEphemeraI18NModel] = msgspec.field(
44
+ default_factory=dict
45
+ )
46
+
47
+
48
+ class SisterQuirkI18NModel(msgspec.Struct):
49
+ """Localization data for a Sister quirk."""
50
+
51
+ name: str = msgspec.field(name="itemName")
52
+ icon: str
53
+ thumb: str
54
+ description: str | None = None
55
+
56
+
57
+ class SisterQuirkModel(msgspec.Struct):
58
+ """Model for Sister quirks."""
59
+
60
+ id: str
61
+ slug: str
62
+ group: str | None = None
63
+ i18n: dict[LanguageCode, SisterQuirkI18NModel] = msgspec.field(default_factory=dict)
@@ -0,0 +1,61 @@
1
+ import msgspec
2
+ from .activity import ActivityModel
3
+ from .achievement import AchievementModel
4
+
5
+
6
+ class UserShortModel(msgspec.Struct):
7
+ """
8
+ Short form user data model.
9
+
10
+ Attributes:
11
+ id: Unique identifier of the user
12
+ ingame_name: In-game name of the user
13
+ avatar: Optional avatar image URL
14
+ reputation: Reputation score
15
+ locale: Preferred communication language (e.g., 'en', 'ko', 'es')
16
+ platform: Gaming platform used by the user
17
+ crossplay: Whether the user has crossplay enabled
18
+ status: Current status of the user
19
+ activity: Current activity of the user
20
+ last_seen: Timestamp of the user's last online presence
21
+ """
22
+
23
+ id: str
24
+ ingame_name: str = msgspec.field(name="ingameName")
25
+ reputation: int
26
+ locale: str
27
+ platform: str
28
+ crossplay: bool
29
+ status: str
30
+ activity: ActivityModel
31
+ last_seen: str = msgspec.field(name="lastSeen")
32
+ avatar: str | None = None
33
+
34
+
35
+ class UserModel(UserShortModel, kw_only=True):
36
+ """
37
+ Full user profile model that extends UserShortModel.
38
+
39
+ Additional Attributes:
40
+ background: Optional profile background image URL
41
+ about: Optional HTML-formatted user description
42
+ mastery_level: Optional in-game mastery level
43
+ achievement_showcase: List of showcased achievements
44
+ banned: Whether the user is currently banned
45
+ ban_until: Optional ban expiration timestamp
46
+ warned: Whether the user has been warned (mod/admin only)
47
+ warn_message: Optional warning message (mod/admin only)
48
+ ban_message: Optional ban reason (mod/admin only)
49
+ """
50
+
51
+ background: str | None = None
52
+ about: str | None = None
53
+ mastery_level: int | None = msgspec.field(default=None, name="masteryLevel")
54
+ achievement_showcase: list[AchievementModel] = msgspec.field(
55
+ default_factory=list, name="achievementShowcase"
56
+ )
57
+ banned: bool | None = None
58
+ ban_until: str | None = msgspec.field(default=None, name="banUntil")
59
+ warned: bool | None = None
60
+ warn_message: str | None = msgspec.field(default=None, name="warnMessage")
61
+ ban_message: str | None = msgspec.field(default=None, name="banMessage")
@@ -0,0 +1,112 @@
1
+ import msgspec
2
+ from enum import Enum
3
+ from typing import List, Optional
4
+
5
+ from .activity import ActivityModel
6
+ from .achievement import AchievementModel
7
+
8
+
9
+ class Role(str, Enum):
10
+ """User roles in the system."""
11
+
12
+ USER = "user"
13
+ MODERATOR = "moderator"
14
+ ADMIN = "admin"
15
+
16
+
17
+ class Tier(str, Enum):
18
+ """Subscription tiers."""
19
+
20
+ NONE = "none"
21
+ BRONZE = "bronze"
22
+ SILVER = "silver"
23
+ GOLD = "gold"
24
+ DIAMOND = "diamond"
25
+
26
+
27
+ class LinkedAccounts(msgspec.Struct):
28
+ """Model for linked external accounts."""
29
+
30
+ pass
31
+
32
+
33
+ class UserPrivateModel(msgspec.Struct):
34
+ """
35
+ Private user profile with full details and sensitive information.
36
+
37
+ Attributes:
38
+ id: Unique identifier of the user
39
+ role: User's role in the system (e.g., user, moderator, admin)
40
+ ingame_name: In-game name of the user
41
+ reputation: Reputation score
42
+ locale: Preferred communication language (e.g., 'en', 'ko', 'es')
43
+ platform: Gaming platform used by the user
44
+ crossplay: Whether the user has crossplay enabled
45
+ status: Current status of the user
46
+ activity: Current activity of the user
47
+ last_seen: Timestamp of the user's last online presence
48
+ mastery_rank: In-game mastery rank
49
+ credits: User's credit balance
50
+ theme: User's selected theme
51
+ verification: Whether the user is verified
52
+ check_code: Verification check code
53
+ tier: Subscription tier of the user
54
+ subscription: Whether the user has an active subscription
55
+ linked_accounts: Linked external accounts information
56
+ has_email: Whether the user has an email address associated with their account
57
+ created_at: Account creation timestamp
58
+ reviews_left: Number of reviews left by the user
59
+ unread_messages: Count of unread messages in the user's inbox
60
+ avatar: Optional avatar image URL
61
+ achievement_showcase: List of achievements showcased by the user
62
+ ignore_list: List of user IDs that this user has ignored
63
+ about: Optional HTML-formatted user description
64
+ about_raw: Optional raw text version of the user's description
65
+ warned: Whether the user has been warned (mod/admin only)
66
+ warn_message: Optional warning message (mod/admin only)
67
+ banned: Whether the user is currently banned (mod/admin only)
68
+ ban_until: Optional timestamp until which the user is banned (mod/admin only)
69
+ ban_message: Optional reason for the ban (mod/admin only)
70
+ delete_in_progress: Whether the user account deletion is in progress (mod/admin only)
71
+ delete_at: Optional timestamp when the account deletion is scheduled (mod/admin only)
72
+ """
73
+
74
+ id: str
75
+ role: Role
76
+ ingame_name: str = msgspec.field(name="ingameName")
77
+ reputation: int
78
+ locale: str
79
+ platform: str
80
+ crossplay: bool
81
+ status: str
82
+ activity: ActivityModel
83
+ last_seen: str = msgspec.field(name="lastSeen")
84
+ mastery_rank: int = msgspec.field(name="masteryRank")
85
+ credits: int
86
+ theme: str
87
+ verification: bool
88
+ check_code: str = msgspec.field(name="checkCode")
89
+ tier: Tier
90
+ subscription: bool
91
+ linked_accounts: LinkedAccounts = msgspec.field(name="linkedAccounts")
92
+ has_email: bool = msgspec.field(name="hasEmail")
93
+ created_at: str = msgspec.field(name="createdAt")
94
+ reviews_left: int = msgspec.field(name="reviewsLeft")
95
+ unread_messages: int = msgspec.field(name="unreadMessages")
96
+ # Optional fields
97
+ avatar: str | None = None
98
+ achievement_showcase: List[AchievementModel] = msgspec.field(
99
+ default_factory=list, name="achievementShowcase"
100
+ )
101
+ ignore_list: List[str] = msgspec.field(default_factory=list, name="ignoreList")
102
+ about: Optional[str] = None
103
+ about_raw: Optional[str] = msgspec.field(default=None, name="aboutRaw")
104
+ warned: Optional[bool] = None
105
+ warn_message: Optional[str] = msgspec.field(default=None, name="warnMessage")
106
+ banned: Optional[bool] = None
107
+ ban_until: Optional[str] = msgspec.field(default=None, name="banUntil")
108
+ ban_message: Optional[str] = msgspec.field(default=None, name="banMessage")
109
+ delete_in_progress: Optional[bool] = msgspec.field(
110
+ default=None, name="deleteInProgress"
111
+ )
112
+ delete_at: Optional[str] = msgspec.field(default=None, name="deleteAt")
File without changes