Skip to content

Commit 8b12860

Browse files
authored
Merge branch 'main' into v-gandiddi/teams-batch-operations
2 parents 0f445ad + a9298c8 commit 8b12860

File tree

4 files changed

+325
-0
lines changed

4 files changed

+325
-0
lines changed

libraries/botbuilder-core/botbuilder/core/activity_handler.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ async def on_turn(
6868

6969
if turn_context.activity.type == ActivityTypes.message:
7070
await self.on_message_activity(turn_context)
71+
elif turn_context.activity.type == ActivityTypes.message_update:
72+
await self.on_message_update_activity(turn_context)
73+
elif turn_context.activity.type == ActivityTypes.message_delete:
74+
await self.on_message_delete_activity(turn_context)
7175
elif turn_context.activity.type == ActivityTypes.conversation_update:
7276
await self.on_conversation_update_activity(turn_context)
7377
elif turn_context.activity.type == ActivityTypes.message_reaction:
@@ -107,6 +111,34 @@ async def on_message_activity( # pylint: disable=unused-argument
107111
"""
108112
return
109113

114+
async def on_message_update_activity( # pylint: disable=unused-argument
115+
self, turn_context: TurnContext
116+
):
117+
"""
118+
Override this method in a derived class to provide logic specific to activities,
119+
such as the conversational logic.
120+
121+
:param turn_context: The context object for this turn
122+
:type turn_context: :class:`botbuilder.core.TurnContext`
123+
124+
:returns: A task that represents the work queued to execute
125+
"""
126+
return
127+
128+
async def on_message_delete_activity( # pylint: disable=unused-argument
129+
self, turn_context: TurnContext
130+
):
131+
"""
132+
Override this method in a derived class to provide logic specific to activities,
133+
such as the conversational logic.
134+
135+
:param turn_context: The context object for this turn
136+
:type turn_context: :class:`botbuilder.core.TurnContext`
137+
138+
:returns: A task that represents the work queued to execute
139+
"""
140+
return
141+
110142
async def on_conversation_update_activity(self, turn_context: TurnContext):
111143
"""
112144
Invoked when a conversation update activity is received from the channel when the base behavior of

libraries/botbuilder-core/botbuilder/core/teams/teams_activity_handler.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,16 @@ async def on_invoke_activity(self, turn_context: TurnContext) -> InvokeResponse:
9090
)
9191
)
9292

