aiobungie/crates/components.py
# MIT License
#
# Copyright (c) 2020 - Present nxtlo
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# 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.
"""Bungie profile components implementation.
Components are returned when fetching a profile. All components may or may not be available
since it depends on components passed to the request or due to privacy by the profile owner.
"""
from __future__ import annotations
__all__ = (
"Component",
"CharacterComponent",
"ProfileComponent",
"RecordsComponent",
"ItemsComponent",
"VendorsComponent",
"RecordsComponent",
"UninstancedItemsComponent",
"StringVariableComponent",
"CraftablesComponent",
)
import typing
import attrs
from aiobungie.internal import enums
from aiobungie.internal import helpers
if typing.TYPE_CHECKING:
import collections.abc as collections
from aiobungie import traits
from aiobungie.crates import activity
from aiobungie.crates import character as character_
from aiobungie.crates import entity
from aiobungie.crates import fireteams
from aiobungie.crates import items
from aiobungie.crates import profile
from aiobungie.crates import records as records_
@typing.final
class ComponentPrivacy(int, enums.Enum):
"""An enum the provides privacy settings for profile components."""
NONE = 0
PUBLIC = 1
PRIVATE = 2
@typing.final
class ComponentFields(enums.Enum):
"""An enum that provides fields found in a base component response."""
PRIVACY = ComponentPrivacy.NONE
DISABLED = False
# Main component cannot inherit from multiple classes that have `__slots__`
# Which's why some components have no slots.
@attrs.frozen(kw_only=True, slots=False)
class RecordsComponent:
"""Represents records-only Bungie component.
This includes all components that falls under the records object.
Notes
-----
* profile_records is for global profile records
* character_records is for character-only records.
Included Components
-------------------
- `Records`
- `ProfileRecords`
- `CharacterRecords`
"""
profile_records: collections.Mapping[int, records_.Record] | None
"""A mapping from the profile record id to a record component.
Notes
-----
* This will be available when `aiobungie.ComponentType.RECORDS`
is passed to the request components. otherwise will be `None`.
* This will always be `None` if it's a character component.
"""
character_records: collections.Mapping[int, records_.CharacterRecord] | None
"""A mapping from character record ids to a character record component.
This will be available when `aiobungie.ComponentType.RECORDS`
is passed to the request components. otherwise will be `None`.
"""
@attrs.frozen(kw_only=True)
class CraftablesComponent:
"""Represents craftables-only Bungie component."""
net: traits.Netrunner = attrs.field(repr=False, eq=False, hash=False)
"""A network state used for making external requests."""
craftables: collections.Mapping[int, items.CraftableItem | None]
"""A mapping from craftable item IDs to a craftable item component.
Items may or may not be available but its hash will always be available.
You can use the hash to fetch those items using `fetch_craftables` method.
"""
crafting_root_node_hash: int
"""The hash for the root presentation node definition of craftable item categories."""
@helpers.deprecated(
since="0.2.10",
removed_in="0.3.0",
use_instead="{self}.net.request.fetch_inventory_item",
hint="You can fetch each item in {self}.craftables concurrently using their keys.",
)
async def fetch_craftables(
self, limit: int | None = None
) -> collections.Sequence[entity.InventoryEntity] | None:
"""Fetch the inventory definitions for the craftables.
Parameters
----------
limit : `int | None`
The maximum number of items to fetch. If not provided, all items will be fetched.
Returns
-------
`collections.Sequence[entity.InventoryEntity] | None`
If the craftables are available, a sequence of inventory entities. Otherwise `None`.
"""
# No reason to turn the map keys if it was empty.
if not self.craftables:
return None
item_ids = tuple(self.craftables.keys())
return await helpers.awaits(
*[
self.net.request.fetch_inventory_item(item_id)
for item_id in item_ids[:limit]
],
)
@attrs.frozen(kw_only=True)
class ProfileComponent:
"""Represents a profile-only Bungie component.
This includes all components that falls under the profile object.
Included Components
-------------------
- `Profiles`
- `ProfileInventories`
- `ProfileCurrencies`
- `ProfileProgression`
"""
profiles: profile.Profile | None
"""The profile component.
This will be available when `aiobungie.ComponentType.PROFILE` is passed to the request components.
otherwise will be `None`.
"""
profile_progression: profile.ProfileProgression | None
"""The profile progression component.
This will be available when `aiobungie.ComponentType.PROFILE_PROGRESSION`
is passed to the request components.
otherwise will be `None`.
"""
profile_currencies: collections.Sequence[profile.ProfileItemImpl] | None
"""A sequence of profile currencies component.
Notes
-----
* This will always be `None` unless `auth="access_token"` is passed to the request.
* This will always be `None` unless `aiobungie.ComponentType.PROFILE_CURRENCIES`
is passed to the request components.
"""
profile_inventories: collections.Sequence[profile.ProfileItemImpl] | None
"""A sequence of profile inventories items component.
Notes
-----
* This will always be `None` unless `auth="access_token"` is passed to the request.
* This will always be `None` unless `aiobungie.ComponentType.PROFILE_INVENTORIES`
is passed to the request components.
"""
@attrs.frozen(kw_only=True)
class UninstancedItemsComponent:
"""Represents Components belonging to the player's uninstanced items."""
objectives: collections.Mapping[
int, collections.Sequence[records_.Objective]
] | None
"""A mapping from the objective id to a sequence of objectives component."""
perks: collections.Mapping[int, collections.Collection[items.ItemPerk]] | None
"""A mapping for the item instance id to its perks."""
@attrs.frozen(kw_only=True, repr=False)
class ItemsComponent(UninstancedItemsComponent):
"""Represents items-only Bungie component.
This component implements most of the `ItemX` components, i.e. ItemInstances, ItemStats, ItemSockets, etc.
Note
-----
Some fields will always be `None` until either `aiobungie.ComponentType.CHARACTER_INVENTORY`
or `aiobungie.ComponentType.CHARACTER_EQUIPMENT` is passed to the request.
"""
instances: collections.Sequence[collections.Mapping[int, items.ItemInstance]] | None
"""A sequence from the item instance id to an item object bound to this instance.
This will be available when `aiobungie.ComponentType.ITEM_INSTANCES` is passed to the request.
otherwise will be `None`.
"""
render_data: collections.Mapping[int, tuple[bool, dict[int, int]]] | None
"""A mapping from the item instance id to tuple that holds two values.
* First one is a bool that determines whether this item uses custom dyes or not.
* Second one is dict that holds int key that maps to int value of the art regions.
This will be available when `aiobungie.ComponentType.ITEM_RENDER_DATA` is passed to the request.
otherwise will be `None`.
"""
stats: collections.Mapping[int, items.ItemStatsView] | None
"""A mapping of the item instance id to a view of its stats.
This will be available when `aiobungie.ComponentType.ITEM_STATS` is passed to the request.
otherwise will be `None`.
"""
sockets: collections.Mapping[int, collections.Sequence[items.ItemSocket]] | None
"""A mapping from the item instance id to a sequence of inserted sockets into it.
This will be available when `aiobungie.ComponentType.ITEM_SOCKETS` is passed to the request.
otherwise will be `None`.
"""
reusable_plugs: collections.Mapping[
int, collections.Sequence[items.PlugItemState]
] | None
"""If the item supports reusable plugs,
this is the mapping from the item instance id to a sequence of plugs that are allowed to be used for the socket.
This will be available when `aiobungie.ComponentType.ITEM_SOCKETS` is passed to the request.
otherwise will be `None`.
"""
plug_objectives: collections.Mapping[
int, collections.Mapping[int, collections.Collection[records_.Objective]]
] | None
"""A mapping from the item instance id to a mapping of the plug hash to
a collections of the plug objectives being returned.
This will be available when `aiobungie.ComponentType.ITEM_OBJECTIVES` is passed to the request.
otherwise will be `None`.
"""
plug_states: collections.Sequence[items.PlugItemState] | None
"""A sequence of the plug states.
This will be available when `aiobungie.ComponentType.ITEM_SOCKETS` is passed to the request.
otherwise will be `None`.
"""
@helpers.deprecated(
since="0.2.10",
removed_in="0.3.0",
)
def any(self) -> bool:
"""Returns `True` if one if the components are available, `False` otherwise."""
return any(
(
self.instances,
self.render_data,
self.stats,
self.sockets,
self.reusable_plugs,
self.plug_objectives,
self.plug_states,
)
)
@helpers.deprecated(
since="0.2.10",
removed_in="0.3.0",
)
def all(self) -> bool:
"""Returns `True` if all components are available, `False` otherwise."""
return all(
(
self.instances,
self.render_data,
self.stats,
self.sockets,
self.reusable_plugs,
self.plug_objectives,
self.plug_states,
)
)
@helpers.unimplemented()
@attrs.frozen(kw_only=True)
class VendorsComponent:
"""Represents vendors-only Bungie component."""
@attrs.frozen(kw_only=True, slots=False)
class StringVariableComponent:
"""Represents the profile string variable component.
This component will be available when `aiobungie.ComponentType.STRING_VARIABLES`
is passed to the request components. otherwise attributes will be `None`.
Included Components
-------------------
- `StringVariables`
"""
profile_string_variables: collections.Mapping[int, int] | None
"""A mapping from an expression mapping definition hash to its value."""
character_string_variables: collections.Mapping[
int, collections.Mapping[int, int]
] | None
"""A mapping from the character id to a mapping from an expression mapping definition hash to its value."""
@attrs.frozen(kw_only=True, slots=False)
class MetricsComponent:
"""Represents the profile metrics component.
This will be available when `aiobungie.ComponentType.METRICS`
is passed to the request components.
otherwise will be `None`.
Included Components
-------------------
- `Metrics`
"""
metrics: collections.Sequence[
collections.Mapping[int, tuple[bool, records_.Objective | None]]
] | None
"""A sequence of mappings from the metrics hash to a tuple contains two elements.
* The first is always a `bool` determines whether the object is visible or not.
* The second is an `aiobungie.crates.Objective` of the metrics object if it has one. Otherwise it will be `None`.
"""
root_node_hash: int | None
"""The metrics presentation root node hash."""
@attrs.frozen(kw_only=True)
class CharacterComponent(RecordsComponent):
"""Represents a character-only Bungie component.
This includes all components that falls under the character object.
Included Components
-------------------
- `Characters`
- `CharacterInventories`
- `CharacterProgression`
- `CharacterRenderData`
- `CharacterActivities`
- `CharacterEquipments`
- `PresentationNodes`
- `CharacterCurrencyLookups`
- `Collectibles`
"""
character: character_.Character | None
"""The character component.
This will be available when `aiobungie.ComponentType.CHARACTERS` is passed to the request.
otherwise will be `None`.
"""
inventory: collections.Sequence[profile.ProfileItemImpl] | None
"""A sequence of the character inventory items component.
Those items may be Weapons, emblems, ships, sparrows, etc.
Notes
-----
* This will always be `None` unless `auth="access_token"` is passed to the request.
* This will always be `None` unless `aiobungie.ComponentType.CHARACTER_INVENTORY`
is passed to the request components.
"""
progressions: character_.CharacterProgression | None
"""The character progression component.
Notes
-----
* This will always be `None` unless `auth="access_token"` is passed to the request.
* This will always be `None` unless `aiobungie.ComponentType.CHARACTER_PROGRESSION`
is passed to the request components.
"""
render_data: character_.RenderedData | None
"""The character rendered data component.
This will always be `None` unless `aiobungie.ComponentType.RENDER_DATA`
is passed to the request components.
"""
activities: activity.CharacterActivity | None
"""A sequence of the character activities component.
This will always be `None` unless `aiobungie.ComponentType.CHARACTER_ACTIVITIES`
is passed to the request components.
"""
equipment: collections.Sequence[profile.ProfileItemImpl] | None
"""A sequence of the character equipment component.
This will always be `None` unless `aiobungie.ComponentType.CHARACTER_EQUIPMENT`
is passed to the request components.
"""
item_components: ItemsComponent | None = attrs.field(repr=False)
"""A component that includes all items components for this character component."""
nodes: collections.Mapping[int, records_.Node] | None
"""A mapping from the presentation node hash to a node object.
This will always be `None` unless `aiobungie.ComponentType.PRESENTATION_NODES`
is passed to the request components.
"""
currency_lookups: collections.Sequence[items.Currency] | None
"""A sequence of the character currency lookups component.
Notes
-----
* This will always be `None` unless `auth="access_token"` is passed to the request.
* This will always be `None` unless `aiobungie.ComponentType.CURRENCY_LOOKUPS`
is passed to the request components.
"""
collectibles: items.Collectible | None
"""The character's collectibles component.
This will always be `None` unless `aiobungie.ComponentType.COLLECTIBLES`
"""
@attrs.frozen(kw_only=True)
class Component(
ProfileComponent, RecordsComponent, StringVariableComponent, MetricsComponent
):
"""Concrete implementation of all Bungie components.
Components that requires auth will return `None` unless an `access_token` was passed to the request parameters.
Example
-------
```py
import aiobungie
# The components to get and return.
components = (
aiobungie.ComponentType.PROFILE,
aiobungie.ComponentType.CHARACTERS,
aiobungie.ComponentType.PROFILE_INVENTORIES,
)
profile = await client.fetch_profile(
id,
aiobungie.MembershipType.STEAM,
components,
# Assuming the component requires an auth token
auth="ACCESS_TOKEN"
)
if items := profile.profile_inventories:
for item in items:
if item.hash == 1946491241:
print(await item.fetch_self())
```
"""
characters: collections.Mapping[int, character_.Character] | None
"""A mapping from character's id to`aiobungie.crates.Character`
of the associated character within the character component.
This will be available when `aiobungie.ComponentType.CHARACTERS` is passed to the request.
otherwise will be `None`.
"""
character_inventories: collections.Mapping[
int, collections.Sequence[profile.ProfileItemImpl]
] | None
"""A mapping from character's id to a sequence of their character inventory items component.
Those items may be Weapons, emblems, ships, sparrows, etc.
Notes
-----
* This will always be `None` unless `auth="access_token"` is passed to the request.
* This will always be `None` unless `aiobungie.ComponentType.CHARACTER_INVENTORY`
is passed to the request components.
"""
character_progressions: collections.Mapping[
int, character_.CharacterProgression
] | None
"""A mapping from character's id to a character progression component.
Notes
-----
* This will always be `None` unless `auth="access_token"` is passed to the request.
* This will always be `None` unless `aiobungie.ComponentType.CHARACTER_PROGRESSION`
is passed to the request components.
"""
character_render_data: collections.Mapping[int, character_.RenderedData] | None
"""A mapping from character's id to a character rendered data component.
This will always be `None` unless `aiobungie.ComponentType.RENDER_DATA`
is passed to the request components.
"""
character_activities: collections.Mapping[int, activity.CharacterActivity] | None
"""A mapping from character's id to a sequence of their character activities component.
This will always be `None` unless `aiobungie.ComponentType.CHARACTER_ACTIVITIES`
is passed to the request components.
"""
character_equipments: collections.Mapping[
int, collections.Sequence[profile.ProfileItemImpl]
] | None
"""A mapping from character's id to a sequence of their character equipment component.
This will always be `None` unless `aiobungie.ComponentType.CHARACTER_EQUIPMENT`
is passed to the request components.
"""
# character_uninstanced_component: collections.Mapping[int, UninstancedItemsComponent] | None
"""A mapping from the character id to its uninstanced item components."""
character_collectibles: collections.Mapping[int, items.Collectible] | None
"""A mapping from each character ID to its collectibles component for this profile.
This will always be `None` unless `aiobungie.ComponentType.COLLECTIBLES`
"""
character_craftables: collections.Mapping[int, CraftablesComponent] | None
"""A mapping from character IDs to its bound craftable component.
Notes
-----
* This will be available when `aiobungie.ComponentType.CRAFTABLES` is passed to the request component.
"""
transitory: fireteams.FireteamParty | None
"""Profile Transitory component.
This component is used to show minimal information about the player's current fireteam party along
with the its members and the activity.
This will always be `None` unless `aiobungie.ComponentType.TRANSITORY`
is passed to the request components.
"""
item_components: ItemsComponent | None = attrs.field(repr=False)
"""A component that includes all items components for this profile component."""
profile_plugsets: collections.Mapping[
int, collections.Sequence[items.PlugItemState]
] | None
"""A mapping from the index of the plugset to a sequence of the profile's plug set objects."""
character_plugsets: collections.Mapping[
int, collections.Mapping[int, collections.Sequence[items.PlugItemState]]
] | None
"""A mapping from the character's id to mapping from the index of
the plug set to a sequence of plug objects bound to that character.
"""
character_nodes: collections.Mapping[
int, collections.Mapping[int, records_.Node]
] | None
"""A mapping from each character ID to a mapping of the node hash
to a sequence of presentation nodes component.
This will always be `None` unless `aiobungie.ComponentType.PRESENTATION_NODES`
is passed to the request components.
"""
platform_silver: collections.Mapping[str, profile.ProfileItemImpl] | None
"""A mapping from each platform name to its silver information.
Notes
-----
* This will always be `None` unless `auth="access_token"` is passed to the request.
* This will always be `None` unless `aiobungie.ComponentType.PLATFORM_SILVER`
is passed to the request components.
"""
profile_nodes: collections.Mapping[int, records_.Node] | None
"""A mapping from the profile presentation node hash to a node object.
This will always be `None` unless `aiobungie.ComponentType.PRESENTATION_NODES`
is passed to the request components.
"""
character_currency_lookups: collections.Mapping[
int, collections.Sequence[items.Currency]
] | None
"""A mapping from each character ID to a sequence of its currency lookups.
Notes
-----
* This will always be `None` unless `auth="access_token"` is passed to the request.
* This will always be `None` unless `aiobungie.ComponentType.CURRENCY_LOOKUPS`
is passed to the request components.
"""
profile_collectibles: items.Collectible | None
"""Represents this profile's collectibles component.
This will always be `None` unless `aiobungie.ComponentType.COLLECTIBLES`
"""