Port: [Teams] Add support for meeting participants added/removed events (#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
This commit is contained in:
gandiddi 2024-07-17 23:15:19 +05:30 коммит произвёл GitHub
Родитель cea5ccc784
Коммит b8dd2052fd
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
5 изменённых файлов: 189 добавлений и 4 удалений

Просмотреть файл

@ -27,6 +27,7 @@ from botbuilder.schema.teams import (
TaskModuleResponse,
TabRequest,
TabSubmit,
MeetingParticipantsEventDetails,
)
from botframework.connector import Channels
from ..serializer_helper import deserializer_helper
@ -913,6 +914,20 @@ class TeamsActivityHandler(ActivityHandler):
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)
@ -941,3 +956,29 @@ class TeamsActivityHandler(ActivityHandler):
: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

Просмотреть файл

@ -32,6 +32,7 @@ from botbuilder.schema.teams import (
TabRequest,
TabSubmit,
TabContext,
MeetingParticipantsEventDetails,
)
from botframework.connector import Channels
from simple_adapter import SimpleAdapter
@ -333,6 +334,22 @@ class TestingTeamsActivityHandler(TeamsActivityHandler):
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(
@ -1157,3 +1174,57 @@ class TestTeamsActivityHandler(aiounittest.AsyncTestCase):
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"

Просмотреть файл

@ -77,6 +77,9 @@ from ._models_py3 import TabSubmit
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",
@ -155,4 +158,7 @@ __all__ = [
"TabSubmitData",
"TabSuggestedActions",
"TaskModuleCardResponse",
"UserMeetingDetails",
"TeamsMeetingMember",
"MeetingParticipantsEventDetails",
]

Просмотреть файл

@ -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

Просмотреть файл

@ -54,10 +54,15 @@ class MicrosoftAppCredentials(AppCredentials, ABC):
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: