@@ -266,6 +266,71 @@ def dispatch(self, event: str, payload: Any | None = None) -> None:
266266 task .add_done_callback (waits .discard )
267267 waits .add (task )
268268
269+ def safe_dispatch (self , name : str , * , payload : Any | None = None ) -> None :
270+ """Method to dispatch a custom :ref:`_Event`.
271+
272+ When an event is dispatched all listeners listening to the event, coroutine functions defined on the
273+ :class:`~twitchio.Client` with the same name, and :meth:`~twitchio.Client.wait_for` waiting for the event will be
274+ triggered.
275+
276+ Internally the :class:`~twitchio.Client` uses a method similar to this, however to avoid conflicts with built-in events
277+ it is not documented and you should use this method instead when needing a custom event dispatched.
278+
279+ This method avoids conflicts with internal events by prefixing the name of the event with ``event_safe_``;
280+ specifically in this case ``_safe_`` is added between the ``event_`` prefix and the name of the event.
281+
282+ All events in TwitchIO, including custom ones, must take between ``0`` or ``1`` arguments only. If ``None`` is
283+ passed to the payload parameter (default), the event will be dispatched with no arguments attached. Otherwise you can
284+ provide one parameter ``payload`` which (ideally) is a class containing all the information and methods needed in
285+ your event. Keep in mind that events expect consistency; E.g. If your event takes a payload argument it should
286+ **always** take a payload argument and vice versa. If designing an ``ext`` for TwitchIO this also will be inline
287+ with how end-users will expect events to work.
288+
289+ .. note::
290+
291+ If you intend on using this in a custom ``ext`` for example; consider also adding a small prefix to the name to
292+ identify your extension from others.
293+
294+ .. warning::
295+
296+ Overriding this method is highly discouraged. Any changes made to the safe name of the event may have hard to
297+ track and unwanted side effects. TwitchIO uses events internally and any changes to these dispatched events
298+ internally by users may also lead to consequences such as being rate limted or banned by Twitch, banned by
299+ broadcasters or; memory, CPU, network and other performance issues.
300+
301+ Parameters
302+ ----------
303+ name: str
304+ The name of the event you wish to dispatch. Remember the name of the event will be prefixed with ``event_safe_``.
305+ payload: Any | None
306+ The payload object dispatched to all your listeners. Defaults to ``None`` which would pass ``0`` arguments to all
307+ listeners.
308+
309+ Example
310+ -------
311+
312+ .. code:: python3
313+
314+ class CoolPayload:
315+
316+ def __init__(self, number: int) -> None:
317+ self.number = number
318+
319+
320+ class CoolComponent(commands.Component):
321+
322+ @commands.Component.listener()
323+ async def event_safe_cool(self, payload: CoolPayload) -> None:
324+ print(f"Received 'cool' event with number: {payload.number}")
325+
326+
327+ # Somewhere...
328+ payload = CoolPayload(number=9)
329+ client.safe_dispatch("cool", payload)
330+ """
331+ name_ = f"safe_{ name } "
332+ self .dispatch (name_ , payload )
333+
269334 async def setup_hook (self ) -> None :
270335 """
271336 Method called after :meth:`~.login` has been called but before the client is ready.
0 commit comments