events
events
¶
cocapi.events -- Event polling system for real-time Clash of Clans monitoring.
Async-only. Requires CocApi in async mode.
Usage::
from cocapi import CocApi, ApiConfig
from cocapi.events import EventStream, EventType
async with CocApi("token", config=ApiConfig(enable_caching=False)) as api:
stream = EventStream(api)
stream.watch_clans(["#ABC"], interval=60)
stream.watch_wars(["#ABC"], interval=30)
async with stream:
async for event in stream:
print(event.event_type, event.tag, event.changes)
PollingState
¶
PollingState()
In-memory state for polled resources with optional JSON persistence.
Source code in cocapi/events/_state.py
28 29 30 31 32 33 34 | |
get_clan
¶
get_clan(tag: str) -> dict[str, Any] | None
Return the last-polled clan snapshot, or None if not yet polled.
Source code in cocapi/events/_state.py
38 39 40 | |
set_clan
¶
set_clan(tag: str, data: dict[str, Any]) -> None
Store the latest clan snapshot for diffing on the next poll.
Source code in cocapi/events/_state.py
42 43 44 | |
get_members
¶
get_members(tag: str) -> list[dict[str, Any]] | None
Return the last-polled member list for a clan, or None.
Source code in cocapi/events/_state.py
48 49 50 | |
set_members
¶
set_members(
tag: str, members: list[dict[str, Any]]
) -> None
Store the latest member list snapshot for a clan.
Source code in cocapi/events/_state.py
52 53 54 | |
get_war
¶
get_war(tag: str) -> dict[str, Any] | None
Return the last-polled war snapshot, or None if not yet polled.
Source code in cocapi/events/_state.py
58 59 60 | |
set_war
¶
set_war(tag: str, data: dict[str, Any]) -> None
Store the latest war snapshot for diffing on the next poll.
Source code in cocapi/events/_state.py
62 63 64 | |
get_war_fsm
¶
get_war_fsm(tag: str) -> WarStateMachine
Return the war state machine for a clan, creating one if needed.
Source code in cocapi/events/_state.py
66 67 68 69 70 | |
get_player
¶
get_player(tag: str) -> dict[str, Any] | None
Return the last-polled player snapshot, or None if not yet polled.
Source code in cocapi/events/_state.py
74 75 76 | |
set_player
¶
set_player(tag: str, data: dict[str, Any]) -> None
Store the latest player snapshot for diffing on the next poll.
Source code in cocapi/events/_state.py
78 79 80 | |
should_poll
¶
should_poll(resource_key: str, interval: float) -> bool
Check if enough time has passed since the last poll.
Source code in cocapi/events/_state.py
84 85 86 87 | |
mark_polled
¶
mark_polled(resource_key: str) -> None
Record the current time as the last poll time for a resource.
Source code in cocapi/events/_state.py
89 90 91 | |
save
¶
save(path: Path) -> None
Persist state to JSON file for restart recovery.
Source code in cocapi/events/_state.py
95 96 97 98 99 100 101 102 103 104 105 106 107 108 | |
load
¶
load(path: Path) -> bool
Load state from JSON file. Returns True if loaded successfully.
Source code in cocapi/events/_state.py
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | |
EventStream
¶
EventStream(
api: CocApi,
queue_size: int = 1000,
persist_path: Path | str | None = None,
)
Real-time event stream for Clash of Clans resources.
Usage as async generator::
async with CocApi("token") as api:
stream = EventStream(api)
stream.watch_clans(["#ABC"], interval=60)
async with stream:
async for event in stream:
print(event.event_type, event.changes)
Usage with callbacks::
@stream.on(EventType.MEMBER_JOINED)
async def on_join(event):
print(f"{event.metadata['member_name']} joined!")
await stream.run()
| PARAMETER | DESCRIPTION |
|---|---|
api
|
A CocApi instance in async mode (inside
TYPE:
|
queue_size
|
Max events buffered before backpressure (0 = unlimited).
TYPE:
|
persist_path
|
Optional path for state persistence across restarts.
TYPE:
|
Initialize the event stream.
| PARAMETER | DESCRIPTION |
|---|---|
api
|
A CocApi instance in async mode (inside
TYPE:
|
queue_size
|
Max events buffered before backpressure (0 = unlimited).
TYPE:
|
persist_path
|
Optional path for state persistence across restarts.
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
RuntimeError
|
If the CocApi instance is not in async mode. |
Source code in cocapi/events/_stream.py
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | |
watch_clans
¶
watch_clans(
tags: list[str],
interval: float = 60.0,
track_members: bool = True,
) -> EventStream
Register clans for polling.
| PARAMETER | DESCRIPTION |
|---|---|
tags
|
Clan tags to watch.
TYPE:
|
interval
|
Seconds between polls (default 60).
TYPE:
|
track_members
|
Track member joins/leaves/updates (default True).
TYPE:
|
Source code in cocapi/events/_stream.py
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | |
watch_wars
¶
watch_wars(
tags: list[str], interval: float = 30.0
) -> EventStream
Register clans for war state polling.
| PARAMETER | DESCRIPTION |
|---|---|
tags
|
Clan tags to watch for war updates.
TYPE:
|
interval
|
Seconds between polls (default 30).
TYPE:
|
Source code in cocapi/events/_stream.py
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | |
watch_players
¶
watch_players(
tags: list[str],
interval: float = 120.0,
include_fields: frozenset[str] | None = None,
exclude_fields: frozenset[str] | None = None,
) -> EventStream
Register players for polling.
| PARAMETER | DESCRIPTION |
|---|---|
tags
|
Player tags to watch.
TYPE:
|
interval
|
Seconds between polls (default 120).
TYPE:
|
include_fields
|
Only report changes to these fields.
TYPE:
|
exclude_fields
|
Ignore changes to these fields.
TYPE:
|
Source code in cocapi/events/_stream.py
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | |
watch_maintenance
¶
watch_maintenance(
interval: float = 30.0, probe_tag: str = "#JY9J2Y99"
) -> EventStream
Enable API maintenance detection.
Polls a known player endpoint to detect 503 maintenance responses.
Emits MAINTENANCE_START and MAINTENANCE_END events.
| PARAMETER | DESCRIPTION |
|---|---|
interval
|
Seconds between probes (default 30).
TYPE:
|
probe_tag
|
Player tag to probe (default
TYPE:
|
Source code in cocapi/events/_stream.py
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | |
on
¶
on(
event_type: EventType | None = None,
) -> Callable[[EventCallback], EventCallback]
Decorator to register an event callback.
| PARAMETER | DESCRIPTION |
|---|---|
event_type
|
Filter for specific event type. None matches all.
TYPE:
|
Source code in cocapi/events/_stream.py
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | |
add_callback
¶
add_callback(
callback: EventCallback,
event_type: EventType | None = None,
) -> None
Register an event callback programmatically.
Source code in cocapi/events/_stream.py
200 201 202 203 204 205 206 | |
start
async
¶
start() -> None
Start all registered watchers.
Source code in cocapi/events/_stream.py
219 220 221 222 223 | |
stop
async
¶
stop() -> None
Stop all watchers and persist state if configured.
Source code in cocapi/events/_stream.py
225 226 227 228 229 230 231 | |
run
async
¶
run() -> None
Run the stream with callback dispatch until stopped.
Call stream.stop() from a callback or signal handler to exit.
Source code in cocapi/events/_stream.py
233 234 235 236 237 238 239 240 | |
Change
dataclass
¶
Change(field: str, old_value: Any, new_value: Any)
A single field-level change between two snapshots.
Event
dataclass
¶
Event(
event_type: EventType,
tag: str,
timestamp: float = time(),
old_data: dict[str, Any] | None = None,
new_data: dict[str, Any] | None = None,
changes: tuple[Change, ...] = (),
metadata: dict[str, Any] = dict(),
)
An event produced by the polling system.
| ATTRIBUTE | DESCRIPTION |
|---|---|
event_type |
The type of event.
TYPE:
|
tag |
The clan or player tag this event relates to.
TYPE:
|
timestamp |
Unix timestamp when the event was created.
TYPE:
|
old_data |
Full previous snapshot (None on first poll).
TYPE:
|
new_data |
Full current snapshot (None for MEMBER_LEFT).
TYPE:
|
changes |
Tuple of field-level changes detected.
TYPE:
|
metadata |
Extra context (member_tag, war_state_from, etc.).
TYPE:
|
EventType
¶
Bases: Enum
All event types produced by the polling system.
WarState
¶
Bases: Enum
War state machine states matching the CoC API.
WarStateMachine
¶
WarStateMachine(initial_state: WarState = NOT_IN_WAR)
Tracks war state transitions for a single clan.
The CoC API returns war state as a string in clan_current_war().
This FSM detects when the state actually changes (vs. the same state
being reported on consecutive polls).
Source code in cocapi/events/_war_fsm.py
16 17 | |
transition
¶
transition(raw_state: str) -> WarState | None
Attempt a state transition.
| PARAMETER | DESCRIPTION |
|---|---|
raw_state
|
The
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
WarState | None
|
The new WarState if a transition occurred, or None if the state |
WarState | None
|
is unchanged or the raw_state is unrecognized. |
Source code in cocapi/events/_war_fsm.py
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | |
BaseWatcher
¶
BaseWatcher(
api: CocApi,
state: PollingState,
queue: Queue[Event],
interval: float,
)
Base class for all watchers.
Subclasses implement _poll_once() which fetches data, diffs it,
and returns a list of events.
Initialize the watcher.
| PARAMETER | DESCRIPTION |
|---|---|
api
|
CocApi instance in async mode.
TYPE:
|
state
|
Shared polling state for snapshot storage.
TYPE:
|
queue
|
Event queue to push detected changes to.
TYPE:
|
interval
|
Seconds between poll cycles.
TYPE:
|
Source code in cocapi/events/_watchers.py
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | |
start
¶
start() -> None
Start the polling loop as an asyncio task.
Source code in cocapi/events/_watchers.py
80 81 82 83 84 85 | |
stop
async
¶
stop() -> None
Stop the polling loop and cancel the background task.
Source code in cocapi/events/_watchers.py
87 88 89 90 91 92 93 94 95 96 | |
ClanWatcher
¶
ClanWatcher(
api: CocApi,
state: PollingState,
queue: Queue[Event],
clan_tags: list[str],
interval: float = 60.0,
track_members: bool = True,
)
Bases: BaseWatcher
Polls clan data and member lists.
Initialize the clan watcher.
| PARAMETER | DESCRIPTION |
|---|---|
api
|
CocApi instance in async mode.
TYPE:
|
state
|
Shared polling state.
TYPE:
|
queue
|
Event queue for detected changes.
TYPE:
|
clan_tags
|
Clan tags to poll.
TYPE:
|
interval
|
Seconds between polls (default 60).
TYPE:
|
track_members
|
Whether to track member join/leave/update events.
TYPE:
|
Source code in cocapi/events/_watchers.py
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | |
MaintenanceWatcher
¶
MaintenanceWatcher(
api: CocApi,
state: PollingState,
queue: Queue[Event],
interval: float = 30.0,
probe_tag: str = "#JY9J2Y99",
)
Bases: BaseWatcher
Detects API maintenance windows by polling a known endpoint.
Emits MAINTENANCE_START when the API starts returning errors
(HTTP 503 or connection failures) and MAINTENANCE_END when it
recovers. Uses a configurable probe tag (defaults to a well-known
player #JY9J2Y99).
Initialize the maintenance watcher.
| PARAMETER | DESCRIPTION |
|---|---|
api
|
CocApi instance in async mode.
TYPE:
|
state
|
Shared polling state.
TYPE:
|
queue
|
Event queue for detected changes.
TYPE:
|
interval
|
Seconds between probes (default 30).
TYPE:
|
probe_tag
|
Player tag to probe for availability.
TYPE:
|
Source code in cocapi/events/_watchers.py
577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 | |
PlayerWatcher
¶
PlayerWatcher(
api: CocApi,
state: PollingState,
queue: Queue[Event],
player_tags: list[str],
interval: float = 120.0,
include_fields: frozenset[str] | None = None,
exclude_fields: frozenset[str] | None = None,
)
Bases: BaseWatcher
Polls player data and detects field changes.
Emits granular events for upgrades (troops, spells, heroes, equipment,
townhall, builder hall) as well as the generic PLAYER_UPDATED event
for any other top-level field change.
Initialize the player watcher.
| PARAMETER | DESCRIPTION |
|---|---|
api
|
CocApi instance in async mode.
TYPE:
|
state
|
Shared polling state.
TYPE:
|
queue
|
Event queue for detected changes.
TYPE:
|
player_tags
|
Player tags to poll.
TYPE:
|
interval
|
Seconds between polls (default 120).
TYPE:
|
include_fields
|
Only report changes to these fields.
TYPE:
|
exclude_fields
|
Ignore changes to these fields.
TYPE:
|
Source code in cocapi/events/_watchers.py
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 | |
WarWatcher
¶
WarWatcher(
api: CocApi,
state: PollingState,
queue: Queue[Event],
clan_tags: list[str],
interval: float = 30.0,
)
Bases: BaseWatcher
Polls current war and tracks state transitions and new attacks.
Initialize the war watcher.
| PARAMETER | DESCRIPTION |
|---|---|
api
|
CocApi instance in async mode.
TYPE:
|
state
|
Shared polling state.
TYPE:
|
queue
|
Event queue for detected changes.
TYPE:
|
clan_tags
|
Clan tags to watch for war updates.
TYPE:
|
interval
|
Seconds between polls (default 30).
TYPE:
|
Source code in cocapi/events/_watchers.py
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 | |
diff_dicts
¶
diff_dicts(
old: dict[str, Any],
new: dict[str, Any],
*,
include_fields: frozenset[str] | None = None,
exclude_fields: frozenset[str] | None = None,
) -> list[Change]
Shallow diff of two dicts, returning a list of Change objects.
Compares top-level keys only. Nested dicts/lists are compared by equality.
| PARAMETER | DESCRIPTION |
|---|---|
old
|
Previous snapshot.
TYPE:
|
new
|
Current snapshot.
TYPE:
|
include_fields
|
If set, only diff these fields.
TYPE:
|
exclude_fields
|
If set, skip these fields.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[Change]
|
List of Change objects for fields that differ. |
Source code in cocapi/events/_diff.py
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | |
diff_member_tags
¶
diff_member_tags(
old_members: list[dict[str, Any]],
new_members: list[dict[str, Any]],
) -> tuple[
list[dict[str, Any]],
list[dict[str, Any]],
list[tuple[dict[str, Any], dict[str, Any]]],
]
Compare member lists by tag to detect joins, leaves, and updates.
| PARAMETER | DESCRIPTION |
|---|---|
old_members
|
Previous member list.
TYPE:
|
new_members
|
Current member list.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[dict[str, Any]]
|
Tuple of (joined, left, updated) where: |
list[dict[str, Any]]
|
|
list[tuple[dict[str, Any], dict[str, Any]]]
|
|
tuple[list[dict[str, Any]], list[dict[str, Any]], list[tuple[dict[str, Any], dict[str, Any]]]]
|
|
Source code in cocapi/events/_diff.py
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | |
diff_named_list
¶
diff_named_list(
old_items: list[dict[str, Any]],
new_items: list[dict[str, Any]],
key: str = "name",
compare_field: str = "level",
) -> list[tuple[str, Any, Any]]
Diff two lists of dicts keyed by key, comparing compare_field.
Used for troops, spells, heroes, and equipment which all share a
{"name": str, "level": int} structure.
| PARAMETER | DESCRIPTION |
|---|---|
old_items
|
Previous list snapshot.
TYPE:
|
new_items
|
Current list snapshot.
TYPE:
|
key
|
Dict key used to identify items (default
TYPE:
|
compare_field
|
Dict key whose value is compared (default
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[tuple[str, Any, Any]]
|
List of |
list[tuple[str, Any, Any]]
|
compare_field changed. New items appear as |
Source code in cocapi/events/_diff.py
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | |