Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support teams meeting start/end events #1724

Merged
merged 2 commits into from
Jun 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
TeamInfo,
ChannelInfo,
FileConsentCardResponse,
MeetingStartEventDetails,
MeetingEndEventDetails,
TeamsChannelData,
TeamsChannelAccount,
MessagingExtensionAction,
Expand Down Expand Up @@ -877,3 +879,64 @@ async def on_teams_channel_restored( # pylint: disable=unused-argument
:returns: A task that represents the work queued to execute.
"""
return

async def on_event_activity(self, turn_context: TurnContext):
"""
Invoked when an event activity is received from the connector when the base behavior of
:meth:`on_turn()` is used.

:param turn_context: The context object for this turn
:type turn_context: :class:`botbuilder.core.TurnContext`

:returns: A task that represents the work queued to execute

.. remarks::
When the :meth:`on_turn()` method receives an event activity, it calls this method.
If the activity name is `tokens/response`, it calls :meth:`on_token_response_event()`;
otherwise, it calls :meth:`on_event()`.

In a derived class, override this method to add logic that applies to all event activities.
Add logic to apply before the specific event-handling logic before the call to this base class method.
Add logic to apply after the specific event-handling logic after the call to this base class method.

Event activities communicate programmatic information from a client or channel to a bot.
The meaning of an event activity is defined by the event activity name property, which is meaningful within
the scope of a channel.
"""
if turn_context.activity.channel_id == Channels.ms_teams:
if turn_context.activity.name == "application/vnd.microsoft.meetingStart":
return await self.on_teams_meeting_start_event(
turn_context.activity.value, turn_context
)
if turn_context.activity.name == "application/vnd.microsoft.meetingEnd":
return await self.on_teams_meeting_end_event(
turn_context.activity.value, turn_context
)

return await super().on_event_activity(turn_context)

async def on_teams_meeting_start_event(
self, meeting: MeetingStartEventDetails, turn_context: TurnContext
): # pylint: disable=unused-argument
"""
Override this in a derived class to provide logic for when a Teams meeting start event is received.

:param meeting: The details of the meeting.
:param turn_context: A context object for this turn.

:returns: A task that represents the work queued to execute.
"""
return

async def on_teams_meeting_end_event(
self, meeting: MeetingEndEventDetails, turn_context: TurnContext
): # pylint: disable=unused-argument
"""
Override this in a derived class to provide logic for when a Teams meeting end event is received.

:param meeting: The details of the meeting.
:param turn_context: A context object for this turn.

:returns: A task that represents the work queued to execute.
"""
return
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
AppBasedLinkQuery,
ChannelInfo,
FileConsentCardResponse,
MeetingStartEventDetails,
MeetingEndEventDetails,
MessageActionsPayload,
MessagingExtensionAction,
MessagingExtensionQuery,
Expand Down Expand Up @@ -309,6 +311,26 @@ async def on_teams_tab_submit(
self.record.append("on_teams_tab_submit")
return await super().on_teams_tab_submit(turn_context, tab_submit)

async def on_event_activity(self, turn_context: TurnContext):
self.record.append("on_event_activity")
return await super().on_event_activity(turn_context)

async def on_teams_meeting_start_event(
self, meeting: MeetingStartEventDetails, turn_context: TurnContext
):
self.record.append("on_teams_meeting_start_event")
return await super().on_teams_meeting_start_event(
turn_context.activity.value, turn_context
)

async def on_teams_meeting_end_event(
self, meeting: MeetingEndEventDetails, turn_context: TurnContext
):
self.record.append("on_teams_meeting_end_event")
return await super().on_teams_meeting_end_event(
turn_context.activity.value, turn_context
)


class NotImplementedAdapter(BotAdapter):
async def delete_activity(
Expand Down Expand Up @@ -1064,3 +1086,37 @@ async def test_typing_activity(self):

assert len(bot.record) == 1
assert bot.record[0] == "on_typing_activity"

async def test_on_teams_meeting_start_event(self):
activity = Activity(
type=ActivityTypes.event,
name="application/vnd.microsoft.meetingStart",
channel_id=Channels.ms_teams,
)

turn_context = TurnContext(SimpleAdapter(), activity)

# Act
bot = TestingTeamsActivityHandler()
await bot.on_turn(turn_context)

assert len(bot.record) == 2
assert bot.record[0] == "on_event_activity"
assert bot.record[1] == "on_teams_meeting_start_event"

async def test_on_teams_meeting_end_event(self):
activity = Activity(
type=ActivityTypes.event,
name="application/vnd.microsoft.meetingEnd",
channel_id=Channels.ms_teams,
)

turn_context = TurnContext(SimpleAdapter(), activity)

# Act
bot = TestingTeamsActivityHandler()
await bot.on_turn(turn_context)

assert len(bot.record) == 2
assert bot.record[0] == "on_event_activity"
assert bot.record[1] == "on_teams_meeting_end_event"
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from ._models_py3 import FileUploadInfo
from ._models_py3 import MeetingDetails
from ._models_py3 import MeetingInfo
from ._models_py3 import MeetingStartEventDetails
from ._models_py3 import MeetingEndEventDetails
from ._models_py3 import MessageActionsPayload
from ._models_py3 import MessageActionsPayloadApp
from ._models_py3 import MessageActionsPayloadAttachment
Expand Down Expand Up @@ -87,6 +89,8 @@
"FileUploadInfo",
"MeetingDetails",
"MeetingInfo",
"MeetingStartEventDetails",
"MeetingEndEventDetails",
"MessageActionsPayload",
"MessageActionsPayloadApp",
"MessageActionsPayloadAttachment",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2372,54 +2372,65 @@ def _custom_init(self):
return


class MeetingDetails(Model):
class MeetingDetailsBase(Model):
"""Specific details of a Teams meeting.

:param id: The meeting's Id, encoded as a BASE64 string.
:type id: str
:param join_url: The URL used to join the meeting.
:type join_url: str
:param title: The title of the meeting.
:type title: str
"""

_attribute_map = {
"id": {"key": "uniqueId", "type": "str"},
"join_url": {"key": "joinUrl", "type": "str"},
"title": {"key": "title", "type": "str"},
}

def __init__(
self, *, id: str = None, join_url: str = None, title: str = None, **kwargs
) -> None:
super(MeetingDetailsBase, self).__init__(**kwargs)
self.id = id
self.join_url = join_url
self.title = title


class MeetingDetails(MeetingDetailsBase):
"""Specific details of a Teams meeting.

:param ms_graph_resource_id: The MsGraphResourceId, used specifically for MS Graph API calls.
:type ms_graph_resource_id: str
:param scheduled_start_time: The meeting's scheduled start time, in UTC.
:type scheduled_start_time: str
:param scheduled_end_time: The meeting's scheduled end time, in UTC.
:type scheduled_end_time: str
:param join_url: The URL used to join the meeting.
:type join_url: str
:param title: The title of the meeting.
:type title: str
:param type: The meeting's type.
:type type: str
"""

_attribute_map = {
"id": {"key": "uniqueId", "type": "str"},
"ms_graph_resource_id": {"key": "msGraphResourceId", "type": "str"},
"scheduled_start_time": {"key": "scheduledStartTime", "type": "str"},
"scheduled_end_time": {"key": "scheduledEndTime", "type": "str"},
"join_url": {"key": "joinUrl", "type": "str"},
"title": {"key": "title", "type": "str"},
"type": {"key": "type", "type": "str"},
}

def __init__(
self,
*,
id: str = None,
ms_graph_resource_id: str = None,
scheduled_start_time: str = None,
scheduled_end_time: str = None,
join_url: str = None,
title: str = None,
type: str = None,
**kwargs
) -> None:
super(MeetingDetails, self).__init__(**kwargs)
self.id = id
self.ms_graph_resource_id = ms_graph_resource_id
self.scheduled_start_time = scheduled_start_time
self.scheduled_end_time = scheduled_end_time
self.join_url = join_url
self.title = title
self.type = type


Expand Down Expand Up @@ -2452,3 +2463,45 @@ def __init__(
self.details = details
self.conversation = conversation
self.organizer = organizer


class MeetingEventDetails(MeetingDetailsBase):
"""Base class for Teams meting start and end events.

:param meeting_type: The meeting's type.
:type meeting_type: str
"""

_attribute_map = {"meeting_type": {"key": "MeetingType", "type": "str"}}

def __init__(self, *, meeting_type: str = None, **kwargs):
super(MeetingEventDetails, self).__init__(**kwargs)
self.meeting_type = meeting_type


class MeetingStartEventDetails(MeetingDetailsBase):
"""Specific details of a Teams meeting start event.

:param start_time: Timestamp for meeting start, in UTC.
:type start_time: str
"""

_attribute_map = {"start_time": {"key": "StartTime", "type": "str"}}

def __init__(self, *, start_time: str = None, **kwargs):
super(MeetingStartEventDetails, self).__init__(**kwargs)
self.start_time = start_time


class MeetingEndEventDetails(MeetingDetailsBase):
"""Specific details of a Teams meeting end event.

:param end_time: Timestamp for meeting end, in UTC.
:type end_time: str
"""

_attribute_map = {"end_time": {"key": "EndTime", "type": "str"}}

def __init__(self, *, end_time: str = None, **kwargs):
super(MeetingEndEventDetails, self).__init__(**kwargs)
self.end_time = end_time