Skip to content

Characters API Models

DestinyCharacterActivitiesComponent

Bases: BaseModel

This component holds activity data for a character. It will tell you about the character's current activity status, as well as activities that are available to the user.

Manifest Information

This model has some attributes which can be filled with additional information found in the manifest (manifest_...). Without additional work, these attributes will be None, since they require additional requests and database lookups.

To fill the manifest dependent attributes, either:

  • Run await ThisClass.fetch_manifest_information(), see here
  • Set Client.always_return_manifest_information to True, see here

Attributes:

Name Type Description
available_activities list[DestinyActivity]

The list of activities that the user can play.

available_activity_interactables list[DestinyActivityInteractableReference]

The list of activity interactables that the player can interact with.

current_activity_hash int

If the user is in an activity, this will be the hash of the Activity being played. Note that you must combine this info with currentActivityModeHash to get a real picture of what the user is doing right now. For instance, PVP "Activities" are just maps: it's the ActivityMode that determines what type of PVP game they're playing.

current_activity_mode_hash int

If the user is in an activity, this will be the hash of the activity mode being played. Combine with currentActivityHash to give a person a full picture of what they're doing right now.

current_activity_mode_hashes list[int]

If the user is in an activity, this will be the hashes of the DestinyActivityModeDefinition being played. Combine with currentActivityHash to give a person a full picture of what they're doing right now.

current_activity_mode_type Union[DestinyActivityModeType, int]

And the current activity's most specific mode type, if it can be found.

current_activity_mode_types list[Union[DestinyActivityModeType, int]]

All Activity Modes that apply to the current activity being played, in enum form.

current_playlist_activity_hash int

If the user is in a playlist, this is the hash identifier for the playlist that they chose.

date_activity_started datetime

The last date that the user started playing an activity.

difficulty_tier_collections dict[int, dict]

The activity difficulty tier states for this character.

last_completed_story_hash int

This will have the activity hash of the last completed story/campaign mission, in case you care about that.

selectable_skull_collections dict[int, dict]

The selectable activity skulls states for this character.

manifest_current_activity_hash Optional[DestinyActivityDefinition]

Manifest information for current_activity_hash

manifest_current_activity_mode_hash Optional[DestinyActivityModeDefinition]

Manifest information for current_activity_mode_hash

manifest_current_playlist_activity_hash Optional[DestinyActivityDefinition]

Manifest information for current_playlist_activity_hash

manifest_last_completed_story_hash Optional[DestinyActivityDefinition]

Manifest information for last_completed_story_hash

Source code in src/bungio/models/bungie/destiny/entities/characters.py
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
@custom_define()
class DestinyCharacterActivitiesComponent(BaseModel):
    """
    This component holds activity data for a character. It will tell you about the character's current activity status, as well as activities that are available to the user.

    Tip: Manifest Information
        This model has some attributes which can be filled with additional information found in the manifest (`manifest_...`).
        Without additional work, these attributes will be `None`, since they require additional requests and database lookups.

        To fill the manifest dependent attributes, either:

        - Run `await ThisClass.fetch_manifest_information()`, see [here](/API Reference/Models/base)
        - Set `Client.always_return_manifest_information` to `True`, see [here](/API Reference/client)

    Attributes:
        available_activities: The list of activities that the user can play.
        available_activity_interactables: The list of activity interactables that the player can interact with.
        current_activity_hash: If the user is in an activity, this will be the hash of the Activity being played. Note that you must combine this info with currentActivityModeHash to get a real picture of what the user is doing right now. For instance, PVP "Activities" are just maps: it's the ActivityMode that determines what type of PVP game they're playing.
        current_activity_mode_hash: If the user is in an activity, this will be the hash of the activity mode being played. Combine with currentActivityHash to give a person a full picture of what they're doing right now.
        current_activity_mode_hashes: If the user is in an activity, this will be the hashes of the DestinyActivityModeDefinition being played. Combine with currentActivityHash to give a person a full picture of what they're doing right now.
        current_activity_mode_type: And the current activity's most specific mode type, if it can be found.
        current_activity_mode_types: All Activity Modes that apply to the current activity being played, in enum form.
        current_playlist_activity_hash: If the user is in a playlist, this is the hash identifier for the playlist that they chose.
        date_activity_started: The last date that the user started playing an activity.
        difficulty_tier_collections: The activity difficulty tier states for this character.
        last_completed_story_hash: This will have the activity hash of the last completed story/campaign mission, in case you care about that.
        selectable_skull_collections: The selectable activity skulls states for this character.
        manifest_current_activity_hash: Manifest information for `current_activity_hash`
        manifest_current_activity_mode_hash: Manifest information for `current_activity_mode_hash`
        manifest_current_playlist_activity_hash: Manifest information for `current_playlist_activity_hash`
        manifest_last_completed_story_hash: Manifest information for `last_completed_story_hash`
    """

    available_activities: list["DestinyActivity"] = custom_field(metadata={"type": """list[DestinyActivity]"""})
    available_activity_interactables: list["DestinyActivityInteractableReference"] = custom_field(
        metadata={"type": """list[DestinyActivityInteractableReference]"""}
    )
    current_activity_hash: int = custom_field()
    current_activity_mode_hash: int = custom_field()
    current_activity_mode_hashes: list[int] = custom_field(metadata={"type": """list[int]"""})
    current_activity_mode_type: Union["DestinyActivityModeType", int] = custom_field(
        converter=enum_converter("DestinyActivityModeType")
    )
    current_activity_mode_types: list[Union["DestinyActivityModeType", int]] = custom_field(
        converter=enum_converter("DestinyActivityModeType")
    )
    current_playlist_activity_hash: int = custom_field()
    date_activity_started: datetime = custom_field()
    difficulty_tier_collections: dict[int, dict] = custom_field(metadata={"type": """dict[int, dict]"""})
    last_completed_story_hash: int = custom_field()
    selectable_skull_collections: dict[int, dict] = custom_field(metadata={"type": """dict[int, dict]"""})
    manifest_current_activity_hash: Optional["DestinyActivityDefinition"] = custom_field(default=None)
    manifest_current_activity_mode_hash: Optional["DestinyActivityModeDefinition"] = custom_field(default=None)
    manifest_current_playlist_activity_hash: Optional["DestinyActivityDefinition"] = custom_field(default=None)
    manifest_last_completed_story_hash: Optional["DestinyActivityDefinition"] = custom_field(default=None)

_convert_to_bungie_case(string) cached staticmethod

Convert a string to how it is represented by bungie: my_name_string -> myNameString

Parameters:

Name Type Description Default
string str

The og string

required

Returns:

Type Description
str

The bungie string

Source code in src/bungio/models/base.py
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
@staticmethod
@functools.cache
def _convert_to_bungie_case(string: str) -> str:
    """
    Convert a string to how it is represented by bungie: my_name_string -> myNameString

    Args:
        string: The og string

    Returns:
        The bungie string
    """

    if "_" not in string:
        return string
    else:
        split = string.split("_")
        return "".join((split[0], *(s.capitalize() for s in split[1:])))

fetch_manifest_information(include=None, exclude=None, _cache=None) async

Fill the model in-place with information from the manifest.

Example

Fill every attribute

1
2
3
4
model: DestinyHistoricalStatsActivity
await model.fetch_manifest_information()
assert model.manifest_director_activity_hash is not None
assert model.manifest_reference_id is not None

Fill only some attribute

1
2
3
4
model: DestinyHistoricalStatsActivity
await model.fetch_manifest_information(include=["manifest_director_activity_hash"])
assert model.manifest_director_activity_hash is not None
assert model.manifest_reference_id is None
1
2
3
4
model: DestinyHistoricalStatsActivity
await model.fetch_manifest_information(exclude=["manifest_director_activity_hash"])
assert model.manifest_director_activity_hash is None
assert model.manifest_reference_id is not None

Parameters:

Name Type Description Default
include Optional[list[str]]

A list of attributes you want to include. Excludes everything not mentioned

None
exclude Optional[list[str]]

A list of attributes you want to exclude. Includes everything not mentioned

