cocapi 4.0.0__tar.gz → 4.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. cocapi-4.0.1/.github/workflows/docs.yml +33 -0
  2. cocapi-4.0.1/PKG-INFO +256 -0
  3. cocapi-4.0.1/README.md +217 -0
  4. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/__init__.py +1 -1
  5. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/events/_state.py +10 -0
  6. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/events/_stream.py +10 -0
  7. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/events/_watchers.py +56 -0
  8. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/schemas.py +90 -0
  9. cocapi-4.0.1/docs/changelog.md +46 -0
  10. cocapi-4.0.1/docs/examples.md +123 -0
  11. cocapi-4.0.1/docs/getting-started/authentication.md +82 -0
  12. cocapi-4.0.1/docs/getting-started/installation.md +53 -0
  13. cocapi-4.0.1/docs/getting-started/quickstart.md +76 -0
  14. cocapi-4.0.1/docs/guides/async.md +112 -0
  15. cocapi-4.0.1/docs/guides/caching.md +57 -0
  16. cocapi-4.0.1/docs/guides/cli.md +94 -0
  17. cocapi-4.0.1/docs/guides/configuration.md +88 -0
  18. cocapi-4.0.1/docs/guides/error-handling.md +86 -0
  19. cocapi-4.0.1/docs/guides/events.md +152 -0
  20. cocapi-4.0.1/docs/guides/metrics.md +54 -0
  21. cocapi-4.0.1/docs/guides/middleware.md +83 -0
  22. cocapi-4.0.1/docs/guides/pagination-batch.md +88 -0
  23. cocapi-4.0.1/docs/guides/pydantic-models.md +70 -0
  24. cocapi-4.0.1/docs/index.md +62 -0
  25. cocapi-4.0.1/mkdocs.yml +135 -0
  26. {cocapi-4.0.0 → cocapi-4.0.1}/pyproject.toml +16 -1
  27. {cocapi-4.0.0 → cocapi-4.0.1}/uv.lock +639 -2
  28. cocapi-4.0.0/PKG-INFO +0 -955
  29. cocapi-4.0.0/README.md +0 -924
  30. cocapi-4.0.0/_config.yml +0 -1
  31. {cocapi-4.0.0 → cocapi-4.0.1}/.github/FUNDING.yml +0 -0
  32. {cocapi-4.0.0 → cocapi-4.0.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  33. {cocapi-4.0.0 → cocapi-4.0.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  34. {cocapi-4.0.0 → cocapi-4.0.1}/.github/workflows/ci.yml +0 -0
  35. {cocapi-4.0.0 → cocapi-4.0.1}/.github/workflows/release.yml +0 -0
  36. {cocapi-4.0.0 → cocapi-4.0.1}/.gitignore +0 -0
  37. {cocapi-4.0.0 → cocapi-4.0.1}/.pre-commit-config.yaml +0 -0
  38. {cocapi-4.0.0 → cocapi-4.0.1}/CODE_OF_CONDUCT.md +0 -0
  39. {cocapi-4.0.0 → cocapi-4.0.1}/LICENSE +0 -0
  40. {cocapi-4.0.0 → cocapi-4.0.1}/SECURITY.md +0 -0
  41. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/api_methods.py +0 -0
  42. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/async_client.py +0 -0
  43. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/cache.py +0 -0
  44. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/cli.py +0 -0
  45. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/client.py +0 -0
  46. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/cocapi.py +0 -0
  47. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/config.py +0 -0
  48. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/events/__init__.py +0 -0
  49. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/events/_diff.py +0 -0
  50. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/events/_types.py +0 -0
  51. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/events/_war_fsm.py +0 -0
  52. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/key_manager.py +0 -0
  53. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/metrics.py +0 -0
  54. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/middleware.py +0 -0
  55. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/model_registry.py +0 -0
  56. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/models.py +0 -0
  57. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/py.typed +0 -0
  58. {cocapi-4.0.0 → cocapi-4.0.1}/cocapi/utils.py +0 -0
  59. {cocapi-4.0.0 → cocapi-4.0.1}/mypy.ini +0 -0
  60. {cocapi-4.0.0 → cocapi-4.0.1}/setup.py +0 -0
  61. {cocapi-4.0.0 → cocapi-4.0.1}/sonar-project.properties +0 -0
@@ -0,0 +1,33 @@
1
+ name: Deploy Docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: write
10
+
11
+ concurrency:
12
+ group: deploy-docs
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ deploy:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - name: Set up Python
22
+ uses: actions/setup-python@v5
23
+ with:
24
+ python-version: "3.12"
25
+
26
+ - name: Install uv
27
+ uses: astral-sh/setup-uv@v3
28
+
29
+ - name: Install dependencies
30
+ run: uv sync --dev --extra pydantic --extra docs
31
+
32
+ - name: Build and deploy docs
33
+ run: uv run mkdocs gh-deploy --force
cocapi-4.0.1/PKG-INFO ADDED
@@ -0,0 +1,256 @@
1
+ Metadata-Version: 2.4
2
+ Name: cocapi
3
+ Version: 4.0.1
4
+ Summary: Async & sync Python wrapper for the Clash of Clans API with auto key management
5
+ Project-URL: Homepage, https://github.com/tonybenoy/cocapi
6
+ Project-URL: Documentation, https://tonybenoy.github.io/cocapi/
7
+ Project-URL: Repository, https://github.com/tonybenoy/cocapi
8
+ Project-URL: Issues, https://github.com/tonybenoy/cocapi/issues
9
+ Author-email: Tony Benoy <me@tonybenoy.com>
10
+ License: GPL-3.0
11
+ License-File: LICENSE
12
+ Keywords: api-wrapper,async,clans,clash,clash-of-clans,coc,coc-api,game-api,supercell
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Framework :: AsyncIO
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Games/Entertainment :: Real Time Strategy
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.10
26
+ Requires-Dist: httpx>=0.23.0
27
+ Provides-Extra: cli
28
+ Requires-Dist: typer>=0.9.0; extra == 'cli'
29
+ Provides-Extra: docs
30
+ Requires-Dist: mkdocs-gen-files>=0.5.0; extra == 'docs'
31
+ Requires-Dist: mkdocs-literate-nav>=0.6.0; extra == 'docs'
32
+ Requires-Dist: mkdocs-material>=9.5.0; extra == 'docs'
33
+ Requires-Dist: mkdocs-section-index>=0.3.9; extra == 'docs'
34
+ Requires-Dist: mkdocs>=1.6.0; extra == 'docs'
35
+ Requires-Dist: mkdocstrings[python]>=0.27.0; extra == 'docs'
36
+ Provides-Extra: pydantic
37
+ Requires-Dist: pydantic>=2.0.0; extra == 'pydantic'
38
+ Description-Content-Type: text/markdown
39
+
40
+ <p>
41
+ <a href="https://github.com/tonybenoy/cocapi/actions">
42
+ <img src="https://github.com/tonybenoy/cocapi/workflows/CI/badge.svg" alt="CI Status" height="20">
43
+ </a>
44
+ <a href="https://pypi.org/project/cocapi/"><img src="https://img.shields.io/pypi/v/cocapi" alt="Pypi version" height="21"></a>
45
+ <a href="https://pypi.org/project/cocapi/"><img src="https://img.shields.io/pypi/dm/cocapi" alt="PyPI Downloads" height="21"></a>
46
+ <a href="https://tonybenoy.github.io/cocapi/"><img src="https://img.shields.io/badge/docs-mkdocs-blue.svg" alt="Documentation" height="21"></a>
47
+ </p>
48
+ <p>
49
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.10+-blue.svg" alt="Python version" height="17"></a>
50
+ <a href="https://github.com/tonybenoy/cocapi/blob/main/LICENSE"><img src="https://img.shields.io/github/license/tonybenoy/cocapi" alt="License" height="17"></a>
51
+ <a href="https://github.com/astral-sh/ruff">
52
+ <img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff" height="17">
53
+ </a>
54
+ </p>
55
+
56
+ # cocapi
57
+
58
+ A Python wrapper for the official [Clash of Clans API](https://developer.clashofclans.com/) with full async support, automatic key management, caching, retries, and optional Pydantic models.
59
+
60
+ **[Full Documentation](https://tonybenoy.github.io/cocapi/)**
61
+
62
+ ## Features
63
+
64
+ - **Sync & async** — same methods, same names, both modes
65
+ - **Automatic key management** — login with email/password, keys are created, reused, and rotated automatically
66
+ - **Caching** — TTL-based response caching with stats
67
+ - **Retries** — exponential backoff for rate limits and server errors
68
+ - **Pagination & batch** — `paginate()` auto-follows cursors, `batch()` fetches many resources at once
69
+ - **Event polling** — real-time monitoring of clans, wars, and players with `async for` or callbacks
70
+ - **Pydantic models** — optional typed response objects with IDE autocompletion
71
+ - **Middleware** — plug in custom request/response processing
72
+ - **CLI** — `cocapi clan "#2PP"` from the terminal
73
+ - **Maintenance detection** — automatic detection of API maintenance windows
74
+
75
+ ## Install
76
+
77
+ ```bash
78
+ pip install cocapi
79
+
80
+ # With Pydantic model support
81
+ pip install 'cocapi[pydantic]'
82
+
83
+ # With CLI
84
+ pip install 'cocapi[cli]'
85
+ ```
86
+
87
+ Requires Python 3.10+.
88
+
89
+ ## Quick Start
90
+
91
+ ### With an API Token
92
+
93
+ Get a token from [developer.clashofclans.com](https://developer.clashofclans.com/):
94
+
95
+ ```python
96
+ from cocapi import CocApi
97
+
98
+ api = CocApi("your_api_token")
99
+
100
+ clan = api.clan_tag("#2PP")
101
+ print(clan["name"])
102
+
103
+ player = api.players("#900PUCPV")
104
+ print(player["trophies"])
105
+ ```
106
+
107
+ ### With Email/Password (Automatic Key Management)
108
+
109
+ Skip manual key creation entirely. cocapi logs into the developer portal, detects your IP, and manages keys for you:
110
+
111
+ ```python
112
+ from cocapi import CocApi
113
+
114
+ api = CocApi.from_credentials("you@example.com", "your_password")
115
+ clan = api.clan_tag("#2PP")
116
+ ```
117
+
118
+ Keys are created automatically, reused when valid, and rotated when your IP changes. See the [Authentication guide](https://tonybenoy.github.io/cocapi/getting-started/authentication/) for details.
119
+
120
+ ### Async
121
+
122
+ ```python
123
+ import asyncio
124
+ from cocapi import CocApi
125
+
126
+ async def main():
127
+ async with CocApi("your_token") as api:
128
+ clan = await api.clan_tag("#2PP")
129
+ player = await api.players("#900PUCPV")
130
+ print(clan["name"], player["trophies"])
131
+
132
+ asyncio.run(main())
133
+ ```
134
+
135
+ See the [Async guide](https://tonybenoy.github.io/cocapi/guides/async/) for more patterns.
136
+
137
+ ## Configuration
138
+
139
+ All options are set through `ApiConfig`:
140
+
141
+ ```python
142
+ from cocapi import CocApi, ApiConfig
143
+
144
+ config = ApiConfig(
145
+ timeout=30,
146
+ max_retries=3,
147
+ enable_caching=True,
148
+ cache_ttl=600,
149
+ enable_rate_limiting=True, # async only
150
+ requests_per_second=10.0,
151
+ enable_metrics=True,
152
+ use_pydantic_models=True, # requires cocapi[pydantic]
153
+ )
154
+
155
+ api = CocApi("your_token", config=config)
156
+ ```
157
+
158
+ See the [Configuration guide](https://tonybenoy.github.io/cocapi/guides/configuration/) for all options.
159
+
160
+ ## Pagination & Batch
161
+
162
+ ```python
163
+ # Auto-paginate through all clan members
164
+ for member in api.paginate(api.clan_members, "#CLAN_TAG"):
165
+ print(member["name"])
166
+
167
+ # Fetch multiple players at once
168
+ results = api.batch(api.players, ["#TAG1", "#TAG2", "#TAG3"])
169
+ ```
170
+
171
+ Both work in sync and async. See the [Pagination & Batch guide](https://tonybenoy.github.io/cocapi/guides/pagination-batch/).
172
+
173
+ ## Event Polling
174
+
175
+ Monitor clans, wars, and players in real time. Async only.
176
+
177
+ ```python
178
+ from cocapi.events import EventStream, EventType
179
+
180
+ async with CocApi("token") as api:
181
+ stream = EventStream(api)
182
+ stream.watch_clans(["#2PP"], interval=60)
183
+ stream.watch_wars(["#2PP"], interval=30)
184
+ stream.watch_players(["#900PUCPV"], interval=120)
185
+
186
+ async with stream:
187
+ async for event in stream:
188
+ print(event.event_type, event.tag)
189
+ for change in event.changes:
190
+ print(f" {change.field}: {change.old_value} -> {change.new_value}")
191
+ ```
192
+
193
+ Supports 20+ event types including member join/leave, war state transitions, troop/hero upgrades, maintenance detection, and more. See the [Event Polling guide](https://tonybenoy.github.io/cocapi/guides/events/).
194
+
195
+ ## CLI
196
+
197
+ ```bash
198
+ pip install 'cocapi[cli]'
199
+
200
+ # Login once (key is persisted)
201
+ cocapi login --email you@example.com --password yourpass
202
+
203
+ # Use without --token
204
+ cocapi clan "#2PP"
205
+ cocapi player "#900PUCPV"
206
+ cocapi war "#2PP"
207
+ cocapi search "clash" --limit 5
208
+ cocapi goldpass
209
+ ```
210
+
211
+ See the [CLI guide](https://tonybenoy.github.io/cocapi/guides/cli/) for all commands.
212
+
213
+ ## Error Handling
214
+
215
+ All endpoints return a dict (or Pydantic model). Errors never raise exceptions:
216
+
217
+ ```python
218
+ result = api.clan_tag("#INVALID")
219
+ if result.get("result") == "error":
220
+ print(result["message"]) # Human-readable message
221
+ print(result["error_type"]) # "timeout", "connection", "http", "json", etc.
222
+ print(result.get("status_code"))
223
+ ```
224
+
225
+ See the [Error Handling guide](https://tonybenoy.github.io/cocapi/guides/error-handling/).
226
+
227
+ ## Examples
228
+
229
+ Runnable scripts in the [`examples/`](examples/) folder:
230
+
231
+ | Script | What it covers |
232
+ |---|---|
233
+ | [`basic_usage.py`](examples/basic_usage.py) | Sync: clan info, player info, search, error handling |
234
+ | [`async_usage.py`](examples/async_usage.py) | Async with context manager, concurrent requests |
235
+ | [`credential_auth.py`](examples/credential_auth.py) | Email/password login, key persistence, auto-refresh |
236
+ | [`pagination_batch.py`](examples/pagination_batch.py) | `paginate()` and `batch()` in sync and async |
237
+ | [`event_polling.py`](examples/event_polling.py) | `EventStream` with `async for` — real-time monitoring |
238
+ | [`event_callbacks.py`](examples/event_callbacks.py) | `@stream.on()` decorators and `stream.run()` |
239
+ | [`configuration.py`](examples/configuration.py) | ApiConfig, caching, metrics, middleware |
240
+
241
+ ## API Reference
242
+
243
+ Full API reference with all endpoints, parameters, and response schemas is available in the [documentation](https://tonybenoy.github.io/cocapi/reference/).
244
+
245
+ Key classes:
246
+ - [`CocApi`](https://tonybenoy.github.io/cocapi/reference/client/) — main entry point (sync/async)
247
+ - [`ApiConfig`](https://tonybenoy.github.io/cocapi/reference/config/) — configuration options
248
+ - [`EventStream`](https://tonybenoy.github.io/cocapi/reference/events/) — real-time event polling
249
+ - [Pydantic Schemas](https://tonybenoy.github.io/cocapi/reference/schemas/) — typed response models
250
+ - [All Endpoints](https://tonybenoy.github.io/cocapi/reference/api_methods/) — every API method
251
+
252
+ ## Credits
253
+
254
+ - [All Contributors](../../contributors)
255
+
256
+ *cocapi is not affiliated with SuperCell.*
cocapi-4.0.1/README.md ADDED
@@ -0,0 +1,217 @@
1
+ <p>
2
+ <a href="https://github.com/tonybenoy/cocapi/actions">
3
+ <img src="https://github.com/tonybenoy/cocapi/workflows/CI/badge.svg" alt="CI Status" height="20">
4
+ </a>
5
+ <a href="https://pypi.org/project/cocapi/"><img src="https://img.shields.io/pypi/v/cocapi" alt="Pypi version" height="21"></a>
6
+ <a href="https://pypi.org/project/cocapi/"><img src="https://img.shields.io/pypi/dm/cocapi" alt="PyPI Downloads" height="21"></a>
7
+ <a href="https://tonybenoy.github.io/cocapi/"><img src="https://img.shields.io/badge/docs-mkdocs-blue.svg" alt="Documentation" height="21"></a>
8
+ </p>
9
+ <p>
10
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.10+-blue.svg" alt="Python version" height="17"></a>
11
+ <a href="https://github.com/tonybenoy/cocapi/blob/main/LICENSE"><img src="https://img.shields.io/github/license/tonybenoy/cocapi" alt="License" height="17"></a>
12
+ <a href="https://github.com/astral-sh/ruff">
13
+ <img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff" height="17">
14
+ </a>
15
+ </p>
16
+
17
+ # cocapi
18
+
19
+ A Python wrapper for the official [Clash of Clans API](https://developer.clashofclans.com/) with full async support, automatic key management, caching, retries, and optional Pydantic models.
20
+
21
+ **[Full Documentation](https://tonybenoy.github.io/cocapi/)**
22
+
23
+ ## Features
24
+
25
+ - **Sync & async** — same methods, same names, both modes
26
+ - **Automatic key management** — login with email/password, keys are created, reused, and rotated automatically
27
+ - **Caching** — TTL-based response caching with stats
28
+ - **Retries** — exponential backoff for rate limits and server errors
29
+ - **Pagination & batch** — `paginate()` auto-follows cursors, `batch()` fetches many resources at once
30
+ - **Event polling** — real-time monitoring of clans, wars, and players with `async for` or callbacks
31
+ - **Pydantic models** — optional typed response objects with IDE autocompletion
32
+ - **Middleware** — plug in custom request/response processing
33
+ - **CLI** — `cocapi clan "#2PP"` from the terminal
34
+ - **Maintenance detection** — automatic detection of API maintenance windows
35
+
36
+ ## Install
37
+
38
+ ```bash
39
+ pip install cocapi
40
+
41
+ # With Pydantic model support
42
+ pip install 'cocapi[pydantic]'
43
+
44
+ # With CLI
45
+ pip install 'cocapi[cli]'
46
+ ```
47
+
48
+ Requires Python 3.10+.
49
+
50
+ ## Quick Start
51
+
52
+ ### With an API Token
53
+
54
+ Get a token from [developer.clashofclans.com](https://developer.clashofclans.com/):
55
+
56
+ ```python
57
+ from cocapi import CocApi
58
+
59
+ api = CocApi("your_api_token")
60
+
61
+ clan = api.clan_tag("#2PP")
62
+ print(clan["name"])
63
+
64
+ player = api.players("#900PUCPV")
65
+ print(player["trophies"])
66
+ ```
67
+
68
+ ### With Email/Password (Automatic Key Management)
69
+
70
+ Skip manual key creation entirely. cocapi logs into the developer portal, detects your IP, and manages keys for you:
71
+
72
+ ```python
73
+ from cocapi import CocApi
74
+
75
+ api = CocApi.from_credentials("you@example.com", "your_password")
76
+ clan = api.clan_tag("#2PP")
77
+ ```
78
+
79
+ Keys are created automatically, reused when valid, and rotated when your IP changes. See the [Authentication guide](https://tonybenoy.github.io/cocapi/getting-started/authentication/) for details.
80
+
81
+ ### Async
82
+
83
+ ```python
84
+ import asyncio
85
+ from cocapi import CocApi
86
+
87
+ async def main():
88
+ async with CocApi("your_token") as api:
89
+ clan = await api.clan_tag("#2PP")
90
+ player = await api.players("#900PUCPV")
91
+ print(clan["name"], player["trophies"])
92
+
93
+ asyncio.run(main())
94
+ ```
95
+
96
+ See the [Async guide](https://tonybenoy.github.io/cocapi/guides/async/) for more patterns.
97
+
98
+ ## Configuration
99
+
100
+ All options are set through `ApiConfig`:
101
+
102
+ ```python
103
+ from cocapi import CocApi, ApiConfig
104
+
105
+ config = ApiConfig(
106
+ timeout=30,
107
+ max_retries=3,
108
+ enable_caching=True,
109
+ cache_ttl=600,
110
+ enable_rate_limiting=True, # async only
111
+ requests_per_second=10.0,
112
+ enable_metrics=True,
113
+ use_pydantic_models=True, # requires cocapi[pydantic]
114
+ )
115
+
116
+ api = CocApi("your_token", config=config)
117
+ ```
118
+
119
+ See the [Configuration guide](https://tonybenoy.github.io/cocapi/guides/configuration/) for all options.
120
+
121
+ ## Pagination & Batch
122
+
123
+ ```python
124
+ # Auto-paginate through all clan members
125
+ for member in api.paginate(api.clan_members, "#CLAN_TAG"):
126
+ print(member["name"])
127
+
128
+ # Fetch multiple players at once
129
+ results = api.batch(api.players, ["#TAG1", "#TAG2", "#TAG3"])
130
+ ```
131
+
132
+ Both work in sync and async. See the [Pagination & Batch guide](https://tonybenoy.github.io/cocapi/guides/pagination-batch/).
133
+
134
+ ## Event Polling
135
+
136
+ Monitor clans, wars, and players in real time. Async only.
137
+
138
+ ```python
139
+ from cocapi.events import EventStream, EventType
140
+
141
+ async with CocApi("token") as api:
142
+ stream = EventStream(api)
143
+ stream.watch_clans(["#2PP"], interval=60)
144
+ stream.watch_wars(["#2PP"], interval=30)
145
+ stream.watch_players(["#900PUCPV"], interval=120)
146
+
147
+ async with stream:
148
+ async for event in stream:
149
+ print(event.event_type, event.tag)
150
+ for change in event.changes:
151
+ print(f" {change.field}: {change.old_value} -> {change.new_value}")
152
+ ```
153
+
154
+ Supports 20+ event types including member join/leave, war state transitions, troop/hero upgrades, maintenance detection, and more. See the [Event Polling guide](https://tonybenoy.github.io/cocapi/guides/events/).
155
+
156
+ ## CLI
157
+
158
+ ```bash
159
+ pip install 'cocapi[cli]'
160
+
161
+ # Login once (key is persisted)
162
+ cocapi login --email you@example.com --password yourpass
163
+
164
+ # Use without --token
165
+ cocapi clan "#2PP"
166
+ cocapi player "#900PUCPV"
167
+ cocapi war "#2PP"
168
+ cocapi search "clash" --limit 5
169
+ cocapi goldpass
170
+ ```
171
+
172
+ See the [CLI guide](https://tonybenoy.github.io/cocapi/guides/cli/) for all commands.
173
+
174
+ ## Error Handling
175
+
176
+ All endpoints return a dict (or Pydantic model). Errors never raise exceptions:
177
+
178
+ ```python
179
+ result = api.clan_tag("#INVALID")
180
+ if result.get("result") == "error":
181
+ print(result["message"]) # Human-readable message
182
+ print(result["error_type"]) # "timeout", "connection", "http", "json", etc.
183
+ print(result.get("status_code"))
184
+ ```
185
+
186
+ See the [Error Handling guide](https://tonybenoy.github.io/cocapi/guides/error-handling/).
187
+
188
+ ## Examples
189
+
190
+ Runnable scripts in the [`examples/`](examples/) folder:
191
+
192
+ | Script | What it covers |
193
+ |---|---|
194
+ | [`basic_usage.py`](examples/basic_usage.py) | Sync: clan info, player info, search, error handling |
195
+ | [`async_usage.py`](examples/async_usage.py) | Async with context manager, concurrent requests |
196
+ | [`credential_auth.py`](examples/credential_auth.py) | Email/password login, key persistence, auto-refresh |
197
+ | [`pagination_batch.py`](examples/pagination_batch.py) | `paginate()` and `batch()` in sync and async |
198
+ | [`event_polling.py`](examples/event_polling.py) | `EventStream` with `async for` — real-time monitoring |
199
+ | [`event_callbacks.py`](examples/event_callbacks.py) | `@stream.on()` decorators and `stream.run()` |
200
+ | [`configuration.py`](examples/configuration.py) | ApiConfig, caching, metrics, middleware |
201
+
202
+ ## API Reference
203
+
204
+ Full API reference with all endpoints, parameters, and response schemas is available in the [documentation](https://tonybenoy.github.io/cocapi/reference/).
205
+
206
+ Key classes:
207
+ - [`CocApi`](https://tonybenoy.github.io/cocapi/reference/client/) — main entry point (sync/async)
208
+ - [`ApiConfig`](https://tonybenoy.github.io/cocapi/reference/config/) — configuration options
209
+ - [`EventStream`](https://tonybenoy.github.io/cocapi/reference/events/) — real-time event polling
210
+ - [Pydantic Schemas](https://tonybenoy.github.io/cocapi/reference/schemas/) — typed response models
211
+ - [All Endpoints](https://tonybenoy.github.io/cocapi/reference/api_methods/) — every API method
212
+
213
+ ## Credits
214
+
215
+ - [All Contributors](../../contributors)
216
+
217
+ *cocapi is not affiliated with SuperCell.*
@@ -123,7 +123,7 @@ except (ImportError, SyntaxError):
123
123
  _SCHEMAS_AVAILABLE = False
124
124
 
125
125
  # Version info
126
- __version__ = "4.0.0"
126
+ __version__ = "4.0.1"
127
127
  __author__ = "Tony Benoy"
128
128
  __email__ = "me@tonybenoy.com"
129
129
 
@@ -36,28 +36,35 @@ class PollingState:
36
36
  # --- Clan ---
37
37
 
38
38
  def get_clan(self, tag: str) -> dict[str, Any] | None:
39
+ """Return the last-polled clan snapshot, or None if not yet polled."""
39
40
  return self._clan_snapshots.get(tag)
40
41
 
41
42
  def set_clan(self, tag: str, data: dict[str, Any]) -> None:
43
+ """Store the latest clan snapshot for diffing on the next poll."""
42
44
  self._clan_snapshots[tag] = data
43
45
 
44
46
  # --- Members ---
45
47
 
46
48
  def get_members(self, tag: str) -> list[dict[str, Any]] | None:
49
+ """Return the last-polled member list for a clan, or None."""
47
50
  return self._member_snapshots.get(tag)
48
51
 
49
52
  def set_members(self, tag: str, members: list[dict[str, Any]]) -> None:
53
+ """Store the latest member list snapshot for a clan."""
50
54
  self._member_snapshots[tag] = members
51
55
 
52
56
  # --- War ---
53
57
 
54
58
  def get_war(self, tag: str) -> dict[str, Any] | None:
59
+ """Return the last-polled war snapshot, or None if not yet polled."""
55
60
  return self._war_snapshots.get(tag)
56
61
 
57
62
  def set_war(self, tag: str, data: dict[str, Any]) -> None:
63
+ """Store the latest war snapshot for diffing on the next poll."""
58
64
  self._war_snapshots[tag] = data
59
65
 
60
66
  def get_war_fsm(self, tag: str) -> WarStateMachine:
67
+ """Return the war state machine for a clan, creating one if needed."""
61
68
  if tag not in self._war_fsms:
62
69
  self._war_fsms[tag] = WarStateMachine()
63
70
  return self._war_fsms[tag]
@@ -65,9 +72,11 @@ class PollingState:
65
72
  # --- Player ---
66
73
 
67
74
  def get_player(self, tag: str) -> dict[str, Any] | None:
75
+ """Return the last-polled player snapshot, or None if not yet polled."""
68
76
  return self._player_snapshots.get(tag)
69
77
 
70
78
  def set_player(self, tag: str, data: dict[str, Any]) -> None:
79
+ """Store the latest player snapshot for diffing on the next poll."""
71
80
  self._player_snapshots[tag] = data
72
81
 
73
82
  # --- Poll timing ---
@@ -78,6 +87,7 @@ class PollingState:
78
87
  return (time.time() - last) >= interval
79
88
 
80
89
  def mark_polled(self, resource_key: str) -> None:
90
+ """Record the current time as the last poll time for a resource."""
81
91
  self._last_poll_times[resource_key] = time.time()
82
92
 
83
93
  # --- Persistence ---
@@ -53,6 +53,16 @@ class EventStream:
53
53
  queue_size: int = 1000,
54
54
  persist_path: Path | str | None = None,
55
55
  ) -> None:
56
+ """Initialize the event stream.
57
+
58
+ Args:
59
+ api: A CocApi instance in async mode (inside ``async with``).
60
+ queue_size: Max events buffered before backpressure (0 = unlimited).
61
+ persist_path: Optional path for state persistence across restarts.
62
+
63
+ Raises:
64
+ RuntimeError: If the CocApi instance is not in async mode.
65
+ """
56
66
  if not api.async_mode:
57
67
  raise RuntimeError(
58
68
  "EventStream requires CocApi in async mode. "