@microsoft/agents-hosting-extensions-teams 1.1.4-geb1c05c291 → 1.2.0-alpha.19.g21cf68366a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,177 @@
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+
4
+ import { AgentErrorDefinition } from '@microsoft/agents-activity'
5
+
6
+ /**
7
+ * Error definitions for the Teams Extensions system.
8
+ * This contains localized error codes for the Teams Extensions subsystem of the AgentSDK.
9
+ *
10
+ * Each error definition includes an error code (starting from -150000), a description, and a help link
11
+ * pointing to an AKA link to get help for the given error.
12
+ *
13
+ * Usage example:
14
+ * ```
15
+ * throw ExceptionHelper.generateException(
16
+ * Error,
17
+ * Errors.ContextRequired
18
+ * );
19
+ * ```
20
+ */
21
+ export const Errors: { [key: string]: AgentErrorDefinition } = {
22
+ // TeamsInfo Errors (-150000 to -150025)
23
+ /**
24
+ * Error thrown when context is required but not provided.
25
+ */
26
+ ContextRequired: {
27
+ code: -150000,
28
+ description: 'context is required.'
29
+ },
30
+
31
+ /**
32
+ * Error thrown when meetingId is required but not provided.
33
+ */
34
+ MeetingIdRequired: {
35
+ code: -150001,
36
+ description: 'meetingId is required.'
37
+ },
38
+
39
+ /**
40
+ * Error thrown when participantId is required but not provided.
41
+ */
42
+ ParticipantIdRequired: {
43
+ code: -150002,
44
+ description: 'participantId is required.'
45
+ },
46
+
47
+ /**
48
+ * Error thrown when teamId is required but not provided.
49
+ */
50
+ TeamIdRequired: {
51
+ code: -150003,
52
+ description: 'teamId is required.'
53
+ },
54
+
55
+ /**
56
+ * Error thrown when TurnContext cannot be null.
57
+ */
58
+ TurnContextCannotBeNull: {
59
+ code: -150004,
60
+ description: 'TurnContext cannot be null'
61
+ },
62
+
63
+ /**
64
+ * Error thrown when Activity cannot be null.
65
+ */
66
+ ActivityCannotBeNull: {
67
+ code: -150005,
68
+ description: 'Activity cannot be null'
69
+ },
70
+
71
+ /**
72
+ * Error thrown when teamsChannelId cannot be null or empty.
73
+ */
74
+ TeamsChannelIdRequired: {
75
+ code: -150006,
76
+ description: 'The teamsChannelId cannot be null or empty'
77
+ },
78
+
79
+ /**
80
+ * Error thrown when activity is required but not provided.
81
+ */
82
+ ActivityRequired: {
83
+ code: -150007,
84
+ description: 'activity is required.'
85
+ },
86
+
87
+ /**
88
+ * Error thrown when tenantId is required but not provided.
89
+ */
90
+ TenantIdRequired: {
91
+ code: -150008,
92
+ description: 'tenantId is required.'
93
+ },
94
+
95
+ /**
96
+ * Error thrown when members list is required but not provided.
97
+ */
98
+ MembersListRequired: {
99
+ code: -150009,
100
+ description: 'members list is required.'
101
+ },
102
+
103
+ /**
104
+ * Error thrown when operationId is required but not provided.
105
+ */
106
+ OperationIdRequired: {
107
+ code: -150010,
108
+ description: 'operationId is required.'
109
+ },
110
+
111
+ // TeamsConnectorClient Errors (-150011 to -150014)
112
+ /**
113
+ * Error thrown when activity parameter is missing.
114
+ */
115
+ MissingActivityParameter: {
116
+ code: -150011,
117
+ description: 'Missing activity parameter'
118
+ },
119
+
120
+ /**
121
+ * Error thrown when method is only valid within MS Teams Team scope.
122
+ */
123
+ OnlyValidInTeamsScope: {
124
+ code: -150012,
125
+ description: 'This method is only valid within the scope of a MS Teams Team.'
126
+ },
127
+
128
+ /**
129
+ * Error thrown when userId is required but not provided.
130
+ */
131
+ UserIdRequired: {
132
+ code: -150013,
133
+ description: 'userId is required'
134
+ },
135
+
136
+ /**
137
+ * Error thrown when conversationId is required but not provided.
138
+ */
139
+ ConversationIdRequired: {
140
+ code: -150014,
141
+ description: 'conversationId is required'
142
+ },
143
+
144
+ /**
145
+ * Error thrown when client is not available in the context.
146
+ */
147
+ ClientNotAvailable: {
148
+ code: -150015,
149
+ description: 'Client is not available in the context.'
150
+ },
151
+
152
+ // TaskModule Errors (-150016)
153
+ /**
154
+ * Error thrown when unexpected TaskModules.submit() is triggered.
155
+ */
156
+ UnexpectedTaskModuleSubmit: {
157
+ code: -150016,
158
+ description: 'Unexpected TaskModules.submit() triggered for activity type: {activityType}'
159
+ },
160
+
161
+ // TeamsActivityHandler Errors (-150017 to -150018)
162
+ /**
163
+ * Error thrown when method is not implemented.
164
+ */
165
+ NotImplemented: {
166
+ code: -150017,
167
+ description: 'NotImplemented'
168
+ },
169
+
170
+ /**
171
+ * Error thrown for bad request.
172
+ */
173
+ BadRequest: {
174
+ code: -150018,
175
+ description: 'BadRequest'
176
+ }
177
+ }
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './activity-extensions'
2
2
  export * from './client'