None
Source code in src/bungio/models/base.py
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
async def fetch_manifest_information(
    self, include: Optional[list[str]] = None, exclude: Optional[list[str]] = None, _cache: Optional[dict] = None
):
    """
    Fill the model in-place with information from the manifest.

    Example:
        Fill every attribute
        ```py
        model: DestinyHistoricalStatsActivity
        await model.fetch_manifest_information()
        assert model.manifest_director_activity_hash is not None
        assert model.manifest_reference_id is not None
        ```

        Fill only some attribute
        ```py
        model: DestinyHistoricalStatsActivity
        await model.fetch_manifest_information(include=["manifest_director_activity_hash"])
        assert model.manifest_director_activity_hash is not None
        assert model.manifest_reference_id is None
        ```
        ```py
        model: DestinyHistoricalStatsActivity
        await model.fetch_manifest_information(exclude=["manifest_director_activity_hash"])
        assert model.manifest_director_activity_hash is None
        assert model.manifest_reference_id is not None
        ```

    Args:
        include: A list of attributes you want to include. Excludes everything not mentioned
        exclude:  A list of attributes you want to exclude. Includes everything not mentioned
    """

    if not isinstance(self._client.manifest_storage, AsyncEngine):
        raise ValueError("Client.manifest_storage must be set up to use this")

    if not _cache:
        _cache = {}

    class_definition = attrs.fields_dict(type(self))  # noqa

    # loop through the class attributes
    for name in self.__dir__():
        if name.startswith("__"):
            continue

        if include and name not in include:
            continue
        if exclude and name in exclude:
            continue

        # manifest entries
        if name.startswith("manifest_"):
            striped_name = name.removeprefix("manifest_")
            value = getattr(self, striped_name)

            if value is MISSING:
                return

            # check the cache to avoid infinite recursion
            if cached := _cache.get(value, None):
                manifest_value = cached

            else:
                attr_definition = class_definition[name]
                manifest_class_name = attr_definition.type.__str__().removesuffix("')]").split("'")[-1]
                manifest_value = await self._client.manifest.fetch(
                    manifest_class=getattr(models, manifest_class_name), value=value
                )

                _cache[value] = manifest_value

                # check if the model has manifest models itself
                if manifest_value:
                    await manifest_value.fetch_manifest_information(_cache=_cache)

            setattr(self, name, manifest_value)

        # sub models which may have manifest entries too
        elif hasattr((value := getattr(self, name)), "fetch_manifest_information"):
            await value.fetch_manifest_information(_cache=_cache)

from_dict(data, client, recursive=False, *args, **kwargs) async classmethod

Convert json data to this model

Parameters:

Name Type Description Default
data dict

The json representation of the model, usually received by bungie

required
client 'Client'

The client obj

required
recursive bool

If this was called recursively

False

Returns:

Type Description
BaseModel

The model

Source code in src/bungio/models/base.py
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
@classmethod
async def from_dict(cls, data: dict, client: "Client", recursive: bool = False, *args, **kwargs) -> BaseModel:
    """
    Convert json data to this model

    Args:
        data: The json representation of the model, usually received by bungie
        client: The client obj
        recursive: If this was called recursively

    Returns:
        The model
    """

    if isinstance(data, cls):
        return data

    if not isinstance(data, dict):
        raise ValueError

    if "Response" in data:
        data = data["Response"]

    # also use the kwargs as data
    data = kwargs | data
    data = cls.process_dict(data=data, client=client)

    prepared = {}
    for name, field in attrs.fields_dict(cls).items():
        if field.init and name != "_client":
            default = field.default

            # get the value we want. This also skips the manifest_... entries since they have no value and a default
            bungie_name = cls._convert_to_bungie_case(name)
            value = data.get(bungie_name, attrs.NOTHING)

            # bungie is veeery inconsistent and sometimes like to start their params with an upper case for some reason
            # only sometimes tho :)
            if value is attrs.NOTHING:
                value = data.get(f"{bungie_name[0].capitalize()}{bungie_name[1:]}", attrs.NOTHING)

            # sadly bungie sometimes does not return info without marking that fact in the api specs
            if value is attrs.NOTHING and default is attrs.NOTHING:
                default = MISSING

            if value is attrs.NOTHING:
                value = default

            else:
                value = await cls._convert_to_type(
                    field_type=field.type, field_metadata=field.metadata, value=value, client=client
                )

            # set the attr
            prepared[name] = value

    res = cls(**prepared)  # noqa

    # fill the manifest information
    if not recursive and res._client.always_return_manifest_information:
        await res.fetch_manifest_information()

    return res

process_dict(data, client, *args, **kwargs) staticmethod

Model specific cleanup

Parameters:

Name Type Description Default
data dict

The json representation of the model, usually received by bungie

required
client 'Client'

The client obj

required

Returns:

Type Description
dict

Clean json

Source code in src/bungio/models/base.py
260
261
262
263
264
265
266
267
268
269
270
271
272
@staticmethod
def process_dict(data: dict, client: "Client", *args, **kwargs) -> dict:
    """
    Model specific cleanup

    Args:
        data: The json representation of the model, usually received by bungie
        client: The client obj

    Returns:
        Clean json
    """
    return data

to_dict(_return_to_bungie_case=True)

Convert the model into a dict representation bungie accepts

Returns:

Type Description
dict

A dict which can be sent to bungie

Source code in src/bungio/models/base.py
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
def to_dict(self, _return_to_bungie_case: bool = True) -> dict:
    """
    Convert the model into a dict representation bungie accepts

    Returns:
        A dict which can be sent to bungie
    """

    result = {}

    for name, field in attrs.fields_dict(type(self)).items():
        if name.startswith("_") or name.startswith("manifest_"):
            continue

        value = getattr(self, name)
        if _return_to_bungie_case:
            name = self._convert_to_bungie_case(name)

        if inspect.ismethod(value) or inspect.isfunction(value):
            continue

        elif isinstance(value, dict):
            raise NotImplementedError(
                "Nested dict conversion is not currently implemented, since bungie does never require that info"
            )

        elif isinstance(value, list):
            list_results = []
            for entry in value:
                if hasattr(entry, "to_dict"):
                    list_results.append(entry.to_dict())
                else:
                    list_results.append(entry)

            result[name] = list_results

        else:
            if hasattr(value, "to_dict"):
                result[name] = value.to_dict()
            else:
                # convert to string if they are int64
                if field.metadata.get("int64", None) is True:
                    value = str(value)

                result[name] = value

    return result

DestinyCharacterComponent

Bases: BaseModel, DestinyCharacterMixin

This component contains base properties of the character. You'll probably want to always request this component, but hey you do you.

Manifest Information

This model has some attributes which can be filled with additional information found in the manifest (manifest_...). Without additional work, these attributes will be None, since they require additional requests and database lookups.

To fill the manifest dependent attributes, either:

  • Run await ThisClass.fetch_manifest_information(), see here
  • Set Client.always_return_manifest_information to True, see here

Attributes:

Name Type Description
base_character_level int

The "base" level of your character, not accounting for any light level.

character_id int

The unique identifier for the character.

class_hash int

Use this hash to look up the character's DestinyClassDefinition.

class_type Union[DestinyClass, int]

Mostly for historical purposes at this point, this is an enumeration for the character's class. It'll be preferable in the general case to look up the related definition: but for some people this was too convenient to remove.

date_last_played datetime

The last date that the user played Destiny.

emblem_background_path str

A shortcut path to the user's currently equipped emblem background image. If you're just showing summary info for a user, this is more convenient than examining their equipped emblem and looking up the definition.

emblem_color DestinyColor

A shortcut for getting the background color of the user's currently equipped emblem without having to do a DestinyInventoryItemDefinition lookup.

emblem_hash int

The hash of the currently equipped emblem for the user. Can be used to look up the DestinyInventoryItemDefinition.

emblem_path str

A shortcut path to the user's currently equipped emblem image. If you're just showing summary info for a user, this is more convenient than examining their equipped emblem and looking up the definition.

gender_hash int

Use this hash to look up the character's DestinyGenderDefinition.

gender_type Union[DestinyGender, int]

Mostly for historical purposes at this point, this is an enumeration for the character's Gender. It'll be preferable in the general case to look up the related definition: but for some people this was too convenient to remove. And yeah, it's an enumeration and not a boolean. Fight me.

level_progression DestinyProgression

The progression that indicates your character's level. Not their light level, but their character level: you know, the thing you max out a couple hours in and then ignore for the sake of light level.

light int

The user's calculated "Light Level". Light level is an indicator of your power that mostly matters in the end game, once you've reached the maximum character level: it's a level that's dependent on the average Attack/Defense power of your items.

membership_id int

Every Destiny Profile has a membershipId. This is provided on the character as well for convenience.

membership_type Union[BungieMembershipType, int]

membershipType tells you the platform on which the character plays. Examine the BungieMembershipType enumeration for possible values.

minutes_played_this_session int

If the user is currently playing, this is how long they've been playing.

minutes_played_total int

If this value is 525,600, then they played Destiny for a year. Or they're a very dedicated Rent fan. Note that this includes idle time, not just time spent actually in activities shooting things.

percent_to_next_level float

A number between 0 and 100, indicating the whole and fractional % remaining to get to the next character level.

race_hash int

Use this hash to look up the character's DestinyRaceDefinition.

race_type Union[DestinyRace, int]

Mostly for historical purposes at this point, this is an enumeration for the character's race. It'll be preferable in the general case to look up the related definition: but for some people this was too convenient to remove.

stats dict[int, int]

Your character's stats, such as Agility, Resilience, etc... not historical stats. You'll have to call a different endpoint for those.

title_record_hash int

If this Character has a title assigned to it, this is the identifier of the DestinyRecordDefinition that has that title information.

manifest_class_hash Optional[DestinyClassDefinition]

Manifest information for class_hash

manifest_emblem_hash Optional[DestinyInventoryItemDefinition]

Manifest information for emblem_hash

manifest_gender_hash Optional[DestinyGenderDefinition]

Manifest information for gender_hash

manifest_race_hash Optional[DestinyRaceDefinition]

Manifest information for race_hash

manifest_title_record_hash Optional[DestinyRecordDefinition]

Manifest information for title_record_hash

