microsoft-agents-a365-notifications 0.2.1.dev13__tar.gz → 0.2.1.dev19__tar.gz

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.
Files changed (28) hide show
  1. {microsoft_agents_a365_notifications-0.2.1.dev13 → microsoft_agents_a365_notifications-0.2.1.dev19}/PKG-INFO +1 -1
  2. microsoft_agents_a365_notifications-0.2.1.dev19/microsoft_agents_a365/notifications/agent_notification.py +457 -0
  3. {microsoft_agents_a365_notifications-0.2.1.dev13 → microsoft_agents_a365_notifications-0.2.1.dev19}/microsoft_agents_a365/notifications/models/__init__.py +10 -3
  4. microsoft_agents_a365_notifications-0.2.1.dev19/microsoft_agents_a365/notifications/models/agent_lifecycle_event.py +22 -0
  5. microsoft_agents_a365_notifications-0.2.1.dev19/microsoft_agents_a365/notifications/models/agent_notification_activity.py +176 -0
  6. microsoft_agents_a365_notifications-0.2.1.dev19/microsoft_agents_a365/notifications/models/agent_subchannel.py +26 -0
  7. microsoft_agents_a365_notifications-0.2.1.dev19/microsoft_agents_a365/notifications/models/email_reference.py +25 -0
  8. microsoft_agents_a365_notifications-0.2.1.dev19/microsoft_agents_a365/notifications/models/email_response.py +51 -0
  9. microsoft_agents_a365_notifications-0.2.1.dev19/microsoft_agents_a365/notifications/models/notification_types.py +22 -0
  10. microsoft_agents_a365_notifications-0.2.1.dev19/microsoft_agents_a365/notifications/models/wpx_comment.py +29 -0
  11. {microsoft_agents_a365_notifications-0.2.1.dev13 → microsoft_agents_a365_notifications-0.2.1.dev19}/microsoft_agents_a365_notifications.egg-info/PKG-INFO +1 -1
  12. {microsoft_agents_a365_notifications-0.2.1.dev13 → microsoft_agents_a365_notifications-0.2.1.dev19}/setup.py +2 -1
  13. microsoft_agents_a365_notifications-0.2.1.dev13/microsoft_agents_a365/notifications/agent_notification.py +0 -186
  14. microsoft_agents_a365_notifications-0.2.1.dev13/microsoft_agents_a365/notifications/models/agent_lifecycle_event.py +0 -10
  15. microsoft_agents_a365_notifications-0.2.1.dev13/microsoft_agents_a365/notifications/models/agent_notification_activity.py +0 -88
  16. microsoft_agents_a365_notifications-0.2.1.dev13/microsoft_agents_a365/notifications/models/agent_subchannel.py +0 -12
  17. microsoft_agents_a365_notifications-0.2.1.dev13/microsoft_agents_a365/notifications/models/email_reference.py +0 -13
  18. microsoft_agents_a365_notifications-0.2.1.dev13/microsoft_agents_a365/notifications/models/email_response.py +0 -28
  19. microsoft_agents_a365_notifications-0.2.1.dev13/microsoft_agents_a365/notifications/models/notification_types.py +0 -10
  20. microsoft_agents_a365_notifications-0.2.1.dev13/microsoft_agents_a365/notifications/models/wpx_comment.py +0 -15
  21. {microsoft_agents_a365_notifications-0.2.1.dev13 → microsoft_agents_a365_notifications-0.2.1.dev19}/README.md +0 -0
  22. {microsoft_agents_a365_notifications-0.2.1.dev13 → microsoft_agents_a365_notifications-0.2.1.dev19}/microsoft_agents_a365/notifications/__init__.py +4 -4
  23. {microsoft_agents_a365_notifications-0.2.1.dev13 → microsoft_agents_a365_notifications-0.2.1.dev19}/microsoft_agents_a365_notifications.egg-info/SOURCES.txt +0 -0
  24. {microsoft_agents_a365_notifications-0.2.1.dev13 → microsoft_agents_a365_notifications-0.2.1.dev19}/microsoft_agents_a365_notifications.egg-info/dependency_links.txt +0 -0
  25. {microsoft_agents_a365_notifications-0.2.1.dev13 → microsoft_agents_a365_notifications-0.2.1.dev19}/microsoft_agents_a365_notifications.egg-info/requires.txt +0 -0
  26. {microsoft_agents_a365_notifications-0.2.1.dev13 → microsoft_agents_a365_notifications-0.2.1.dev19}/microsoft_agents_a365_notifications.egg-info/top_level.txt +0 -0
  27. {microsoft_agents_a365_notifications-0.2.1.dev13 → microsoft_agents_a365_notifications-0.2.1.dev19}/pyproject.toml +0 -0
  28. {microsoft_agents_a365_notifications-0.2.1.dev13 → microsoft_agents_a365_notifications-0.2.1.dev19}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: microsoft-agents-a365-notifications
3
- Version: 0.2.1.dev13
3
+ Version: 0.2.1.dev19
4
4
  Summary: Notification and messaging extensions for AI agent applications
5
5
  Author-email: Microsoft <support@microsoft.com>
6
6
  License: MIT
