"""Models relatd to the character bazaar."""
import datetime
from abc import ABC, abstractmethod
from typing import Generic, Optional, TypeVar
from tibiapy.enums import (
AuctionBattlEyeFilter,
AuctionOrderBy,
AuctionOrderDirection,
AuctionSearchType,
AuctionSkillFilter,
AuctionStatus,
AuctionVocationFilter,
BazaarType,
BidType,
PvpTypeFilter,
Sex,
Vocation,
)
from tibiapy.models import BaseModel
from tibiapy.models.pagination import AjaxPaginator, PaginatedWithUrl
__all__ = (
"AchievementEntry",
"Auction",
"AuctionDetails",
"AuctionFilters",
"BestiaryEntry",
"BlessingEntry",
"CharacterBazaar",
"CharmEntry",
"DisplayImage",
"FamiliarEntry",
"Familiars",
"ItemEntry",
"ItemSummary",
"MountEntry",
"Mounts",
"OutfitEntry",
"OutfitImage",
"Outfits",
"RevealedGem",
"SalesArgument",
"SkillEntry",
)
from tibiapy.urls import get_auction_url, get_bazaar_url, get_character_url
[docs]
class AchievementEntry(BaseModel):
"""An unlocked achievement by the character."""
name: str
"""The name of the achievement."""
is_secret: bool
"""Whether the achievement is secret or not."""
[docs]
class AuctionFilters(BaseModel):
"""The auction filters available in the auctions section.
All attributes are optional.
"""
world: Optional[str] = None
"""The character's world to show characters for."""
pvp_type: Optional[PvpTypeFilter] = None
"""The PvP type of the character's worlds to show."""
battleye: Optional[AuctionBattlEyeFilter] = None
"""The type of BattlEye protection of the character's worlds to show."""
vocation: Optional[AuctionVocationFilter] = None
"""The character vocation to show results for."""
min_level: Optional[int] = None
"""The minimum level to display."""
max_level: Optional[int] = None
"""The maximum level to display."""
skill: Optional[AuctionSkillFilter] = None
"""The skill to filter by its level range."""
min_skill_level: Optional[int] = None
"""The minimum skill level of the selected :attr:`skill` to display."""
max_skill_level: Optional[int] = None
"""The maximum skill level of the selected :attr:`skill` to display."""
order_by: Optional[AuctionOrderBy] = None
"""The column or value to order by."""
order: Optional[AuctionOrderDirection] = None
"""The ordering direction for the results."""
search_string: Optional[str] = None
"""The search term to filter out auctions."""
search_type: Optional[AuctionSearchType] = None
"""The type of search to use. Defines the behaviour of :py:attr:`search_string`."""
available_worlds: list[str] = []
"""The list of available worlds to select to filter."""
@property
def query_params(self) -> dict[str, str]:
"""The query parameters representing this filter."""
params = {
"filter_profession": self.vocation.value if self.vocation is not None else None,
"filter_levelrangefrom": self.min_level,
"filter_levelrangeto": self.max_level,
"filter_world": self.world,
"filter_worldpvptype": self.pvp_type.value if self.pvp_type is not None else None,
"filter_worldbattleyestate": self.battleye.value if self.battleye is not None else None,
"filter_skillid": self.skill.value if self.skill is not None else None,
"filter_skillrangefrom": self.min_skill_level,
"filter_skillrangeto": self.max_skill_level,
"order_column": self.order_by.value if self.order_by is not None else None,
"order_direction": self.order.value if self.order is not None else None,
"searchstring": self.search_string,
"searchtype": self.search_type.value if self.search_type is not None else None,
}
return {k: v for k, v in params.items() if v is not None}
[docs]
class BestiaryEntry(BaseModel):
"""The bestiary progress for a specific creature."""
name: str
"""The name of the creature."""
kills: int
"""The number of kills of this creature the player has done."""
step: int
"""The current step to unlock this creature the character is in, where 4 is fully unlocked."""
@property
def is_completed(self) -> bool:
"""Whether the entry is completed or not."""
return self.step == 4
[docs]
class BlessingEntry(BaseModel):
"""A character's blessings."""
name: str
"""The name of the blessing."""
amount: int
"""The amount of blessing charges the character has."""
[docs]
class CharmEntry(BaseModel):
"""An unlocked charm by the character."""
name: str
"""The name of the charm."""
cost: int
"""The cost of the charm in charm points."""
class BaseOutfit(BaseModel):
"""A base outfit displayed in auctions."""
outfit_id: int
"""The internal ID of the outfit."""
addons: int
"""The selected or unlocked addons."""
image_url: str
class DisplayImage(BaseModel):
"""An image displayed in the auction."""
image_url: str
"""The URL to the image."""
name: str
"""The element's name."""
[docs]
class ItemEntry(DisplayImage):
"""Represents an item displayed on an auction, or the character's items in the auction detail."""
image_url: str
"""The URL to the item's image."""
name: str
"""The item's name."""
description: Optional[str] = None
"""The item's description, if any."""
count: int = 1
"""The item's count."""
item_id: int
"""The item's client id."""
tier: int = 0
"""The item's tier."""
[docs]
class MountEntry(DisplayImage):
"""Represents a mount owned or unlocked by the character."""
image_url: str
"""The URL to the image."""
name: str
"""The mount's name."""
mount_id: int
"""The internal ID of the mount."""
[docs]
class OutfitEntry(DisplayImage, BaseOutfit):
"""Represents an outfit owned or unlocked by the character."""
image_url: str
"""The URL to the image."""
name: str
"""The outfit's name."""
outfit_id: int
"""The internal ID of the outfit."""
[docs]
class FamiliarEntry(DisplayImage):
"""Represents a familiar owned or unlocked by the character."""
image_url: str
"""The URL to the image."""
name: str
"""The familiar's name."""
familiar_id: int
"""The internal ID of the familiar."""
[docs]
class OutfitImage(BaseOutfit):
"""The image of the outfit currently being worn by the character."""
image_url: str
"""The URL of the image."""
outfit_id: int
"""The ID of the outfit."""
addons: int
"""The addons displayed in the outfit."""
T = TypeVar("T", bound=DisplayImage)
class AuctionSummary(AjaxPaginator[T], Generic[T], ABC):
def get_by_name(self, name: str):
"""Get an entry by its name.
Parameters
----------
name: :class:`str`
The name of the entry, case-insensitive.
Returns
-------
:class:`object`:
The entry matching the name.
"""
return next((e for e in self.entries if e.name.lower() == name.lower()), None)
def search(self, value: str):
"""Search an entry by its name.
Parameters
----------
value: :class:`str`
The value to look for.
Returns
-------
:class:`list`
A list of entries with names containing the search term.
"""
return [e for e in self.entries if value.lower() in e.name.lower()]
@abstractmethod
def get_by_id(self, entry_id: int):
...
[docs]
class ItemSummary(AuctionSummary[ItemEntry]):
"""Items in a character's inventory and depot."""
[docs]
def get_by_id(self, entry_id: int) -> Optional[ItemEntry]:
"""Get an item by its item id.
Parameters
----------
entry_id: :class:`int`
The ID of the item.
Returns
-------
:class:`ItemEntry`
The item matching the id.
"""
return next((e for e in self.entries if e.item_id == entry_id), None)
[docs]
class Mounts(AuctionSummary[MountEntry]):
"""The mounts the character has unlocked or purchased."""
[docs]
def get_by_id(self, entry_id: int) -> Optional[MountEntry]:
"""Get a mount by its mount id.
Parameters
----------
entry_id: :class:`int`
The ID of the mount.
Returns
-------
:class:`MountEntry`
The mount matching the id.
"""
return next((e for e in self.entries if e.mount_id == entry_id), None)
[docs]
class Familiars(AuctionSummary[FamiliarEntry]):
"""The familiars the character has unlocked or purchased."""
[docs]
def get_by_id(self, entry_id: int) -> Optional[FamiliarEntry]:
"""Get a familiar by its familiar id.
Parameters
----------
entry_id: :class:`int`
The ID of the familiar.
Returns
-------
:class:`FamiliarEntry`
The familiar matching the id.
"""
return next((e for e in self.entries if e.familiar_id == entry_id), None)
[docs]
class Outfits(AuctionSummary[OutfitEntry]):
"""The outfits the character has unlocked or purchased."""
[docs]
def get_by_id(self, entry_id: int) -> Optional[OutfitEntry]:
"""Get an outfit by its outfit id.
Parameters
----------
entry_id: :class:`int`
The ID of the outfit.
Returns
-------
:class:`OutfitEntry`
The outfit matching the id.
"""
return next((e for e in self.entries if e.outfit_id == entry_id), None)
[docs]
class SalesArgument(BaseModel):
"""Represents a sales argument.
Sales arguments can be selected when creating an auction, and allow the user to highlight certain
character features in the auction listing.
"""
category_id: int
category_image: str
"""The URL to the category icon."""
content: str
"""The content of the sales argument."""
[docs]
class SkillEntry(BaseModel):
"""Represents the character's skills."""
name: str
"""The name of the skill."""
level: int
"""The current level."""
progress: float
"""The percentage of progress for the next level."""
[docs]
class RevealedGem(BaseModel):
"""A gem that has been revealed for the character."""
gem_type: str
"""The type of gem."""
mods: list[str]
"""The mods or effects the gem has."""
[docs]
class AuctionDetails(BaseModel):
"""The details of an auction."""
hit_points: int
"""The hit points of the character."""
mana: int
"""The mana points of the character."""
capacity: int
"""The character's capacity in ounces."""
speed: int
"""The character's speed."""
blessings_count: int
"""The number of blessings the character has."""
mounts_count: int
"""The number of mounts the character has."""
outfits_count: int
"""The number of outfits the character has."""
titles_count: int
"""The number of titles the character has."""
skills: list[SkillEntry]
"""The current skills of the character."""
creation_date: datetime.datetime
"""The date when the character was created."""
experience: int
"""The total experience of the character."""
gold: int
"""The total amount of gold the character has."""
achievement_points: int
"""The number of achievement points of the character."""
regular_world_transfer_available_date: Optional[datetime.datetime] = None
"""The date after regular world transfers will be available to purchase and use.
:obj:`None` indicates it is available immediately."""
charm_expansion: bool
"""Whether the character has a charm expansion or not."""
available_charm_points: int
"""The amount of charm points the character has available to spend."""
spent_charm_points: int
"""The total charm points the character has spent."""
prey_wildcards: int
"""The number of Prey Wildcards the character has."""
daily_reward_streak: int
"""The current daily reward streak."""
hunting_task_points: int
permanent_hunting_task_slots: int
"""The number of hunting task slots."""
permanent_prey_slots: int
"""The number of prey slots."""
hirelings: int
"""The number of hirelings the character has."""
hireling_jobs: int
"""The number of hireling jobs the character has."""
hireling_outfits: int
"""The number of hireling outfits the character has."""
exalted_dust: int
"""The amount of exalted dust the character has."""
exalted_dust_limit: int
"""The dust limit of the character."""
boss_points: int
"""The boss points of the character."""
bonus_promotion_points: int
"""The bonus promotion points of the character."""
items: ItemSummary
"""The items the character has across inventory, depot and item stash."""
store_items: ItemSummary
"""The store items the character has."""
mounts: Mounts
"""The mounts the character has unlocked."""
store_mounts: Mounts
"""The mounts the character has purchased from the store."""
outfits: Outfits
"""The outfits the character has unlocked."""
store_outfits: Outfits
"""The outfits the character has purchased from the store."""
familiars: Familiars
"""The familiars the character has purchased or unlocked."""
blessings: list[BlessingEntry]
"""The blessings the character has."""
imbuements: list[str]
"""The imbuements the character has unlocked access to."""
charms: list[CharmEntry]
"""The charms the character has unlocked."""
completed_cyclopedia_map_areas: list[str]
"""The cyclopedia map areas that the character has fully discovered."""
completed_quest_lines: list[str]
"""The quest lines the character has fully completed."""
titles: list[str]
"""The titles the character has unlocked."""
achievements: list[AchievementEntry]
"""The achievements the character has unlocked."""
bestiary_progress: list[BestiaryEntry]
"""The bestiary progress of the character."""
bosstiary_progress: list[BestiaryEntry]
"""The bosstiary progress of the character."""
revealed_gems: list[RevealedGem]
"""The gems that have been revealed by the character."""
@property
def completed_bestiary_entries(self) -> list[BestiaryEntry]:
"""Get a list of completed bestiary entries."""
return [e for e in self.bestiary_progress if e.is_completed]
@property
def regular_world_transfer_available(self) -> bool:
"""Whether regular world transfers are available immediately for this character."""
return self.regular_world_transfer_available_date is None
@property
def skills_map(self) -> dict[str, SkillEntry]:
"""A mapping of skills by their name."""
return {skill.name: skill for skill in self.skills}
[docs]
class Auction(BaseModel):
"""Represents an auction in the list, containing the summary."""
auction_id: int
"""The internal id of the auction."""
name: str
"""The name of the character."""
level: int
"""The level of the character."""
world: str
"""The world the character is in."""
vocation: Vocation
"""The vocation of the character."""
sex: Sex
"""The sex of the character."""
outfit: OutfitImage
"""The current outfit selected by the user."""
displayed_items: list[ItemEntry]
"""The items selected to be displayed."""
sales_arguments: list[SalesArgument]
"""The sale arguments selected for the auction."""
auction_start: datetime.datetime
"""The date when the auction started."""
auction_end: datetime.datetime
"""The date when the auction ends."""
bid: int
"""The current bid in Tibia Coins."""
bid_type: BidType
"""The type of the auction's bid."""
status: AuctionStatus
"""The current status of the auction."""
details: Optional[AuctionDetails] = None
"""The auction's details."""
@property
def character_url(self) -> str:
"""The URL of the character's information page on Tibia.com."""
return get_character_url(self.name)
@property
def url(self) -> str:
"""The URL to this auction's detail page on Tibia.com."""
return get_auction_url(self.auction_id)
[docs]
class CharacterBazaar(PaginatedWithUrl[Auction]):
"""Represents the char bazaar."""
type: BazaarType
"""The type of auctions being displayed, either current or auction history."""
filters: Optional[AuctionFilters] = None
"""The currently set filtering options."""
[docs]
def get_page_url(self, page: int) -> str:
"""Get the URL to a given page of the bazaar.
Parameters
----------
page: :class:`int`
The desired page.
Returns
-------
:class:`str`
The URL to the desired page.
"""
return get_bazaar_url(self.type, page, self.filters)
@property
def url(self) -> str:
"""The URL to the Character Bazaar with the current parameters."""
return get_bazaar_url(self.type, self.current_page, self.filters)