@microsoft/agents-hosting-extensions-teams
Version:
Microsoft 365 Agents SDK for JavaScript. Teams extensions
431 lines (391 loc) • 16.7 kB
text/typescript
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { TeamsChannelAccount } from './activity-extensions/teamsChannelAccount'
import { TeamsMeetingParticipant } from './meeting/teamsMeetingParticipant'
import { MeetingInfo } from './meeting/meetingInfo'
import { MeetingNotification } from './meeting/meetingNotification'
import { MeetingNotificationResponse } from './meeting/meetingNotificationResponse'
import { Activity, Channels, ConversationReference, ConversationParameters } from '@microsoft/agents-activity'
import { TeamsConnectorClient } from './client/teamsConnectorClient'
import { parseTeamsChannelData } from './activity-extensions/teamsChannelDataParser'
import { CloudAdapter, ConnectorClient, TurnContext, TurnState } from '@microsoft/agents-hosting'
import { ChannelInfo } from './activity-extensions/channelInfo'
import { BatchFailedEntriesResponse, BatchOperationResponse, BatchOperationStateResponse, CancelOperationResponse, TeamDetails, TeamsMember, TeamsPagedMembersResult } from './client/teamsConnectorClient.types'
/**
* Provides utility methods for interacting with Microsoft Teams-specific features.
* This class includes methods for retrieving team details, meeting information, sending messages,
* and managing operations within the Teams environment.
*/
export class TeamsInfo {
/**
* Gets the meeting participant information.
*
* @param {TurnContext} context - The turn context.
* @param {string} [meetingId] - The meeting ID.
* @param {string} [participantId] - The participant ID.
* @param {string} [tenantId] - The tenant ID.
* @returns {Promise<TeamsMeetingParticipant>} - The meeting participant information.
*/
static async getMeetingParticipant (
context: TurnContext,
meetingId?: string,
participantId?: string,
tenantId?: string
): Promise<TeamsMeetingParticipant<TurnState>> {
if (!context) {
throw new Error('context is required.')
}
const activity = context.activity
const teamsChannelData = parseTeamsChannelData(context.activity.channelData) // teamsGetTeamMeetingInfo(activity)
if (meetingId == null) {
meetingId = teamsChannelData.meeting?.id
}
if (!meetingId) {
throw new Error('meetingId is required.')
}
if (participantId == null) {
const from = activity.from
participantId = from?.aadObjectId
}
if (!participantId) {
throw new Error('participantId is required.')
}
if (tenantId === undefined) {
const tenant = teamsChannelData.tenant // teamsGetTenant(activity)
tenantId = tenant?.id
}
// return this.getTeamsConnectorClient(context).teams.fetchMeetingParticipant(meetingId, participantId, { tenantId })
const res = await this.getRestClient(context).fetchMeetingParticipant(meetingId, participantId, tenantId!)
return res as TeamsMeetingParticipant<TurnState>
}
/**
* Gets the meeting information.
*
* @param {TurnContext} context - The turn context.
* @param {string} [meetingId] - The meeting ID.
* @returns {Promise<MeetingInfo>} - The meeting information.
*/
static async getMeetingInfo (context: TurnContext, meetingId?: string): Promise<MeetingInfo> {
if (!meetingId) {
const teamsChannelData = parseTeamsChannelData(context.activity.channelData)
meetingId = teamsChannelData.meeting?.id
}
const res = await this.getRestClient(context).fetchMeetingInfo(meetingId!)
return res as MeetingInfo
}
/**
* Gets the team details.
*
* @param {TurnContext} context - The turn context.
* @param {string} [teamId] - The team ID.
* @returns {Promise<TeamDetails>} - The team details.
*/
static async getTeamDetails (context: TurnContext, teamId?: string): Promise<TeamDetails> {
if (!teamId) {
const teamsChannelData = parseTeamsChannelData(context.activity.channelData)
teamId = teamsChannelData.team?.id
}
if (!teamId) {
throw new Error('teamId is required.')
}
const res = await this.getRestClient(context).fetchTeamDetails(teamId!)
return res as TeamDetails
}
/**
* Sends a message to a Teams channel.
*
* @param {TurnContext} context - The turn context.
* @param {Activity} activity - The activity to send.
* @param {string} teamsChannelId - The Teams channel ID.
* @param {string} [appId] - The application ID.
* @returns {Promise<[ConversationReference, string]>} - The conversation reference and new activity ID.
*/
static async sendMessageToTeamsChannel (context: TurnContext, activity: Activity, teamsChannelId: string, appId?: string): Promise<[ConversationReference, string]> {
if (!context) {
throw new Error('TurnContext cannot be null')
}
if (!activity) {
throw new Error('Activity cannot be null')
}
if (!teamsChannelId) {
throw new Error('The teamsChannelId cannot be null or empty')
}
const convoParams = {
isGroup: true,
channelData: {
channel: {
id: teamsChannelId,
},
},
activity,
agent: context.activity.recipient,
} as ConversationParameters
let conversationReference: Partial<ConversationReference>
let newActivityId: string
if (appId && context.adapter instanceof CloudAdapter) {
await context.adapter.createConversationAsync(
appId,
Channels.Msteams,
context.activity.serviceUrl!,
'https://api.botframework.com',
convoParams,
async (turnContext) => {
conversationReference = turnContext.activity.getConversationReference()
newActivityId = turnContext.activity.id!
}
)
} else {
// const connectorClient = context.adapter.createConnectorClient(
// context.activity.serviceUrl
// )
const connectorClient : ConnectorClient = context.turnState.get<ConnectorClient>('connectorClient')
const conversationResourceResponse = await connectorClient.createConversationAsync(convoParams)
conversationReference = context.activity.getConversationReference()
conversationReference.conversation!.id = conversationResourceResponse.id
newActivityId = conversationResourceResponse.activityId
}
// @ts-ignore
return [conversationReference as ConversationReference, newActivityId]
}
/**
* Gets the channels of a team.
*
* @param {TurnContext} context - The turn context.
* @param {string} [teamId] - The team ID.
* @returns {Promise<ChannelInfo[]>} - The list of channels.
*/
static async getTeamChannels (context: TurnContext, teamId?: string): Promise<ChannelInfo[]> {
if (!teamId) {
const teamsChannelData = parseTeamsChannelData(context.activity.channelData)
teamId = teamsChannelData.team?.id
}
if (!teamId) {
throw new Error('teamId is required.')
}
return await this.getRestClient(context).fetchChannelList(teamId!)
}
/**
* Gets the paged members of a team or conversation.
*
* @param {TurnContext} context - The turn context.
* @param {number} [pageSize] - The page size.
* @param {string} [continuationToken] - The continuation token.
* @returns {Promise<TeamsPagedMembersResult>} - The paged members result.
*/
static async getPagedMembers (context: TurnContext, pageSize?: number, continuationToken?: string): Promise<TeamsPagedMembersResult> {
const teamsChannelData = parseTeamsChannelData(context.activity.channelData)
const teamId = teamsChannelData.team?.id
if (teamId) {
return await this.getPagedTeamMembers(context, teamId, pageSize, continuationToken)
} else {
const conversation = context.activity.conversation
const conversationId = conversation && conversation.id ? conversation.id : undefined
return this.getRestClient(context).getConversationPagedMember(conversationId!, pageSize!, continuationToken!)
}
}
/**
* Gets a member of a team or conversation.
*
* @param {TurnContext} context - The turn context.
* @param {string} userId - The user ID.
* @returns {Promise<TeamsChannelAccount>} - The member information.
*/
static async getMember (context: TurnContext, userId: string): Promise<TeamsChannelAccount> {
const teamsChannelData = parseTeamsChannelData(context.activity.channelData)
const teamId = teamsChannelData.team?.id
if (teamId) {
return await this.getTeamMember(context, teamId, userId)
} else {
const conversationId = context.activity.conversation!.id
return await this.getMemberInternal(context, conversationId, userId)
}
}
/**
* Gets the paged members of a team.
*
* @param {TurnContext} context - The turn context.
* @param {string} [teamId] - The team ID.
* @param {number} [pageSize] - The page size.
* @param {string} [continuationToken] - The continuation token.
* @returns {Promise<TeamsPagedMembersResult>} - The paged members result.
*/
static async getPagedTeamMembers (context: TurnContext, teamId?: string, pageSize?: number, continuationToken?: string): Promise<TeamsPagedMembersResult> {
if (!teamId) {
const teamsChannelData = parseTeamsChannelData(context.activity.channelData)
teamId = teamsChannelData.team?.id
}
if (!teamId) {
throw new Error('teamId is required.')
}
const pagedResults = await this.getRestClient(context).getConversationPagedMember(teamId, pageSize!, continuationToken!)
do {
if (pagedResults.continuationToken) {
const nextResults = await this.getRestClient(context).getConversationPagedMember(teamId, pageSize!, pagedResults.continuationToken)
pagedResults.members.push(...nextResults.members)
pagedResults.continuationToken = nextResults.continuationToken
}
} while (pagedResults.continuationToken)
return pagedResults
}
/**
* Gets a member of a team.
*
* @param {TurnContext} context - The turn context.
* @param {string} teamId - The team ID.
* @param {string} userId - The user ID.
* @returns {Promise<TeamsChannelAccount>} - The member information.
*/
static async getTeamMember (context: TurnContext, teamId: string, userId: string): Promise<TeamsChannelAccount> {
return await this.getRestClient(context).getConversationMember(teamId, userId)
}
/**
* Sends a meeting notification.
*
* @param {TurnContext} context - The turn context.
* @param {MeetingNotification} notification - The meeting notification.
* @param {string} [meetingId] - The meeting ID.
* @returns {Promise<MeetingNotificationResponse>} - The meeting notification response.
*/
static async sendMeetingNotification (context: TurnContext, notification: MeetingNotification, meetingId?: string): Promise<MeetingNotificationResponse> {
const activity = context.activity
if (meetingId == null) {
const teamsChannelData = parseTeamsChannelData(activity.channelData)
// const meeting = teamsGetTeamMeetingInfo(activity)
const meeting = teamsChannelData.meeting
meetingId = meeting?.id
}
if (!meetingId) {
throw new Error('meetingId is required.')
}
return await this.getRestClient(context).sendMeetingNotification(meetingId, notification)
}
/**
* Sends a message to a list of users.
*
* @param {TurnContext} context - The turn context.
* @param {Activity} activity - The activity to send.
* @param {string} tenantId - The tenant ID.
* @param {TeamsMember[]} members - The list of members.
* @returns {Promise<BatchOperationResponse>} - The batch operation response.
*/
static async sendMessageToListOfUsers (context: TurnContext, activity: Activity, tenantId: string, members: TeamsMember[]): Promise<BatchOperationResponse> {
if (!activity) {
throw new Error('activity is required.')
}
if (!tenantId) {
throw new Error('tenantId is required.')
}
if (!members || members.length === 0) {
throw new Error('members list is required.')
}
return await this.getRestClient(context).sendMessageToListOfUsers(activity, tenantId, members)
}
/**
* Sends a message to all users in a tenant.
*
* @param {TurnContext} context - The turn context.
* @param {Activity} activity - The activity to send.
* @param {string} tenantId - The tenant ID.
* @returns {Promise<BatchOperationResponse>} - The batch operation response.
*/
static async sendMessageToAllUsersInTenant (context: TurnContext, activity: Activity, tenantId: string): Promise<BatchOperationResponse> {
if (!activity) {
throw new Error('activity is required.')
}
if (!tenantId) {
throw new Error('tenantId is required.')
}
return await this.getRestClient(context).sendMessageToAllUsersInTenant(activity, tenantId)
}
/**
* Sends a message to all users in a team.
*
* @param {TurnContext} context - The turn context.
* @param {Activity} activity - The activity to send.
* @param {string} tenantId - The tenant ID.
* @param {string} teamId - The team ID.
* @returns {Promise<BatchOperationResponse>} - The batch operation response.
*/
static async sendMessageToAllUsersInTeam (context: TurnContext, activity: Activity, tenantId: string, teamId: string): Promise<BatchOperationResponse> {
if (!activity) {
throw new Error('activity is required.')
}
if (!tenantId) {
throw new Error('tenantId is required.')
}
if (!teamId) {
throw new Error('teamId is required.')
}
return await this.getRestClient(context).sendMessageToAllUsersInTeam(activity, tenantId, teamId)
}
/**
* Sends a message to a list of channels.
*
* @param {TurnContext} context - The turn context.
* @param {Activity} activity - The activity to send.
* @param {string} tenantId - The tenant ID.
* @param {TeamsMember[]} members - The list of members.
* @returns {Promise<BatchOperationResponse>} - The batch operation response.
*/
static async sendMessageToListOfChannels (context: TurnContext, activity: Activity, tenantId: string, members: TeamsMember[]): Promise<BatchOperationResponse> {
if (!activity) {
throw new Error('activity is required.')
}
if (!tenantId) {
throw new Error('tenantId is required.')
}
if (!members || members.length === 0) {
throw new Error('members list is required.')
}
return this.getRestClient(context).sendMessageToListOfChannels(activity, tenantId, members)
}
/**
* Gets the operation state.
*
* @param {TurnContext} context - The turn context.
* @param {string} operationId - The operation ID.
* @returns {Promise<BatchOperationStateResponse>} - The operation state response.
*/
static async getOperationState (context: TurnContext, operationId: string): Promise<BatchOperationStateResponse> {
if (!operationId) {
throw new Error('operationId is required.')
}
return await this.getRestClient(context).getOperationState(operationId)
}
/**
* Gets the failed entries of an operation.
*
* @param {TurnContext} context - The turn context.
* @param {string} operationId - The operation ID.
* @returns {Promise<BatchFailedEntriesResponse>} - The failed entries response.
*/
static async getFailedEntries (context: TurnContext, operationId: string): Promise<BatchFailedEntriesResponse> {
if (!operationId) {
throw new Error('operationId is required.')
}
return await this.getRestClient(context).getFailedEntries(operationId)
}
/**
* Cancels an operation.
*
* @param {TurnContext} context - The turn context.
* @param {string} operationId - The operation ID.
* @returns {Promise<CancelOperationResponse>} - The cancel operation response.
*/
static async cancelOperation (context: TurnContext, operationId: string): Promise<CancelOperationResponse> {
if (!operationId) {
throw new Error('operationId is required.')
}
return await this.getRestClient(context).cancelOperation(operationId)
}
private static async getMemberInternal (context: TurnContext, conversationId: string, userId: string): Promise<TeamsChannelAccount> {
const connectorClient : ConnectorClient = context.turnState.get<ConnectorClient>('connectorClient')
return await connectorClient.getConversationMember(conversationId, userId)
}
private static getRestClient (context: TurnContext) : TeamsConnectorClient {
const connectorClient : ConnectorClient = context.turnState.get<ConnectorClient>('connectorClient')
return new TeamsConnectorClient(connectorClient)
}
}