@@ -0,0 +1,457 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ from __future__ import annotations
5
+
6
+ from collections.abc import Awaitable, Callable, Iterable
7
+ from typing import Any, TypeVar
8
+
9
+ from microsoft_agents.activity import ChannelId
10
+ from microsoft_agents.hosting.core import TurnContext
11
+ from microsoft_agents.hosting.core.app.state import TurnState
12
+
13
+ from .models.agent_lifecycle_event import AgentLifecycleEvent
14
+ from .models.agent_notification_activity import AgentNotificationActivity, NotificationTypes
15
+ from .models.agent_subchannel import AgentSubChannel
16
+
17
+ TContext = TypeVar("TContext", bound=TurnContext)
18
+ TState = TypeVar("TState", bound=TurnState)
19
+
20
+ #: Type alias for agent notification handler functions.
21
+ #:
22
+ #: Agent handlers are async functions that process notifications from Microsoft 365
23
+ #: applications. They receive the turn context, application state, and a typed
24
+ #: notification activity wrapper.
25
+ #:
26
+ #: Args:
27
+ #: context: The turn context for the current conversation turn.
28
+ #: state: The application state for the current turn.
29
+ #: notification: The typed notification activity with parsed entities.
30
+ #:
31
+ #: Example:
32
+ #: ```python
33
+ #: async def handle_email(
34
+ #: context: TurnContext,
35
+ #: state: TurnState,
36
+ #: notification: AgentNotificationActivity
37
+ #: ) -> None:
38
+ #: email = notification.email
39
+ #: if email:
40
+ #: print(f"Processing email: {email.id}")
41
+ #: ```
42
+ AgentHandler = Callable[[TContext, TState, AgentNotificationActivity], Awaitable[None]]
43
+
44
+
45
+ class AgentNotification:
46
+ """Handler for agent notifications from Microsoft 365 applications.
47
+
48
+ This class provides decorators for registering handlers that respond to notifications
49
+ from various Microsoft 365 channels and subchannels. It supports routing based on
50
+ channel ID, subchannel, and lifecycle events.
51
+
52
+ Args:
53
+ app: The application instance that will handle the routed notifications.
54
+ known_subchannels: Optional iterable of recognized subchannels. If None,
55
+ defaults to all values in the AgentSubChannel enum.
56
+ known_lifecycle_events: Optional iterable of recognized lifecycle events. If None,
57
+ defaults to all values in the AgentLifecycleEvent enum.
58
+
59
+ Example:
60
+ ```python
61
+ from microsoft_agents.hosting import Application
62
+ from microsoft_agents_a365.notifications import AgentNotification
63
+
64
+ app = Application()
65
+ notifications = AgentNotification(app)
66
+
67
+ @notifications.on_email()
68
+ async def handle_email(context, state, notification):
69
+ email = notification.email
70
+ if email:
71
+ await context.send_activity(f"Received email: {email.id}")
72
+ ```
73
+ """
74
+
75
+ def __init__(
76
+ self,
77
+ app: Any,
78
+ known_subchannels: Iterable[str | AgentSubChannel] | None = None,
79
+ known_lifecycle_events: Iterable[str | AgentLifecycleEvent] | None = None,
80
+ ):
81
+ self._app = app
82
+ if known_subchannels is None:
83
+ source_subchannels: Iterable[str | AgentSubChannel] = AgentSubChannel
84
+ else:
85
+ source_subchannels = known_subchannels
86
+
87
+ self._known_subchannels = {
88
+ normalized
89
+ for normalized in (
90
+ self._normalize_subchannel(sub_channel) for sub_channel in source_subchannels
91
+ )
92
+ if normalized
93
+ }
94
+
95
+ if known_lifecycle_events is None:
96
+ source_lifecycle_events: Iterable[str | AgentLifecycleEvent] = AgentLifecycleEvent
97
+ else:
98
+ source_lifecycle_events = known_lifecycle_events
99
+
100
+ self._known_lifecycle_events = {
101
+ normalized
102
+ for normalized in (
103
+ self._normalize_lifecycleevent(lifecycle_event)
104
+ for lifecycle_event in source_lifecycle_events
105
+ )
106
+ if normalized
107
+ }
108
+
109
+ def on_agent_notification(
110
+ self,
111
+ channel_id: ChannelId,
112
+ **kwargs: Any,
113
+ ):
114
+ """Register a handler for notifications from a specific channel and subchannel.
115
+
116
+ This decorator registers a handler function to be called when a notification is
117
+ received from the specified channel and optional subchannel. The handler will
118
+ receive a typed AgentNotificationActivity wrapper.
119
+
120
+ Args:
121
+ channel_id: The channel ID specifying the channel and optional subchannel
122
+ to listen for. Use "*" as the subchannel to match all subchannels.
123
+ **kwargs: Additional keyword arguments passed to the app's add_route method.
124
+
125
+ Returns:
126
+ A decorator function that registers the handler with the application.
127
+
128
+ Example:
129
+ ```python
130
+ from microsoft_agents.activity import ChannelId
131
+
132
+ @notifications.on_agent_notification(
133
+ ChannelId(channel="agents", sub_channel="email")
134
+ )
135
+ async def handle_custom_channel(context, state, notification):
136
+ print(f"Received notification on {notification.channel}/{notification.sub_channel}")
137
+ ```
138
+ """
139
+ registered_channel = channel_id.channel.lower()
140
+ registered_subchannel = (channel_id.sub_channel or "*").lower()
141
+
142
+ def route_selector(context: TurnContext) -> bool:
143
+ ch = context.activity.channel_id
144
+ received_channel = (ch.channel if ch else "").lower()
145
+ received_subchannel = (ch.sub_channel if ch and ch.sub_channel else "").lower()
146
+ if received_channel != registered_channel:
147
+ return False
148
+ if registered_subchannel == "*":
149
+ return True
150
+ if registered_subchannel not in self._known_subchannels:
151
+ return False
152
+ return received_subchannel == registered_subchannel
153
+
154
+ def create_handler(handler: AgentHandler):
155
+ async def route_handler(context: TurnContext, state: TurnState):
156
+ ana = AgentNotificationActivity(context.activity)
157
+ await handler(context, state, ana)
158
+
159
+ return route_handler
160
+
161
+ def decorator(handler: AgentHandler):
162
+ route_handler = create_handler(handler)
163
+ self._app.add_route(route_selector, route_handler, **kwargs)
164
+ return route_handler
165
+
166
+ return decorator
167
+
168
+ def on_agent_lifecycle_notification(
169
+ self,
170
+ lifecycle_event: str,
171
+ **kwargs: Any,
172
+ ):
173
+ """Register a handler for agent lifecycle event notifications.
174
+
175
+ This decorator registers a handler function to be called when lifecycle events
176
+ occur, such as user creation, deletion, or workload onboarding updates.
177
+
178
+ Args:
179
+ lifecycle_event: The lifecycle event to listen for. Use "*" to match all
180
+ lifecycle events, or specify a specific event from AgentLifecycleEvent.
181
+ **kwargs: Additional keyword arguments passed to the app's add_route method.
182
+
183
+ Returns:
184
+ A decorator function that registers the handler with the application.
185
+
186
+ Example:
187
+ ```python
188
+ @notifications.on_agent_lifecycle_notification("agenticuseridentitycreated")
189
+ async def handle_user_created(context, state, notification):
190
+ print("New user created")
191
+ ```
192
+ """
193
+
194
+ def route_selector(context: TurnContext) -> bool:
195
+ ch = context.activity.channel_id
196
+ received_channel = ch.channel if ch else ""
197
+ received_channel = received_channel.lower()
198
+ if received_channel != "agents":
199
+ return False
200
+ if context.activity.name != NotificationTypes.AGENT_LIFECYCLE:
201
+ return False
202
+ if lifecycle_event == "*":
203
+ return True
204
+ if context.activity.value_type not in self._known_lifecycle_events:
205
+ return False
206
+ return True
207
+
208
+ def create_handler(handler: AgentHandler):
209
+ async def route_handler(context: TurnContext, state: TurnState):
210
+ ana = AgentNotificationActivity(context.activity)
211
+ await handler(context, state, ana)
212
+
213
+ return route_handler
214
+
215
+ def decorator(handler: AgentHandler):
216
+ route_handler = create_handler(handler)
217
+ self._app.add_route(route_selector, route_handler, **kwargs)
218
+ return route_handler
219
+
220
+ return decorator
221
+
222
+ def on_email(
223
+ self, **kwargs: Any
224
+ ) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
225
+ """Register a handler for Outlook email notifications.
226
+
227
+ This is a convenience decorator that registers a handler for notifications
228
+ from the email subchannel.
229
+
230
+ Args:
231
+ **kwargs: Additional keyword arguments passed to the app's add_route method.
232
+
233
+ Returns:
234
+ A decorator function that registers the handler with the application.
235
+
236
+ Example:
237
+ ```python
238
+ @notifications.on_email()
239
+ async def handle_email(context, state, notification):
240
+ email = notification.email
241
+ if email:
242
+ print(f"Received email: {email.id}")
243
+ # Send a response
244
+ response = EmailResponse.create_email_response_activity(
245
+ "<p>Thank you for your email.</p>"
246
+ )
247
+ await context.send_activity(response)
248
+ ```
249
+ """
250
+ return self.on_agent_notification(
251
+ ChannelId(channel="agents", sub_channel=AgentSubChannel.EMAIL), **kwargs
252
+ )
253
+
254
+ def on_word(
255
+ self, **kwargs: Any
256
+ ) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
257
+ """Register a handler for Microsoft Word comment notifications.
258
+
259
+ This is a convenience decorator that registers a handler for notifications
260
+ from the Word subchannel.
261
+
262
+ Args:
263
+ **kwargs: Additional keyword arguments passed to the app's add_route method.
264
+
265
+ Returns:
266
+ A decorator function that registers the handler with the application.
267
+
268
+ Example:
269
+ ```python
270
+ @notifications.on_word()
271
+ async def handle_word_comment(context, state, notification):
272
+ comment = notification.wpx_comment
273
+ if comment:
274
+ print(f"Received Word comment: {comment.comment_id}")
275
+ ```
276
+ """
277
+ return self.on_agent_notification(
278
+ ChannelId(channel="agents", sub_channel=AgentSubChannel.WORD), **kwargs
279
+ )
280
+
281
+ def on_excel(
282
+ self, **kwargs: Any
283
+ ) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
284
+ """Register a handler for Microsoft Excel comment notifications.
285
+
286
+ This is a convenience decorator that registers a handler for notifications
287
+ from the Excel subchannel.
288
+
289
+ Args:
290
+ **kwargs: Additional keyword arguments passed to the app's add_route method.
291
+
292
+ Returns:
293
+ A decorator function that registers the handler with the application.
294
+
295
+ Example:
296
+ ```python
297
+ @notifications.on_excel()
298
+ async def handle_excel_comment(context, state, notification):
299
+ comment = notification.wpx_comment
300
+ if comment:
301
+ print(f"Received Excel comment: {comment.comment_id}")
302
+ ```
303
+ """
304
+ return self.on_agent_notification(
305
+ ChannelId(channel="agents", sub_channel=AgentSubChannel.EXCEL), **kwargs
306
+ )
307
+
308
+ def on_powerpoint(
309
+ self, **kwargs: Any
310
+ ) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
311
+ """Register a handler for Microsoft PowerPoint comment notifications.
312
+
313
+ This is a convenience decorator that registers a handler for notifications
314
+ from the PowerPoint subchannel.
315
+
316
+ Args:
317
+ **kwargs: Additional keyword arguments passed to the app's add_route method.
318
+
319
+ Returns:
320
+ A decorator function that registers the handler with the application.
321
+
322
+ Example:
323
+ ```python
324
+ @notifications.on_powerpoint()
325
+ async def handle_powerpoint_comment(context, state, notification):
326
+ comment = notification.wpx_comment
327
+ if comment:
328
+ print(f"Received PowerPoint comment: {comment.comment_id}")
329
+ ```
330
+ """
331
+ return self.on_agent_notification(
332
+ ChannelId(channel="agents", sub_channel=AgentSubChannel.POWERPOINT), **kwargs
333
+ )
334
+
335
+ def on_lifecycle(
336
+ self, **kwargs: Any
337
+ ) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
338
+ """Register a handler for all agent lifecycle event notifications.
339
+
340
+ This is a convenience decorator that registers a handler for all lifecycle
341
+ events using the wildcard "*" matcher.
342
+
343
+ Args:
344
+ **kwargs: Additional keyword arguments passed to the app's add_route method.
345
+
346
+ Returns:
347
+ A decorator function that registers the handler with the application.
348
+
349
+ Example:
350
+ ```python
351
+ @notifications.on_lifecycle()
352
+ async def handle_any_lifecycle_event(context, state, notification):
353
+ print(f"Lifecycle event type: {notification.notification_type}")
354
+ ```
355
+ """
356
+ return self.on_lifecycle_notification("*", **kwargs)
357
+
358
+ def on_user_created(
359
+ self, **kwargs: Any
360
+ ) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
361
+ """Register a handler for user creation lifecycle events.
362
+
363
+ This is a convenience decorator that registers a handler specifically for
364
+ agentic user identity creation events.
365
+
366
+ Args:
367
+ **kwargs: Additional keyword arguments passed to the app's add_route method.
368
+
369
+ Returns:
370
+ A decorator function that registers the handler with the application.
371
+
372
+ Example:
373
+ ```python
374
+ @notifications.on_user_created()
375
+ async def handle_user_created(context, state, notification):
376
+ print("New agentic user identity created")
377
+ ```
378
+ """
379
+ return self.on_lifecycle_notification(AgentLifecycleEvent.USERCREATED, **kwargs)
380
+
381
+ def on_user_workload_onboarding(
382
+ self, **kwargs: Any
383
+ ) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
384
+ """Register a handler for user workload onboarding update events.
385
+
386
+ This is a convenience decorator that registers a handler for events that occur
387
+ when a user's workload onboarding status is updated.
388
+
389
+ Args:
390
+ **kwargs: Additional keyword arguments passed to the app's add_route method.
391
+
392
+ Returns:
393
+ A decorator function that registers the handler with the application.
394
+
395
+ Example:
396
+ ```python
397
+ @notifications.on_user_workload_onboarding()
398
+ async def handle_onboarding_update(context, state, notification):
399
+ print("User workload onboarding status updated")
400
+ ```
401
+ """
402
+ return self.on_lifecycle_notification(
403
+ AgentLifecycleEvent.USERWORKLOADONBOARDINGUPDATED, **kwargs
404
+ )
405
+
406
+ def on_user_deleted(
407
+ self, **kwargs: Any
408
+ ) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
409
+ """Register a handler for user deletion lifecycle events.
410
+
411
+ This is a convenience decorator that registers a handler specifically for
412
+ agentic user identity deletion events.
413
+
414
+ Args:
415
+ **kwargs: Additional keyword arguments passed to the app's add_route method.
416
+
417
+ Returns:
418
+ A decorator function that registers the handler with the application.
419
+
420
+ Example:
421
+ ```python
422
+ @notifications.on_user_deleted()
423
+ async def handle_user_deleted(context, state, notification):
424
+ print("Agentic user identity deleted")
425
+ ```
426
+ """
427
+ return self.on_lifecycle_notification(AgentLifecycleEvent.USERDELETED, **kwargs)
428
+
429
+ @staticmethod
430
+ def _normalize_subchannel(value: str | AgentSubChannel | None) -> str:
431
+ """Normalize a subchannel value to a lowercase string.
432
+
433
+ Args:
434
+ value: The subchannel value to normalize, either as an enum or string.
435
+
436
+ Returns:
437
+ The normalized lowercase subchannel string, or empty string if None.
438
+ """
439
+ if value is None:
440
+ return ""
441
+ resolved = value.value if isinstance(value, AgentSubChannel) else str(value)
442
+ return resolved.lower().strip()
443
+
444
+ @staticmethod
445
+ def _normalize_lifecycleevent(value: str | AgentLifecycleEvent | None) -> str:
446
+ """Normalize a lifecycle event value to a lowercase string.
447
+
448
+ Args:
449
+ value: The lifecycle event value to normalize, either as an enum or string.
450
+
451
+ Returns:
452
+ The normalized lowercase lifecycle event string, or empty string if None.
453
+ """
454
+ if value is None:
455
+ return ""
456
+ resolved = value.value if isinstance(value, AgentLifecycleEvent) else str(value)
457
+ return resolved.lower().strip()
@@ -1,13 +1,20 @@
1
1
  # Copyright (c) Microsoft Corporation.
