Skip to content

Commit 6ef041a

Browse files
committed
3.0.2
1 parent af1f20c commit 6ef041a

File tree

8 files changed

+67
-34
lines changed

8 files changed

+67
-34
lines changed

docs/source/changelog.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ Glossary
3737
Releases
3838
---------------------
3939

40+
v3.0.2
41+
====================
42+
- Fixed AutoGUILD not sending messages (events emitted prematurely).
43+
- Fixed TextMESSAGE and VoiceMESSAGE not being removed after n sends when using AutoCHANNEL.
44+
- Added missing :py:attr:`daf.guild.AutoGUILD.removed_messages` property.
45+
4046
v3.0.1
4147
====================
4248
- Downgraded Selenium version from 4.13 to 4.12 since 4.13 does not support headless, which

src/daf/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@
1717
from .remote import *
1818

1919

20-
VERSION = "3.0.1"
20+
VERSION = "3.0.2"

src/daf/client.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -428,14 +428,15 @@ async def initialize(self):
428428
raise exc
429429

430430
self._event_ctrl.add_listener(EventID.account_update, self._on_update)
431-
for server in self._servers:
432-
if (await server.initialize(self, self._event_ctrl)) is not None:
433-
await self._on_remove_server(server)
434-
435431
self._event_ctrl.add_listener(EventID.server_removed, self._on_remove_server)
436432
self._event_ctrl.add_listener(EventID.server_added, self._on_add_server)
437433
self._event_ctrl.start()
438434
self._running = True
435+
async with self._event_ctrl.critical():
436+
for server in self._servers:
437+
if (await server.initialize(self, self._event_ctrl)) is not None:
438+
await self._on_remove_server(server)
439+
439440

440441
def generate_log_context(self) -> Dict[str, Union[str, int]]:
441442
"""

src/daf/core.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -500,12 +500,13 @@ async def shutdown() -> None:
500500

501501
GLOBALS.accounts.clear()
502502
evt.remove_listener(EventID.g_account_expired, cleanup_account)
503-
await evt.stop()
503+
504+
trace("Shutdown complete.", TraceLEVELS.NORMAL)
504505

505506
if remote.GLOBALS.remote_client is not None:
506507
await remote.GLOBALS.remote_client._close()
507508

508-
trace("Shutdown complete.", TraceLEVELS.NORMAL)
509+
await evt.stop()
509510

510511

511512
def _shutdown_clean(loop: asyncio.AbstractEventLoop) -> None:

src/daf/events.py

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
Module used to support listening and emitting events.
33
It also contains the event loop definitions.
44
"""
5-
from contextlib import suppress
5+
from contextlib import suppress, asynccontextmanager
66
from enum import Enum, auto
77
from typing import Any, List, Dict, Callable, TypeVar, Coroutine, Set, Union
8+
from sys import _getframe
89

910
from .misc.doc import doc_category
1011

@@ -47,6 +48,7 @@ def __init__(self) -> None:
4748
self.event_queue = asyncio.Queue()
4849
self.loop_task: asyncio.Task = None
4950
self.running = False
51+
self._critical_lock = asyncio.Lock()
5052

5153
def start(self):
5254
"""
@@ -61,6 +63,16 @@ def start(self):
6163
if self is not GLOBAL.g_controller:
6264
GLOBAL.non_global_controllers.add(self)
6365

66+
@asynccontextmanager
67+
async def critical(self):
68+
"""
69+
Asynchronous Context manager (``async with`` statement), that prevents
70+
any events from being processed until this critical section is exited.
71+
"""
72+
await self._critical_lock.acquire()
73+
yield
74+
self._critical_lock.release()
75+
6476
def stop(self):
6577
"Stops event loop asynchronously"
6678
if self.running:
@@ -142,6 +154,13 @@ def emit(self, event: "EventID", *args, **kwargs) -> asyncio.Future:
142154
future = asyncio.Future()
143155
if not self.running:
144156
future.set_result(None)
157+
caller_frame = _getframe(1)
158+
caller_info = caller_frame.f_code
159+
caller_text = f"{caller_info.co_name} ({caller_info.co_filename})"
160+
warnings.warn(
161+
f"{self} is not running, but {event} was emitted, which was ignored! Caller: {caller_text}",
162+
stacklevel=2
163+
)
145164
return future
146165

147166
self.event_queue.put_nowait((event, args, kwargs, future))
@@ -174,21 +193,21 @@ async def event_loop(self):
174193

175194
while self.running:
176195
event_id, args, kwargs, future = await queue.get()
196+
async with self._critical_lock:
197+
for listener in listeners.get(event_id, [])[:]:
198+
try:
199+
if listener.predicate is None or listener.predicate(*args, **kwargs):
200+
if isinstance(r:= listener(*args, **kwargs), Coroutine):
201+
await r
177202

178-
for listener in listeners.get(event_id, [])[:]:
179-
try:
180-
if listener.predicate is None or listener.predicate(*args, **kwargs):
181-
if isinstance(r:= listener(*args, **kwargs), Coroutine):
182-
await r
203+
except Exception as exc:
204+
warnings.warn(f"({exc}) Could not call event handler {listener} for event {event_id}.")
205+
future.set_exception(exc)
206+
break
183207

184-
except Exception as exc:
185-
warnings.warn(f"({exc}) Could not call event handler {listener} for event {event_id}.")
186-
future.set_exception(exc)
187-
break
188208

189-
190-
if not future.done(): # In case exception was set
191-
future.set_result(None)
209+
if not future.done(): # In case exception was set
210+
future.set_result(None)
192211

193212
if self is not GLOBAL.g_controller:
194213
GLOBAL.non_global_controllers.remove(self)
@@ -364,6 +383,7 @@ async def server_update(server):
364383
:type server: daf.guild.GUILD | daf.guild.USER | daf.guild.AutoGUILD
365384
"""
366385

