microsoft-agents-hosting-teams 0.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
@@ -0,0 +1,653 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+ """Teams information utilities for Microsoft Agents."""
5
+
6
+ from typing import Optional, Tuple, Dict, Any, List
7
+
8
+ from microsoft.agents.activity import Activity, Channels, ConversationParameters
9
+
10
+ from microsoft.agents.activity.teams import (
11
+ TeamsChannelAccount,
12
+ TeamsMeetingParticipant,
13
+ MeetingInfo,
14
+ TeamDetails,
15
+ TeamsPagedMembersResult,
16
+ MeetingNotification,
17
+ MeetingNotificationResponse,
18
+ TeamsMember,
19
+ BatchOperationStateResponse,
20
+ BatchFailedEntriesResponse,
21
+ CancelOperationResponse,
22
+ TeamsBatchOperationResponse,
23
+ ChannelInfo,
24
+ )
25
+ from microsoft.agents.hosting.core.connector.teams import TeamsConnectorClient
26
+ from microsoft.agents.hosting.core import ChannelServiceAdapter, TurnContext
27
+
28
+
29
+ class TeamsInfo:
30
+ """Teams information utilities for interacting with Teams-specific data."""
31
+
32
+ @staticmethod
33
+ async def get_meeting_participant(
34
+ context: TurnContext,
35
+ meeting_id: Optional[str] = None,
36
+ participant_id: Optional[str] = None,
37
+ tenant_id: Optional[str] = None,
38
+ ) -> TeamsMeetingParticipant:
39
+ """
40
+ Gets the meeting participant information.
41
+
42
+ Args:
43
+ context: The turn context.
44
+ meeting_id: The meeting ID. If not provided, it will be extracted from the activity.
45
+ participant_id: The participant ID. If not provided, it will be extracted from the activity.
46
+ tenant_id: The tenant ID. If not provided, it will be extracted from the activity.
47
+
48
+ Returns:
49
+ The meeting participant information.
50
+
51
+ Raises:
52
+ ValueError: If required parameters are missing.
53
+ """
54
+ if not context:
55
+ raise ValueError("context is required.")
56
+
57
+ activity = context.activity
58
+ teams_channel_data: dict = activity.channel_data
59
+
60
+ if meeting_id is None:
61
+ meeting_id = teams_channel_data.get("meeting", {}).get("id", None)
62
+
63
+ if not meeting_id:
64
+ raise ValueError("meeting_id is required.")
65
+
66
+ if participant_id is None:
67
+ participant_id = getattr(activity.from_property, "aad_object_id", None)
68
+
69
+ if not participant_id:
70
+ raise ValueError("participant_id is required.")
71
+
72
+ if tenant_id is None:
73
+ tenant_id = teams_channel_data.get("tenant", {}).get("id", None)
74
+
75
+ rest_client = TeamsInfo._get_rest_client(context)
76
+ result = await rest_client.fetch_meeting_participant(
77
+ meeting_id, participant_id, tenant_id
78
+ )
79
+ return result
80
+
81
+ @staticmethod
82
+ async def get_meeting_info(
83
+ context: TurnContext, meeting_id: Optional[str] = None
84
+ ) -> MeetingInfo:
85
+ """
86
+ Gets the meeting information.
87
+
88
+ Args:
89
+ context: The turn context.
90
+ meeting_id: The meeting ID. If not provided, it will be extracted from the activity.
91
+
92
+ Returns:
93
+ The meeting information.
94
+
95
+ Raises:
96
+ ValueError: If required parameters are missing.
97
+ """
98
+ if not meeting_id:
99
+ teams_channel_data: dict = context.activity.channel_data
100
+ meeting_id = teams_channel_data.get("meeting", {}).get("id", None)
101
+
102
+ if not meeting_id:
103
+ raise ValueError("meeting_id is required.")
104
+
105
+ rest_client = TeamsInfo._get_rest_client(context)
106
+ result = await rest_client.fetch_meeting_info(meeting_id)
107
+ return result
108
+
109
+ @staticmethod
110
+ async def get_team_details(
111
+ context: TurnContext, team_id: Optional[str] = None
112
+ ) -> TeamDetails:
113
+ """
114
+ Gets the team details.
115
+
116
+ Args:
117
+ context: The turn context.
118
+ team_id: The team ID. If not provided, it will be extracted from the activity.
119
+
120
+ Returns:
121
+ The team details.
122
+
123
+ Raises:
124
+ ValueError: If required parameters are missing.
125
+ """
126
+ if not team_id:
127
+ teams_channel_data: dict = context.activity.channel_data
128
+ team_id = teams_channel_data.get("team", {}).get("id", None)
129
+
130
+ if not team_id:
131
+ raise ValueError("team_id is required.")
132
+
133
+ rest_client = TeamsInfo._get_rest_client(context)
134
+ result = await rest_client.fetch_team_details(team_id)
135
+ return result
136
+
137
+ @staticmethod
138
+ async def send_message_to_teams_channel(
139
+ context: TurnContext,
140
+ activity: Activity,
141
+ teams_channel_id: str,
142
+ app_id: Optional[str] = None,
143
+ ) -> Tuple[Dict[str, Any], str]:
144
+ """
145
+ Sends a message to a Teams channel.
146
+
147
+ Args:
148
+ context: The turn context.
149
+ activity: The activity to send.
150
+ teams_channel_id: The Teams channel ID.
151
+ app_id: The application ID.
152
+
153
+ Returns:
154
+ A tuple containing the conversation reference and new activity ID.
155
+
156
+ Raises:
157
+ ValueError: If required parameters are missing.
158
+ """
159
+ if not context:
160
+ raise ValueError("TurnContext cannot be None")
161
+
162
+ if not activity:
163
+ raise ValueError("Activity cannot be None")
164
+
165
+ if not teams_channel_id:
166
+ raise ValueError("The teams_channel_id cannot be None or empty")
167
+
168
+ convo_params = ConversationParameters(
169
+ is_group=True,
170
+ channel_data={
171
+ "channel": {
172
+ "id": teams_channel_id,
173
+ },
174
+ },
175
+ activity=activity,
176
+ agent=context.activity.recipient,
177
+ )
178
+
179
+ conversation_reference = None
180
+ new_activity_id = None
181
+
182
+ if app_id and isinstance(context.adapter, ChannelServiceAdapter):
183
+
184
+ async def _conversation_callback(
185
+ turn_context: TurnContext,
186
+ ) -> None:
187
+ """
188
+ Callback for create_conversation.
189
+
190
+ Args:
191
+ turn_context: The turn context.
192
+ conversation_reference: The conversation reference to update.
193
+ new_activity_id: The new activity ID to update.
194
+ """
195
+ nonlocal conversation_reference, new_activity_id
196
+ conversation_reference = (
197
+ turn_context.activity.get_conversation_reference()
198
+ )
199
+ new_activity_id = turn_context.activity.id
200
+
201
+ await context.adapter.create_conversation(
202
+ app_id,
203
+ Channels.ms_teams,
204
+ context.activity.service_url,
205
+ "https://api.botframework.com",
206
+ convo_params,
207
+ _conversation_callback,
208
+ )
209
+ else:
210
+ connector_client = TeamsInfo._get_rest_client(context)
211
+ conversation_resource_response = (
212
+ await connector_client.conversations.create_conversation(convo_params)
213
+ )
214
+ conversation_reference = context.activity.get_conversation_reference()
215
+ conversation_reference.conversation.id = conversation_resource_response.id
216
+ new_activity_id = conversation_resource_response.activity_id
217
+
218
+ return conversation_reference, new_activity_id
219
+
220
+ @staticmethod
221
+ async def get_team_channels(
222
+ context: TurnContext, team_id: Optional[str] = None
223
+ ) -> List[ChannelInfo]:
224
+ """
225
+ Gets the channels of a team.
226
+
227
+ Args:
228
+ context: The turn context.
229
+ team_id: The team ID. If not provided, it will be extracted from the activity.
230
+
231
+ Returns:
232
+ The list of channels.
233
+
234
+ Raises:
235
+ ValueError: If required parameters are missing.
236
+ """
237
+ if not team_id:
238
+ teams_channel_data: dict = context.activity.channel_data
239
+ team_id = teams_channel_data.get("team", {}).get("id", None)
240
+
241
+ if not team_id:
242
+ raise ValueError("team_id is required.")
243
+
244
+ rest_client = TeamsInfo._get_rest_client(context)
245
+ return await rest_client.fetch_channel_list(team_id)
246
+
247
+ @staticmethod
248
+ async def get_paged_members(
249
+ context: TurnContext,
250
+ page_size: Optional[int] = None,
251
+ continuation_token: Optional[str] = None,
252
+ ) -> TeamsPagedMembersResult:
253
+ """
254
+ Gets the paged members of a team or conversation.
255
+
256
+ Args:
257
+ context: The turn context.
258
+ page_size: The page size.
259
+ continuation_token: The continuation token.
260
+
261
+ Returns:
262
+ The paged members result.
263
+
264
+ Raises:
265
+ ValueError: If required parameters are missing.
266
+ """
267
+ teams_channel_data: dict = context.activity.channel_data
268
+ team_id = teams_channel_data.get("team", {}).get("id", None)
269
+
270
+ if team_id:
271
+ return await TeamsInfo.get_paged_team_members(
272
+ context, team_id, page_size, continuation_token
273
+ )
274
+ else:
275
+ conversation_id = (
276
+ context.activity.conversation.id
277
+ if context.activity.conversation
278
+ else None
279
+ )
280
+ if not conversation_id:
281
+ raise ValueError("conversation_id is required.")
282
+
283
+ rest_client = TeamsInfo._get_rest_client(context)
284
+ return await rest_client.get_conversation_paged_member(
285
+ conversation_id, page_size, continuation_token
286
+ )
287
+
288
+ @staticmethod
289
+ async def get_member(context: TurnContext, user_id: str) -> TeamsChannelAccount:
290
+ """
291
+ Gets a member of a team or conversation.
292
+
293
+ Args:
294
+ context: The turn context.
295
+ user_id: The user ID.
296
+
297
+ Returns:
298
+ The member information.
299
+
300
+ Raises:
301
+ ValueError: If required parameters are missing.
302
+ """
303
+ teams_channel_data: dict = context.activity.channel_data
304
+ team_id = teams_channel_data.get("team", {}).get("id", None)
305
+
306
+ if team_id:
307
+ return await TeamsInfo.get_team_member(context, team_id, user_id)
308
+ else:
309
+ conversation_id = (
310
+ context.activity.conversation.id
311
+ if context.activity.conversation
312
+ else None
313
+ )
314
+ if not conversation_id:
315
+ raise ValueError("conversation_id is required.")
316
+
317
+ return await TeamsInfo._get_member_internal(
318
+ context, conversation_id, user_id
319
+ )
320
+
321
+ @staticmethod
322
+ async def get_paged_team_members(
323
+ context: TurnContext,
324
+ team_id: Optional[str] = None,
325
+ page_size: Optional[int] = None,
326
+ continuation_token: Optional[str] = None,
327
+ ) -> TeamsPagedMembersResult:
328
+ """
329
+ Gets the paged members of a team.
330
+
331
+ Args:
332
+ context: The turn context.
333
+ team_id: The team ID. If not provided, it will be extracted from the activity.
334
+ page_size: The page size.
335
+ continuation_token: The continuation token.
336
+
337
+ Returns:
338
+ The paged members result.
339
+
340
+ Raises:
341
+ ValueError: If required parameters are missing.
342
+ """
343
+ if not team_id:
344
+ teams_channel_data: dict = context.activity.channel_data
345
+ team_id = teams_channel_data.get("team", {}).get("id", None)
346
+
347
+ if not team_id:
348
+ raise ValueError("team_id is required.")
349
+
350
+ rest_client = TeamsInfo._get_rest_client(context)
351
+ paged_results = await rest_client.get_conversation_paged_member(
352
+ team_id, page_size, continuation_token
353
+ )
354
+
355
+ # Fetch all pages if there are more
356
+ while paged_results.continuation_token:
357
+ next_results = await rest_client.get_conversation_paged_member(
358
+ team_id, page_size, paged_results.continuation_token
359
+ )
360
+ paged_results.members.extend(next_results.members)
361
+ paged_results.continuation_token = next_results.continuation_token
362
+
363
+ return paged_results
364
+
365
+ @staticmethod
366
+ async def get_team_member(
367
+ context: TurnContext, team_id: str, user_id: str
368
+ ) -> TeamsChannelAccount:
369
+ """
370
+ Gets a member of a team.
371
+
372
+ Args:
373
+ context: The turn context.
374
+ team_id: The team ID.
375
+ user_id: The user ID.
376
+
377
+ Returns:
378
+ The member information.
379
+
380
+ Raises:
381
+ ValueError: If required parameters are missing.
382
+ """
383
+ rest_client = TeamsInfo._get_rest_client(context)
384
+ return await rest_client.get_conversation_member(team_id, user_id)
385
+
386
+ @staticmethod
387
+ async def send_meeting_notification(
388
+ context: TurnContext,
389
+ notification: MeetingNotification,
390
+ meeting_id: Optional[str] = None,
391
+ ) -> MeetingNotificationResponse:
392
+ """
393
+ Sends a meeting notification.
394
+
395
+ Args:
396
+ context: The turn context.
397
+ notification: The meeting notification.
398
+ meeting_id: The meeting ID. If not provided, it will be extracted from the activity.
399
+
400
+ Returns:
401
+ The meeting notification response.
402
+
403
+ Raises:
404
+ ValueError: If required parameters are missing.
405
+ """
406
+ activity = context.activity
407
+
408
+ if meeting_id is None:
409
+ teams_channel_data: dict = activity.channel_data
410
+ meeting_id = teams_channel_data.get("meeting", {}).get("id", None)
411
+
412
+ if not meeting_id:
413
+ raise ValueError("meeting_id is required.")
414
+
415
+ rest_client = TeamsInfo._get_rest_client(context)
416
+ return await rest_client.send_meeting_notification(meeting_id, notification)
417
+
418
+ @staticmethod
419
+ async def send_message_to_list_of_users(
420
+ context: TurnContext,
421
+ activity: Activity,
422
+ tenant_id: str,
423
+ members: List[TeamsMember],
424
+ ) -> TeamsBatchOperationResponse:
425
+ """
426
+ Sends a message to a list of users.
427
+
428
+ Args:
429
+ context: The turn context.
430
+ activity: The activity to send.
431
+ tenant_id: The tenant ID.
432
+ members: The list of members.
433
+
434
+ Returns:
435
+ The batch operation response.
436
+
437
+ Raises:
438
+ ValueError: If required parameters are missing.
439
+ """
440
+ if not activity:
441
+ raise ValueError("activity is required.")
442
+ if not tenant_id:
443
+ raise ValueError("tenant_id is required.")
444
+ if not members or len(members) == 0:
445
+ raise ValueError("members list is required.")
446
+
447
+ rest_client = TeamsInfo._get_rest_client(context)
448
+ return await rest_client.send_message_to_list_of_users(
449
+ activity, tenant_id, members
450
+ )
451
+
452
+ @staticmethod
453
+ async def send_message_to_all_users_in_tenant(
454
+ context: TurnContext, activity: Activity, tenant_id: str
455
+ ) -> TeamsBatchOperationResponse:
456
+ """
457
+ Sends a message to all users in a tenant.
458
+
459
+ Args:
460
+ context: The turn context.
461
+ activity: The activity to send.
462
+ tenant_id: The tenant ID.
463
+
464
+ Returns:
465
+ The batch operation response.
466
+
467
+ Raises:
468
+ ValueError: If required parameters are missing.
469
+ """
470
+ if not activity:
471
+ raise ValueError("activity is required.")
472
+ if not tenant_id:
473
+ raise ValueError("tenant_id is required.")
474
+
475
+ rest_client = TeamsInfo._get_rest_client(context)
476
+ return await rest_client.send_message_to_all_users_in_tenant(
477
+ activity, tenant_id
478
+ )
479
+
480
+ @staticmethod
481
+ async def send_message_to_all_users_in_team(
482
+ context: TurnContext, activity: Activity, tenant_id: str, team_id: str
483
+ ) -> TeamsBatchOperationResponse:
484
+ """
485
+ Sends a message to all users in a team.
486
+
487
+ Args:
488
+ context: The turn context.
489
+ activity: The activity to send.
490
+ tenant_id: The tenant ID.
491
+ team_id: The team ID.
492
+
493
+ Returns:
494
+ The batch operation response.
495
+
496
+ Raises:
497
+ ValueError: If required parameters are missing.
498
+ """
499
+ if not activity:
500
+ raise ValueError("activity is required.")
501
+ if not tenant_id:
502
+ raise ValueError("tenant_id is required.")
503
+ if not team_id:
504
+ raise ValueError("team_id is required.")
505
+
506
+ rest_client = TeamsInfo._get_rest_client(context)
507
+ return await rest_client.send_message_to_all_users_in_team(
508
+ activity, tenant_id, team_id
509
+ )
510
+
511
+ @staticmethod
512
+ async def send_message_to_list_of_channels(
513
+ context: TurnContext,
514
+ activity: Activity,
515
+ tenant_id: str,
516
+ members: List[TeamsMember],
517
+ ) -> TeamsBatchOperationResponse:
518
+ """
519
+ Sends a message to a list of channels.
520
+
521
+ Args:
522
+ context: The turn context.
523
+ activity: The activity to send.
524
+ tenant_id: The tenant ID.
525
+ members: The list of members.
526
+
527
+ Returns:
528
+ The batch operation response.
529
+
530
+ Raises:
531
+ ValueError: If required parameters are missing.
532
+ """
533
+ if not activity:
534
+ raise ValueError("activity is required.")
535
+ if not tenant_id:
536
+ raise ValueError("tenant_id is required.")
537
+ if not members or len(members) == 0:
538
+ raise ValueError("members list is required.")
539
+
540
+ rest_client = TeamsInfo._get_rest_client(context)
541
+ return await rest_client.send_message_to_list_of_channels(
542
+ activity, tenant_id, members
543
+ )
544
+
545
+ @staticmethod
546
+ async def get_operation_state(
547
+ context: TurnContext, operation_id: str
548
+ ) -> BatchOperationStateResponse:
549
+ """
550
+ Gets the operation state.
551
+
552
+ Args:
553
+ context: The turn context.
554
+ operation_id: The operation ID.
555
+
556
+ Returns:
557
+ The operation state response.
558
+
559
+ Raises:
560
+ ValueError: If required parameters are missing.
561
+ """
562
+ if not operation_id:
563
+ raise ValueError("operation_id is required.")
564
+
565
+ rest_client = TeamsInfo._get_rest_client(context)
566
+ return await rest_client.get_operation_state(operation_id)
567
+
568
+ @staticmethod
569
+ async def get_failed_entries(
570
+ context: TurnContext, operation_id: str
571
+ ) -> BatchFailedEntriesResponse:
572
+ """
573
+ Gets the failed entries of an operation.
574
+
575
+ Args:
576
+ context: The turn context.
577
+ operation_id: The operation ID.
578
+
579
+ Returns:
580
+ The failed entries response.
581
+
582
+ Raises:
583
+ ValueError: If required parameters are missing.
584
+ """
585
+ if not operation_id:
586
+ raise ValueError("operation_id is required.")
587
+
588
+ rest_client = TeamsInfo._get_rest_client(context)
589
+ return await rest_client.get_failed_entries(operation_id)
590
+
591
+ @staticmethod
592
+ async def cancel_operation(
593
+ context: TurnContext, operation_id: str
594
+ ) -> CancelOperationResponse:
595
+ """
596
+ Cancels an operation.
597
+
598
+ Args:
599
+ context: The turn context.
600
+ operation_id: The operation ID.
601
+
602
+ Returns:
603
+ The cancel operation response.
604
+
605
+ Raises:
606
+ ValueError: If required parameters are missing.
607
+ """
608
+ if not operation_id:
609
+ raise ValueError("operation_id is required.")
610
+
611
+ rest_client = TeamsInfo._get_rest_client(context)
612
+ return await rest_client.cancel_operation(operation_id)
613
+
614
+ @staticmethod
615
+ async def _get_member_internal(
616
+ context: TurnContext, conversation_id: str, user_id: str
617
+ ) -> TeamsChannelAccount:
618
+ """
619
+ Internal method to get a member from a conversation.
620
+
621
+ Args:
622
+ context: The turn context.
623
+ conversation_id: The conversation ID.
624
+ user_id: The user ID.
625
+
626
+ Returns:
627
+ The member information.
628
+
629
+ Raises:
630
+ ValueError: If required parameters are missing.
631
+ """
632
+ rest_client = TeamsInfo._get_rest_client(context)
633
+ return await rest_client.get_conversation_member(conversation_id, user_id)
634
+
635
+ @staticmethod
636
+ def _get_rest_client(context: TurnContext) -> TeamsConnectorClient:
637
+ """
638
+ Gets the Teams connector client from the context.
639
+
640
+ Args:
641
+ context: The turn context.
642
+
643
+ Returns:
644
+ The Teams connector client.
645
+
646
+ Raises:
647
+ ValueError: If the client is not available in the context.
648
+ """
649
+ # TODO: Varify key
650
+ client = context.turn_state.get("ConnectorClient")
651
+ if not client:
652
+ raise ValueError("TeamsConnectorClient is not available in the context.")
653
+ return client
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.4
2
+ Name: microsoft-agents-hosting-teams
3
+ Version: 0.0.0
4
+ Summary: Integration library for Microsoft Agents with Teams
5
+ Author: Microsoft Corporation
6
+ Project-URL: Homepage, https://github.com/microsoft/Agents
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.9
11
+ Requires-Dist: microsoft-agents-hosting-core==0.0.0
12
+ Requires-Dist: aiohttp>=3.11.11
13
+ Dynamic: requires-dist
@@ -0,0 +1,8 @@
1
+ microsoft/agents/hosting/teams/__init__.py,sha256=dFygIL_ogurLI5uq9ikd5C-MZl7uXVw5rUVfi1_H5jY,140
2
+ microsoft/agents/hosting/teams/teams_activity_handler.py,sha256=OVutqeBNgcJJaO3uZarbP-cNg5vxUOlYLJ8VRuriOc0,32762
3
+ microsoft/agents/hosting/teams/teams_cloud_adapter.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ microsoft/agents/hosting/teams/teams_info.py,sha256=lj7OhfxNsYBry2J4286jyK_Q7GMGo8dVRrcDVP7JvoI,20756
5
+ microsoft_agents_hosting_teams-0.0.0.dist-info/METADATA,sha256=QRqQLoNdDggCHRB0Yc_XTQyvPzZKaK7tFY9lZfHceJY,500
6
+ microsoft_agents_hosting_teams-0.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
+ microsoft_agents_hosting_teams-0.0.0.dist-info/top_level.txt,sha256=egwWDmpnNBTGerc55Oa6VVW9hTKdtxFjHWHywqmVMpM,10
8
+ microsoft_agents_hosting_teams-0.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+