"""Games are the umbrella entities under which all mods are stored."""
import json
from typing import List, Literal, Optional
from .mod import Mod
from .entities import Event, Image, Message, GameStats, ModStats, GamePlatform, TagOption, User
from .objects import Filter, NewMod, Pagination, Returned
from .utils import _convert_date, find
from .enums import APIAccess, Community, Curation, MaturityOptions, Presentation, Revenue, Status, Submission
from .mixins import OwnerMixin, ReportMixin
[docs]class Game(ReportMixin, OwnerMixin):
"""Represents an instance of a Game. Do not create manually.
Attributes
-----------
id : int
ID of the game. Filter attribute.
status : Status
Status of the game. (see `status and visibility <https://docs.mod.io/#status-amp-visibility>`_ for details) Filter attribute.
submitter : Optional[User]
Instance of the modio user who submitted the game. Filter
attribute.
date : datetime.datetime
UNIX timestamp of the date the game was registered. Filter attribute.
updated : datetime.datetime
UNIX timestamp of the date the game was last updated. Filter attribute.
live : datetime.datetime
UNIX timestamp of the date the game went live. Filter attribute.
presentation : Presentation
Presentation style used on the mod.io website. Filter attribute.
submission : Submission
Submission process modders must follow. Filter attribute.
curation : Curation
Curation process used to approve mods. Filter attribute.
community : Community
Community features enabled on the mod.io website. Filter attribute.
revenue : Revenue
Revenue capabilities mods can enable. Filter attribute.
api : APIAccess
Level of API access allowed by this game. Filter attribute.
maturity_options : MaturityOptions
Switch to allow developers to select if they flag their mods as containing mature
content. Filter attribute.
ugc : str
Word used to describe user-generated content (mods, items, addons etc).
Filter attribute.
icon : Image
The game icon
logo : Image
The game logo
header : Image
The game header
name : str
Name of the game. Filter attribute.
name_id : str
sub_domain name for the game (https://name_id.mod.io). Filter attribute.
summary : str
Summary of the game. Filter attribute.
instructions : str
Instructions on uploading mods for this game, only applicable
if :attr:`submission` equals 0
instructions_url : str
Link to a mod.io guide, your modding wiki or a page where modders can learn how to
make and submit mods to your games profile. Filter attribute.
profile : str
URL to the game's mod.io page.
tag_options : List[TagOption]
List of tags from which mods can pick
stats : Optional[GameStats]
The game stats
other_urls : Dict[str, str]
A dictionnary of labels and urls for
the game
platforms : List[GamePlatform]
Platforms this games supports
"""
_resource_type = "games"
def __init__(self, **attrs):
self.id = attrs.pop("id")
self.status = Status(attrs.pop("status"))
self.date = _convert_date(attrs.pop("date_added"))
self.updated = _convert_date(attrs.pop("date_updated"))
self.live = _convert_date(attrs.pop("date_live"))
self.presentation = Presentation(attrs.pop("presentation_option"))
self.submission = Submission(attrs.pop("submission_option"))
self.curation = Curation(attrs.pop("curation_option"))
self.community = Community(attrs.pop("community_options"))
self.revenue = Revenue(attrs.pop("revenue_options"))
self.api = APIAccess(
attrs.pop(
"api_access_options",
)
)
self.ugc = attrs.pop("ugc_name")
self.icon = Image(**attrs.pop("icon", None))
self.logo = Image(**attrs.pop("logo", None))
self.header = Image(**attrs.pop("header", None))
self.homepage = attrs.pop("homepage", None)
self.name = attrs.pop("name")
self.name_id = attrs.pop("name_id")
self.summary = attrs.pop("summary")
self.instructions = attrs.pop("instructions", None)
self.instructions_url = attrs.pop("instructions_url", None)
self.profile = attrs.pop("profile_url")
self.tag_options = [TagOption(**tag) for tag in attrs.pop("tag_options", [])]
self.maturity_options = MaturityOptions(attrs.pop("maturity_options"))
self.connection = attrs.pop("connection")
self.submitter = None
self.stats = None
# self.theme = Theme(**attrs.pop("theme"))
self.other_urls = {value["label"]: value["url"] for value in attrs.pop("other_urls")}
self.platforms = [GamePlatform(**platform) for platform in attrs.pop("platforms")]
_submitter = attrs.pop("submitted_by", {})
if _submitter:
self.submitter = User(connection=self.connection, **_submitter)
_stats = attrs.pop("stats", {})
if _stats:
self.stats = GameStats(**_stats)
def __repr__(self):
return f"<Game id={self.id} name={self.name}>"
[docs] def get_mod(self, mod_id: int) -> Mod:
"""Queries the mod.io API for the given mod ID and if found returns it as a
Mod instance. If not found raises NotFound.
|coro|
Parameters
-----------
mod_id : int
The ID of the mod to query the API for
Raises
-------
NotFound
A mod with the supplied id was not found.
Returns
--------
:class: `Mod`
The mod with the given ID
"""
mod_json = self.connection.get_request(f"/games/{self.id}/mods/{mod_id}")
return Mod(connection=self.connection, **mod_json)
[docs] async def async_get_mod(self, mod_id: int) -> Mod:
mod_json = await self.connection.async_get_request(f"/games/{self.id}/mods/{mod_id}")
return Mod(connection=self.connection, **mod_json)
[docs] def get_mods(self, *, filters: Filter = None) -> Returned[Mod]:
"""Gets all the mods available for the game. Returns a
named tuple with parameters results and pagination. |filterable|
|coro|
Parameters
-----------
filters : Optional[Filter]
A instance of Filter to be used for filtering, paginating and sorting
results
Returns
--------
Returned[List[Mod], Pagination]
The results and pagination tuple from this request
"""
mod_json = self.connection.get_request(f"/games/{self.id}/mods", filters=filters)
return Returned(
[Mod(connection=self.connection, **mod) for mod in mod_json["data"]], Pagination(**mod_json)
)
[docs] async def async_get_mods(self, *, filters: Filter = None) -> Returned[Mod]:
mod_json = await self.connection.async_get_request(f"/games/{self.id}/mods", filters=filters)
return Returned(
[Mod(connection=self.connection, **mod) for mod in mod_json["data"]], Pagination(**mod_json)
)
[docs] def get_mod_events(self, *, filters: Filter = None) -> Returned[Event]:
"""Gets all the mod events available for this game sorted by latest event first. |filterable|
|coro|
Parameters
-----------
filters : Optional[Filter]
A instance of Filter to be used for filtering, paginating and sorting
results
Returns
--------
Returned[List[Event], Pagination]
The results and pagination tuple from this request
"""
event_json = self.connection.get_request(f"/games/{self.id}/mods/events", filters=filters)
return Returned([Event(**event) for event in event_json["data"]], Pagination(**event_json))
[docs] async def async_get_mod_events(self, *, filters: Filter = None) -> Returned[Event]:
event_json = await self.connection.async_get_request(f"/games/{self.id}/mods/events", filters=filters)
return Returned([Event(**event) for event in event_json["data"]], Pagination(**event_json))
[docs] def get_tag_options(self, *, filters: Filter = None):
"""Gets all the game tags available for this game. Updates the tag_option attribute. |filterable|
|coro|
Parameters
-----------
filters : Optional[Filter]
A instance of Filter to be used for filtering, paginating and sorting
results
Returns
--------
Returned[List[TagOption], Pagination]
The results and pagination tuple from this request
"""
tag_json = self.connection.get_request(f"/games/{self.id}/tags", filters=filters)
self.tag_options = tags = [TagOption(**tag_option) for tag_option in tag_json["data"]]
return Returned(tags, Pagination(**tag_json))
[docs] async def async_get_tag_options(self, *, filters: Filter = None):
tag_json = await self.connection.async_get_request(f"/games/{self.id}/tags", filters=filters)
self.tag_options = tags = [TagOption(**tag_option) for tag_option in tag_json["data"]]
return Returned(tags, Pagination(**tag_json))
[docs] def get_stats(self, *, filters: Filter = None):
"""Get the stats for the game. |filterable|
|coro|
Parameters
-----------
filter : Optional[Filter]
A instance of Filter to be used for filtering, paginating and sorting
results
Returns
--------
GameStats
The stats for the game.
"""
stats_json = self.connection.get_request(f"/games/{self.id}/stats", filters=filters)
return GameStats(**stats_json)
[docs] async def async_get_stats(self, *, filters: Filter = None):
stats_json = await self.connection.async_get_request(f"/games/{self.id}/stats", filters=filters)
return GameStats(**stats_json)
[docs] def get_mods_stats(self, *, filters: Filter = None):
"""Gets the stat for all the mods of this game. |filterable|
|coro|
Parameters
-----------
filter : Optional[Filter]
A instance of Filter to be used for filtering, paginating and sorting
results
Returns
--------
Returned[List[ModStats], Pagination]
The results and pagination tuple from this request
"""
stats_json = self.connection.get_request(f"/games/{self.id}/mods/stats", filters=filters)
return Returned([ModStats(**stats) for stats in stats_json["data"]], Pagination(**stats_json))
[docs] async def async_get_mods_stats(self, *, filters: Filter = None):
stats_json = await self.connection.async_get_request(f"/games/{self.id}/mods/stats", filters=filters)
return Returned([ModStats(**stats) for stats in stats_json["data"]], Pagination(**stats_json))
[docs] def add_mod(self, mod: NewMod) -> Mod:
"""Add a mod to this game.
|coro|
Parameters
-----------
mod : NewMod
The mod to be submitted
Raises
-------
ValueError
One of the requirements for a parameter has not been met.
Returns
--------
Mod
The newly created mod
"""
mod_d = mod.__dict__.copy()
tags = list(mod_d.pop("tags"))
for index, tag in enumerate(tags):
mod_d[f"tags[{index}]"] = tag
with open(mod_d.pop("logo"), "rb") as f:
mod_json = self.connection.post_request(
f"/games/{self.id}/mods", h_type=1, data=mod_d, files={"logo": f}
)
return Mod(connection=self.connection, **mod_json)
[docs] async def async_add_mod(self, mod: NewMod) -> Mod:
mod_d = mod.__dict__.copy()
tags = list(mod_d.pop("tags"))
for index, tag in enumerate(tags):
mod_d[f"tags[{index}]"] = tag
with open(mod_d.pop("logo"), "rb") as f:
mod_json = await self.connection.async_post_request(
f"/games/{self.id}/mods", h_type=1, data=mod_d, files={"logo": f}
)
return Mod(connection=self.connection, **mod_json)
[docs] def add_tag_options(
self,
name: str,
*,
tags: Optional[List[str]] = None,
hidden: Optional[bool] = False,
locked: Optional[bool] = False,
tag_type: Optional[Literal["dropdown", "checkboxes"]] = "dropdown",
):
"""Add tags which mods can apply to their profiles. If the tag names already exists,
settings such as hidden or type will be overwritten to the values provided and all the
tags will be added to the group.
|coro|
Parameters
-----------
name : str
Name of the tag group
type : Optional[Literal['dropdown', 'checkboxes']]
Defaults to dropdown
dropdown : Mods can select only one tag from this group, dropdown menu shown on site profile.
checkboxes : Mods can select multiple tags from this group, checkboxes shown on site profile.
hidden : Optional[bool]
Whether or not this group of tags should be hidden from users and mod devs. Defaults to False
locked : Optional[bool]
Whether or not mods can assign from this group of tag to themselves. If locked only game admins
will be able to assign the tag. Defaults to False.
tags : Optional[List[str]]
Array of tags that mod creators can apply to their mod
"""
if tags is None:
tags = []
tags = {f"tags[{index}]": tag for index, tag in enumerate(tags)}
tags = {
"name": name,
"type": tag_type,
"hidden": json.dumps(hidden),
"locked": json.dumps(locked),
**tags,
}
message = self.connection.post_request(f"/games/{self.id}/tags", data=tags)
tag_option = find(self.tag_options, name=name)
if not tag_option:
self.tag_options.append(TagOption(**tags))
else:
tag_option.type = tag_type
tag_option.hidden = hidden
tag_option.tags.extend(tags)
return Message(**message)
[docs] async def async_add_tag_options(
self,
name: str,
*,
tags: Optional[List[str]] = None,
hidden: Optional[bool] = False,
locked: Optional[bool] = False,
tag_type: Optional[Literal["dropdown", "checkboxes"]] = "dropdown",
):
if tags is None:
tags = []
tags = {f"tags[{index}]": tag for index, tag in enumerate(tags)}
tags = {
"name": name,
"type": tag_type,
"hidden": json.dumps(hidden),
"locked": json.dumps(locked),
**tags,
}
message = await self.connection.async_post_request(f"/games/{self.id}/tags", data=tags)
tag_option = find(self.tag_options, name=name)
if not tag_option:
self.tag_options.append(TagOption(**tags))
else:
tag_option.type = tag_type
tag_option.hidden = hidden
tag_option.tags.extend(tags)
return Message(**message)
[docs] def delete_tag_options(self, name: str, *, tags: Optional[List[str]] = None) -> bool:
"""Delete one or more tags from a tag option.
|coro|
Parameters
-----------
name : str
Name of the group from which you wish to delete from
tags : Optional[List[str]]
Optional. Tags to delete from group. If left blank the entire group will be
deleted
Returns
--------
bool
Returns True if the tags were sucessfully removed, False if the requests was
sucessful but the tags was not removed (if the tag wasn't part of the option.)
"""
data = {f"tags[{index}]": tag for index, tag in enumerate(tags)} if tags else {"tags[]": ""}
data["name"] = name
resp = self.connection.delete_request(f"/games/{self.id}/tags", data=data)
option = find(self.tag_options, name=name)
if tags:
option.tags = [tag for tag in option.tags if tag not in tags]
else:
self.tag_options.remove(option)
return not isinstance(resp, dict)
[docs] async def async_delete_tag_options(self, name: str, *, tags: Optional[List[str]] = None) -> bool:
data = {f"tags[{index}]": tag for index, tag in enumerate(tags)} if tags else {"tags[]": ""}
data["name"] = name
resp = await self.connection.async_delete_request(f"/games/{self.id}/tags", data=data)
option = find(self.tag_options, name=name)
if tags:
option.tags = [tag for tag in option.tags if tag not in tags]
else:
self.tag_options.remove(option)
return not isinstance(resp, dict)