1- from typing import Dict , Optional , Union , Any
1+ from typing import Dict , List , Optional , Union , Any
22import json
33import uuid
44import base64
@@ -184,7 +184,27 @@ async def _render_(
184184 # TODO: using Jinja Templates on Teams Cards.
185185 return payload
186186
187- async def _send_ (self , to : Actor , message : Union [str , Any ], ** kwargs ) -> Any :
187+ async def send_to_group (
188+ self ,
189+ recipient : List [Actor ],
190+ message : Union [str , Any ],
191+ ** kwargs
192+ ) -> Any :
193+ """send_to_group.
194+ Send message to Microsoft Teams channel using an incoming webhook.
195+ """
196+ return await self ._send_ (
197+ to = recipient ,
198+ message = message ,
199+ ** kwargs
200+ )
201+
202+ async def _send_ (
203+ self ,
204+ to : Union [Actor , List [Actor ], TeamsChannel , TeamsChat , TeamsWebhook ],
205+ message : Union [str , Any ],
206+ ** kwargs
207+ ) -> Any :
188208 """_send_.
189209 Send message to Microsoft Teams channel using an incoming webhook.
190210 """
@@ -223,6 +243,10 @@ async def _send_(self, to: Actor, message: Union[str, Any], **kwargs) -> Any:
223243 result = await self .send_direct_message (
224244 to , msg
225245 )
246+ elif isinstance (to , list ) and all (isinstance (r , Actor ) for r in to ):
247+ result = await self .send_group_direct_message (
248+ to , msg , topic = kwargs .get ('topic' )
249+ )
226250 else :
227251 raise NotifyException (
228252 "Invalid Recipient Object: Need an string or a TeamsChannel Object"
@@ -466,6 +490,36 @@ async def _create_chat(self, owner, user_id: str) -> str:
466490 result = await self ._graph .chats .post (request_body )
467491 return result .id
468492
493+ async def _create_group_chat (
494+ self ,
495+ member_ids : list [str ],
496+ topic : Optional [str ] = None
497+ ) -> str :
498+ """
499+ Create a group chat between the delegated account and N users.
500+ member_ids should include self._owner_id as well.
501+ """
502+ members = [
503+ AadUserConversationMember (
504+ odata_type = "#microsoft.graph.aadUserConversationMember" ,
505+ roles = ["owner" ],
506+ additional_data = {
507+ # Mejor usar v1.0 en lugar de /beta
508+ "user@odata.bind" : f"https://graph.microsoft.com/v1.0/users('{ user_id } ')"
509+ },
510+ )
511+ for user_id in member_ids
512+ ]
513+
514+ request_body = Chat (
515+ chat_type = ChatType .Group ,
516+ topic = topic ,
517+ members = members ,
518+ )
519+
520+ result = await self ._graph .chats .post (request_body )
521+ return result .id
522+
469523 async def _get_chat (self , user_id : str ) -> str :
470524 """
471525 Create a new chat with the specified user or return if it already exists.
@@ -494,6 +548,45 @@ async def _get_chat(self, user_id: str) -> str:
494548 return chat .id
495549 return None
496550
551+ async def _get_group_chat (
552+ self ,
553+ member_ids : list [str ],
554+ restricted : bool = True
555+ ) -> Optional [str ]:
556+ """
557+ Find an existing group chat that contains *at least* all member_ids.
558+ """
559+
560+ query_params = ChatsRequestBuilder .ChatsRequestBuilderGetQueryParameters (
561+ filter = "chatType eq 'group'" ,
562+ expand = ["members" ],
563+ )
564+
565+ request_configuration = RequestConfiguration (
566+ query_parameters = query_params ,
567+ )
568+
569+ chats = await self ._graph .chats .get (
570+ request_configuration = request_configuration
571+ )
572+ if not chats .value :
573+ return None
574+
575+ members_set = set (member_ids )
576+ print ('::: Members Set > ' , members_set )
577+
578+ for chat in chats .value :
579+ if not chat .members :
580+ continue
581+ chat_member_ids = {m .user_id for m in chat .members if m .user_id }
582+ if restricted :
583+ if chat_member_ids == members_set :
584+ return chat .id
585+ else :
586+ if members_set .issubset (chat_member_ids ):
587+ return chat .id
588+ return None
589+
497590 async def get_teams_user (self , email : str ) -> Dict [str , Any ]:
498591 """
499592 Retrieve a user from Microsoft Graph by email, returns the user object (JSON).
@@ -519,3 +612,31 @@ async def get_teams_user(self, email: str) -> Dict[str, Any]:
519612 self .logger .error (
520613 f"Failed to retrieve user info for { email } : { e } "
521614 )
615+
616+ async def send_group_direct_message (
617+ self ,
618+ recipients : list [Actor ],
619+ message : Dict [str , Any ],
620+ topic : Optional [str ] = None
621+ ):
622+ """
623+ Send a message to a group chat between the delegated account and 2+ users.
624+ """
625+
626+ if not self ._owner_id :
627+ raise NotifyException (
628+ "send_group_direct_message requires as_user=True so _owner_id is set."
629+ )
630+
631+ # Resolve the user ids for all recipients
632+ user_ids : list [str ] = []
633+ for recipient in recipients :
634+ user = await self .get_teams_user (recipient .account .address )
635+ user_ids .append (user .id )
636+
637+ # Todos los miembros = owner + usuarios
638+ member_ids = [self ._owner_id , * user_ids ]
639+ # Buscar chat existente o crear nuevo
640+ chat_id = await self ._get_group_chat (member_ids ) or await self ._create_group_chat (member_ids , topic = topic )
641+ # Enviar mensaje usando tu lógica actual
642+ return await self .send_message_to_chat (chat_id , message )
0 commit comments