2
2
  # Licensed under the MIT License.
3
3
 
4
+ """Models and data classes for agent notifications.
5
+
6
+ This module contains the data models and enums used to represent notifications
7
+ from Microsoft 365 applications, including email references, document comments,
8
+ and lifecycle events.
9
+ """
10
+
11
+ from .agent_lifecycle_event import AgentLifecycleEvent
4
12
  from .agent_notification_activity import AgentNotificationActivity
13
+ from .agent_subchannel import AgentSubChannel
5
14
  from .email_reference import EmailReference
6
- from .wpx_comment import WpxComment
7
15
  from .email_response import EmailResponse
8
16
  from .notification_types import NotificationTypes
9
- from .agent_subchannel import AgentSubChannel
10
- from .agent_lifecycle_event import AgentLifecycleEvent
17
+ from .wpx_comment import WpxComment
11
18
 
12
19
  __all__ = [
13
20
  "AgentNotificationActivity",
@@ -0,0 +1,22 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ from enum import Enum
5
+
6
+
7
+ class AgentLifecycleEvent(str, Enum):
8
+ """Enumeration of agent lifecycle event types.
9
+
10
+ This enum defines the different lifecycle events that can occur for agentic user
11
+ identities in the Microsoft 365 ecosystem.
12
+
13
+ Attributes:
14
+ USERCREATED: Event triggered when a new agentic user identity is created.
15
+ USERWORKLOADONBOARDINGUPDATED: Event triggered when a user's workload
16
+ onboarding status is updated.
17
+ USERDELETED: Event triggered when an agentic user identity is deleted.
18
+ """
19
+
20
+ USERCREATED = "agenticuseridentitycreated"
21
+ USERWORKLOADONBOARDINGUPDATED = "agenticuserworkloadonboardingupdated"
22
+ USERDELETED = "agenticuseridentitydeleted"
@@ -0,0 +1,176 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ from typing import Any, Optional, Type, TypeVar
5
+ from microsoft_agents.activity import Activity
6
+ from .notification_types import NotificationTypes
7
+ from .email_reference import EmailReference
8
+ from .wpx_comment import WpxComment
9
+
10
+ TModel = TypeVar("TModel")
11
+
12
+
13
+ class AgentNotificationActivity:
14
+ """Wrapper around an Activity object with typed notification entities.
15
+
16
+ This class provides convenient access to typed notification entities extracted from
17
+ an Activity's entities collection. It automatically parses and validates email
18
+ notifications, Word/PowerPoint/Excel comments, and lifecycle events at construction
19
+ time.
20
+
21
+ Args:
22
+ activity: The Activity object to wrap. Must not be None.
23
+
24
+ Raises:
25
+ ValueError: If the activity parameter is None.
26
+
27
+ Attributes:
28
+ activity: The underlying Activity object.
29
+
30
+ Example:
31
+ ```python
32
+ async def email_handler(context: TurnContext, state: TurnState, notification: AgentNotificationActivity):
33
+ email = notification.email
34
+ if email:
35
+ print(f"Received email: {email.id}")
36
+ print(f"Body: {email.html_body}")
37
+ ```
38
+ """
39
+
40
+ def __init__(self, activity: Activity):
41
+ if not activity:
42
+ raise ValueError("activity parameter is required and cannot be None")
43
+ self.activity = activity
44
+ self._email: Optional[EmailReference] = None
45
+ self._wpx_comment: Optional[WpxComment] = None
46
+ self._notification_type: Optional[NotificationTypes] = None
47
+
48
+ entities = self.activity.entities or []
49
+ for ent in entities:
50
+ etype = ent.type.lower()
51
+ payload = getattr(ent, "additional_properties", ent)
52
+
53
+ if etype == NotificationTypes.EMAIL_NOTIFICATION.lower() and self._email is None:
54
+ try:
55
+ self._email = EmailReference.model_validate(payload)
56
+ self._notification_type = NotificationTypes.EMAIL_NOTIFICATION
57
+ except Exception:
58
+ self._email = None
59
+
60
+ if etype == NotificationTypes.WPX_COMMENT.lower() and self._wpx_comment is None:
61
+ try:
62
+ self._wpx_comment = WpxComment.model_validate(payload)
63
+ self._notification_type = NotificationTypes.WPX_COMMENT
64
+ except Exception:
65
+ self._wpx_comment = None
66
+
67
+ # Set notification type from activity name if not already set
68
+ if self._notification_type is None:
69
+ self._notification_type = (
70
+ NotificationTypes.AGENT_LIFECYCLE
71
+ if NotificationTypes(self.activity.name) is NotificationTypes.AGENT_LIFECYCLE
72
+ else None
73
+ )
74
+
75
+ # ---- passthroughs ----
76
+ @property
77
+ def channel(self) -> Optional[str]:
78
+ """The channel identifier from the activity's channel_id.
79
+
80
+ Returns:
81
+ The channel name (e.g., 'agents', 'msteams') or None if not available.
82
+ """
83
+ ch = self.activity.channel_id
84
+ return ch.channel if ch else None
85
+
86
+ @property
87
+ def sub_channel(self) -> Optional[str]:
88
+ """The subchannel identifier from the activity's channel_id.
89
+
90
+ Returns:
91
+ The subchannel name (e.g., 'email', 'word') or None if not available.
92
+ """
93
+ ch = self.activity.channel_id
94
+ return ch.sub_channel if ch else None
95
+
96
+ @property
97
+ def value(self) -> Any:
98
+ """The value payload from the activity.
99
+
100
+ Returns:
101
+ The activity's value, which may contain additional notification data.
102
+ """
103
+ return self.activity.value
104
+
105
+ @property
106
+ def type(self) -> Optional[str]:
107
+ """The activity type.
108
+
109
+ Returns:
110
+ The type of the activity (e.g., 'message', 'event') or None if not set.
111
+ """
112
+ return self.activity.type
113
+
114
+ # --- typed entities available directly on the activity ---
115
+ @property
116
+ def email(self) -> Optional[EmailReference]:
117
+ """The parsed email reference entity, if present.
118
+
119
+ Returns:
120
+ An EmailReference object if an email notification entity was found and
121
+ successfully parsed, otherwise None.
122
+ """
123
+ return self._email
124
+
125
+ @property
126
+ def wpx_comment(self) -> Optional[WpxComment]:
127
+ """The parsed Word/PowerPoint/Excel comment entity, if present.
128
+
129
+ Returns:
130
+ A WpxComment object if a comment entity was found and successfully parsed,
131
+ otherwise None.
132
+ """
133
+ return self._wpx_comment
134
+
135
+ @property
136
+ def notification_type(self) -> Optional[NotificationTypes]:
137
+ """The detected notification type.
138
+
139
+ Returns:
140
+ The NotificationTypes enum value indicating the type of notification
141
+ (EMAIL_NOTIFICATION, WPX_COMMENT, or AGENT_LIFECYCLE), or None if the
142
+ notification type could not be determined.
143
+ """
144
+ return self._notification_type
145
+
146
+ # Generic escape hatch
147
+ def as_model(self, model: Type[TModel]) -> Optional[TModel]:
148
+ """Parse the activity value as a custom model type.
149
+
150
+ This method provides a generic way to validate and parse the activity's value
151
+ payload into any Pydantic model type. Useful for custom notification types not
152
+ directly supported by the typed properties.
153
+
154
+ Args:
155
+ model: A Pydantic model class to validate and parse the activity value into.
156
+
157
+ Returns:
158
+ An instance of the specified model type if validation succeeds, otherwise None.
159
+
160
+ Example:
161
+ ```python
162
+ from pydantic import BaseModel
163
+
164
+ class CustomNotification(BaseModel):
165
+ custom_field: str
166
+
167
+ notification = AgentNotificationActivity(activity)
168
+ custom = notification.as_model(CustomNotification)
169
+ if custom:
170
+ print(custom.custom_field)
171
+ ```
172
+ """
173
+ try:
174
+ return model.model_validate(self.value or {})
175
+ except Exception:
176
+ return None
@@ -0,0 +1,26 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ from enum import Enum
5
+
6
+
7
+ class AgentSubChannel(str, Enum):
8
+ """Enumeration of agent subchannels within Microsoft 365 applications.
9
+
10
+ This enum defines the different subchannels through which agents can receive
11
+ notifications and messages from specific Microsoft 365 applications.
12
+
13
+ Attributes:
14
+ EMAIL: Email subchannel for Outlook-related notifications.
15
+ EXCEL: Excel subchannel for spreadsheet-related notifications.
16
+ WORD: Word subchannel for document-related notifications.
17
+ POWERPOINT: PowerPoint subchannel for presentation-related notifications.
18
+ FEDERATED_KNOWLEDGE_SERVICE: Federated Knowledge Service subchannel for
19
+ knowledge graph and search-related notifications.
20
+ """
21
+
22
+ EMAIL = "email"
23
+ EXCEL = "excel"
24
+ WORD = "word"
25
+ POWERPOINT = "powerpoint"
26
+ FEDERATED_KNOWLEDGE_SERVICE = "federatedknowledgeservice"
@@ -0,0 +1,25 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ from typing import Optional, Literal
5
+ from microsoft_agents.activity.entity import Entity
6
+ from .notification_types import NotificationTypes
7
+
8
+
9
+ class EmailReference(Entity):
10
+ """Entity representing an email notification reference.
11
+
12
+ This class encapsulates information about an email notification that an agent
13
+ receives from Outlook, including the email ID, conversation context, and content.
14
+
15
+ Attributes:
16
+ type: The notification type identifier, always set to "emailNotification".
17
+ id: The unique identifier of the email message.
18
+ conversation_id: The identifier of the conversation thread this email belongs to.
19
+ html_body: The HTML content of the email body.
20
+ """
21
+
22
+ type: Literal["emailNotification"] = NotificationTypes.EMAIL_NOTIFICATION
23
+ id: Optional[str] = None
24
+ conversation_id: Optional[str] = None
25
+ html_body: Optional[str] = None
@@ -0,0 +1,51 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ from typing import Literal
5
+
6
+ from microsoft_agents.activity.activity import Activity
7
+ from microsoft_agents.activity.entity import Entity
8
+
9
+
10
+ class EmailResponse(Entity):
11
+ """Entity representing an email response to be sent by an agent.
12
+
13
+ This class encapsulates the HTML content that will be sent as a response to an
14
+ email notification. It is used to construct reply messages in Outlook scenarios.
15
+
16
+ Attributes:
17
+ type: The entity type identifier, always set to "emailResponse".
18
+ html_body: The HTML content of the email response body. Defaults to empty string.
19
+ """
20
+
21
+ type: Literal["emailResponse"] = "emailResponse"
22
+ html_body: str = ""
23
+
24
+ @staticmethod
25
+ def create_email_response_activity(email_response_html_body: str) -> Activity:
26
+ """Create a new Activity with an EmailResponse entity.
27
+
28
+ This factory method constructs a message activity containing an EmailResponse
29
+ entity, which can be sent back to respond to an email notification.
30
+
31
+ Args:
32
+ email_response_html_body: The HTML content for the email response body.
33
+
34
+ Returns:
35
+ A new Activity instance with type set to 'message' and the EmailResponse
36
+ entity attached to its entities list.
37
+
38
+ Example:
39
+ ```python
40
+ activity = EmailResponse.create_email_response_activity(
41
+ "<p>Thank you for your email. I'll get back to you soon.</p>"
42
+ )
43
+ await context.send_activity(activity)
44
+ ```
45
+ """
46
+ working_activity = Activity(type="message")
47
+ email_response = EmailResponse(html_body=email_response_html_body)
48
+ if working_activity.entities is None:
49
+ working_activity.entities = []
50
+ working_activity.entities.append(email_response)
51
+ return working_activity
@@ -0,0 +1,22 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ from enum import Enum
5
+
6
+
7
+ class NotificationTypes(str, Enum):
8
+ """Enumeration of notification types supported by Agent 365.
9
+
10
+ This enum defines the different types of notifications that agents can receive
11
+ from Microsoft 365 applications and services.
12
+
13
+ Attributes:
14
+ EMAIL_NOTIFICATION: Notification related to email events in Outlook.
15
+ WPX_COMMENT: Notification related to comments in Word, PowerPoint, or Excel.
16
+ AGENT_LIFECYCLE: Notification related to agent lifecycle events such as user
17
+ creation, deletion, or workload onboarding updates.
18
+ """
19
+
20
+ EMAIL_NOTIFICATION = "emailNotification"
21
+ WPX_COMMENT = "wpxComment"
22
+ AGENT_LIFECYCLE = "agentLifecycle"
@@ -0,0 +1,29 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ from typing import Optional, Literal
5
+ from microsoft_agents.activity.entity import Entity
6
+ from .notification_types import NotificationTypes
7
+
8
+
9
+ class WpxComment(Entity):
10
+ """Entity representing a comment notification from Word, PowerPoint, or Excel.
11
+
12
+ This class encapsulates information about a comment made in a Microsoft Office
13
+ document (Word, PowerPoint, or Excel), including the document context and
14
+ comment hierarchy.
15
+
16
+ Attributes:
17
+ type: The notification type identifier, always set to "wpxComment".
18
+ odata_id: The OData identifier for the comment resource.
19
+ document_id: The unique identifier of the document containing the comment.
20
+ parent_comment_id: The identifier of the parent comment, if this is a reply.
21
+ comment_id: The unique identifier of this comment.
22
+ """
23
+
24
+ type: Literal["wpxComment"] = NotificationTypes.WPX_COMMENT
25
+
26
+ odata_id: Optional[str] = None
27
+ document_id: Optional[str] = None
28
+ parent_comment_id: Optional[str] = None
29
+ comment_id: Optional[str] = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: microsoft-agents-a365-notifications
3
- Version: 0.2.1.dev13
3
+ Version: 0.2.1.dev19
4
4
  Summary: Notification and messaging extensions for AI agent applications
5
5
  Author-email: Microsoft <support@microsoft.com>
6
6
  License: MIT
@@ -2,8 +2,9 @@
2
2
  # Licensed under the MIT License.
3
3
 
4
4
  import sys
5
- from pathlib import Path
6
5
  from os import environ
6
+ from pathlib import Path
7
+
7
8
  from setuptools import setup
8
9
 
9
10
  # Get version from environment variable set by CI/CD
@@ -1,186 +0,0 @@
1
- # Copyright (c) Microsoft Corporation.
2
- # Licensed under the MIT License.
3
-
4
- from __future__ import annotations
5
-
6
- from collections.abc import Awaitable, Callable, Iterable
7
- from typing import Any, TypeVar
8
-
9
- from microsoft_agents.activity import ChannelId
10
- from microsoft_agents.hosting.core import TurnContext
11
- from microsoft_agents.hosting.core.app.state import TurnState
12
- from .models.agent_notification_activity import AgentNotificationActivity, NotificationTypes
13
- from .models.agent_subchannel import AgentSubChannel
14
- from .models.agent_lifecycle_event import AgentLifecycleEvent
15
-
16
- TContext = TypeVar("TContext", bound=TurnContext)
17
- TState = TypeVar("TState", bound=TurnState)
18
-
19
- AgentHandler = Callable[[TContext, TState, AgentNotificationActivity], Awaitable[None]]
20
-
21
-
22
- class AgentNotification:
23
- def __init__(
24
- self,
25
- app: Any,
26
- known_subchannels: Iterable[str | AgentSubChannel] | None = None,
27
- known_lifecycle_events: Iterable[str | AgentLifecycleEvent] | None = None,
28
- ):
29
- self._app = app
30
- if known_subchannels is None:
31
- source_subchannels: Iterable[str | AgentSubChannel] = AgentSubChannel
32
- else:
33
- source_subchannels = known_subchannels
34
-
35
- self._known_subchannels = {
36
- normalized
37
- for normalized in (
38
- self._normalize_subchannel(sub_channel) for sub_channel in source_subchannels
39
- )
40
- if normalized
41
- }
42
-
43
- if known_lifecycle_events is None:
44
- source_lifecycle_events: Iterable[str | AgentLifecycleEvent] = AgentLifecycleEvent
45
- else:
46
- source_lifecycle_events = known_lifecycle_events
47
-
48
- self._known_lifecycle_events = {
49
- normalized
50
- for normalized in (
51
- self._normalize_lifecycleevent(lifecycle_event)
52
- for lifecycle_event in source_lifecycle_events
53
- )
54
- if normalized
55
- }
56
-
57
- def on_agent_notification(
58
- self,
59
- channel_id: ChannelId,
60
- **kwargs: Any,
61
- ):
62
- registered_channel = channel_id.channel.lower()
63
- registered_subchannel = (channel_id.sub_channel or "*").lower()
64
-
65
- def route_selector(context: TurnContext) -> bool:
66
- ch = context.activity.channel_id
67
- received_channel = (ch.channel if ch else "").lower()
68
- received_subchannel = (ch.sub_channel if ch and ch.sub_channel else "").lower()
69
- if received_channel != registered_channel:
70
- return False
71
- if registered_subchannel == "*":
72
- return True
73
- if registered_subchannel not in self._known_subchannels:
74
- return False
75
- return received_subchannel == registered_subchannel
76
-
77
- def create_handler(handler: AgentHandler):
78
- async def route_handler(context: TurnContext, state: TurnState):
79
- ana = AgentNotificationActivity(context.activity)
80
- await handler(context, state, ana)
81
-
82
- return route_handler
83
-
84
- def decorator(handler: AgentHandler):
85
- route_handler = create_handler(handler)
86
- self._app.add_route(route_selector, route_handler, **kwargs)
87
- return route_handler
88
-
89
- return decorator
90
-
91
- def on_agent_lifecycle_notification(
92
- self,
93
- lifecycle_event: str,
94
- **kwargs: Any,
95
- ):
96
- def route_selector(context: TurnContext) -> bool:
97
- ch = context.activity.channel_id
98
- received_channel = ch.channel if ch else ""
99
- received_channel = received_channel.lower()
100
- if received_channel != "agents":
101
- return False
102
- if context.activity.name != NotificationTypes.AGENT_LIFECYCLE:
103
- return False
104
- if lifecycle_event == "*":
105
- return True
106
- if context.activity.value_type not in self._known_lifecycle_events:
107
- return False
108
- return True
109
-
110
- def create_handler(handler: AgentHandler):
111
- async def route_handler(context: TurnContext, state: TurnState):
112
- ana = AgentNotificationActivity(context.activity)
113
- await handler(context, state, ana)
114
-
115
- return route_handler
116
-
117
- def decorator(handler: AgentHandler):
118
- route_handler = create_handler(handler)
119
- self._app.add_route(route_selector, route_handler, **kwargs)
120
- return route_handler
121
-
122
- return decorator
123
-
124
- def on_email(
125
- self, **kwargs: Any
126
- ) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
127
- return self.on_agent_notification(
128
- ChannelId(channel="agents", sub_channel=AgentSubChannel.EMAIL), **kwargs
129
- )
130
-
131
- def on_word(
132
- self, **kwargs: Any
133
- ) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
134
- return self.on_agent_notification(
135
- ChannelId(channel="agents", sub_channel=AgentSubChannel.WORD), **kwargs
136
- )
137
-
138
- def on_excel(
139
- self, **kwargs: Any
140
- ) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
141
- return self.on_agent_notification(
142
- ChannelId(channel="agents", sub_channel=AgentSubChannel.EXCEL), **kwargs
143
- )
144
-
145
- def on_powerpoint(
146
- self, **kwargs: Any
147
- ) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
148
- return self.on_agent_notification(
149
- ChannelId(channel="agents", sub_channel=AgentSubChannel.POWERPOINT), **kwargs
150
- )
151
-
152
- def on_lifecycle(
153
- self, **kwargs: Any
154
- ) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
155
- return self.on_lifecycle_notification("*", **kwargs)
156
-
157
- def on_user_created(
158
- self, **kwargs: Any
159
- ) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
160
- return self.on_lifecycle_notification(AgentLifecycleEvent.USERCREATED, **kwargs)
161
-
162
- def on_user_workload_onboarding(
163
- self, **kwargs: Any
164
- ) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
165
- return self.on_lifecycle_notification(
166
- AgentLifecycleEvent.USERWORKLOADONBOARDINGUPDATED, **kwargs
167
- )
168
-
169
- def on_user_deleted(
170
- self, **kwargs: Any
171
- ) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
172
- return self.on_lifecycle_notification(AgentLifecycleEvent.USERDELETED, **kwargs)
173
-
174
- @staticmethod
175
- def _normalize_subchannel(value: str | AgentSubChannel | None) -> str:
176
- if value is None:
177
- return ""
178
- resolved = value.value if isinstance(value, AgentSubChannel) else str(value)
179
- return resolved.lower().strip()
180
-
181
- @staticmethod
182
- def _normalize_lifecycleevent(value: str | AgentLifecycleEvent | None) -> str:
183
- if value is None:
184
- return ""
185
- resolved = value.value if isinstance(value, AgentLifecycleEvent) else str(value)
186
- return resolved.lower().strip()
@@ -1,10 +0,0 @@
1
- # Copyright (c) Microsoft Corporation.
2
- # Licensed under the MIT License.
3
-
4
- from enum import Enum
5
-
6
-
7
- class AgentLifecycleEvent(str, Enum):
8
- USERCREATED = "agenticuseridentitycreated"
9
- USERWORKLOADONBOARDINGUPDATED = "agenticuserworkloadonboardingupdated"
10
- USERDELETED = "agenticuseridentitydeleted"
@@ -1,88 +0,0 @@
1
- # Copyright (c) Microsoft Corporation.
2
- # Licensed under the MIT License.
3
-
4
- from typing import Any, Optional, Type, TypeVar
5
- from microsoft_agents.activity import Activity
6
- from .notification_types import NotificationTypes
7
- from .email_reference import EmailReference
8
- from .wpx_comment import WpxComment
9
-
10
- TModel = TypeVar("TModel")
11
-
12
-
13
- class AgentNotificationActivity:
14
- """Light wrapper around an Activity object with typed entities parsed at create time."""
15
-
16
- def __init__(self, activity: Activity):
17
- if not activity:
18
- raise ValueError("activity parameter is required and cannot be None")
19
- self.activity = activity
20
- self._email: Optional[EmailReference] = None
21
- self._wpx_comment: Optional[WpxComment] = None
22
- self._notification_type: Optional[NotificationTypes] = None
23
-
24
- entities = self.activity.entities or []
25
- for ent in entities:
26
- etype = ent.type.lower()
27
- payload = getattr(ent, "additional_properties", ent)
28
-
29
- if etype == NotificationTypes.EMAIL_NOTIFICATION.lower() and self._email is None:
30
- try:
31
- self._email = EmailReference.model_validate(payload)
32
- self._notification_type = NotificationTypes.EMAIL_NOTIFICATION
33
- except Exception:
34
- self._email = None
35
-
36
- if etype == NotificationTypes.WPX_COMMENT.lower() and self._wpx_comment is None:
37
- try:
38
- self._wpx_comment = WpxComment.model_validate(payload)
39
- self._notification_type = NotificationTypes.WPX_COMMENT
40
- except Exception:
41
- self._wpx_comment = None
42
-
43
- # Set notification type from activity name if not already set
44
- if self._notification_type is None:
45
- self._notification_type = (
46
- NotificationTypes.AGENT_LIFECYCLE
47
- if NotificationTypes(self.activity.name) is NotificationTypes.AGENT_LIFECYCLE
48
- else None
49
- )
50
-
51
- # ---- passthroughs ----
52
- @property
53
- def channel(self) -> Optional[str]:
54
- ch = self.activity.channel_id
55
- return ch.channel if ch else None
56
-
57
- @property
58
- def sub_channel(self) -> Optional[str]:
59
- ch = self.activity.channel_id
60
- return ch.sub_channel if ch else None
61
-
62
- @property
63
- def value(self) -> Any:
64
- return self.activity.value
65
-
66
- @property
67
- def type(self) -> Optional[str]:
68
- return self.activity.type
69
-
70
- # --- typed entities available directly on the activity ---
71
- @property
72
- def email(self) -> Optional[EmailReference]:
73
- return self._email
74
-
75
- @property
76
- def wpx_comment(self) -> Optional[WpxComment]:
77
- return self._wpx_comment
78
-
79
- @property
80
- def notification_type(self) -> Optional[NotificationTypes]:
81
- return self._notification_type
82
-
83
- # Generic escape hatch
84
- def as_model(self, model: Type[TModel]) -> Optional[TModel]:
85
- try:
86
- return model.model_validate(self.value or {})
87
- except Exception:
88
- return None
@@ -1,12 +0,0 @@
1
- # Copyright (c) Microsoft Corporation.
2
- # Licensed under the MIT License.
3
-
4
- from enum import Enum
5
-
6
-
7
- class AgentSubChannel(str, Enum):
8
- EMAIL = "email"
9
- EXCEL = "excel"
10
- WORD = "word"
11
- POWERPOINT = "powerpoint"
12
- FEDERATED_KNOWLEDGE_SERVICE = "federatedknowledgeservice"
@@ -1,13 +0,0 @@
1
- # Copyright (c) Microsoft Corporation.
2
- # Licensed under the MIT License.
3
-
4
- from typing import Optional, Literal
5
- from microsoft_agents.activity.entity import Entity
6
- from .notification_types import NotificationTypes
7
-
8
-
9
- class EmailReference(Entity):
10
- type: Literal["emailNotification"] = NotificationTypes.EMAIL_NOTIFICATION
11
- id: Optional[str] = None
12
- conversation_id: Optional[str] = None
13
- html_body: Optional[str] = None
@@ -1,28 +0,0 @@
1
- # Copyright (c) Microsoft Corporation.
2
- # Licensed under the MIT License.
3
-
4
- from typing import Literal
5
- from microsoft_agents.activity.activity import Activity
6
- from microsoft_agents.activity.entity import Entity
7
-
8
-
9
- class EmailResponse(Entity):
10
- type: Literal["emailResponse"] = "emailResponse"
11
- html_body: str = ""
12
-
13
- @staticmethod
14
- def create_email_response_activity(email_response_html_body: str) -> Activity:
15
- """Create a new Activity with an EmailResponse entity.
16
-
17
- Args:
18
- email_response_html_body: The HTML content for the email response.
19
-
20
- Returns:
21
- A new Activity instance with type='message' and the EmailResponse entity attached.
22
- """
23
- working_activity = Activity(type="message")
24
- email_response = EmailResponse(html_body=email_response_html_body)
25
- if working_activity.entities is None:
26
- working_activity.entities = []
27
- working_activity.entities.append(email_response)
28
- return working_activity
@@ -1,10 +0,0 @@
1
- # Copyright (c) Microsoft Corporation.
2
- # Licensed under the MIT License.
3
-
4
- from enum import Enum
5
-
6
-
7
- class NotificationTypes(str, Enum):
8
- EMAIL_NOTIFICATION = "emailNotification"
9
- WPX_COMMENT = "wpxComment"
10
- AGENT_LIFECYCLE = "agentLifecycle"
@@ -1,15 +0,0 @@
1
- # Copyright (c) Microsoft Corporation.
2
- # Licensed under the MIT License.
3
-
4
- from typing import Optional, Literal
5
- from microsoft_agents.activity.entity import Entity
6
- from .notification_types import NotificationTypes
7
-
8
-
9
- class WpxComment(Entity):
10
- type: Literal["wpxComment"] = NotificationTypes.WPX_COMMENT
11
-
12
- odata_id: Optional[str] = None
13
- document_id: Optional[str] = None
14
- parent_comment_id: Optional[str] = None
15
- comment_id: Optional[str] = None
@@ -10,19 +10,19 @@ This module provides utilities for handling agent notifications and routing.
10
10
 
11
11
  # Main notification handler class
12
12
  from .agent_notification import (
13
- AgentNotification,
14
13
  AgentHandler,
14
+ AgentNotification,
15
15
  )
16
16
 
17
17
  # Import all models from the models subpackage
18
18
  from .models import (
19
+ AgentLifecycleEvent,
19
20
  AgentNotificationActivity,
21
+ AgentSubChannel,
20
22
  EmailReference,
21
- WpxComment,
22
23
  EmailResponse,
23
24
  NotificationTypes,
24
- AgentSubChannel,
25
- AgentLifecycleEvent,
25
+ WpxComment,
26
26
  )
27
27
 
28
28
  __all__ = [