Source code in src/bungio/models/bungie/destiny/entities/characters.py
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
@custom_define()
class DestinyCharacterComponent(BaseModel, DestinyCharacterMixin):
    """
    This component contains base properties of the character. You'll probably want to always request this component, but hey you do you.

    Tip: Manifest Information
        This model has some attributes which can be filled with additional information found in the manifest (`manifest_...`).
        Without additional work, these attributes will be `None`, since they require additional requests and database lookups.

        To fill the manifest dependent attributes, either:

        - Run `await ThisClass.fetch_manifest_information()`, see [here](/API Reference/Models/base)
        - Set `Client.always_return_manifest_information` to `True`, see [here](/API Reference/client)

    Attributes:
        base_character_level: The "base" level of your character, not accounting for any light level.
        character_id: The unique identifier for the character.
        class_hash: Use this hash to look up the character's DestinyClassDefinition.
        class_type: Mostly for historical purposes at this point, this is an enumeration for the character's class. It'll be preferable in the general case to look up the related definition: but for some people this was too convenient to remove.
        date_last_played: The last date that the user played Destiny.
        emblem_background_path: A shortcut path to the user's currently equipped emblem background image. If you're just showing summary info for a user, this is more convenient than examining their equipped emblem and looking up the definition.
        emblem_color: A shortcut for getting the background color of the user's currently equipped emblem without having to do a DestinyInventoryItemDefinition lookup.
        emblem_hash: The hash of the currently equipped emblem for the user. Can be used to look up the DestinyInventoryItemDefinition.
        emblem_path: A shortcut path to the user's currently equipped emblem image. If you're just showing summary info for a user, this is more convenient than examining their equipped emblem and looking up the definition.
        gender_hash: Use this hash to look up the character's DestinyGenderDefinition.
        gender_type: Mostly for historical purposes at this point, this is an enumeration for the character's Gender. It'll be preferable in the general case to look up the related definition: but for some people this was too convenient to remove. And yeah, it's an enumeration and not a boolean. Fight me.
        level_progression: The progression that indicates your character's level. Not their light level, but their character level: you know, the thing you max out a couple hours in and then ignore for the sake of light level.
        light: The user's calculated "Light Level". Light level is an indicator of your power that mostly matters in the end game, once you've reached the maximum character level: it's a level that's dependent on the average Attack/Defense power of your items.
        membership_id: Every Destiny Profile has a membershipId. This is provided on the character as well for convenience.
        membership_type: membershipType tells you the platform on which the character plays. Examine the BungieMembershipType enumeration for possible values.
        minutes_played_this_session: If the user is currently playing, this is how long they've been playing.
        minutes_played_total: If this value is 525,600, then they played Destiny for a year. Or they're a very dedicated Rent fan. Note that this includes idle time, not just time spent actually in activities shooting things.
        percent_to_next_level: A number between 0 and 100, indicating the whole and fractional % remaining to get to the next character level.
        race_hash: Use this hash to look up the character's DestinyRaceDefinition.
        race_type: Mostly for historical purposes at this point, this is an enumeration for the character's race. It'll be preferable in the general case to look up the related definition: but for some people this was too convenient to remove.
        stats: Your character's stats, such as Agility, Resilience, etc... *not* historical stats. You'll have to call a different endpoint for those.
        title_record_hash: If this Character has a title assigned to it, this is the identifier of the DestinyRecordDefinition that has that title information.
        manifest_class_hash: Manifest information for `class_hash`
        manifest_emblem_hash: Manifest information for `emblem_hash`
        manifest_gender_hash: Manifest information for `gender_hash`
        manifest_race_hash: Manifest information for `race_hash`
        manifest_title_record_hash: Manifest information for `title_record_hash`
    """

    base_character_level: int = custom_field()
    character_id: int = custom_field(metadata={"int64": True})
    class_hash: int = custom_field()
    class_type: Union["DestinyClass", int] = custom_field(converter=enum_converter("DestinyClass"))
    date_last_played: datetime = custom_field()
    emblem_background_path: str = custom_field()
    emblem_color: "DestinyColor" = custom_field()
    emblem_hash: int = custom_field()
    emblem_path: str = custom_field()
    gender_hash: int = custom_field()
    gender_type: Union["DestinyGender", int] = custom_field(converter=enum_converter("DestinyGender"))
    level_progression: "DestinyProgression" = custom_field()
    light: int = custom_field()
    membership_id: int = custom_field(metadata={"int64": True})
    membership_type: Union["BungieMembershipType", int] = custom_field(converter=enum_converter("BungieMembershipType"))
    minutes_played_this_session: int = custom_field(metadata={"int64": True})
    minutes_played_total: int = custom_field(metadata={"int64": True})
    percent_to_next_level: float = custom_field()
    race_hash: int = custom_field()
    race_type: Union["DestinyRace", int] = custom_field(converter=enum_converter("DestinyRace"))
    stats: dict[int, int] = custom_field(metadata={"type": """dict[int, int]"""})
    title_record_hash: int = custom_field()
    manifest_class_hash: Optional["DestinyClassDefinition"] = custom_field(default=None)
    manifest_emblem_hash: Optional["DestinyInventoryItemDefinition"] = custom_field(default=None)
    manifest_gender_hash: Optional["DestinyGenderDefinition"] = custom_field(default=None)
    manifest_race_hash: Optional["DestinyRaceDefinition"] = custom_field(default=None)
    manifest_title_record_hash: Optional["DestinyRecordDefinition"] = custom_field(default=None)

_convert_to_bungie_case(string) cached staticmethod

Convert a string to how it is represented by bungie: my_name_string -> myNameString

Parameters:

Name Type Description Default
string str

The og string

required

Returns:

Type Description
str

The bungie string

Source code in src/bungio/models/base.py
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
@staticmethod
@functools.cache
def _convert_to_bungie_case(string: str) -> str:
    """
    Convert a string to how it is represented by bungie: my_name_string -> myNameString

    Args:
        string: The og string

    Returns:
        The bungie string
    """

    if "_" not in string:
        return string
    else:
        split = string.split("_")
        return "".join((split[0], *(s.capitalize() for s in split[1:])))

_fuzzy_getattr(name)

Returns the objs attribute that fully matches the name, or if that does not exist, the first attribute that includes the name

Parameters:

Name Type Description Default
name str

The name to match

required

Raises:

Type Description
KeyError

If no match is found

Returns:

Type Description
Any

The attribute value

Source code in src/bungio/models/base.py
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
def _fuzzy_getattr(self, name: str) -> Any:
    """
    Returns the objs attribute that fully matches the name, or if that does not exist, the first attribute that includes the name

    Args:
        name: The name to match

    Raises:
        KeyError: If no match is found

    Returns:
        The attribute value
    """

    try:
        found_attr = getattr(self, name)
        return found_attr
    except AttributeError:
        for attr_name in self.__dir__():
            if name in attr_name:
                return getattr(self, attr_name)
        raise KeyError(f"`{name}` not found in `{self.__dir__()}`")

fetch_manifest_information(include=None, exclude=None, _cache=None) async

Fill the model in-place with information from the manifest.

Example

Fill every attribute

1
2
3
4
model: DestinyHistoricalStatsActivity
await model.fetch_manifest_information()
assert model.manifest_director_activity_hash is not None
assert model.manifest_reference_id is not None

Fill only some attribute

1
2
3
4
model: DestinyHistoricalStatsActivity
await model.fetch_manifest_information(include=["manifest_director_activity_hash"])
assert model.manifest_director_activity_hash is not None
assert model.manifest_reference_id is None
1
2
3
4
model: DestinyHistoricalStatsActivity
await model.fetch_manifest_information(exclude=["manifest_director_activity_hash"])
assert model.manifest_director_activity_hash is None
assert model.manifest_reference_id is not None

Parameters:

Name Type Description Default
include Optional[list[str]]

A list of attributes you want to include. Excludes everything not mentioned

None
exclude Optional[list[str]]

A list of attributes you want to exclude. Includes everything not mentioned

None
Source code in src/bungio/models/base.py
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
async def fetch_manifest_information(
    self, include: Optional[list[str]] = None, exclude: Optional[list[str]] = None, _cache: Optional[dict] = None
):
    """
    Fill the model in-place with information from the manifest.

    Example:
        Fill every attribute
        ```py
        model: DestinyHistoricalStatsActivity
        await model.fetch_manifest_information()
        assert model.manifest_director_activity_hash is not None
        assert model.manifest_reference_id is not None
        ```

        Fill only some attribute
        ```py
        model: DestinyHistoricalStatsActivity
        await model.fetch_manifest_information(include=["manifest_director_activity_hash"])
        assert model.manifest_director_activity_hash is not None
        assert model.manifest_reference_id is None
        ```
        ```py
        model: DestinyHistoricalStatsActivity
        await model.fetch_manifest_information(exclude=["manifest_director_activity_hash"])
        assert model.manifest_director_activity_hash is None
        assert model.manifest_reference_id is not None
        ```

    Args:
        include: A list of attributes you want to include. Excludes everything not mentioned
        exclude:  A list of attributes you want to exclude. Includes everything not mentioned
    """

    if not isinstance(self._client.manifest_storage, AsyncEngine):
        raise ValueError("Client.manifest_storage must be set up to use this")

    if not _cache:
        _cache = {}

    class_definition = attrs.fields_dict(type(self))  # noqa

    # loop through the class attributes
    for name in self.__dir__():
        if name.startswith("__"):
            continue

        if include and name not in include:
            continue
        if exclude and name in exclude:
            continue

        # manifest entries
        if name.startswith("manifest_"):
            striped_name = name.removeprefix("manifest_")
            value = getattr(self, striped_name)

            if value is MISSING:
                return

            # check the cache to avoid infinite recursion
            if cached := _cache.get(value, None):
                manifest_value = cached

            else:
                attr_definition = class_definition[name]
                manifest_class_name = attr_definition.type.__str__().removesuffix("')]").split("'")[-1]
                manifest_value = await self._client.manifest.fetch(
                    manifest_class=getattr(models, manifest_class_name), value=value
                )

                _cache[value] = manifest_value

                # check if the model has manifest models itself
                if manifest_value:
                    await manifest_value.fetch_manifest_information(_cache=_cache)

            setattr(self, name, manifest_value)

        # sub models which may have manifest entries too
        elif hasattr((value := getattr(self, name)), "fetch_manifest_information"):
            await value.fetch_manifest_information(_cache=_cache)