386+
@doc_category("Event reference")
367387
async def auto_guild_start_join(auto_guild):
368388
"""
369389
Event that is emitted when the join for new server should start.

src/daf/guild/autoguild.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,12 @@ def __init__(
101101
self.include_pattern = re.sub(r"\s*\|\s*", '|', include_pattern) if include_pattern else None
102102
self.exclude_pattern = re.sub(r"\s*\|\s*", '|', exclude_pattern) if exclude_pattern else None
103103
self._remove_after = remove_after
104-
self._messages = messages
104+
self._messages: List[BaseMESSAGE] = messages
105105
self.logging = logging
106106
self.auto_join = auto_join
107107
self.parent = None
108108
self.guild_query_iter = None
109109
self.guild_join_count = 0
110-
self._messages: List[BaseMESSAGE] = []
111110
self.removal_buffer_length = removal_buffer_length
112111
self._removal_timer_handle: asyncio.Task = None
113112
self._guild_join_timer_handle: asyncio.Task = None
@@ -124,6 +123,11 @@ def __init__(
124123
def __repr__(self) -> str:
125124
return f"AutoGUILD(include_pattern='{self.include_pattern}', exclude_pattern='{self.exclude_pattern})'"
126125

126+
@property
127+
def removed_messages(self) -> List[BaseMESSAGE]:
128+
"Returns a list of messages that were removed from server (last ``removal_buffer_length`` messages)."
129+
return self._removed_messages[:]
130+
127131
@property
128132
def messages(self) -> List[Union[TextMESSAGE, VoiceMESSAGE]]:
129133
"""
@@ -263,11 +267,6 @@ async def initialize(self, parent: Any, event_ctrl: EventController):
263267

264268
self.guild_query_iter = self.auto_join._query_request()
265269

266-
message: BaseChannelMessage
267-
for message in self._messages:
268-
if (await message.initialize(self, event_ctrl, self._get_channels)) is not None:
269-
await self._on_remove_message(self, message)
270-
271270
if self._remove_after is not None:
272271
if isinstance(self._remove_after, timedelta):
273272
self._remove_after = datetime.now().astimezone() + self._remove_after
@@ -323,6 +322,11 @@ async def initialize(self, parent: Any, event_ctrl: EventController):
323322
event_ctrl.add_listener(EventID.message_removed, self._on_remove_message, lambda server, m: server is self)
324323
event_ctrl.add_listener(EventID.server_update, self._on_update, lambda server, *args, **kwargs: server is self)
325324

325+
message: BaseChannelMessage
326+
for message in self._messages:
327+
if (await message.initialize(self, event_ctrl, self._get_channels)) is not None:
328+
await self._on_remove_message(self, message)
329+
326330
def _generate_guild_log_context(self, guild: discord.Guild):
327331
return {
328332
"name": guild.name,
@@ -368,10 +372,6 @@ async def _advertise(self, _, message: BaseMESSAGE):
368372
"""
369373
Advertises thru all the GUILDs.
370374
"""
371-
if message._check_state():
372-
await self.remove_message(message)
373-
return
374-
375375
author_ctx = self.parent.generate_log_context()
376376
message_context = await message._send()
377377
if message_context and self.logging:

src/daf/guild/guilduser.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,6 @@ async def initialize(self, parent: Any, event_ctrl: EventController, getter: Cal
276276
trace(f"Invalid ID {self.snowflake} - {self}", TraceLEVELS.ERROR, exception_cls=ValueError)
277277

278278
self._apiobject = _apiobject
279-
await self._init_messages()
280-
281279
if self._remove_after is not None:
282280
if isinstance(self._remove_after, timedelta):
283281
self._remove_after = datetime.now().astimezone() + self._remove_after
@@ -298,6 +296,8 @@ async def initialize(self, parent: Any, event_ctrl: EventController, getter: Cal
298296
event_ctrl.add_listener(EventID.message_added, self._on_add_message, lambda server, m: server is self)
299297
event_ctrl.add_listener(EventID.server_update, self._on_update, lambda server, *h, **k: server is self)
300298

299+
await self._init_messages()
300+
301301
def generate_invite_log_context(self, member: discord.Member, invite_id: str) -> dict:
302302
raise NotImplementedError
303303

src/daf/message/base.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ def remove_after(self) -> Union[int, datetime, None]:
585585
"""
586586
# An integer was originally provided, the dict is remove_after by each channel
587587
if isinstance(self._remove_after_original, int):
588-
return max(self._remove_after.values()) if self._remove_after else self._remove_after_original
588+
return max(self._remove_after.values(), default=self._remove_after_original)
589589

590590
return self._remove_after
591591

@@ -611,6 +611,11 @@ def _update_state(self, succeeded_channels: List[ChannelType], err_channels: Lis
611611
)
612612
self.channels.remove(channel)
613613
del self._remove_after[snowflake]
614+
615+
if not self._remove_after: # All channel counts expired
616+
self._event_ctrl.emit(EventID.message_removed, self.parent, self)
617+
# Prevent dual removal events, errored channels also don't need to be removed if the message is removed
618+
return
614619

615620
# Remove any channels that returned with code status 404 (They no longer exist)
616621
for data in err_channels:

0 commit comments

Comments
 (0)