feat: emit raw gateway events (#15)
This commit is contained in:
parent
4c203f6792
commit
06f972851b
@ -236,21 +236,54 @@ class EventDispatcher:
|
||||
if not waiters:
|
||||
self._waiters.pop(event_name, None)
|
||||
|
||||
async def dispatch(self, event_name: str, raw_data: Dict[str, Any]):
|
||||
"""
|
||||
Dispatches an event to all registered listeners.
|
||||
|
||||
Args:
|
||||
event_name (str): The name of the event (e.g., 'MESSAGE_CREATE').
|
||||
raw_data (Dict[str, Any]): The raw data payload from the Discord Gateway for this event.
|
||||
"""
|
||||
event_name_upper = event_name.upper()
|
||||
listeners = self._listeners.get(event_name_upper)
|
||||
|
||||
async def _dispatch_to_listeners(self, event_name: str, data: Any) -> None:
|
||||
listeners = self._listeners.get(event_name)
|
||||
if not listeners:
|
||||
# print(f"No listeners for event {event_name_upper}")
|
||||
return
|
||||
|
||||
self._resolve_waiters(event_name, data)
|
||||
|
||||
for listener in listeners:
|
||||
try:
|
||||
sig = inspect.signature(listener)
|
||||
num_params = len(sig.parameters)
|
||||
|
||||
if num_params == 0:
|
||||
await listener()
|
||||
elif num_params == 1:
|
||||
await listener(data)
|
||||
else:
|
||||
print(
|
||||
f"Warning: Listener {listener.__name__} for {event_name} has an unhandled number of parameters ({num_params}). Skipping or attempting with one arg."
|
||||
)
|
||||
if num_params > 0:
|
||||
await listener(data)
|
||||
|
||||
except Exception as e:
|
||||
callback = self.on_dispatch_error
|
||||
if callback is not None:
|
||||
try:
|
||||
await callback(event_name, e, listener)
|
||||
except Exception as hook_error:
|
||||
print(f"Error in on_dispatch_error hook itself: {hook_error}")
|
||||
else:
|
||||
print(
|
||||
f"Error in event listener {listener.__name__} for {event_name}: {e}"
|
||||
)
|
||||
if hasattr(self._client, "on_error"):
|
||||
try:
|
||||
await self._client.on_error(event_name, e, listener)
|
||||
except Exception as client_err_e:
|
||||
print(f"Error in client.on_error itself: {client_err_e}")
|
||||
|
||||
async def dispatch(self, event_name: str, raw_data: Dict[str, Any]):
|
||||
"""Dispatch an event and its raw counterpart to all listeners."""
|
||||
|
||||
event_name_upper = event_name.upper()
|
||||
raw_event_name = f"RAW_{event_name_upper}"
|
||||
|
||||
await self._dispatch_to_listeners(raw_event_name, raw_data)
|
||||
|
||||
parsed_data: Any = raw_data
|
||||
if event_name_upper in self._event_parsers:
|
||||
try:
|
||||
@ -258,53 +291,6 @@ class EventDispatcher:
|
||||
parsed_data = parser(raw_data)
|
||||
except Exception as e:
|
||||
print(f"Error parsing event data for {event_name_upper}: {e}")
|
||||
# Optionally, dispatch with raw_data or raise, or log more formally
|
||||
# For now, we'll proceed to dispatch with raw_data if parsing fails,
|
||||
# or just log and return if parsed_data is critical.
|
||||
# Let's assume if a parser exists, its output is critical.
|
||||
return
|
||||
|
||||
self._resolve_waiters(event_name_upper, parsed_data)
|
||||
# print(f"Dispatching event {event_name_upper} with data: {parsed_data} to {len(listeners)} listeners.")
|
||||
for listener in listeners:
|
||||
try:
|
||||
# Inspect the listener to see how many arguments it expects
|
||||
sig = inspect.signature(listener)
|
||||
num_params = len(sig.parameters)
|
||||
|
||||
if num_params == 0: # Listener takes no arguments
|
||||
await listener()
|
||||
elif (
|
||||
num_params == 1
|
||||
): # Listener takes one argument (the parsed data or model)
|
||||
await listener(parsed_data)
|
||||
# elif num_params == 2 and event_name_upper == "MESSAGE_CREATE": # Special case for (client, message)
|
||||
# await listener(self._client, parsed_data) # This might be too specific here
|
||||
else:
|
||||
# Fallback or error if signature doesn't match expected patterns
|
||||
# For now, assume one arg is the most common for parsed data.
|
||||
# Or, if you want to be strict:
|
||||
print(
|
||||
f"Warning: Listener {listener.__name__} for {event_name_upper} has an unhandled number of parameters ({num_params}). Skipping or attempting with one arg."
|
||||
)
|
||||
if num_params > 0: # Try with one arg if it takes any
|
||||
await listener(parsed_data)
|
||||
|
||||
except Exception as e:
|
||||
callback = self.on_dispatch_error
|
||||
if callback is not None:
|
||||
try:
|
||||
await callback(event_name_upper, e, listener)
|
||||
|
||||
except Exception as hook_error:
|
||||
print(f"Error in on_dispatch_error hook itself: {hook_error}")
|
||||
else:
|
||||
# Default error handling if no hook is set
|
||||
print(
|
||||
f"Error in event listener {listener.__name__} for {event_name_upper}: {e}"
|
||||
)
|
||||
if hasattr(self._client, "on_error"):
|
||||
try:
|
||||
await self._client.on_error(event_name_upper, e, listener)
|
||||
except Exception as client_err_e:
|
||||
print(f"Error in client.on_error itself: {client_err_e}")
|
||||
await self._dispatch_to_listeners(event_name_upper, parsed_data)
|
||||
|
@ -3,6 +3,16 @@
|
||||
Disagreement dispatches Gateway events to asynchronous callbacks. Handlers can be registered with `@client.event` or `client.on_event`.
|
||||
Listeners may be removed later using `EventDispatcher.unregister(event_name, coro)`.
|
||||
|
||||
## Raw Events
|
||||
|
||||
Every Gateway event is also emitted with a `RAW_` prefix containing the unparsed payload. Raw events fire **before** any caching or parsing occurs.
|
||||
|
||||
```python
|
||||
@client.on_event("RAW_MESSAGE_DELETE")
|
||||
async def handle_raw_delete(payload: dict):
|
||||
print("message deleted", payload["id"])
|
||||
```
|
||||
|
||||
|
||||
## PRESENCE_UPDATE
|
||||
|
||||
|
@ -8,6 +8,7 @@ from disagreement.event_dispatcher import EventDispatcher
|
||||
class DummyClient:
|
||||
def __init__(self):
|
||||
self.parsed = {}
|
||||
self._messages = {"1": "cached"}
|
||||
|
||||
def parse_message(self, data):
|
||||
self.parsed["message"] = True
|
||||
@ -66,3 +67,27 @@ async def test_unregister_listener():
|
||||
dispatcher.unregister("MESSAGE_CREATE", listener)
|
||||
await dispatcher.dispatch("MESSAGE_CREATE", {"id": 1})
|
||||
assert not called
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_raw_event_dispatched_before_parsing():
|
||||
client = DummyClient()
|
||||
dispatcher = EventDispatcher(client)
|
||||
|
||||
events = {}
|
||||
|
||||
async def raw_listener(payload):
|
||||
events["raw"] = payload
|
||||
events["cache_before"] = client._messages.get("1")
|
||||
|
||||
async def delete_listener(_):
|
||||
events["cache_after"] = client._messages.get("1")
|
||||
|
||||
dispatcher.register("RAW_MESSAGE_DELETE", raw_listener)
|
||||
dispatcher.register("MESSAGE_DELETE", delete_listener)
|
||||
|
||||
await dispatcher.dispatch("MESSAGE_DELETE", {"id": "1"})
|
||||
|
||||
assert events["raw"]["id"] == "1"
|
||||
assert events["cache_before"] == "cached"
|
||||
assert events["cache_after"] is None
|
||||
|
Loading…
x
Reference in New Issue
Block a user