from_dict(data, client, recursive=False, *args, **kwargs) async classmethod

Convert json data to this model

Parameters:

Name Type Description Default
data dict

The json representation of the model, usually received by bungie

required
client 'Client'

The client obj

required
recursive bool

If this was called recursively

False

Returns:

Type Description
BaseModel

The model

Source code in src/bungio/models/base.py
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
@classmethod
async def from_dict(cls, data: dict, client: "Client", recursive: bool = False, *args, **kwargs) -> BaseModel:
    """
    Convert json data to this model

    Args:
        data: The json representation of the model, usually received by bungie
        client: The client obj
        recursive: If this was called recursively

    Returns:
        The model
    """

    if isinstance(data, cls):
        return data

    if not isinstance(data, dict):
        raise ValueError

    if "Response" in data:
        data = data["Response"]

    # also use the kwargs as data
    data = kwargs | data
    data = cls.process_dict(data=data, client=client)

    prepared = {}
    for name, field in attrs.fields_dict(cls).items():
        if field.init and name != "_client":
            default = field.default

            # get the value we want. This also skips the manifest_... entries since they have no value and a default
            bungie_name = cls._convert_to_bungie_case(name)
            value = data.get(bungie_name, attrs.NOTHING)

            # bungie is veeery inconsistent and sometimes like to start their params with an upper case for some reason
            # only sometimes tho :)
            if value is attrs.NOTHING:
                value = data.get(f"{bungie_name[0].capitalize()}{bungie_name[1:]}", attrs.NOTHING)

            # sadly bungie sometimes does not return info without marking that fact in the api specs
            if value is attrs.NOTHING and default is attrs.NOTHING:
                default = MISSING

            if value is attrs.NOTHING:
                value = default

            else:
                value = await cls._convert_to_type(
                    field_type=field.type, field_metadata=field.metadata, value=value, client=client
                )

            # set the attr
            prepared[name] = value

    res = cls(**prepared)  # noqa

    # fill the manifest information
    if not recursive and res._client.always_return_manifest_information:
        await res.fetch_manifest_information()

    return res

get_activity_history(count, mode, page, auth=None) async

Gets activity history stats for indicated character.

Parameters:

Name Type Description Default
count int

Number of rows to return

required
mode Union[DestinyActivityModeType, int]

A filter for the activity mode to be returned. None returns all activities. See the documentation for DestinyActivityModeType for valid values, and pass in string representation.

required
page int

Page number to return, starting with 0.

required
auth Optional[AuthData]

Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

None

Returns:

Type Description
DestinyActivityHistoryResults

The model which is returned by bungie. General endpoint information.

Source code in src/bungio/models/mixins/character.py
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
async def get_activity_history(
    self, count: int, mode: Union["DestinyActivityModeType", int], page: int, auth: Optional["AuthData"] = None
) -> "DestinyActivityHistoryResults":
    """
    Gets activity history stats for indicated character.

    Args:
        count: Number of rows to return
        mode: A filter for the activity mode to be returned. None returns all activities. See the documentation for DestinyActivityModeType for valid values, and pass in string representation.
        page: Page number to return, starting with 0.
        auth: Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

    Returns:
        The model which is returned by bungie. [General endpoint information.](https://bungie-net.github.io/multi/index.html)
    """

    return await self._client.api.get_activity_history(
        character_id=self._fuzzy_getattr("character_id"),
        destiny_membership_id=self._fuzzy_getattr("membership_id"),
        membership_type=self._fuzzy_getattr("membership_type"),
        count=count,
        mode=mode,
        page=page,
        auth=auth,
    )

get_character(components, auth=None) async

Returns character information for the supplied character.

Parameters:

Name Type Description Default
components list[Union[DestinyComponentType, int]]

A comma separated list of components to return (as strings or numeric values). See the DestinyComponentType enum for valid components to request. You must request at least one component to receive results.

required
auth Optional[AuthData]

Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

None

Returns:

Type Description
DestinyCharacterResponse

The model which is returned by bungie. General endpoint information.

Source code in src/bungio/models/mixins/character.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
async def get_character(
    self, components: list[Union["DestinyComponentType", int]], auth: Optional["AuthData"] = None
) -> "DestinyCharacterResponse":
    """
    Returns character information for the supplied character.

    Args:
        components: A comma separated list of components to return (as strings or numeric values). See the DestinyComponentType enum for valid components to request. You must request at least one component to receive results.
        auth: Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

    Returns:
        The model which is returned by bungie. [General endpoint information.](https://bungie-net.github.io/multi/index.html)
    """

    return await self._client.api.get_character(
        character_id=self._fuzzy_getattr("character_id"),
        destiny_membership_id=self._fuzzy_getattr("membership_id"),
        membership_type=self._fuzzy_getattr("membership_type"),
        components=components,
        auth=auth,
    )

get_collectible_node_details(collectible_presentation_node_hash, components, auth=None) async

Given a Presentation Node that has Collectibles as direct descendants, this will return item details about those descendants in the context of the requesting character.

Parameters:

Name Type Description Default
collectible_presentation_node_hash int

The hash identifier of the Presentation Node for whom we should return collectible details. Details will only be returned for collectibles that are direct descendants of this node.

required
components list[Union[DestinyComponentType, int]]

A comma separated list of components to return (as strings or numeric values). See the DestinyComponentType enum for valid components to request. You must request at least one component to receive results.

required
auth Optional[AuthData]

Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

None

Returns:

Type Description
DestinyCollectibleNodeDetailResponse

The model which is returned by bungie. General endpoint information.

Source code in src/bungio/models/mixins/character.py
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
async def get_collectible_node_details(
    self,
    collectible_presentation_node_hash: int,
    components: list[Union["DestinyComponentType", int]],
    auth: Optional["AuthData"] = None,
) -> "DestinyCollectibleNodeDetailResponse":
    """
    Given a Presentation Node that has Collectibles as direct descendants, this will return item details about those descendants in the context of the requesting character.

    Args:
        collectible_presentation_node_hash: The hash identifier of the Presentation Node for whom we should return collectible details. Details will only be returned for collectibles that are direct descendants of this node.
        components: A comma separated list of components to return (as strings or numeric values). See the DestinyComponentType enum for valid components to request. You must request at least one component to receive results.
        auth: Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

    Returns:
        The model which is returned by bungie. [General endpoint information.](https://bungie-net.github.io/multi/index.html)
    """

    return await self._client.api.get_collectible_node_details(
        character_id=self._fuzzy_getattr("character_id"),
        collectible_presentation_node_hash=collectible_presentation_node_hash,
        destiny_membership_id=self._fuzzy_getattr("membership_id"),
        membership_type=self._fuzzy_getattr("membership_type"),
        components=components,
        auth=auth,
    )

get_destiny_aggregate_activity_stats(auth=None) async

Gets all activities the character has participated in together with aggregate statistics for those activities.

Parameters:

Name Type Description Default
auth Optional[AuthData]

Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

None

Returns:

Type Description
DestinyAggregateActivityResults

The model which is returned by bungie. General endpoint information.

Source code in src/bungio/models/mixins/character.py
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
async def get_destiny_aggregate_activity_stats(
    self, auth: Optional["AuthData"] = None
) -> "DestinyAggregateActivityResults":
    """
    Gets all activities the character has participated in together with aggregate statistics for those activities.

    Args:
        auth: Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

    Returns:
        The model which is returned by bungie. [General endpoint information.](https://bungie-net.github.io/multi/index.html)
    """

    return await self._client.api.get_destiny_aggregate_activity_stats(
        character_id=self._fuzzy_getattr("character_id"),
        destiny_membership_id=self._fuzzy_getattr("membership_id"),
        membership_type=self._fuzzy_getattr("membership_type"),
        auth=auth,
    )

get_historical_stats(dayend, daystart, groups, modes, period_type, auth=None) async

Gets historical stats for indicated character.

Parameters:

Name Type Description Default
dayend datetime

Last day to return when daily stats are requested. Use the format YYYY-MM-DD. Currently, we cannot allow more than 31 days of daily data to be requested in a single request.

required
daystart datetime

First day to return when daily stats are requested. Use the format YYYY-MM-DD. Currently, we cannot allow more than 31 days of daily data to be requested in a single request.