3
3
  export * from './compat/teamsActivityHandler'
4
+ export { Errors as TeamsExtensionErrors } from './errorHelper'
4
5
  export * from './feedbackLoopData'
5
6
  export * from './meeting'
6
7
  export * from './meeting/meeting'
@@ -1,5 +1,6 @@
1
- import { Activity, ActivityTypes, Channels } from '@microsoft/agents-activity'
1
+ import { Activity, ActivityTypes, Channels, ExceptionHelper } from '@microsoft/agents-activity'
2
2
  import { AgentApplication, INVOKE_RESPONSE_KEY, InvokeResponse, RouteHandler, RouteSelector, TurnContext, TurnState } from '@microsoft/agents-hosting'
3
+ import { Errors } from '../errorHelper'
3
4
  import { TaskModuleTaskInfo } from './taskModuleTaskInfo'
4
5
  import { TaskModuleResponse } from './taskModuleResponse'
5
6
 
@@ -79,10 +80,8 @@ export class TaskModule<TState extends TurnState> {
79
80
  async (context, state) => {
80
81
  if (context?.activity?.channelId === Channels.Msteams) {
81
82
  if (context?.activity?.type !== ActivityTypes.Invoke || context?.activity?.name !== SUBMIT_INVOKE_NAME) {
82
- throw new Error(`Unexpected TaskModules.submit() triggered for activity type: ${context?.activity?.type}`
83
- )
83
+ throw ExceptionHelper.generateException(Error, Errors.UnexpectedTaskModuleSubmit, undefined, { activityType: context?.activity?.type })
84
84
  }
85
-
86
85
  const result = await handler(context, state, (context.activity.value as any).data ?? {})
87
86
 
88
87
  if (!result) {
@@ -3,90 +3,11 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { Attachment } from '@microsoft/agents-activity'
7
- import { ConnectorClient, InputFile, InputFileDownloader, TurnContext, TurnState } from '@microsoft/agents-hosting'
8
- import axios, { AxiosInstance } from 'axios'
9
- import { z } from 'zod'
6
+ import { TeamsAttachmentDownloader as AppTeamsAttachmentDownloader } from '@microsoft/agents-hosting'
7
+
10
8
  /**
9
+ * @deprecated Use {@link TeamsAttachmentDownloader} from @microsoft/agents-hosting instead.
11
10
  * Downloads attachments from Teams using the bots access token.
12
11
  */
13
- export class TeamsAttachmentDownloader<TState extends TurnState = TurnState> implements InputFileDownloader<TState> {
14
- private _httpClient: AxiosInstance
15
- private _stateKey: string
16
- public constructor (stateKey: string = 'inputFiles') {
17
- this._httpClient = axios.create()
18
- this._stateKey = stateKey
19
- }
20
-
21
- /**
22
- * Download any files relative to the current user's input.
23
- * @template TState - Type of the state object passed to the `TurnContext.turnState` method.
24
- * @param {TurnContext} context Context for the current turn of conversation.
25
- * @param {TState} state Application state for the current turn of conversation.
26
- * @returns {Promise<InputFile[]>} Promise that resolves to an array of downloaded input files.
27
- */
28
- public async downloadFiles (context: TurnContext): Promise<InputFile[]> {
29
- // Filter out HTML attachments
30
- const attachments = context.activity.attachments?.filter((a) => !a.contentType.startsWith('text/html'))
31
- if (!attachments || attachments.length === 0) {
32
- return Promise.resolve([])
33
- }
34
-
35
- const connectorClient : ConnectorClient = context.turnState.get<ConnectorClient>('connectorClient')
36
- this._httpClient.defaults.headers = connectorClient.axiosInstance.defaults.headers
37
-
38
- const files: InputFile[] = []
39
- for (const attachment of attachments) {
40
- const file = await this.downloadFile(attachment)
41
- if (file) {
42
- files.push(file)
43
- }
44
- }
45
-
46
- return files
47
- }
48
-
49
- /**
50
- * @private
51
- * @param {Attachment} attachment - Attachment to download.
52
- * @param {string} accessToken - Access token to use for downloading.
53
- * @returns {Promise<InputFile>} - Promise that resolves to the downloaded input file.
54
- */
55
- private async downloadFile (attachment: Attachment): Promise<InputFile | undefined> {
56
- let inputFile: InputFile | undefined
57
- if (attachment.contentType === 'application/vnd.microsoft.teams.file.download.info') {
58
- const contentSchema = z.object({ downloadUrl: z.string() })
59
- const contentValue = contentSchema.parse(attachment.content)
60
- const response = await this._httpClient.get(contentValue.downloadUrl, { responseType: 'arraybuffer' })
61
- const content = Buffer.from(response.data, 'binary')
62
- let contentType = attachment.contentType
63
- if (contentType === 'image/*') {
64
- contentType = 'image/png'
65
- }
66
- inputFile = { content, contentType, contentUrl: attachment.contentUrl }
67
- } else if (attachment.contentType === 'image/*') {
68
- const response = await this._httpClient.get(attachment.contentUrl!, { responseType: 'arraybuffer' })
69
- const content = Buffer.from(response.data, 'binary')
70
- inputFile = { content, contentType: attachment.contentType, contentUrl: attachment.contentUrl }
71
- } else {
72
- inputFile = {
73
- content: Buffer.from(attachment.content as ArrayBuffer),
74
- contentType: attachment.contentType,
75
- contentUrl: attachment.contentUrl
76
- }
77
- }
78
- return inputFile
79
- }
80
-
81
- /**
82
- * Downloads files from the attachments in the current turn context and stores them in state.
83
- *
84
- * @param context The turn context containing the activity with attachments.
85
- * @param state The turn state to store the files in.
86
- * @returns A promise that resolves when the downloaded files are stored.
87
- */
88
- public async downloadAndStoreFiles (context: TurnContext, state: TState): Promise<void> {
89
- const files = await this.downloadFiles(context)
90
- state.setValue(this._stateKey, files)
91
- }
12
+ export class TeamsAttachmentDownloader extends AppTeamsAttachmentDownloader {
92
13
  }
package/src/teamsInfo.ts CHANGED
@@ -3,12 +3,13 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
+ import { Activity, Channels, ConversationParameters, ConversationReference, ExceptionHelper } from '@microsoft/agents-activity'
7
+ import { Errors } from './errorHelper'
6
8
  import { TeamsChannelAccount } from './activity-extensions/teamsChannelAccount'
7
9
  import { TeamsMeetingParticipant } from './meeting/teamsMeetingParticipant'
8
10
  import { MeetingInfo } from './meeting/meetingInfo'
9
11
  import { MeetingNotification } from './meeting/meetingNotification'
10
12
  import { MeetingNotificationResponse } from './meeting/meetingNotificationResponse'
11
- import { Activity, Channels, ConversationReference, ConversationParameters } from '@microsoft/agents-activity'
12
13
  import { TeamsConnectorClient } from './client/teamsConnectorClient'
13
14
  import { parseTeamsChannelData } from './activity-extensions/teamsChannelDataParser'
14
15
  import { CloudAdapter, ConnectorClient, TurnContext, TurnState } from '@microsoft/agents-hosting'
@@ -37,7 +38,7 @@ export class TeamsInfo {
37
38
  tenantId?: string
38
39
  ): Promise<TeamsMeetingParticipant<TurnState>> {
39
40
  if (!context) {
40
- throw new Error('context is required.')
41
+ throw ExceptionHelper.generateException(Error, Errors.ContextRequired)
41
42
  }
42
43
 
43
44
  const activity = context.activity
@@ -48,7 +49,7 @@ export class TeamsInfo {
48
49
  }
49
50
 
50
51
  if (!meetingId) {
51
- throw new Error('meetingId is required.')
52
+ throw ExceptionHelper.generateException(Error, Errors.MeetingIdRequired)
52
53
  }
53
54
 
54
55
  if (participantId == null) {
@@ -57,7 +58,7 @@ export class TeamsInfo {
57
58
  }
58
59
 
59
60
  if (!participantId) {
60
- throw new Error('participantId is required.')
61
+ throw ExceptionHelper.generateException(Error, Errors.ParticipantIdRequired)
61
62
  }
62
63
 
63
64
  if (tenantId === undefined) {
@@ -99,7 +100,7 @@ export class TeamsInfo {
99
100
  teamId = teamsChannelData.team?.id
100
101
  }
101
102
  if (!teamId) {
102
- throw new Error('teamId is required.')
103
+ throw ExceptionHelper.generateException(Error, Errors.TeamIdRequired)
103
104
  }
104
105
  const res = await this.getRestClient(context).fetchTeamDetails(teamId!)
105
106
  return res as TeamDetails
@@ -116,15 +117,15 @@ export class TeamsInfo {
116
117
  */
117
118
  static async sendMessageToTeamsChannel (context: TurnContext, activity: Activity, teamsChannelId: string, appId?: string): Promise<[ConversationReference, string]> {
118
119
  if (!context) {
119
- throw new Error('TurnContext cannot be null')
120
+ throw ExceptionHelper.generateException(Error, Errors.TurnContextCannotBeNull)
120
121
  }
121
122
 
122
123
  if (!activity) {
123
- throw new Error('Activity cannot be null')
124
+ throw ExceptionHelper.generateException(Error, Errors.ActivityCannotBeNull)
124
125
  }
125
126
 
126
127
  if (!teamsChannelId) {
127
- throw new Error('The teamsChannelId cannot be null or empty')
128
+ throw ExceptionHelper.generateException(Error, Errors.TeamsChannelIdRequired)
128
129
  }
129
130
  const convoParams = {
130
131
  isGroup: true,
@@ -179,7 +180,7 @@ export class TeamsInfo {
179
180
  teamId = teamsChannelData.team?.id
180
181
  }
181
182
  if (!teamId) {
182
- throw new Error('teamId is required.')
183
+ throw ExceptionHelper.generateException(Error, Errors.TeamIdRequired)
183
184
  }
184
185
  return await this.getRestClient(context).fetchChannelList(teamId!)
185
186
  }
@@ -237,7 +238,7 @@ export class TeamsInfo {
237
238
  teamId = teamsChannelData.team?.id
238
239
  }
239
240
  if (!teamId) {
240
- throw new Error('teamId is required.')
241
+ throw ExceptionHelper.generateException(Error, Errors.TeamIdRequired)
241
242
  }
242
243
  const pagedResults = await this.getRestClient(context).getConversationPagedMember(teamId, pageSize!, continuationToken!)
243
244
  do {
@@ -281,7 +282,7 @@ export class TeamsInfo {
281
282
  }
282
283
 
283
284
  if (!meetingId) {
284
- throw new Error('meetingId is required.')
285
+ throw ExceptionHelper.generateException(Error, Errors.MeetingIdRequired)
285
286
  }
286
287
 
287
288
  return await this.getRestClient(context).sendMeetingNotification(meetingId, notification)
@@ -298,13 +299,13 @@ export class TeamsInfo {
298
299
  */
299
300
  static async sendMessageToListOfUsers (context: TurnContext, activity: Activity, tenantId: string, members: TeamsMember[]): Promise<BatchOperationResponse> {
300
301
  if (!activity) {
301
- throw new Error('activity is required.')
302
+ throw ExceptionHelper.generateException(Error, Errors.ActivityRequired)
302
303
  }
303
304
  if (!tenantId) {
304
- throw new Error('tenantId is required.')
305
+ throw ExceptionHelper.generateException(Error, Errors.TenantIdRequired)
305
306
  }
306
307
  if (!members || members.length === 0) {
307
- throw new Error('members list is required.')
308
+ throw ExceptionHelper.generateException(Error, Errors.MembersListRequired)
308
309
  }
309
310
 
310
311
  return await this.getRestClient(context).sendMessageToListOfUsers(activity, tenantId, members)
@@ -320,10 +321,10 @@ export class TeamsInfo {
320
321
  */
321
322
  static async sendMessageToAllUsersInTenant (context: TurnContext, activity: Activity, tenantId: string): Promise<BatchOperationResponse> {
322
323
  if (!activity) {
323
- throw new Error('activity is required.')
324
+ throw ExceptionHelper.generateException(Error, Errors.ActivityRequired)
324
325
  }
325
326
  if (!tenantId) {
326
- throw new Error('tenantId is required.')
327
+ throw ExceptionHelper.generateException(Error, Errors.TenantIdRequired)
327
328
  }
328
329
 
329
330
  return await this.getRestClient(context).sendMessageToAllUsersInTenant(activity, tenantId)
@@ -340,13 +341,13 @@ export class TeamsInfo {
340
341
  */
341
342
  static async sendMessageToAllUsersInTeam (context: TurnContext, activity: Activity, tenantId: string, teamId: string): Promise<BatchOperationResponse> {
342
343
  if (!activity) {
343
- throw new Error('activity is required.')
344
+ throw ExceptionHelper.generateException(Error, Errors.ActivityRequired)
344
345
  }
345
346
  if (!tenantId) {
346
- throw new Error('tenantId is required.')
347
+ throw ExceptionHelper.generateException(Error, Errors.TenantIdRequired)
347
348
  }
348
349
  if (!teamId) {
349
- throw new Error('teamId is required.')
350
+ throw ExceptionHelper.generateException(Error, Errors.TeamIdRequired)
350
351
  }
351
352
  return await this.getRestClient(context).sendMessageToAllUsersInTeam(activity, tenantId, teamId)
352
353
  }
@@ -362,13 +363,13 @@ export class TeamsInfo {
362
363
  */
363
364
  static async sendMessageToListOfChannels (context: TurnContext, activity: Activity, tenantId: string, members: TeamsMember[]): Promise<BatchOperationResponse> {
364
365
  if (!activity) {
365
- throw new Error('activity is required.')
366
+ throw ExceptionHelper.generateException(Error, Errors.ActivityRequired)
366
367
  }
367
368
  if (!tenantId) {
368
- throw new Error('tenantId is required.')
369
+ throw ExceptionHelper.generateException(Error, Errors.TenantIdRequired)
369
370
  }
370
371
  if (!members || members.length === 0) {
371
- throw new Error('members list is required.')
372
+ throw ExceptionHelper.generateException(Error, Errors.MembersListRequired)
372
373
  }
373
374
  return this.getRestClient(context).sendMessageToListOfChannels(activity, tenantId, members)
374
375
  }
@@ -382,7 +383,7 @@ export class TeamsInfo {
382
383
  */
383
384
  static async getOperationState (context: TurnContext, operationId: string): Promise<BatchOperationStateResponse> {
384
385
  if (!operationId) {
385
- throw new Error('operationId is required.')
386
+ throw ExceptionHelper.generateException(Error, Errors.OperationIdRequired)
386
387
  }
387
388
 
388
389
  return await this.getRestClient(context).getOperationState(operationId)
@@ -397,7 +398,7 @@ export class TeamsInfo {
397
398
  */
398
399
  static async getFailedEntries (context: TurnContext, operationId: string): Promise<BatchFailedEntriesResponse> {
399
400
  if (!operationId) {
400
- throw new Error('operationId is required.')
401
+ throw ExceptionHelper.generateException(Error, Errors.OperationIdRequired)
401
402
  }
402
403
 
403
404
  return await this.getRestClient(context).getFailedEntries(operationId)
@@ -412,7 +413,7 @@ export class TeamsInfo {
412
413
  */
413
414
  static async cancelOperation (context: TurnContext, operationId: string): Promise<CancelOperationResponse> {
414
415
  if (!operationId) {
415
- throw new Error('operationId is required.')
416
+ throw ExceptionHelper.generateException(Error, Errors.OperationIdRequired)
416
417
  }
417
418
 
418
419
  return await this.getRestClient(context).cancelOperation(operationId)