empire-core 0.7.3__py3-none-any.whl
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.
- empire_core/__init__.py +36 -0
- empire_core/_archive/actions.py +511 -0
- empire_core/_archive/automation/__init__.py +24 -0
- empire_core/_archive/automation/alliance_tools.py +266 -0
- empire_core/_archive/automation/battle_reports.py +196 -0
- empire_core/_archive/automation/building_queue.py +242 -0
- empire_core/_archive/automation/defense_manager.py +124 -0
- empire_core/_archive/automation/map_scanner.py +370 -0
- empire_core/_archive/automation/multi_account.py +296 -0
- empire_core/_archive/automation/quest_automation.py +94 -0
- empire_core/_archive/automation/resource_manager.py +380 -0
- empire_core/_archive/automation/target_finder.py +153 -0
- empire_core/_archive/automation/tasks.py +224 -0
- empire_core/_archive/automation/unit_production.py +719 -0
- empire_core/_archive/cli.py +68 -0
- empire_core/_archive/client_async.py +469 -0
- empire_core/_archive/commands.py +201 -0
- empire_core/_archive/connection_async.py +228 -0
- empire_core/_archive/defense.py +156 -0
- empire_core/_archive/events/__init__.py +35 -0
- empire_core/_archive/events/base.py +153 -0
- empire_core/_archive/events/manager.py +85 -0
- empire_core/accounts.py +190 -0
- empire_core/client/__init__.py +0 -0
- empire_core/client/client.py +459 -0
- empire_core/config.py +87 -0
- empire_core/exceptions.py +42 -0
- empire_core/network/__init__.py +0 -0
- empire_core/network/connection.py +378 -0
- empire_core/protocol/__init__.py +0 -0
- empire_core/protocol/models/__init__.py +339 -0
- empire_core/protocol/models/alliance.py +186 -0
- empire_core/protocol/models/army.py +444 -0
- empire_core/protocol/models/attack.py +229 -0
- empire_core/protocol/models/auth.py +216 -0
- empire_core/protocol/models/base.py +403 -0
- empire_core/protocol/models/building.py +455 -0
- empire_core/protocol/models/castle.py +317 -0
- empire_core/protocol/models/chat.py +150 -0
- empire_core/protocol/models/defense.py +300 -0
- empire_core/protocol/models/map.py +269 -0
- empire_core/protocol/packet.py +104 -0
- empire_core/services/__init__.py +31 -0
- empire_core/services/alliance.py +222 -0
- empire_core/services/base.py +107 -0
- empire_core/services/castle.py +221 -0
- empire_core/state/__init__.py +0 -0
- empire_core/state/manager.py +398 -0
- empire_core/state/models.py +215 -0
- empire_core/state/quest_models.py +60 -0
- empire_core/state/report_models.py +115 -0
- empire_core/state/unit_models.py +75 -0
- empire_core/state/world_models.py +269 -0
- empire_core/storage/__init__.py +1 -0
- empire_core/storage/database.py +237 -0
- empire_core/utils/__init__.py +0 -0
- empire_core/utils/battle_sim.py +172 -0
- empire_core/utils/calculations.py +170 -0
- empire_core/utils/crypto.py +8 -0
- empire_core/utils/decorators.py +69 -0
- empire_core/utils/enums.py +111 -0
- empire_core/utils/helpers.py +252 -0
- empire_core/utils/response_awaiter.py +153 -0
- empire_core/utils/troops.py +93 -0
- empire_core-0.7.3.dist-info/METADATA +197 -0
- empire_core-0.7.3.dist-info/RECORD +67 -0
- empire_core-0.7.3.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Asyncio Task Loop Helper
|
|
3
|
+
Inspired by discord.ext.tasks.
|
|
4
|
+
|
|
5
|
+
This module provides a simple way to run background tasks at a specific interval.
|
|
6
|
+
It is designed to be used as a decorator.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
from empire_core.automation import tasks
|
|
10
|
+
|
|
11
|
+
@tasks.loop(seconds=10)
|
|
12
|
+
async def my_background_task():
|
|
13
|
+
print("Doing something...")
|
|
14
|
+
|
|
15
|
+
my_background_task.start()
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import asyncio
|
|
19
|
+
import datetime
|
|
20
|
+
import inspect
|
|
21
|
+
import logging
|
|
22
|
+
import traceback
|
|
23
|
+
from typing import Any, Awaitable, Callable, Optional
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Loop:
|
|
29
|
+
"""
|
|
30
|
+
A background task loop.
|
|
31
|
+
Created via the @tasks.loop decorator.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
coro: Callable[..., Awaitable[Any]],
|
|
37
|
+
seconds: float,
|
|
38
|
+
minutes: float,
|
|
39
|
+
hours: float,
|
|
40
|
+
count: Optional[int],
|
|
41
|
+
reconnect: bool,
|
|
42
|
+
loop: Optional[asyncio.AbstractEventLoop],
|
|
43
|
+
):
|
|
44
|
+
self.coro = coro
|
|
45
|
+
self.seconds = seconds
|
|
46
|
+
self.minutes = minutes
|
|
47
|
+
self.hours = hours
|
|
48
|
+
self.count = count
|
|
49
|
+
self.reconnect = reconnect
|
|
50
|
+
self._loop = loop or asyncio.get_event_loop()
|
|
51
|
+
|
|
52
|
+
self._task: Optional[asyncio.Task] = None
|
|
53
|
+
self._injected_args: tuple = ()
|
|
54
|
+
self._injected_kwargs: dict = {}
|
|
55
|
+
self._stop_next_iteration = False
|
|
56
|
+
self._current_loop = 0
|
|
57
|
+
self._next_iteration: Optional[datetime.datetime] = None
|
|
58
|
+
self._last_iteration: Optional[datetime.datetime] = None
|
|
59
|
+
self._before_loop: Optional[Callable[..., Awaitable[Any]]] = None
|
|
60
|
+
self._after_loop: Optional[Callable[..., Awaitable[Any]]] = None
|
|
61
|
+
self._error_handler: Optional[Callable[[Exception], Awaitable[Any]]] = None
|
|
62
|
+
|
|
63
|
+
# Calculate interval in seconds
|
|
64
|
+
self._interval = seconds + (minutes * 60.0) + (hours * 3600.0)
|
|
65
|
+
|
|
66
|
+
if self._interval < 0:
|
|
67
|
+
raise ValueError("Interval cannot be negative")
|
|
68
|
+
|
|
69
|
+
async def _runner(self, *args, **kwargs):
|
|
70
|
+
"""The main loop runner."""
|
|
71
|
+
try:
|
|
72
|
+
if self._before_loop:
|
|
73
|
+
await self._before_loop(*args, **kwargs)
|
|
74
|
+
except Exception as e:
|
|
75
|
+
logger.error(f"Exception in before_loop for {self.coro.__name__}: {e}")
|
|
76
|
+
if self._error_handler:
|
|
77
|
+
await self._error_handler(e)
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
self._current_loop = 0
|
|
81
|
+
self._stop_next_iteration = False
|
|
82
|
+
|
|
83
|
+
# Main execution loop
|
|
84
|
+
while True:
|
|
85
|
+
if self._stop_next_iteration:
|
|
86
|
+
break
|
|
87
|
+
|
|
88
|
+
if self.count is not None and self._current_loop >= self.count:
|
|
89
|
+
break
|
|
90
|
+
|
|
91
|
+
self._last_iteration = datetime.datetime.now(datetime.timezone.utc)
|
|
92
|
+
self._next_iteration = self._last_iteration + datetime.timedelta(seconds=self._interval)
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
await self.coro(*args, **kwargs)
|
|
96
|
+
except asyncio.CancelledError:
|
|
97
|
+
self._stop_next_iteration = True
|
|
98
|
+
raise
|
|
99
|
+
except Exception as e:
|
|
100
|
+
await self._handle_error(e)
|
|
101
|
+
if not self.reconnect:
|
|
102
|
+
self._stop_next_iteration = True
|
|
103
|
+
|
|
104
|
+
self._current_loop += 1
|
|
105
|
+
|
|
106
|
+
if self._stop_next_iteration:
|
|
107
|
+
break
|
|
108
|
+
|
|
109
|
+
# Sleep until next iteration
|
|
110
|
+
now = datetime.datetime.now(datetime.timezone.utc)
|
|
111
|
+
if now < self._next_iteration:
|
|
112
|
+
sleep_seconds = (self._next_iteration - now).total_seconds()
|
|
113
|
+
await asyncio.sleep(sleep_seconds)
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
if self._after_loop:
|
|
117
|
+
await self._after_loop(*args, **kwargs)
|
|
118
|
+
except Exception as e:
|
|
119
|
+
logger.error(f"Exception in after_loop for {self.coro.__name__}: {e}")
|
|
120
|
+
await self._handle_error(e)
|
|
121
|
+
|
|
122
|
+
async def _handle_error(self, exception: Exception):
|
|
123
|
+
"""Internal error handling dispatch."""
|
|
124
|
+
if self._error_handler:
|
|
125
|
+
try:
|
|
126
|
+
await self._error_handler(exception)
|
|
127
|
+
except Exception as e:
|
|
128
|
+
logger.error(f"Error in error handler for {self.coro.__name__}: {e}")
|
|
129
|
+
else:
|
|
130
|
+
logger.error(f"Unhandled exception in task {self.coro.__name__}: {exception}")
|
|
131
|
+
traceback.print_exc()
|
|
132
|
+
|
|
133
|
+
def start(self, *args, **kwargs):
|
|
134
|
+
"""
|
|
135
|
+
Starts the background task.
|
|
136
|
+
arguments passed here are passed to the coroutine.
|
|
137
|
+
"""
|
|
138
|
+
if self._task is not None and not self._task.done():
|
|
139
|
+
raise RuntimeError("Task is already running")
|
|
140
|
+
|
|
141
|
+
self._injected_args = args
|
|
142
|
+
self._injected_kwargs = kwargs
|
|
143
|
+
self._task = self._loop.create_task(self._runner(*args, **kwargs))
|
|
144
|
+
return self._task
|
|
145
|
+
|
|
146
|
+
def stop(self):
|
|
147
|
+
"""Stops the background task cleanly after the current iteration."""
|
|
148
|
+
self._stop_next_iteration = True
|
|
149
|
+
|
|
150
|
+
def cancel(self):
|
|
151
|
+
"""Cancels the background task immediately."""
|
|
152
|
+
if self._task:
|
|
153
|
+
self._task.cancel()
|
|
154
|
+
self._stop_next_iteration = True
|
|
155
|
+
|
|
156
|
+
def restart(self, *args, **kwargs):
|
|
157
|
+
"""Restarts the task."""
|
|
158
|
+
self.cancel()
|
|
159
|
+
self.start(*args, **kwargs)
|
|
160
|
+
|
|
161
|
+
def before_loop(self, coro):
|
|
162
|
+
"""Decorator to register a coroutine to run before the loop starts."""
|
|
163
|
+
if not inspect.iscoroutinefunction(coro):
|
|
164
|
+
raise TypeError("Expected coroutine function")
|
|
165
|
+
self._before_loop = coro
|
|
166
|
+
return coro
|
|
167
|
+
|
|
168
|
+
def after_loop(self, coro):
|
|
169
|
+
"""Decorator to register a coroutine to run after the loop finishes."""
|
|
170
|
+
if not inspect.iscoroutinefunction(coro):
|
|
171
|
+
raise TypeError("Expected coroutine function")
|
|
172
|
+
self._after_loop = coro
|
|
173
|
+
return coro
|
|
174
|
+
|
|
175
|
+
def error(self, coro):
|
|
176
|
+
"""Decorator to register a coroutine to handle errors in the loop."""
|
|
177
|
+
if not inspect.iscoroutinefunction(coro):
|
|
178
|
+
raise TypeError("Expected coroutine function")
|
|
179
|
+
self._error_handler = coro
|
|
180
|
+
return coro
|
|
181
|
+
|
|
182
|
+
@property
|
|
183
|
+
def is_running(self) -> bool:
|
|
184
|
+
"""Check if the task is currently running."""
|
|
185
|
+
return self._task is not None and not self._task.done()
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def current_loop(self) -> int:
|
|
189
|
+
"""Get the current iteration count."""
|
|
190
|
+
return self._current_loop
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def loop(
|
|
194
|
+
seconds: float = 0,
|
|
195
|
+
minutes: float = 0,
|
|
196
|
+
hours: float = 0,
|
|
197
|
+
count: Optional[int] = None,
|
|
198
|
+
reconnect: bool = True,
|
|
199
|
+
loop: Optional[asyncio.AbstractEventLoop] = None,
|
|
200
|
+
):
|
|
201
|
+
"""
|
|
202
|
+
Decorator to create a background task loop.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
seconds: Duration in seconds between iterations.
|
|
206
|
+
minutes: Duration in minutes.
|
|
207
|
+
hours: Duration in hours.
|
|
208
|
+
count: Optional number of loops to run before stopping.
|
|
209
|
+
reconnect: If True, handles errors and continues. If False, stops on error.
|
|
210
|
+
loop: Optional asyncio loop.
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
def decorator(func: Callable[..., Awaitable[Any]]) -> Loop:
|
|
214
|
+
return Loop(
|
|
215
|
+
func,
|
|
216
|
+
seconds=seconds,
|
|
217
|
+
minutes=minutes,
|
|
218
|
+
hours=hours,
|
|
219
|
+
count=count,
|
|
220
|
+
reconnect=reconnect,
|
|
221
|
+
loop=loop,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
return decorator
|