Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2.0.2 update #117

Merged
merged 40 commits into from
Aug 28, 2022
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
cf0304a
[Added coc.Client.close_client coroutine] Added coroutine to allow us…
majordoobie Aug 18, 2022
162ec4f
[Fixed detailed members bug] Fixed bug where detail members was tryin…
majordoobie Aug 18, 2022
f954f07
[Added better launcher for daemon.py
majordoobie Aug 19, 2022
4c81781
add shutdown command as example
doluk Aug 19, 2022
d332da1
added support for the api changes regarding the clan capital update
doluk Aug 24, 2022
d8be7fd
keep input structure
doluk Aug 24, 2022
dcae155
exclude changes in the discord_bot example
doluk Aug 24, 2022
502b765
exclude changes in the discord_bot example 2
doluk Aug 24, 2022
825125e
return empty list for clans without clan capital
doluk Aug 24, 2022
bcd9520
[Modified .gitignore] Update gitignore
majordoobie Aug 25, 2022
fe1865c
[Merged clancapitalupdate] Merged update from doluk
majordoobie Aug 25, 2022
e3d81b7
[Migrated doloks update] Merged Doluks update and fixed the discord_b…
majordoobie Aug 25, 2022
e7426df
[Updated events_example.py] Modified the example to follow the new de…
majordoobie Aug 25, 2022
aa94f12
[Updated events_example.py] Modified the example to follow the new de…
majordoobie Aug 25, 2022
0a0800a
Merge pull request #116 from majordoobie/2.0.2_clan_castle_update
majordoobie Aug 25, 2022
188791c
[Testing github action]
majordoobie Aug 25, 2022
6e0db24
[Session bug fix] When creating the initial connection to the CoC API…
majordoobie Aug 25, 2022
0bb44a0
[Session cleanup] When login fails, the login should cleanup after it…
majordoobie Aug 25, 2022
ad519e9
[Session cleanup] When login fails, the login should cleanup after it…
majordoobie Aug 25, 2022
2a7241b
[Removed close] EventClient no longer needs to override the parent cl…
majordoobie Aug 25, 2022
44d96d8
Returned the reason message from http exception
majordoobie Aug 25, 2022
1b5efa4
[Removed tests] Removed the discord tests since it is pretty difficul…
majordoobie Aug 25, 2022
a6f22f9
[Updated discord examples] Updated the procedural example to use the …
majordoobie Aug 25, 2022
f7a43d1
[Propogated trace] I did not propogate the trace when caught for cleanup
majordoobie Aug 25, 2022
e5d9fd8
[Updated events_example.py] to use a loop runner and use the registra…
majordoobie Aug 25, 2022
5f62057
[Updated war_logs.py] Updated war_log example to use asyncio.run() wr…
majordoobie Aug 25, 2022
fb18081
[Added close method] When logging into the discord link, there was no…
majordoobie Aug 25, 2022
75d8ee3
[Updated discord_links.py] Discord links now uses asyncio.run and pro…
majordoobie Aug 25, 2022
7c52ac4
[Expand test] Expand test to ensure it works with all version of python
majordoobie Aug 25, 2022
c06436a
[Expand test] Expand test to ensure it works with all version of python
majordoobie Aug 25, 2022
0797212
[Expand test] Expand test to ensure it works with all version of python
majordoobie Aug 25, 2022
62ea45d
[Updated events_example.py] Removed the sy.exit that was causing the …
majordoobie Aug 25, 2022
b70cde9
[Updated python min level] Python versions 3.5 and 3.6 do not support…
majordoobie Aug 25, 2022
811df58
[Updated errors.py] invalid credentials message
majordoobie Aug 27, 2022
0d72f23
[Updated http.py] Move invalid credential error handling to http inst…
majordoobie Aug 27, 2022
b18485e
[Modified login.py] Since the goal is to move away from managing the …
majordoobie Aug 27, 2022
de462de
[Updated readme.md]
majordoobie Aug 27, 2022
b6cb5ad
[Finished updates]
majordoobie Aug 27, 2022
9faa1b4
[Reversed coc.login changes] Reverted making `coc.login` a coroutine
majordoobie Aug 28, 2022
563270a
[Diagnosing build action]
majordoobie Aug 28, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ jobs:
# You can use PyPy versions in python-version.
# For example, pypy2 and pypy3
matrix:
python-version: [3.8]
python-version: ["3.7.13", "3.8.13", "3.9.13", "3.10"]

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
# You can test your matrix by printing the current Python version
Expand All @@ -24,7 +24,6 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install discord.py==1.5.0
pip install -r requirements.txt
- name: Running examples as tests
env:
Expand All @@ -35,7 +34,6 @@ jobs:
LINKS_API_PASSWORD: ${{ secrets.LINKS_API_PASSWORD }}
RUNNING_TESTS: true
run: |
python -m examples.discord_bot
python -m examples.discord_links
python -m examples.events
python -m examples.events_example
python -m examples.war_logs
9 changes: 5 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Byte-compiled / optimized / DLL files
.idea/
__pycache__/
*.py[cod]
*$py.class
Expand Down Expand Up @@ -82,11 +83,11 @@ celerybeat-schedule
*.sage.py

# Environments
.env
examples/.env
majordoobie marked this conversation as resolved.
Show resolved Hide resolved
.venv
env/
examples/.env/
venv/
ENV/
examples/.env/
env.bak/
venv.bak/

Expand All @@ -107,4 +108,4 @@ venv.bak/
examples/creds.py

# vscode
.vscode/
.vscode/
19 changes: 14 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Getting Started

Installing
-----------
**Python 3.5 or higher is required**
**Python 3.7 or higher is required**

.. code:: sh

Expand All @@ -47,11 +47,14 @@ This example will get a player with a certain tag, and search for 5 clans with a

.. code:: py

import asyncio
import coc

client = coc.login('email', 'password')

async def main():
# Create a login session
client = coc.Client()
await client.login("email", "password")

player = await client.get_player("tag")
print("{0.name} has {0.trophies} trophies!".format(player))

Expand All @@ -65,8 +68,14 @@ This example will get a player with a certain tag, and search for 5 clans with a
except coc.PrivateWarLog:
print("Uh oh, they have a private war log!")

client.loop.run_until_complete(main())
client.close()
# Make sure to close the session
await client.close_client()
majordoobie marked this conversation as resolved.
Show resolved Hide resolved

if __name__ == '__main__':
try:
asyncio.run(main())
except KeyboardInterrupt:
pass

Basic Events Example
---------------------
Expand Down
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__ = "2.0.1"
__version__ = "2.0.2"

from .abc import BasePlayer, BaseClan
from .clans import RankedClan, Clan
Expand Down
6 changes: 5 additions & 1 deletion coc/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,11 @@ def get_detailed_members(self, cls: Type["Player"] = None, load_game_data: bool
if load_game_data and not isinstance(load_game_data, bool):
raise TypeError("load_game_data must be either True or False.")

return PlayerIterator(self._client, (p.tag for p in self.members), cls=cls, load_game_data=load_game_data)
return PlayerIterator(self._client,
(p.tag for p in self.members),
cls=cls,
load_game_data=load_game_data,
members=self.members_dict)


class BasePlayer:
Expand Down
38 changes: 31 additions & 7 deletions coc/clans.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@


from .players import ClanMember
from .miscmodels import try_enum, ChatLanguage, Location, Label, WarLeague
from .miscmodels import try_enum, ChatLanguage, Location, Label, WarLeague, CapitalDistrict
from .utils import get, cached_property, correct_tag
from .abc import BaseClan

Expand Down Expand Up @@ -129,6 +129,9 @@ class Clan(BaseClan):
member_cls: :class:`coc.ClanMember`
The type which the members found in :attr:`Clan.members` will be of.
Ensure any overriding of this inherits from :class:`coc.ClanMember`.
capital_district_cls: :class:`coc.CapitalDistrict`
The type which the clan capital districts found in :attr:`Clan.capital_districts` will be of.
Ensure any overriding of this inherits from :class:`coc.CapitalDistrict`.
war_league: :class:`coc.WarLeague`
The clan's CWL league.
"""
Expand All @@ -149,24 +152,28 @@ class Clan(BaseClan):
"member_count",
"_labels",
"_members",
"_districts",
"_client",
"label_cls",
"member_cls",
"capital_district_cls",
"war_league",
"chat_language",

"_cs_labels",
"_cs_members",
"_cs_members_dict",
"_cs_capital_districts",
"_iter_labels",
"_iter_members",
"_iter_capital_districts"
)

def __init__(self, *, data, client, **_):
super().__init__(data=data, client=client)
self.label_cls = Label
self.member_cls = ClanMember

self._members = None # type: typing.Optional[typing.Dict[str, ClanMember]]
self.capital_district_cls = CapitalDistrict

self._from_data(data)

Expand Down Expand Up @@ -200,6 +207,13 @@ def _from_data(self, data: dict) -> None:
member_cls(data=mdata, client=self._client, clan=self) for mdata in data_get("memberList", [])
)

capital_district_cls = self.capital_district_cls
if data_get("clanCapital"):
self._iter_capital_districts = (capital_district_cls(data=cddata, client=self._client) for cddata in
data_get("clanCapital")["districts"])
else:
self._iter_capital_districts = ()

@cached_property("_cs_labels")
def labels(self) -> typing.List[Label]:
"""List[:class:`Label`]: A :class:`List` of :class:`Label` that the clan has."""
Expand All @@ -208,8 +222,18 @@ def labels(self) -> typing.List[Label]:
@cached_property("_cs_members")
def members(self) -> typing.List[ClanMember]:
"""List[:class:`ClanMember`]: A list of members that belong to the clan."""
dict_members = self._members = {m.tag: m for m in self._iter_members}
return list(dict_members.values())
return list(self.members_dict.values())

@cached_property("_cs_members_dict")
def members_dict(self) -> typing.Dict[str, ClanMember]:
"""Dict[str, :class:`ClanMember`]: A dict of members that belong to the clan."""
return {m.tag: m for m in self._iter_members}


@cached_property("_cs_capital_districts")
def capital_districts(self) -> typing.List[CapitalDistrict]:
"""List[:class:`CapitalDistrict`]: A :class:`List` of :class:`CapitalDistrict` that the clan has."""
return list(self._iter_capital_districts)

def get_member(self, tag: str) -> typing.Optional[ClanMember]:
"""Return a :class:`ClanMember` with the tag provided. Returns ``None`` if not found.
Expand All @@ -226,11 +250,11 @@ def get_member(self, tag: str) -> typing.Optional[ClanMember]:
The member who matches the tag provided: Optional[:class:`ClanMember`]
"""
tag = correct_tag(tag)
if not self._members:
if not self.members_dict:
_ = self.members

try:
return self._members[tag]
return self.members_dict[tag]
except KeyError:
return None

Expand Down
22 changes: 12 additions & 10 deletions coc/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

import asyncio
import logging

Expand Down Expand Up @@ -257,9 +256,15 @@ async def login(self, email: str, password: str) -> None:
"""
self.http = http = self._create_client(email, password)
await http.create_session(self.connector, self.timeout)
await http.initialise_keys()
self._create_holders()

try:
await http.initialise_keys()
except Exception as error:
majordoobie marked this conversation as resolved.
Show resolved Hide resolved
LOG.error("Invalid credentials\n%s", error)
await http.close()
raise
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this re-raise the InvalidCredentials error? For people who don't have logging setup a detailed exception is important because it's ambiguous why it failed otherwise

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it re-raises coc.InvalidCredentials so the user should already be tracking. You can see it in action in the examples


self._create_holders()
LOG.debug("HTTP connection created. Client is ready for use.")

def login_with_keys(self, *keys: str) -> None:
Expand All @@ -278,13 +283,10 @@ def login_with_keys(self, *keys: str) -> None:

LOG.debug("HTTP connection created. Client is ready for use.")

def close(self) -> None:
"""Closes the HTTP connection
"""
LOG.info("Clash of Clans client logging out...")
self.dispatch("on_client_close")
self.loop.run_until_complete(self.http.close())
self.loop.close()
async def close(self) -> None:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idk if anyone used it but should this still dispatch that on_client_close event?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried looking at the dispatch stuff but I don't really understand it. Could you provide some guidance on it?

"""Closes the HTTP connection from within a loop function such as
async def main()"""
await self.http.close()

def dispatch(self, event_name: str, *args, **kwargs) -> None:
"""Dispatches an event listener matching the `event_name` parameter."""
Expand Down
12 changes: 6 additions & 6 deletions coc/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -719,12 +719,12 @@ def run_forever(self):
except KeyboardInterrupt:
self.close()

def close(self):
"""Closes the client and all running tasks."""
tasks = {t for t in asyncio.Task.all_tasks(loop=self.loop) if not t.done()}
for task in tasks:
task.cancel()
super().close()
# async def close(self):
majordoobie marked this conversation as resolved.
Show resolved Hide resolved
# """Closes the client and all running tasks."""
# tasks = {t for t in asyncio.all_tasks(loop=self.loop) if not t.done()}
# for task in tasks:
# task.cancel()
# await super().close()

def dispatch(self, event_name: str, *args, **kwargs):
# pylint: disable=broad-except
Expand Down
1 change: 0 additions & 1 deletion coc/events.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,3 @@ class EventsClient(Client):
def add_events(self, *events: Callable) -> None: ...
def remove_events(self, *events: Callable) -> None: ...
def run_forever(self) -> None: ...
def close(self) -> None: ...
4 changes: 4 additions & 0 deletions coc/ext/discordlinks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ def __init__(self, username: str, password: str, loop: asyncio.AbstractEventLoop

self.http_session = aiohttp.ClientSession(loop=self.loop)

async def close(self):
"""Close the client session established"""
await self.http_session.close()

async def _request(self, method, url, *, token_request: bool = False, **kwargs):
url = self.BASE_URL + url

Expand Down
Loading