Skip to content

Commit

Permalink
Port: [Teams] Add support for meeting participants added/removed even…
Browse files Browse the repository at this point in the history
…ts (#2137)

* [Teams] Add support for meeting participants added/removed events

* test case issue and code format fix

* code format fix for microsoft_app_credentials.py

* removing unnecessary else
  • Loading branch information
gandiddi authored Jul 17, 2024
1 parent cea5ccc commit b8dd205
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
TaskModuleResponse,
TabRequest,
TabSubmit,
MeetingParticipantsEventDetails,
)
from botframework.connector import Channels
from ..serializer_helper import deserializer_helper
Expand Down Expand Up @@ -913,6 +914,20 @@ async def on_event_activity(self, turn_context: TurnContext):
return await self.on_teams_meeting_end_event(
turn_context.activity.value, turn_context
)
if (
turn_context.activity.name
== "application/vnd.microsoft.meetingParticipantJoin"
):
return await self.on_teams_meeting_participants_join_event(
turn_context.activity.value, turn_context
)
if (
turn_context.activity.name
== "application/vnd.microsoft.meetingParticipantLeave"
):
return await self.on_teams_meeting_participants_leave_event(
turn_context.activity.value, turn_context
)

return await super().on_event_activity(turn_context)

Expand Down Expand Up @@ -941,3 +956,29 @@ async def on_teams_meeting_end_event(
:returns: A task that represents the work queued to execute.
"""
return

async def on_teams_meeting_participants_join_event(
self, meeting: MeetingParticipantsEventDetails, turn_context: TurnContext
): # pylint: disable=unused-argument
"""
Override this in a derived class to provide logic for when meeting participants are added.
: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_participants_leave_event(
self, meeting: MeetingParticipantsEventDetails, turn_context: TurnContext
): # pylint: disable=unused-argument
"""
Override this in a derived class to provide logic for when meeting participants are removed.
: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 @@ -32,6 +32,7 @@
TabRequest,
TabSubmit,
TabContext,
MeetingParticipantsEventDetails,
)
from botframework.connector import Channels
from simple_adapter import SimpleAdapter
Expand Down Expand Up @@ -333,6 +334,22 @@ async def on_teams_meeting_end_event(
turn_context.activity.value, turn_context
)

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

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


class NotImplementedAdapter(BotAdapter):
async def delete_activity(
Expand Down Expand Up @@ -1157,3 +1174,57 @@ async def test_on_teams_meeting_end_event(self):
assert len(bot.record) == 2
assert bot.record[0] == "on_event_activity"
assert bot.record[1] == "on_teams_meeting_end_event"

async def test_on_teams_meeting_participants_join_event(self):
# arrange
activity = Activity(
type=ActivityTypes.event,
channel_id=Channels.ms_teams,
name="application/vnd.microsoft.meetingParticipantJoin",
value={
"members": [
{
"user": {"id": "123", "name": "name"},
"meeting": {"role": "role", "in_meeting": True},
}
],
},
)

turn_context = TurnContext(SimpleAdapter(), activity)

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

# Assert
assert len(bot.record) == 2
assert bot.record[0] == "on_event_activity"
assert bot.record[1] == "on_teams_meeting_participants_join_event"

async def test_on_teams_meeting_participants_leave_event(self):
# arrange
activity = Activity(
type=ActivityTypes.event,
channel_id=Channels.ms_teams,
name="application/vnd.microsoft.meetingParticipantLeave",
value={
"members": [
{
"user": {"id": "id", "name": "name"},
"meeting": {"role": "role", "in_meeting": True},
}
],
},
)

turn_context = TurnContext(SimpleAdapter(), activity)

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

# Assert
assert len(bot.record) == 2
assert bot.record[0] == "on_event_activity"
assert bot.record[1] == "on_teams_meeting_participants_leave_event"
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@
from ._models_py3 import TabSubmitData
from ._models_py3 import TabSuggestedActions
from ._models_py3 import TaskModuleCardResponse
from ._models_py3 import UserMeetingDetails
from ._models_py3 import TeamsMeetingMember
from ._models_py3 import MeetingParticipantsEventDetails

__all__ = [
"AppBasedLinkQuery",
Expand Down Expand Up @@ -155,4 +158,7 @@
"TabSubmitData",
"TabSuggestedActions",
"TaskModuleCardResponse",
"UserMeetingDetails",
"TeamsMeetingMember",
"MeetingParticipantsEventDetails",
]
62 changes: 62 additions & 0 deletions libraries/botbuilder-schema/botbuilder/schema/teams/_models_py3.py
Original file line number Diff line number Diff line change
Expand Up @@ -2506,3 +2506,65 @@ class MeetingEndEventDetails(MeetingDetailsBase):
def __init__(self, *, end_time: str = None, **kwargs):
super(MeetingEndEventDetails, self).__init__(**kwargs)
self.end_time = end_time


class UserMeetingDetails(Model):
"""Specific details of a user in a Teams meeting.
:param role: Role of the participant in the current meeting.
:type role: str
:param in_meeting: True, if the participant is in the meeting.
:type in_meeting: bool
"""

_attribute_map = {
"role": {"key": "role", "type": "str"},
"in_meeting": {"key": "inMeeting", "type": "bool"},
}

def __init__(self, *, role: str = None, in_meeting: bool = None, **kwargs) -> None:
super(UserMeetingDetails, self).__init__(**kwargs)
self.in_meeting = in_meeting
self.role = role


class TeamsMeetingMember(Model):
"""Data about the meeting participants.
:param user: The channel user data.
:type user: TeamsChannelAccount
:param meeting: The user meeting details.
:type meeting: UserMeetingDetails
"""

_attribute_map = {
"user": {"key": "user", "type": "TeamsChannelAccount"},
"meeting": {"key": "meeting", "type": "UserMeetingDetails"},
}

def __init__(
self,
*,
user: TeamsChannelAccount = None,
meeting: UserMeetingDetails = None,
**kwargs
) -> None:
super(TeamsMeetingMember, self).__init__(**kwargs)
self.user = user
self.meeting = meeting


class MeetingParticipantsEventDetails(Model):
"""Data about the meeting participants.
:param members: The members involved in the meeting event.
:type members: list[~botframework.connector.models.TeamsMeetingMember]
"""

_attribute_map = {
"conversations": {"key": "members", "type": "[TeamsMeetingMember]"},
}

def __init__(self, *, members: List[TeamsMeetingMember] = None, **kwargs) -> None:
super(MeetingParticipantsEventDetails, self).__init__(**kwargs)
self.members = members
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,15 @@ def get_access_token(self, force_refresh: bool = False) -> str:
auth_token = self.__get_msal_app().acquire_token_for_client(scopes=scopes)
if "access_token" in auth_token:
return auth_token["access_token"]
else:
error = auth_token["error"] if "error" in auth_token else "Unknown error"
error_description = auth_token["error_description"] if "error_description" in auth_token else "Unknown error description"
raise PermissionError(f"Failed to get access token with error: {error}, error_description: {error_description}")
error = auth_token["error"] if "error" in auth_token else "Unknown error"
error_description = (
auth_token["error_description"]
if "error_description" in auth_token
else "Unknown error description"
)
raise PermissionError(
f"Failed to get access token with error: {error}, error_description: {error_description}"
)

def __get_msal_app(self):
if not self.app:
Expand Down

0 comments on commit b8dd205

Please sign in to comment.