required
groups list[Union[DestinyStatsGroupType, int]]

Group of stats to include, otherwise only general stats are returned. Comma separated list is allowed. Values: General, Weapons, Medals

required
modes list[Union[DestinyActivityModeType, int]]

Game modes to return. See the documentation for DestinyActivityModeType for valid values, and pass in string representation, comma delimited.

required
period_type Union[PeriodType, int]

Indicates a specific period type to return. Optional. May be: Daily, AllTime, or Activity

required
auth Optional[AuthData]

Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

None

Returns:

Type Description
dict[str, DestinyHistoricalStatsByPeriod]

The model which is returned by bungie. General endpoint information.

Source code in src/bungio/models/mixins/character.py
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
async def get_historical_stats(
    self,
    dayend: datetime,
    daystart: datetime,
    groups: list[Union["DestinyStatsGroupType", int]],
    modes: list[Union["DestinyActivityModeType", int]],
    period_type: Union["PeriodType", int],
    auth: Optional["AuthData"] = None,
) -> dict[str, "DestinyHistoricalStatsByPeriod"]:
    """
    Gets historical stats for indicated character.

    Args:
        dayend: Last day to return when daily stats are requested. Use the format YYYY-MM-DD. Currently, we cannot allow more than 31 days of daily data to be requested in a single request.
        daystart: First day to return when daily stats are requested. Use the format YYYY-MM-DD. Currently, we cannot allow more than 31 days of daily data to be requested in a single request.
        groups: Group of stats to include, otherwise only general stats are returned. Comma separated list is allowed. Values: General, Weapons, Medals
        modes: Game modes to return. See the documentation for DestinyActivityModeType for valid values, and pass in string representation, comma delimited.
        period_type: Indicates a specific period type to return. Optional. May be: Daily, AllTime, or Activity
        auth: Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

    Returns:
        The model which is returned by bungie. [General endpoint information.](https://bungie-net.github.io/multi/index.html)
    """

    return await self._client.api.get_historical_stats(
        character_id=self._fuzzy_getattr("character_id"),
        destiny_membership_id=self._fuzzy_getattr("membership_id"),
        membership_type=self._fuzzy_getattr("membership_type"),
        dayend=dayend,
        daystart=daystart,
        groups=groups,
        modes=modes,
        period_type=period_type,
        auth=auth,
    )

get_leaderboards_for_character(maxtop, modes, statid, auth=None) async

Gets leaderboards with the signed in user's friends and the supplied destinyMembershipId as the focus. PREVIEW: This endpoint is still in beta, and may experience rough edges. The schema is in final form, but there may be bugs that prevent desirable operation.

Parameters:

Name Type Description Default
maxtop int

Maximum number of top players to return. Use a large number to get entire leaderboard.

required
modes str

List of game modes for which to get leaderboards. See the documentation for DestinyActivityModeType for valid values, and pass in string representation, comma delimited.

required
statid str

ID of stat to return rather than returning all Leaderboard stats.

required
auth Optional[AuthData]

Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

None

Returns:

Type Description
dict[str, dict[str, DestinyLeaderboard]]

The model which is returned by bungie. General endpoint information.

Source code in src/bungio/models/mixins/character.py
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
async def get_leaderboards_for_character(
    self, maxtop: int, modes: str, statid: str, auth: Optional["AuthData"] = None
) -> dict[str, dict[str, "DestinyLeaderboard"]]:
    """
    Gets leaderboards with the signed in user's friends and the supplied destinyMembershipId as the focus. PREVIEW: This endpoint is still in beta, and may experience rough edges. The schema is in final form, but there may be bugs that prevent desirable operation.

    Args:
        maxtop: Maximum number of top players to return. Use a large number to get entire leaderboard.
        modes: List of game modes for which to get leaderboards. See the documentation for DestinyActivityModeType for valid values, and pass in string representation, comma delimited.
        statid: ID of stat to return rather than returning all Leaderboard stats.
        auth: Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

    Returns:
        The model which is returned by bungie. [General endpoint information.](https://bungie-net.github.io/multi/index.html)
    """

    return await self._client.api.get_leaderboards_for_character(
        character_id=self._fuzzy_getattr("character_id"),
        destiny_membership_id=self._fuzzy_getattr("membership_id"),
        membership_type=self._fuzzy_getattr("membership_type"),
        maxtop=maxtop,
        modes=modes,
        statid=statid,
        auth=auth,
    )

get_unique_weapon_history(auth=None) async

Gets details about unique weapon usage, including all exotic weapons.

Parameters:

Name Type Description Default
auth Optional[AuthData]

Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

None

Returns:

Type Description
DestinyHistoricalWeaponStatsData

The model which is returned by bungie. General endpoint information.

Source code in src/bungio/models/mixins/character.py
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
async def get_unique_weapon_history(self, auth: Optional["AuthData"] = None) -> "DestinyHistoricalWeaponStatsData":
    """
    Gets details about unique weapon usage, including all exotic weapons.

    Args:
        auth: Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

    Returns:
        The model which is returned by bungie. [General endpoint information.](https://bungie-net.github.io/multi/index.html)
    """

    return await self._client.api.get_unique_weapon_history(
        character_id=self._fuzzy_getattr("character_id"),
        destiny_membership_id=self._fuzzy_getattr("membership_id"),
        membership_type=self._fuzzy_getattr("membership_type"),
        auth=auth,
    )

get_vendor(vendor_hash, components, auth=None) async

Get the details of a specific Vendor.

Parameters:

Name Type Description Default
vendor_hash int

The Hash identifier of the Vendor to be returned.

required
components list[Union[DestinyComponentType, int]]

A comma separated list of components to return (as strings or numeric values). See the DestinyComponentType enum for valid components to request. You must request at least one component to receive results.

required
auth Optional[AuthData]

Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

None

Returns:

Type Description
DestinyVendorResponse

The model which is returned by bungie. General endpoint information.

Source code in src/bungio/models/mixins/character.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
async def get_vendor(
    self, vendor_hash: int, components: list[Union["DestinyComponentType", int]], auth: Optional["AuthData"] = None
) -> "DestinyVendorResponse":
    """
    Get the details of a specific Vendor.

    Args:
        vendor_hash: The Hash identifier of the Vendor to be returned.
        components: A comma separated list of components to return (as strings or numeric values). See the DestinyComponentType enum for valid components to request. You must request at least one component to receive results.
        auth: Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

    Returns:
        The model which is returned by bungie. [General endpoint information.](https://bungie-net.github.io/multi/index.html)
    """

    return await self._client.api.get_vendor(
        character_id=self._fuzzy_getattr("character_id"),
        destiny_membership_id=self._fuzzy_getattr("membership_id"),
        membership_type=self._fuzzy_getattr("membership_type"),
        vendor_hash=vendor_hash,
        components=components,
        auth=auth,
    )

get_vendors(components, filter, auth=None) async

Get currently available vendors from the list of vendors that can possibly have rotating inventory. Note that this does not include things like preview vendors and vendors-as-kiosks, neither of whom have rotating/dynamic inventories. Use their definitions as-is for those.

Parameters:

Name Type Description Default
components list[Union[DestinyComponentType, int]]

A comma separated list of components to return (as strings or numeric values). See the DestinyComponentType enum for valid components to request. You must request at least one component to receive results.

required
filter Union[DestinyVendorFilter, int]

The filter of what vendors and items to return, if any.

required
auth Optional[AuthData]

Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

None

Returns:

Type Description
DestinyVendorsResponse

The model which is returned by bungie. General endpoint information.

Source code in src/bungio/models/mixins/character.py
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
async def get_vendors(
    self,
    components: list[Union["DestinyComponentType", int]],
    filter: Union["DestinyVendorFilter", int],
    auth: Optional["AuthData"] = None,
) -> "DestinyVendorsResponse":
    """
    Get currently available vendors from the list of vendors that can possibly have rotating inventory. Note that this does not include things like preview vendors and vendors-as-kiosks, neither of whom have rotating/dynamic inventories. Use their definitions as-is for those.

    Args:
        components: A comma separated list of components to return (as strings or numeric values). See the DestinyComponentType enum for valid components to request. You must request at least one component to receive results.
        filter: The filter of what vendors and items to return, if any.
        auth: Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

    Returns:
        The model which is returned by bungie. [General endpoint information.](https://bungie-net.github.io/multi/index.html)
    """

    return await self._client.api.get_vendors(
        character_id=self._fuzzy_getattr("character_id"),
        destiny_membership_id=self._fuzzy_getattr("membership_id"),
        membership_type=self._fuzzy_getattr("membership_type"),
        components=components,
        filter=filter,
        auth=auth,
    )

process_dict(data, client, *args, **kwargs) staticmethod

Model specific cleanup

Parameters:

Name Type Description Default
data dict

The json representation of the model, usually received by bungie

required
client 'Client'

The client obj

required

Returns:

Type Description
dict

Clean json

Source code in src/bungio/models/base.py
260
261
262
263
264
265
266
267
268
269
270
271
272
@staticmethod
def process_dict(data: dict, client: "Client", *args, **kwargs) -> dict:
    """
    Model specific cleanup

    Args:
        data: The json representation of the model, usually received by bungie
        client: The client obj

    Returns:
        Clean json
    """
    return data

to_dict(_return_to_bungie_case=True)

Convert the model into a dict representation bungie accepts

Returns:

Type Description
dict

A dict which can be sent to bungie

Source code in src/bungio/models/base.py
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
def to_dict(self, _return_to_bungie_case: bool = True) -> dict:
    """
    Convert the model into a dict representation bungie accepts

    Returns:
        A dict which can be sent to bungie
    """

    result = {}

    for name, field in attrs.fields_dict(type(self)).items():
        if name.startswith("_") or name.startswith("manifest_"):
            continue

        value = getattr(self, name)
        if _return_to_bungie_case:
            name = self._convert_to_bungie_case(name)

        if inspect.ismethod(value) or inspect.isfunction(value):
            continue

        elif isinstance(value, dict):
            raise NotImplementedError(
                "Nested dict conversion is not currently implemented, since bungie does never require that info"
            )

        elif isinstance(value, list):
            list_results = []
            for entry in value:
                if hasattr(entry, "to_dict"):
                    list_results.append(entry.to_dict())
                else:
                    list_results.append(entry)

            result[name] = list_results

        else:
            if hasattr(value, "to_dict"):
                result[name] = value.to_dict()
            else:
                # convert to string if they are int64
                if field.metadata.get("int64", None) is True:
                    value = str(value)

                result[name] = value

    return result

yield_activity_history(mode, earliest_allowed_datetime=None, latest_allowed_datetime=None, auth=None) async

Yields character activity history. Sorted by date descending, the latest one first.

Parameters:

Name Type Description Default
mode Union[DestinyActivityModeType, int]

A filter for the activity mode to be returned. None returns all activities. See the documentation for DestinyActivityModeType for valid values, and pass in string representation.

required
earliest_allowed_datetime Optional[datetime]

The earliest time the activity is allowed to have, fe. only entries after the 1/1/2020.

None
latest_allowed_datetime Optional[datetime]

The latest time the activity is allowed to have, fe. only entries before the 1/1/2020.

None
auth Optional[AuthData]

Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

None

Returns:

Type Description
AsyncGenerator[DestinyHistoricalStatsPeriodGroup, None]

A generator for the model which is returned by bungie. General endpoint information.

Source code in src/bungio/models/mixins/character.py
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
async def yield_activity_history(
    self,
    mode: Union["DestinyActivityModeType", int],
    earliest_allowed_datetime: Optional[datetime] = None,
    latest_allowed_datetime: Optional[datetime] = None,
    auth: Optional["AuthData"] = None,
) -> AsyncGenerator["DestinyHistoricalStatsPeriodGroup", None]:
    """
    Yields character activity history. Sorted by date descending, the latest one first.

    Args:
        mode: A filter for the activity mode to be returned. None returns all activities. See the documentation for DestinyActivityModeType for valid values, and pass in string representation.
        earliest_allowed_datetime: The earliest time the activity is allowed to have, fe. only entries after the 1/1/2020.
        latest_allowed_datetime: The latest time the activity is allowed to have, fe. only entries before the 1/1/2020.
        auth: Authentication information. Required when users with a private profile are queried, or when Bungie feels like it

    Returns:
        A generator for the model which is returned by bungie. [General endpoint information.](https://bungie-net.github.io/multi/index.html)
    """

    stop = False
    page = 0
    while True:
        if stop:
            break

        activities = await self.get_activity_history(count=250, mode=mode, page=page, auth=auth)

        # break if empty, fe. when pages are over
        if activities.activities is MISSING:
            break

        # yield the activities
        for activity in activities.activities:
            # check times if wanted
            if earliest_allowed_datetime or latest_allowed_datetime:
                # check if the activity started later than the earliest allowed, else pass this one and do the next
                # This works bc Bungie sorts the api with the newest entry on top
                if earliest_allowed_datetime:
                    if activity.period < earliest_allowed_datetime:
                        stop = True
                        break

                # check if the time is still in the timeframe, else break
                if latest_allowed_datetime:
                    if activity.period > latest_allowed_datetime:
                        continue

            yield activity
        page += 1

DestinyCharacterProgressionComponent

Bases: BaseModel

This component returns anything that could be considered "Progression" on a user: data where the user is gaining levels, reputation, completions, rewards, etc...

None Attributes: checklists: The set of checklists that can be examined for this specific character, keyed by the hash identifier of the Checklist (DestinyChecklistDefinition) For each checklist returned, its value is itself a Dictionary keyed by the checklist's hash identifier with the value being a boolean indicating if it's been discovered yet. factions: A dictionary of all known Factions, keyed by the Faction's hash. It contains data about this character's status with the faction. milestones: Milestones are related to the simple progressions shown in the game, but return additional and hopefully helpful information for users about the specifics of the Milestone's status. progressions: A Dictionary of all known progressions for the Character, keyed by the Progression's hash. Not all progressions have user-facing data, but those who do will have that data contained in the DestinyProgressionDefinition. quests: If the user has any active quests, the quests' statuses will be returned here. Note that quests have been largely supplanted by Milestones, but that doesn't mean that they won't make a comeback independent of milestones at some point. (Fun fact: quests came back as I feared they would, but we never looped back to populate this... I'm going to put that in the backlog.) seasonal_artifact: Data related to your progress on the current season's artifact that can vary per character. unclaimed_order_rewards: Unclaimed rewards earned by completing Orders. uninstanced_item_objectives: Sometimes, you have items in your inventory that don't have instances, but still have Objective information. This provides you that objective information for uninstanced items. This dictionary is keyed by the item's hash: which you can use to look up the name and description for the overall task(s) implied by the objective. The value is the list of objectives for this item, and their statuses. uninstanced_item_perks: Sometimes, you have items in your inventory that don't have instances, but still have perks (for example: Trials passage cards). This gives you the perk information for uninstanced items. This dictionary is keyed by item hash, which you can use to look up the corresponding item definition. The value is the list of perks states for the item.

Source code in src/bungio/models/bungie/destiny/entities/characters.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
@custom_define()
class DestinyCharacterProgressionComponent(BaseModel):
    """
    This component returns anything that could be considered "Progression" on a user: data where the user is gaining levels, reputation, completions, rewards, etc...

    None
    Attributes:
        checklists: The set of checklists that can be examined for this specific character, keyed by the hash identifier of the Checklist (DestinyChecklistDefinition) For each checklist returned, its value is itself a Dictionary keyed by the checklist's hash identifier with the value being a boolean indicating if it's been discovered yet.
        factions: A dictionary of all known Factions, keyed by the Faction's hash. It contains data about this character's status with the faction.
        milestones: Milestones are related to the simple progressions shown in the game, but return additional and hopefully helpful information for users about the specifics of the Milestone's status.
        progressions: A Dictionary of all known progressions for the Character, keyed by the Progression's hash. Not all progressions have user-facing data, but those who do will have that data contained in the DestinyProgressionDefinition.
        quests: If the user has any active quests, the quests' statuses will be returned here.  Note that quests have been largely supplanted by Milestones, but that doesn't mean that they won't make a comeback independent of milestones at some point.  (Fun fact: quests came back as I feared they would, but we never looped back to populate this... I'm going to put that in the backlog.)
        seasonal_artifact: Data related to your progress on the current season's artifact that can vary per character.
        unclaimed_order_rewards: Unclaimed rewards earned by completing Orders.
        uninstanced_item_objectives: Sometimes, you have items in your inventory that don't have instances, but still have Objective information. This provides you that objective information for uninstanced items.  This dictionary is keyed by the item's hash: which you can use to look up the name and description for the overall task(s) implied by the objective. The value is the list of objectives for this item, and their statuses.
        uninstanced_item_perks: Sometimes, you have items in your inventory that don't have instances, but still have perks (for example: Trials passage cards). This gives you the perk information for uninstanced items. This dictionary is keyed by item hash, which you can use to look up the corresponding item definition. The value is the list of perks states for the item.
    """

    checklists: dict[int, dict[int, bool]] = custom_field(metadata={"type": """dict[int, dict[int, bool]]"""})
    factions: dict[int, "DestinyFactionProgression"] = custom_field(
        metadata={"type": """dict[int, DestinyFactionProgression]"""}
    )
    milestones: dict[int, "DestinyMilestone"] = custom_field(metadata={"type": """dict[int, DestinyMilestone]"""})
    progressions: dict[int, "DestinyProgression"] = custom_field(metadata={"type": """dict[int, DestinyProgression]"""})
    quests: list["DestinyQuestStatus"] = custom_field(metadata={"type": """list[DestinyQuestStatus]"""})
    seasonal_artifact: "DestinyArtifactCharacterScoped" = custom_field()
    unclaimed_order_rewards: dict[int, int] = custom_field(metadata={"type": """dict[int, int]"""})
    uninstanced_item_objectives: dict[int, list["DestinyObjectiveProgress"]] = custom_field(
        metadata={"type": """dict[int, list[DestinyObjectiveProgress]]"""}
    )
    uninstanced_item_perks: dict[int, "DestinyItemPerksComponent"] = custom_field(
        metadata={"type": """dict[int, DestinyItemPerksComponent]"""}
    )

_convert_to_bungie_case(string) cached staticmethod

Convert a string to how it is represented by bungie: my_name_string -> myNameString

Parameters:

Name Type Description Default
string str

The og string

required

Returns:

Type Description
str

The bungie string

Source code in src/bungio/models/base.py
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
@staticmethod
@functools.cache
def _convert_to_bungie_case(string: str) -> str:
    """
    Convert a string to how it is represented by bungie: my_name_string -> myNameString

    Args:
        string: The og string

    Returns:
        The bungie string
    """

    if "_" not in string:
        return string
    else:
        split = string.split("_")
        return "".join((split[0], *(s.capitalize() for s in split[1:])))

fetch_manifest_information(include=None, exclude=None, _cache=None) async

Fill the model in-place with information from the manifest.

Example

Fill every attribute

1
2
3
4
model: DestinyHistoricalStatsActivity
await model.fetch_manifest_information()
assert model.manifest_director_activity_hash is not None
assert model.manifest_reference_id is not None

Fill only some attribute

1
2
3
4
model: DestinyHistoricalStatsActivity
await model.fetch_manifest_information(include=["manifest_director_activity_hash"])
assert model.manifest_director_activity_hash is not None
assert model.manifest_reference_id is None
1
2
3
4
model: DestinyHistoricalStatsActivity
await model.fetch_manifest_information(exclude=["manifest_director_activity_hash"])
assert model.manifest_director_activity_hash is None
assert model.manifest_reference_id is not None

Parameters:

Name Type Description Default
include Optional[list[str]]

A list of attributes you want to include. Excludes everything not mentioned

None
exclude Optional[list[str]]

A list of attributes you want to exclude. Includes everything not mentioned

None
Source code in src/bungio/models/base.py
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
async def fetch_manifest_information(
    self, include: Optional[list[str]] = None, exclude: Optional[list[str]] = None, _cache: Optional[dict] = None
):
    """
    Fill the model in-place with information from the manifest.

    Example:
        Fill every attribute
        ```py
        model: DestinyHistoricalStatsActivity
        await model.fetch_manifest_information()
        assert model.manifest_director_activity_hash is not None
        assert model.manifest_reference_id is not None
        ```

        Fill only some attribute
        ```py
        model: DestinyHistoricalStatsActivity
        await model.fetch_manifest_information(include=["manifest_director_activity_hash"])
        assert model.manifest_director_activity_hash is not None
        assert model.manifest_reference_id is None
        ```
        ```py
        model: DestinyHistoricalStatsActivity
        await model.fetch_manifest_information(exclude=["manifest_director_activity_hash"])
        assert model.manifest_director_activity_hash is None
        assert model.manifest_reference_id is not None
        ```

    Args:
        include: A list of attributes you want to include. Excludes everything not mentioned
        exclude:  A list of attributes you want to exclude. Includes everything not mentioned
    """

    if not isinstance(self._client.manifest_storage, AsyncEngine):
        raise ValueError("Client.manifest_storage must be set up to use this")

    if not _cache:
        _cache = {}

    class_definition = attrs.fields_dict(type(self))  # noqa

    # loop through the class attributes
    for name in self.__dir__():
        if name.startswith("__"):
            continue

        if include and name not in include:
            continue
        if exclude and name in exclude:
            continue

        # manifest entries
        if name.startswith("manifest_"):
            striped_name = name.removeprefix("manifest_")
            value = getattr(self, striped_name)

            if value is MISSING:
                return

            # check the cache to avoid infinite recursion
            if cached := _cache.get(value, None):
                manifest_value = cached

            else:
                attr_definition = class_definition[name]
                manifest_class_name = attr_definition.type.__str__().removesuffix("')]").split("'")[-1]
                manifest_value = await self._client.manifest.fetch(
                    manifest_class=getattr(models, manifest_class_name), value=value
                )

                _cache[value] = manifest_value

                # check if the model has manifest models itself
                if manifest_value:
                    await manifest_value.fetch_manifest_information(_cache=_cache)

            setattr(self, name, manifest_value)

        # sub models which may have manifest entries too
        elif hasattr((value := getattr(self, name)), "fetch_manifest_information"):
            await value.fetch_manifest_information(_cache=_cache)

from_dict(data, client, recursive=False, *args, **kwargs) async classmethod

Convert json data to this model

Parameters:

Name Type Description Default
data dict

The json representation of the model, usually received by bungie

required
client 'Client'

The client obj

required
recursive bool

If this was called recursively

False

Returns:

Type Description
BaseModel

The model

Source code in src/bungio/models/base.py
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
@classmethod
async def from_dict(cls, data: dict, client: "Client", recursive: bool = False, *args, **kwargs) -> BaseModel:
    """
    Convert json data to this model

    Args:
        data: The json representation of the model, usually received by bungie
        client: The client obj
        recursive: If this was called recursively

    Returns:
        The model
    """

    if isinstance(data, cls):
        return data

    if not isinstance(data, dict):
        raise ValueError

    if "Response" in data:
        data = data["Response"]

    # also use the kwargs as data
    data = kwargs | data
    data = cls.process_dict(data=data, client=client)

    prepared = {}
    for name, field in attrs.fields_dict(cls).items():
        if field.init and name != "_client":
            default = field.default

            # get the value we want. This also skips the manifest_... entries since they have no value and a default
            bungie_name = cls._convert_to_bungie_case(name)
            value = data.get(bungie_name, attrs.NOTHING)

            # bungie is veeery inconsistent and sometimes like to start their params with an upper case for some reason
            # only sometimes tho :)
            if value is attrs.NOTHING:
                value = data.get(f"{bungie_name[0].capitalize()}{bungie_name[1:]}", attrs.NOTHING)

            # sadly bungie sometimes does not return info without marking that fact in the api specs
            if value is attrs.NOTHING and default is attrs.NOTHING:
                default = MISSING

            if value is attrs.NOTHING:
                value = default

            else:
                value = await cls._convert_to_type(
                    field_type=field.type, field_metadata=field.metadata, value=value, client=client
                )

            # set the attr
            prepared[name] = value

    res = cls(**prepared)  # noqa

    # fill the manifest information
    if not recursive and res._client.always_return_manifest_information:
        await res.fetch_manifest_information()

    return res

process_dict(data, client, *args, **kwargs) staticmethod

Model specific cleanup

Parameters:

Name Type Description Default
data dict

The json representation of the model, usually received by bungie

required
client 'Client'

The client obj

required

Returns:

Type Description
dict

Clean json

Source code in src/bungio/models/base.py
260
261
262
263
264
265
266
267
268
269
270
271
272
@staticmethod
def process_dict(data: dict, client: "Client", *args, **kwargs) -> dict:
    """
    Model specific cleanup

    Args:
        data: The json representation of the model, usually received by bungie
        client: The client obj

    Returns:
        Clean json
    """
    return data

to_dict(_return_to_bungie_case=True)

Convert the model into a dict representation bungie accepts

Returns:

Type Description
dict

A dict which can be sent to bungie

Source code in src/bungio/models/base.py
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
def to_dict(self, _return_to_bungie_case: bool = True) -> dict:
    """
    Convert the model into a dict representation bungie accepts

    Returns:
        A dict which can be sent to bungie
    """

    result = {}

    for name, field in attrs.fields_dict(type(self)).items():
        if name.startswith("_") or name.startswith("manifest_"):
            continue

        value = getattr(self, name)
        if _return_to_bungie_case:
            name = self._convert_to_bungie_case(name)

        if inspect.ismethod(value) or inspect.isfunction(value):
            continue

        elif isinstance(value, dict):
            raise NotImplementedError(
                "Nested dict conversion is not currently implemented, since bungie does never require that info"
            )

        elif isinstance(value, list):
            list_results = []
            for entry in value:
                if hasattr(entry, "to_dict"):
                    list_results.append(entry.to_dict())
                else:
                    list_results.append(entry)

            result[name] = list_results

        else:
            if hasattr(value, "to_dict"):
                result[name] = value.to_dict()
            else:
                # convert to string if they are int64
                if field.metadata.get("int64", None) is True:
                    value = str(value)

                result[name] = value

    return result

DestinyCharacterRenderComponent

Bases: BaseModel

Only really useful if you're attempting to render the character's current appearance in 3D, this returns a bare minimum of information, pre-aggregated, that you'll need to perform that rendering. Note that you need to combine this with other 3D assets and data from our servers. Examine the Javascript returned by https://bungie.net/sharedbundle/spasm to see how we use this data, but be warned: the rabbit hole goes pretty deep.

None Attributes: custom_dyes: Custom dyes, calculated by iterating over the character's equipped items. Useful for pre-fetching all of the dye data needed from our server. customization: This is actually something that Spasm.js doesn't do right now, and that we don't return assets for yet. This is the data about what character customization options you picked. You can combine this with DestinyCharacterCustomizationOptionDefinition to show some cool info, and hopefully someday to actually render a user's face in 3D. We'll see if we ever end up with time for that. peer_view: A minimal view of: - Equipped items - The rendering-related custom options on those equipped items Combined, that should be enough to render all of the items on the equipped character.

