@@ -106,13 +106,17 @@ def _process_handlers(cls) -> None:
106106
107107 # Process WebSocket handlers
108108 if hasattr (attr , "_ws_handler_info" ):
109- handler_info = attr ._ws_handler_info
110- cls ._MESSAGE_HANDLER_INFO_MAP [handler_info ["action" ]] = handler_info
109+ handler_info : AsyncAPIHandlerInfo = attr ._ws_handler_info
110+ cls ._MESSAGE_HANDLER_INFO_MAP [handler_info ["message_action" ]] = (
111+ handler_info
112+ )
111113
112114 # Process event handlers
113115 if hasattr (attr , "_event_handler_info" ):
114- handler_info = attr ._event_handler_info
115- cls ._EVENT_HANDLER_INFO_MAP [handler_info ["action" ]] = handler_info
116+ handler_info : AsyncAPIHandlerInfo = attr ._event_handler_info
117+ cls ._EVENT_HANDLER_INFO_MAP [handler_info ["message_action" ]] = (
118+ handler_info
119+ )
116120
117121 # Extract types and build unions/adapters
118122 cls ._build_adapters ()
@@ -264,14 +268,23 @@ async def websocket_connect(self, message: WebSocketConnectEvent) -> None:
264268 await self .accept ()
265269
266270 # Authenticate the connection
267- auth_result = None
268271 if self .authenticator :
269272 auth_result = await self .authenticator .authenticate (self .scope )
270273
271274 if not auth_result :
272275 await self .close ()
273276 return
274277
278+ try :
279+ for group in self .groups :
280+ channel_layer = get_channel_layer (self .channel_layer_alias )
281+ if channel_layer :
282+ await channel_layer .group_add (group , self .channel_name )
283+ except AttributeError :
284+ raise ValueError (
285+ "BACKEND is unconfigured or doesn't support groups"
286+ ) from None
287+
275288 await self .post_authentication ()
276289
277290 async def websocket_disconnect (self , message : WebSocketDisconnectEvent ) -> None :
@@ -367,10 +380,10 @@ async def receive_message(self, message: BaseMessage) -> None:
367380 message: The validated message object (BaseMessage instance)
368381 """
369382 # Extract the action from the discriminator field
370- action = getattr (message , self .discriminator_field )
383+ message_action = getattr (message , self .discriminator_field )
371384
372- # Find the handler for this action
373- handler_info = self .__class__ ._MESSAGE_HANDLER_INFO_MAP .get (action )
385+ # Find the handler for this message_action
386+ handler_info = self .__class__ ._MESSAGE_HANDLER_INFO_MAP .get (message_action )
374387
375388 # Get the handler method by name
376389 method_name = handler_info ["method_name" ]
@@ -385,7 +398,7 @@ async def receive_message(self, message: BaseMessage) -> None:
385398 await self .handle_result (result )
386399
387400 except Exception as e :
388- await self .handle_error (e , action , message )
401+ await self .handle_error (e , message_action , message )
389402
390403 async def handle_result (self , result : BaseMessage ) -> None :
391404 """
@@ -458,12 +471,12 @@ async def handle_error(
458471 await logger .aexception (f"Handler error for action '{ action } ': { error } " )
459472
460473 # Group operations methods
461- async def send_group_message (
474+ async def broadcast_message (
462475 self ,
463476 message : BaseMessage | dict [str , Any ],
464477 groups : list [str ] | None = None ,
465478 * ,
466- exclude_current : bool = True ,
479+ exclude_current : bool = False ,
467480 ) -> None :
468481 """
469482 Send a BaseMessage object to one or more channel groups.
@@ -479,24 +492,27 @@ async def send_group_message(
479492 exclude_current: Whether to exclude the sending consumer from receiving
480493 the broadcast (prevents echo effects)
481494 """
495+ channel_layer = get_channel_layer (self .channel_layer_alias )
496+ assert channel_layer
497+
482498 if groups is None :
483499 groups = self .groups or []
484500
485501 if isinstance (message , BaseMessage ):
486502 message = message .model_dump (mode = "json" )
487503
488504 for group in groups :
489- await self . channel_layer .group_send (
505+ await channel_layer .group_send (
490506 group ,
491507 {
492- "type" : "send_group_member " ,
508+ "type" : "handle_group_message " ,
493509 "message" : message ,
494510 "exclude_current" : exclude_current ,
495511 "from_channel" : self .channel_name ,
496512 },
497513 )
498514
499- async def send_group_member (self , event : BaseMessage ) -> None :
515+ async def handle_group_message (self , event : BaseMessage ) -> None :
500516 """
501517 Handle incoming group message and relay to client.
502518
@@ -534,7 +550,7 @@ async def send_group_member(self, event: BaseMessage) -> None:
534550
535551 # Channel event system methods
536552 @classmethod
537- async def asend_event (
553+ async def send_event (
538554 cls ,
539555 channel_name : str ,
540556 event : ReceiveEvent ,
@@ -562,13 +578,13 @@ async def asend_event(
562578 )
563579
564580 @classmethod
565- def send_event (
581+ def send_event_sync (
566582 cls ,
567583 channel_name : str ,
568584 event : ReceiveEvent ,
569585 ) -> None :
570586 """
571- Synchronous version of asend_event for use in Django tasks/views.
587+ Synchronous version of send_event for use in Django tasks/views.
572588
573589 This method provides the same functionality as asend_channel_event but
574590 can be called from synchronous code like Django tasks, views, or signals.
@@ -577,13 +593,13 @@ def send_event(
577593 channel_name: Channel name to send to
578594 event: The typed event to send (BaseMessage subclass)
579595 """
580- async_to_sync (cls .asend_event )(channel_name , event )
596+ async_to_sync (cls .send_event )(channel_name , event )
581597
582598 @classmethod
583- async def abroadcast_event (
599+ async def broadcast_event (
584600 cls ,
585- group_name : str ,
586601 event : ReceiveEvent ,
602+ groups : Iterable [str ] | None = None ,
587603 ) -> None :
588604 """
589605 Broadcast a typed channel event to a channel group.
@@ -593,37 +609,41 @@ async def abroadcast_event(
593609 places where you don't have a consumer instance.
594610
595611 Args:
596- group_name: Group name to broadcast the event to
597612 event: The typed event to broadcast (BaseMessage subclass)
613+ groups: Groups to broadcast the event to
598614 """
599615 channel_layer = get_channel_layer (cls .channel_layer_alias )
600616 assert channel_layer is not None
601617
602- await channel_layer .group_send (
603- group_name ,
604- {
605- "type" : "handle_channel_event" ,
606- "event_data" : event .model_dump (mode = "json" ),
607- },
608- )
618+ if groups is None :
619+ groups = cls .groups or []
620+
621+ for group in groups :
622+ await channel_layer .group_send (
623+ group ,
624+ {
625+ "type" : "handle_channel_event" ,
626+ "event_data" : event .model_dump (mode = "json" ),
627+ },
628+ )
609629
610630 @classmethod
611- def broadcast_event (
631+ def broadcast_event_sync (
612632 cls ,
613- group_name : str ,
614633 event : ReceiveEvent ,
634+ groups : Iterable [str ] | None = None ,
615635 ) -> None :
616636 """
617- Synchronous version of abroadcast_event for use in Django tasks/views.
637+ Synchronous version of broadcast_event for use in sync tasks/views.
618638
619- This method provides the same functionality as abroadcast_event but
639+ This method provides the same functionality as broadcast_event but
620640 can be called from synchronous code like Django tasks, views, or signals.
621641
622642 Args:
623- group_name: Group name to broadcast to
624643 event: The typed event to broadcast (BaseMessage subclass)
644+ groups: Groups to broadcast to
625645 """
626- async_to_sync (cls .abroadcast_event )( group_name , event )
646+ async_to_sync (cls .broadcast_event )( event , groups )
627647
628648 async def handle_channel_event (self , event_payload : EventPayload ) -> None :
629649 """
0 commit comments