From d814ef70bb5080c5d4f1b82041b4440839198394 Mon Sep 17 00:00:00 2001 From: Tomo <68489118+tomodachi94@users.noreply.github.com> Date: Sat, 23 Dec 2023 14:52:55 -0800 Subject: [PATCH] catalog: init --- roblox/catalog.py | 108 ++++++++++++++++++++++++++++++++++++++++++++++ roblox/client.py | 46 +++++++++++++++++++- 2 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 roblox/catalog.py diff --git a/roblox/catalog.py b/roblox/catalog.py new file mode 100644 index 00000000..0a1878fe --- /dev/null +++ b/roblox/catalog.py @@ -0,0 +1,108 @@ +""" + +This module contains classes intended to parse and deal with data from Roblox catalog endpoint. + +""" +from __future__ import annotations +from datetime import datetime + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from .client import Client + from typing import Optional +from .bases.basecatalogitem import BaseCatalogItem +from .bases.baseuser import BaseUser +from .assets import AssetType +from .partials.partialgroup import PartialGroup +from .partials.partialuser import CatalogCreatorPartialUser + +class CatalogItem(BaseCatalogItem): + """ + Represents a Catalog/Avatar Shop/Marketplace item. + + Attributes: + id: The item's ID. + name: The item's name. + item_type: Unknown. + asset_type: The asset's type as an instance of AssetType + description: The item's description. + is_offsale: If the item is offsale. + creator: A class representing the creator of the item. + price: The price of the item, in Robux. + purchase_count: The number of times the item has been purchased. + favorite_count: The number of times the item has been favorited. + sale_location_type: Unknown. + premium_pricing: A dictionary storing information about pricing for Roblox Premium members. + premium_pricing.in_robux: The pricing for Roblox Premium members, in Robux. + premium_pricing.discount_percentage: The percentage that Roblox Premium members get discounted. + """ + + def __init__(self, client: Client, data: dict): + self._client: Client = client + self.id: int = data["id"] + self.item_type = data["itemType"] + super().__init__(client=self._client, catalog_item_id=self.id, catalog_item_type=self.item_type) + + self.name: str = data["name"] + self.description: str = data["description"] + + self.asset_type: AssetType = AssetType(type_id=data["assetType"]) + + self.is_offsale: bool = data["isOffsale"] + + # Creator + self.creator: CatalogCreatorPartialUser or CatalogCreatorPartialGroup + if data["creatorType"] == "User": + self.creator = CatalogCreatorPartialUser(client=client, data=data) + elif data["creatorType"] == "Group": + self.creator = CatalogCreatorPartialGroup(client=client, group_id=data) + + self.price: int = data["price"] + self.purchase_count: int = data["purchaseCount"] + self.favorite_count: int = data["favoriteCount"] + self.sale_location_type: str = data["saleLocationType"] + + + + if data["premiumPricing"]: + self.premium_pricing = {} + self.premium_pricing.in_robux: int = data["premiumPricing"]["premiumPriceInRobux"] + self.premium_pricing.discount_percentage: int = data["premiumPricing"]["premiumDiscountPercentage"] + + + def __repr__(self): + return f"<{self.__class__.__name__} name={self.name!r}>" + + +class LimitedCatalogItem(CatalogItem): + """ + Represents a limited Catalog/Avatar Shop/Marketplace item. + + See also: + CatalogItem, which this class inherits. + + Attributes: + collectible_item_id: Unknown. + quantity_limit_per_user: The maximum number of this item that a user can own. + units_available_for_consumption: The amount of items that can be bought by all users. + total_quantity: The amount of items that are owned or can be purchased. + has_resellers: If the item has resellers. + offsale_deadline: The time that an item goes offsale (as an instance of a datetime.datetime object). + lowest_price: The lowest price, in Robux, offered to obtain this item. + lowest_resale_price: The lowest resale price, in Robux, offered to obtain this item. + price_status: Unknown. + """ + + def __init__(self, client=client, data=data): + super.__init__(client=client, data=data) + + self.collectible_item_id: str = data["collectibleItemId"] + self.quantity_limit_per_user: int = data["quantityLimitPerUser"] + self.units_available_for_consumption: int = data["unitsAvailableForConsumption"] + self.total_quantity: int = data["totalQuantity"] + self.has_resellers: bool = data["hasResellers"] + self.offsale_deadline: Optional[datetime] = datetime.fromtimestamp(data["offsaleDeadline"]) + self.lowest_price: int = data["lowestPrice"] + self.lowest_resale_price: int = data["lowestResalePrice"] + self.price_status: str = data["priceStatus"] diff --git a/roblox/client.py b/roblox/client.py index fbea3a43..94cfe94e 100644 --- a/roblox/client.py +++ b/roblox/client.py @@ -4,7 +4,7 @@ """ -from typing import Union, List, Optional +from typing import Union, List, Optional, Literal, TypedDict from .account import AccountProvider from .assets import EconomyAsset @@ -550,3 +550,47 @@ def get_base_gamepass(self, gamepass_id: int) -> BaseGamePass: Returns: A BaseGamePass. """ return BaseGamePass(client=self, gamepass_id=gamepass_id) + + # Catalog + def get_catalog_items(self, catalog_item_array: List[TypedDict[catalog_id: int, catalog_item_type: Literal[1, 2]]]) -> List[CatalogItem]: + """ + Gets a catalog item with the passed ID. + + The catalog is also known as the Avatar Shop or the Marketplace. + + Arguments: + catalog_id: A Roblox catalog item ID. + catalog_item_type: The type of item. 1 for an asset, and 2 for a bundle. + + Returns: + A list of CatalogItem. + """ + try: + catalog_item_response = await self._requests.post( + url=self._url_generator.get_url( + "catalog", "v1/catalog/items/details" + ), + data={"data": catalog_item_array} + ) + except NotFound as exception: + raise CatalogItemNotFound( + message="Invalid catalog item.", + response=exception.response + ) from None + catalog_item_data = catalog_item_response.json() + catalog_list: Literal[CatalogItem] = [] + for catalog_item in catalog_item_data: + if data["collectibleItemId"]: # This is the only consistent indicator of an item's limited status + catalog_list.append(LimitedCatalogItem(client=self, data=catalog_item)) + else: + catalog_list.append(CatalogItem(client=self, data=catalog_item)) + + return catalog_list + + def get_base_catalog_items(self, catalog_item_array: List[TypedDict[catalog_id: int, catalog_item_type: Literal[1, 2]]]) -> List[CatalogItem]: + catalog_list: Literal[CatalogItem] = [] + + for catalog_item in catalog_item_array: + catalog_list.append(BaseCatalogItem(client=self, data=catalog_item)) + + return catalog_list