Skip to content

Commit

Permalink
Merge pull request #238 from mathsman5133/3.5.4
Browse files Browse the repository at this point in the history
Fixing broken docs & Adding manual ip overwrite
  • Loading branch information
lukasthaler authored May 17, 2024
2 parents a477898 + e7a3528 commit e72a313
Show file tree
Hide file tree
Showing 13 changed files with 61 additions and 42 deletions.
2 changes: 1 addition & 1 deletion coc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
SOFTWARE.
"""

__version__ = "3.5.3"
__version__ = "3.6.0"

from .abc import BasePlayer, BaseClan
from .clans import RankedClan, Clan
Expand Down
2 changes: 1 addition & 1 deletion coc/clans.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def _from_data(self, data: dict) -> None:

@cached_property("_cs_labels")
def labels(self) -> typing.List[Label]:
"""List[:class:`Label`]: A :class:`List` of :class:`Label`s that the clan has."""
"""List[:class:`Label`]: A :class:`List` of :class:`Label`\s that the clan has."""
return list(self._iter_labels)

@cached_property("_cs_members")
Expand Down
8 changes: 8 additions & 0 deletions coc/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ class Client:
base_url: :class:`str`
The base URL to use for API requests. Defaults to "https://api.clashofclans.com/v1"
ip: :class:`str`
The IP address to use for API requests. Defaults to None, which means the IP address will be automatically
detected.
Attributes
----------
loop : :class:`asyncio.AbstractEventLoop`
Expand All @@ -153,6 +157,7 @@ class Client:

__slots__ = (
"base_url",
"ip",
"loop",
"correct_key_count",
"key_names",
Expand Down Expand Up @@ -197,6 +202,7 @@ def __init__(
realtime=False,
raw_attribute=False,
base_url: str = "https://api.clashofclans.com/v1",
ip: Optional[str] = None,
**kwargs,
):

Expand All @@ -222,6 +228,7 @@ def __init__(
self.correct_tags = correct_tags
self.load_game_data = load_game_data
self.base_url = base_url
self.ip = ip
# cache
self._players = {}
self._clans = {}
Expand All @@ -247,6 +254,7 @@ def _create_client(self, email, password):
cache_max_size=self.cache_max_size,
stats_max_size=self.stats_max_size,
base_url=self.base_url,
ip=self.ip,
)

def _load_holders(self):
Expand Down
7 changes: 5 additions & 2 deletions coc/ext/triggers/triggers.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ class IntervalTrigger(BaseTrigger):
error_handler: Optional[:class:`coc.ext.triggers.CoroFunction`]
an optional coroutine function that will be called on each error incurred during the trigger execution.
The handler will receive three arguments:
function_name: :class:`str`
the name of the failing trigger's decorated function
arg: Optional[:class:`Any`]
Expand Down Expand Up @@ -320,6 +321,7 @@ class CronTrigger(BaseTrigger):
error_handler: Optional[:class:`coc.ext.triggers.CoroFunction`]
an optional coroutine function that will be called on each error incurred during the trigger execution.
The handler will receive three arguments:
function_name: :class:`str`
the name of the failing trigger's decorated function
arg: Optional[:class:`Any`]
Expand All @@ -340,12 +342,13 @@ class CronTrigger(BaseTrigger):
Example
----------
-------
.. code-block:: python3
@CronTrigger(cron_schedule='0 0 * * *', iter_args=['#2PP', '#2PPP'])
async def download_current_war(clan_tag: str):
# use your coc client to fetch war data, store it to a file or database, ...
# use your coc client to fetch war data, store it to a file or database,
pass
"""
Expand Down
8 changes: 6 additions & 2 deletions coc/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ def __init__(
cache_max_size=10000,
stats_max_size=1000,
base_url="https://api.clashofclans.com/v1",
ip=None,
):
self.client = client
self.loop = loop
Expand All @@ -222,6 +223,7 @@ def __init__(
self.base_url = base_url
else:
raise ValueError("base_url must be a string and not empty.")
self.ip = ip
if issubclass(throttler, BasicThrottler):
self.__throttle = throttler(1 / per_second)
elif issubclass(throttler, BatchThrottler):
Expand Down Expand Up @@ -507,8 +509,10 @@ async def initialise_keys(self):
LOG.info("Successfully logged into the developer site.")

resp_payload = await resp.json()
ip = json_loads(base64_b64decode(resp_payload["temporaryAPIToken"].split(".")[1] + "====").decode("utf-8"))["limits"][1]["cidrs"][0].split("/")[0]

if not self.ip:
ip = json_loads(base64_b64decode(resp_payload["temporaryAPIToken"].split(".")[1] + "====").decode("utf-8"))["limits"][1]["cidrs"][0].split("/")[0]
else:
ip = self.ip
LOG.info("Found IP address to be %s", ip)

resp = await session.post("https://developer.clashofclans.com/api/apikey/list")
Expand Down
4 changes: 2 additions & 2 deletions coc/miscmodels.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ class Badge:
:class:`str` - URL for a large sized badge (512x512).
"""

__slots__ = ("small", "medium", "large", "url", "_client")
__slots__ = ("small", "medium", "large", "_client")

def __repr__(self):
attrs = [
Expand Down Expand Up @@ -459,7 +459,7 @@ class Icon:
:class:`str`: URL for a medium sized icon (288x288).
"""

__slots__ = ("small", "medium", "tiny", "url", "_client")
__slots__ = ("small", "medium", "tiny", "_client")

def __repr__(self):
attrs = [
Expand Down
32 changes: 16 additions & 16 deletions coc/players.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,13 @@ async def get_detailed_clan(self) -> Optional["Clan"]:

@cached_property("_cs_player_house_elements")
def player_house_elements(self) -> List[PlayerHouseElement]:
"""List[:class:`PlayerHouseElement`]: A :class:`List` of :class:`PlayerHouseElement`s that the player has."""
"""List[:class:`PlayerHouseElement`]: A :class:`List` of :class:`PlayerHouseElement`\s that the player has."""
return list(self._iter_player_house_elements)


class RankedPlayer(ClanMember):
"""Represents a leaderboard-ranked player.
"""
Represents a leaderboard-ranked player.
Attributes
----------
Expand Down Expand Up @@ -209,7 +210,8 @@ def _from_data(self, data: dict) -> None:


class Player(ClanMember):
"""Represents a Clash of Clans Player.
"""
Represents a Clash of Clans Player.
Attributes
----------
Expand Down Expand Up @@ -247,8 +249,6 @@ class Player(ClanMember):
war_opted_in: Optional[:class:`bool`]
Whether the player has selected that they are opted "in" (True) for wars, or opted "out" (False).
This will be ``None`` if the player is not in a clan.
equipment: List[:class:`Equipment`]
The player's unlocked hero equipment
"""

__slots__ = (
Expand Down Expand Up @@ -362,7 +362,7 @@ def _from_data(self, data: dict) -> None:
label_cls = self.label_cls
achievement_cls = self.achievement_cls
troop_loader = self._client._troop_holder.load if self._client else None
hero_loader = self._client._hero_holder.load if self._client else None
hero_loader = self._client._hero_holder.load if self._client else None
spell_loader = self._client._spell_holder.load if self._client else None
pet_loader = self._client._pet_holder.load if self._client else None
equipment_loader = self._client._equipment_holder.load if self._client else None
Expand Down Expand Up @@ -462,7 +462,7 @@ def load_game_data(self):

@cached_property("_cs_labels")
def labels(self) -> List[Label]:
"""List[:class:`Label`]: A :class:`List` of :class:`Label`s that the player has."""
"""List[:class:`Label`]: A :class:`List` of :class:`Label`\s that the player has."""
return list(self._iter_labels)

@cached_property("_cs_achievements")
Expand Down Expand Up @@ -547,7 +547,7 @@ def troops(self) -> List[Troop]:
def home_troops(self) -> List[Troop]:
"""List[:class:`Troop`]: A :class:`List` of the player's home-base :class:`Troop`.
This will return troops in the order found in both barracks and labatory in-game.
This will return troops in the order found in both barracks and laboratory in-game.
This includes:
- Elixir Troops (Barbarian, Balloon, etc.)
Expand All @@ -566,7 +566,7 @@ def home_troops(self) -> List[Troop]:
def builder_troops(self) -> List[Troop]:
"""List[:class:`Troop`]: A :class:`List` of the player's builder-base :class:`Troop`.
This will return troops in the order found in both barracks and labatory in-game.
This will return troops in the order found in both barracks and laboratory in-game.
This includes:
- Builder troops
Expand All @@ -582,7 +582,7 @@ def builder_troops(self) -> List[Troop]:
def siege_machines(self) -> List[Troop]:
"""List[:class:`Troop`]: A :class:`List` of the player's siege-machine :class:`Troop`.
This will return siege machines in the order found in both barracks and labatory in-game.
This will return siege machines in the order found in both barracks and laboratory in-game.
This includes:
- Siege machines only.
Expand Down Expand Up @@ -699,7 +699,7 @@ def get_troop(self, name: str, is_home_troop=None, default_value=None) -> Option
Returns
--------
Optional[:class:`Troop`]
The returned troop or the ``default_value`` if not found, which defaults to ``None``..
The returned troop or the ``default_value`` if not found, which defaults to ``None``.
"""
_ = self.troops

Expand All @@ -721,7 +721,7 @@ def get_troop(self, name: str, is_home_troop=None, default_value=None) -> Option
def heroes(self) -> List[Hero]:
"""List[:class:`Hero`]: A :class:`List` of the player's :class:`Hero`.
This will return heroes in the order found in the store and labatory in-game.
This will return heroes in the order found in the store and laboratory in-game.
"""
heroes_dict = {h.name: h for h in self._iter_heroes}
sorted_heroes = {}
Expand Down Expand Up @@ -763,15 +763,15 @@ def get_hero(self, name: str, default_value=None) -> Optional[Hero]:
def spells(self) -> List[Spell]:
"""List[:class:`Spell`]: A :class:`List` of the player's :class:`Spell` ordered as they appear in-game.
This will return spells in the order found in both spell factory and labatory in-game.
This will return spells in the order found in both spell factory and laboratory in-game.
"""
self._spells = {s.name: s for s in self._iter_spells}
dict_spells = self._spells
order = {k: v for v, k in enumerate(SPELL_ORDER)}

return list(sorted(
dict_spells.values(),
key=lambda s: order.get(s.name, 0)))
dict_spells.values(),
key=lambda s: order.get(s.name, 0)))

def get_spell(self, name: str, default_value=None) -> Optional[Spell]:
"""Gets the spell with the given name.
Expand All @@ -786,7 +786,7 @@ def get_spell(self, name: str, default_value=None) -> Optional[Spell]:
Returns
--------
Optional[:class:`Spell`]
The returned spell or the ``default_value`` if not found, which defaults to ``None``..
The returned spell or the ``default_value`` if not found, which defaults to ``None``.
"""
if not self._spells:
_ = self.spells
Expand Down
6 changes: 4 additions & 2 deletions docs/client/logging_in.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ Logging In
==========
A coc.py client instance can be created directly by :class:`Client`. The instance can then be logged in with the methods

.. autofunction:: coc.Client.login
.. automethod:: coc.Client.login
:noindex:

.. autofunction:: coc.Client.login_with_tokens
.. automethod:: coc.Client.login_with_tokens
:noindex:


Example
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
project = 'coc'
copyright = '2022, mathsman5133'
author = 'mathsman5133'
release = '3.5.3'
release = '3.6.0'

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
Expand Down
17 changes: 15 additions & 2 deletions docs/miscellaneous/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,24 @@ Changelog
This page keeps a fairly detailed, human readable version
of what has changed, and whats new for each version of the lib.

v3.5.3
v3.6.0
------

Bugs Fixed:
~~~~~~~~~~~
- Issues causing the documentation to not build properly have been fixed.
- Fixed a few spelling errors in the documentation.

Additions:
~~~~~~~~~~
- Added :attr:`coc.Client.ip` to manually overwrite the IP address used for generating API keys. This is especially useful
for using the API with a proxy.

v3.5.3
------

Bugs Fixed:
~~~~~~~~~~~
- :attr:`coc.Badge.url` and :attr:`coc.Icon.url` now use differently sized fallbacks if the default URL is not
available. The same is true if :func:`coc.Badge.save` or :func:`coc.Icon.save` are called without the optional
size parameter
Expand Down Expand Up @@ -158,7 +171,7 @@ Additions:
- Added optional cls parameters to all the :func:`coc.Client.get_location_...` methods
in order to allow the usage of custom classes

- Added the :ref:`triggers`
- Added the :ref:`triggers_extension` as an extension

- Added some cached properties to raid classes: :func:`coc.RaidClan.looted`,
:func:`coc.RaidLogEntry.total_defensive_loot`, :func:`coc.RaidLogEntry.defense_attack_count`
Expand Down
2 changes: 1 addition & 1 deletion docs/miscellaneous/migrating_to_v1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -520,4 +520,4 @@ For more information on custom class support, please see :ref:`custom_classes`.

Discord Links Extension
-----------------------
For more information on the new discord links extension, please see :ref:`links_extension`.
For more information on the new discord links extension, please see `links_extension`.
11 changes: 0 additions & 11 deletions docs/models/models.rst

This file was deleted.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "coc.py"
authors = [{ name = "mathsman5133" }]
maintainers = [{ name = "majordoobie" }, { name = "MagicTheDev" }, { name = "Kuchenmampfer" },
{ name = "lukasthaler"}, { name = "doluk"}]
version = "3.5.3"
version = "3.6.0"
description = "A python wrapper for the Clash of Clans API"
requires-python = ">=3.7.3"
readme = "README.rst"
Expand Down

0 comments on commit e72a313

Please sign in to comment.