93+
if turn_context.activity.name == "composeExtension/anonymousQueryLink":
94+
return self._create_invoke_response(
95+
await self.on_teams_anonymous_app_based_link_query(
96+
turn_context,
97+
deserializer_helper(
98+
AppBasedLinkQuery, turn_context.activity.value
99+
),
100+
)
101+
)
102+
93103
if turn_context.activity.name == "composeExtension/query":
94104
return self._create_invoke_response(
95105
await self.on_teams_messaging_extension_query(
@@ -331,6 +341,19 @@ async def on_teams_app_based_link_query( # pylint: disable=unused-argument
331341
"""
332342
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
333343

344+
async def on_teams_anonymous_app_based_link_query( # pylint: disable=unused-argument
345+
self, turn_context: TurnContext, query: AppBasedLinkQuery
346+
) -> MessagingExtensionResponse:
347+
"""
348+
Invoked when an anonymous app based link query activity is received from the connector.
349+
350+
:param turn_context: A context object for this turn.
351+
:param query: The invoke request body type for app-based link query.
352+
353+
:returns: The Messaging Extension Response for the query.
354+
"""
355+
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
356+
334357
async def on_teams_messaging_extension_query( # pylint: disable=unused-argument
335358
self, turn_context: TurnContext, query: MessagingExtensionQuery
336359
) -> MessagingExtensionResponse:
@@ -1042,3 +1065,73 @@ async def on_teams_meeting_participants_leave_event(
10421065
:returns: A task that represents the work queued to execute.
10431066
"""
10441067
return
1068+
1069+
async def on_message_update_activity(self, turn_context: TurnContext):
1070+
"""
1071+
Invoked when a message update activity is received, such as a message edit or undelete.
1072+
1073+
:param turn_context: A context object for this turn.
1074+
1075+
:returns: A task that represents the work queued to execute.
1076+
"""
1077+
if turn_context.activity.channel_id == Channels.ms_teams:
1078+
channel_data = TeamsChannelData().deserialize(
1079+
turn_context.activity.channel_data
1080+
)
1081+
1082+
if channel_data:
1083+
if channel_data.event_type == "editMessage":
1084+
return await self.on_teams_message_edit(turn_context)
1085+
if channel_data.event_type == "undeleteMessage":
1086+
return await self.on_teams_message_undelete(turn_context)
1087+
1088+
return await super().on_message_update_activity(turn_context)
1089+
1090+
async def on_message_delete_activity(self, turn_context: TurnContext):
1091+
"""
1092+
Invoked when a message delete activity is received, such as a soft delete message.
1093+
1094+
:param turn_context: A context object for this turn.
1095+
1096+
:returns: A task that represents the work queued to execute.
1097+
"""
1098+
if turn_context.activity.channel_id == Channels.ms_teams:
1099+
channel_data = TeamsChannelData().deserialize(
1100+
turn_context.activity.channel_data
1101+
)
1102+
1103+
if channel_data:
1104+
if channel_data.event_type == "softDeleteMessage":
1105+
return await self.on_teams_message_soft_delete(turn_context)
1106+
1107+
return await super().on_message_delete_activity(turn_context)
1108+
1109+
async def on_teams_message_edit(self, turn_context: TurnContext):
1110+
"""
1111+
Invoked when a Teams edit message event activity is received.
1112+
1113+
:param turn_context: A context object for this turn.
1114+
1115+
:returns: A task that represents the work queued to execute.
1116+
"""
1117+
return
1118+
1119+
async def on_teams_message_undelete(self, turn_context: TurnContext):
1120+
"""
1121+
Invoked when a Teams undo soft delete message event activity is received.
1122+
1123+
:param turn_context: A context object for this turn.
1124+
1125+
:returns: A task that represents the work queued to execute.
1126+
"""
1127+
return
1128+
1129+
async def on_teams_message_soft_delete(self, turn_context: TurnContext):
1130+
"""
1131+
Invoked when a Teams soft delete message event activity is received.
1132+
1133+
:param turn_context: A context object for this turn.
1134+
1135+
:returns: A task that represents the work queued to execute.
1136+
"""
1137+
return

libraries/botbuilder-core/tests/teams/test_teams_activity_handler.py

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
TabContext,
3535
MeetingParticipantsEventDetails,
3636
ReadReceiptInfo,
37+
TeamsChannelData,
3738
)
3839
from botframework.connector import Channels
3940
from simple_adapter import SimpleAdapter
@@ -219,6 +220,14 @@ async def on_teams_messaging_extension_query(
219220
self.record.append("on_teams_messaging_extension_query")
220221
return await super().on_teams_messaging_extension_query(turn_context, query)
221222

223+
async def on_teams_anonymous_app_based_link_query(
224+
self, turn_context: TurnContext, query: AppBasedLinkQuery
225+
):
226+
self.record.append("on_teams_anonymous_app_based_link_query")
227+
return await super().on_teams_anonymous_app_based_link_query(
228+
turn_context, query
229+
)
230+
222231
async def on_teams_messaging_extension_submit_action_dispatch(
223232
self, turn_context: TurnContext, action: MessagingExtensionAction
224233
):
@@ -351,6 +360,26 @@ async def on_teams_meeting_end_event(
351360
turn_context.activity.value, turn_context
352361
)
353362

363+
async def on_message_update_activity(self, turn_context: TurnContext):
364+
self.record.append("on_message_update_activity")
365+
return await super().on_message_update_activity(turn_context)
366+
367+
async def on_teams_message_edit(self, turn_context: TurnContext):
368+
self.record.append("on_teams_message_edit")
369+
return await super().on_teams_message_edit(turn_context)
370+
371+
async def on_teams_message_undelete(self, turn_context: TurnContext):
372+
self.record.append("on_teams_message_undelete")
373+
return await super().on_teams_message_undelete(turn_context)
374+
375+
async def on_message_delete_activity(self, turn_context: TurnContext):
376+
self.record.append("on_message_delete_activity")
377+
return await super().on_message_delete_activity(turn_context)
378+
379+
async def on_teams_message_soft_delete(self, turn_context: TurnContext):
380+
self.record.append("on_teams_message_soft_delete")
381+
return await super().on_teams_message_soft_delete(turn_context)
382+
354383
async def on_teams_meeting_participants_join_event(
355384
self, meeting: MeetingParticipantsEventDetails, turn_context: TurnContext
356385
):
@@ -816,6 +845,25 @@ async def test_on_app_based_link_query(self):
816845
assert bot.record[0] == "on_invoke_activity"
817846
assert bot.record[1] == "on_teams_messaging_extension_query"
818847

848+
async def test_compose_extension_anonymous_query_link(self):
849+
# arrange
850+
activity = Activity(
851+
type=ActivityTypes.invoke,
852+
name="composeExtension/anonymousQueryLink",
853+
value={"url": "http://www.test.com"},
854+
)
855+
856+
turn_context = TurnContext(SimpleAdapter(), activity)
857+
858+
# Act
859+
bot = TestingTeamsActivityHandler()
860+
await bot.on_turn(turn_context)
861+
862+
# Assert
863+
assert len(bot.record) == 2
864+
assert bot.record[0] == "on_invoke_activity"
865+
assert bot.record[1] == "on_teams_anonymous_app_based_link_query"
866+
819867
async def test_on_teams_messaging_extension_bot_message_preview_edit_activity(self):
820868
# Arrange
821869

@@ -1254,6 +1302,124 @@ async def test_on_teams_meeting_end_event(self):
12541302
assert bot.record[0] == "on_event_activity"
12551303
assert bot.record[1] == "on_teams_meeting_end_event"
12561304

1305+
async def test_message_update_activity_teams_message_edit(self):
1306+
# Arrange
1307+
activity = Activity(
1308+
type=ActivityTypes.message_update,
1309+
channel_data=TeamsChannelData(event_type="editMessage"),
1310+
channel_id=Channels.ms_teams,
1311+
)
1312+
turn_context = TurnContext(SimpleAdapter(), activity)
1313+
1314+
# Act
1315+
bot = TestingTeamsActivityHandler()
1316+
await bot.on_turn(turn_context)
1317+
1318+
# Assert
1319+
self.assertEqual(2, len(bot.record))
1320+
self.assertEqual("on_message_update_activity", bot.record[0])
1321+
self.assertEqual("on_teams_message_edit", bot.record[1])
1322+
1323+
async def test_message_update_activity_teams_message_undelete(self):
1324+
# Arrange
1325+
activity = Activity(
1326+
type=ActivityTypes.message_update,
1327+
channel_data=TeamsChannelData(event_type="undeleteMessage"),
1328+
channel_id=Channels.ms_teams,
1329+
)
1330+
turn_context = TurnContext(SimpleAdapter(), activity)
1331+
1332+
# Act
1333+
bot = TestingTeamsActivityHandler()
1334+
await bot.on_turn(turn_context)
1335+
1336+
# Assert
1337+
self.assertEqual(2, len(bot.record))
1338+
self.assertEqual("on_message_update_activity", bot.record[0])
1339+
self.assertEqual("on_teams_message_undelete", bot.record[1])
1340+
1341+
async def test_message_update_activity_teams_message_undelete_no_msteams(self):
1342+
# Arrange
1343+
activity = Activity(
1344+
type=ActivityTypes.message_update,
1345+
channel_data=TeamsChannelData(event_type="undeleteMessage"),
1346+
)
1347+
turn_context = TurnContext(SimpleAdapter(), activity)
1348+
1349+
# Act
1350+
bot = TestingTeamsActivityHandler()
1351+
await bot.on_turn(turn_context)
1352+
1353+
# Assert
1354+
self.assertEqual(1, len(bot.record))
1355+
self.assertEqual("on_message_update_activity", bot.record[0])
1356+
1357+
async def test_message_update_activity_teams_no_channel_data(self):
1358+
# Arrange
1359+
activity = Activity(
1360+
type=ActivityTypes.message_update,
1361+
channel_id=Channels.ms_teams,
1362+
)
1363+
turn_context = TurnContext(SimpleAdapter(), activity)
1364+
1365+
# Act
1366+
bot = TestingTeamsActivityHandler()
1367+
await bot.on_turn(turn_context)
1368+
1369+
# Assert
1370+
self.assertEqual(1, len(bot.record))
1371+
self.assertEqual("on_message_update_activity", bot.record[0])
1372+
1373+
async def test_message_delete_activity_teams_message_soft_delete(self):
1374+
# Arrange
1375+
activity = Activity(
1376+
type=ActivityTypes.message_delete,
1377+
channel_data=TeamsChannelData(event_type="softDeleteMessage"),
1378+
channel_id=Channels.ms_teams,
1379+
)
1380+
turn_context = TurnContext(SimpleAdapter(), activity)
1381+
1382+
# Act
1383+
bot = TestingTeamsActivityHandler()
1384+
await bot.on_turn(turn_context)
1385+
1386+
# Assert
1387+
self.assertEqual(2, len(bot.record))
1388+
self.assertEqual("on_message_delete_activity", bot.record[0])
1389+
self.assertEqual("on_teams_message_soft_delete", bot.record[1])
1390+
1391+
async def test_message_delete_activity_teams_message_soft_delete_no_msteams(self):
1392+
# Arrange
1393+
activity = Activity(
1394+
type=ActivityTypes.message_delete,
1395+
channel_data=TeamsChannelData(event_type="softDeleteMessage"),
1396+
)
1397+
turn_context = TurnContext(SimpleAdapter(), activity)
1398+
1399+
# Act
1400+
bot = TestingTeamsActivityHandler()
1401+
await bot.on_turn(turn_context)
1402+
1403+
# Assert
1404+
self.assertEqual(1, len(bot.record))
1405+
self.assertEqual("on_message_delete_activity", bot.record[0])
1406+
1407+
async def test_message_delete_activity_teams_no_channel_data(self):
1408+
# Arrange
1409+
activity = Activity(
1410+
type=ActivityTypes.message_delete,
1411+
channel_id=Channels.ms_teams,
1412+
)
1413+
turn_context = TurnContext(SimpleAdapter(), activity)
1414+
1415+
# Act
1416+
bot = TestingTeamsActivityHandler()
1417+
await bot.on_turn(turn_context)
1418+
1419+
# Assert
1420+
self.assertEqual(1, len(bot.record))
1421+
self.assertEqual("on_message_delete_activity", bot.record[0])
1422+
12571423
async def test_on_teams_meeting_participants_join_event(self):
12581424
# arrange
12591425
activity = Activity(

0 commit comments

Comments
 (0)