Source code in src/bungio/models/bungie/destiny/entities/characters.py
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
@custom_define()
class DestinyCharacterRenderComponent(BaseModel):
    """
    Only really useful if you're attempting to render the character's current appearance in 3D, this returns a bare minimum of information, pre-aggregated, that you'll need to perform that rendering. Note that you need to combine this with other 3D assets and data from our servers. Examine the Javascript returned by https://bungie.net/sharedbundle/spasm to see how we use this data, but be warned: the rabbit hole goes pretty deep.

    None
    Attributes:
        custom_dyes: Custom dyes, calculated by iterating over the character's equipped items. Useful for pre-fetching all of the dye data needed from our server.
        customization: This is actually something that Spasm.js *doesn't* do right now, and that we don't return assets for yet. This is the data about what character customization options you picked. You can combine this with DestinyCharacterCustomizationOptionDefinition to show some cool info, and hopefully someday to actually render a user's face in 3D. We'll see if we ever end up with time for that.
        peer_view: A minimal view of: - Equipped items - The rendering-related custom options on those equipped items Combined, that should be enough to render all of the items on the equipped character.
    """

    custom_dyes: list["DyeReference"] = custom_field(metadata={"type": """list[DyeReference]"""})
    customization: "DestinyCharacterCustomization" = custom_field()
    peer_view: "DestinyCharacterPeerView" = custom_field()

_convert_to_bungie_case(string) cached staticmethod

Convert a string to how it is represented by bungie: my_name_string -> myNameString

Parameters:

Name Type Description Default
string str

The og string

required

Returns:

Type Description
str

The bungie string

Source code in src/bungio/models/base.py
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
@staticmethod
@functools.cache
def _convert_to_bungie_case(string: str) -> str:
    """
    Convert a string to how it is represented by bungie: my_name_string -> myNameString

    Args:
        string: The og string

    Returns:
        The bungie string
    """

    if "_" not in string:
        return string
    else:
        split = string.split("_")
        return "".join((split[0], *(s.capitalize() for s in split[1:])))

fetch_manifest_information(include=None, exclude=None, _cache=None) async

Fill the model in-place with information from the manifest.

Example

Fill every attribute

1
2
3
4
model: DestinyHistoricalStatsActivity
await model.fetch_manifest_information()
assert model.manifest_director_activity_hash is not None
assert model.manifest_reference_id is not None

Fill only some attribute

1
2
3
4
model: DestinyHistoricalStatsActivity
await model.fetch_manifest_information(include=["manifest_director_activity_hash"])
assert model.manifest_director_activity_hash is not None
assert model.manifest_reference_id is None
1
2
3
4
model: DestinyHistoricalStatsActivity
await model.fetch_manifest_information(exclude=["manifest_director_activity_hash"])
assert model.manifest_director_activity_hash is None
assert model.manifest_reference_id is not None

Parameters:

Name Type Description Default
include Optional[list[str]]

A list of attributes you want to include. Excludes everything not mentioned

None
exclude Optional[list[str]]

A list of attributes you want to exclude. Includes everything not mentioned

None
Source code in src/bungio/models/base.py
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
async def fetch_manifest_information(
    self, include: Optional[list[str]] = None, exclude: Optional[list[str]] = None, _cache: Optional[dict] = None
):
    """
    Fill the model in-place with information from the manifest.

    Example:
        Fill every attribute
        ```py
        model: DestinyHistoricalStatsActivity
        await model.fetch_manifest_information()
        assert model.manifest_director_activity_hash is not None
        assert model.manifest_reference_id is not None
        ```

        Fill only some attribute
        ```py
        model: DestinyHistoricalStatsActivity
        await model.fetch_manifest_information(include=["manifest_director_activity_hash"])
        assert model.manifest_director_activity_hash is not None
        assert model.manifest_reference_id is None
        ```
        ```py
        model: DestinyHistoricalStatsActivity
        await model.fetch_manifest_information(exclude=["manifest_director_activity_hash"])
        assert model.manifest_director_activity_hash is None
        assert model.manifest_reference_id is not None
        ```

    Args:
        include: A list of attributes you want to include. Excludes everything not mentioned
        exclude:  A list of attributes you want to exclude. Includes everything not mentioned
    """

    if not isinstance(self._client.manifest_storage, AsyncEngine):
        raise ValueError("Client.manifest_storage must be set up to use this")

    if not _cache:
        _cache = {}

    class_definition = attrs.fields_dict(type(self))  # noqa

    # loop through the class attributes
    for name in self.__dir__():
        if name.startswith("__"):
            continue

        if include and name not in include:
            continue
        if exclude and name in exclude:
            continue

        # manifest entries
        if name.startswith("manifest_"):
            striped_name = name.removeprefix("manifest_")
            value = getattr(self, striped_name)

            if value is MISSING:
                return

            # check the cache to avoid infinite recursion
            if cached := _cache.get(value, None):
                manifest_value = cached

            else:
                attr_definition = class_definition[name]
                manifest_class_name = attr_definition.type.__str__().removesuffix("')]").split("'")[-1]
                manifest_value = await self._client.manifest.fetch(
                    manifest_class=getattr(models, manifest_class_name), value=value
                )

                _cache[value] = manifest_value

                # check if the model has manifest models itself
                if manifest_value:
                    await manifest_value.fetch_manifest_information(_cache=_cache)

            setattr(self, name, manifest_value)

        # sub models which may have manifest entries too
        elif hasattr((value := getattr(self, name)), "fetch_manifest_information"):
            await value.fetch_manifest_information(_cache=_cache)

from_dict(data, client, recursive=False, *args, **kwargs) async classmethod

Convert json data to this model

Parameters:

Name Type Description Default
data dict

The json representation of the model, usually received by bungie

required
client 'Client'

The client obj

required
recursive bool

If this was called recursively

False

Returns:

Type Description
BaseModel

The model

Source code in src/bungio/models/base.py
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
@classmethod
async def from_dict(cls, data: dict, client: "Client", recursive: bool = False, *args, **kwargs) -> BaseModel:
    """
    Convert json data to this model

    Args:
        data: The json representation of the model, usually received by bungie
        client: The client obj
        recursive: If this was called recursively

    Returns:
        The model
    """

    if isinstance(data, cls):
        return data

    if not isinstance(data, dict):
        raise ValueError

    if "Response" in data:
        data = data["Response"]

    # also use the kwargs as data
    data = kwargs | data
    data = cls.process_dict(data=data, client=client)

    prepared = {}
    for name, field in attrs.fields_dict(cls).items():
        if field.init and name != "_client":
            default = field.default

            # get the value we want. This also skips the manifest_... entries since they have no value and a default
            bungie_name = cls._convert_to_bungie_case(name)
            value = data.get(bungie_name, attrs.NOTHING)

            # bungie is veeery inconsistent and sometimes like to start their params with an upper case for some reason
            # only sometimes tho :)
            if value is attrs.NOTHING:
                value = data.get(f"{bungie_name[0].capitalize()}{bungie_name[1:]}", attrs.NOTHING)

            # sadly bungie sometimes does not return info without marking that fact in the api specs
            if value is attrs.NOTHING and default is attrs.NOTHING:
                default = MISSING

            if value is attrs.NOTHING:
                value = default

            else:
                value = await cls._convert_to_type(
                    field_type=field.type, field_metadata=field.metadata, value=value, client=client
                )

            # set the attr
            prepared[name] = value

    res = cls(**prepared)  # noqa

    # fill the manifest information
    if not recursive and res._client.always_return_manifest_information:
        await res.fetch_manifest_information()

    return res

process_dict(data, client, *args, **kwargs) staticmethod

Model specific cleanup

Parameters:

Name Type Description Default
data dict

The json representation of the model, usually received by bungie

required
client 'Client'

The client obj

required

Returns:

Type Description
dict

Clean json

Source code in src/bungio/models/base.py
260
261
262
263
264
265
266
267
268
269
270
271
272
@staticmethod
def process_dict(data: dict, client: "Client", *args, **kwargs) -> dict:
    """
    Model specific cleanup

    Args:
        data: The json representation of the model, usually received by bungie
        client: The client obj

    Returns:
        Clean json
    """
    return data

to_dict(_return_to_bungie_case=True)

Convert the model into a dict representation bungie accepts

Returns:

Type Description
dict

A dict which can be sent to bungie

Source code in src/bungio/models/base.py
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
def to_dict(self, _return_to_bungie_case: bool = True) -> dict:
    """
    Convert the model into a dict representation bungie accepts

    Returns:
        A dict which can be sent to bungie
    """

    result = {}

    for name, field in attrs.fields_dict(type(self)).items():
        if name.startswith("_") or name.startswith("manifest_"):
            continue

        value = getattr(self, name)
        if _return_to_bungie_case:
            name = self._convert_to_bungie_case(name)

        if inspect.ismethod(value) or inspect.isfunction(value):
            continue

        elif isinstance(value, dict):
            raise NotImplementedError(
                "Nested dict conversion is not currently implemented, since bungie does never require that info"
            )

        elif isinstance(value, list):
            list_results = []
            for entry in value:
                if hasattr(entry, "to_dict"):
                    list_results.append(entry.to_dict())
                else:
                    list_results.append(entry)

            result[name] = list_results

        else:
            if hasattr(value, "to_dict"):
                result[name] = value.to_dict()
            else:
                # convert to string if they are int64
                if field.metadata.get("int64", None) is True:
                    value = str(value)

                result[name] = value

    return result